第六次训练(Pandas)¶
本部分训练需要2次上机训练,且需要认真研读代码。
Pandas也是数据分析和处理流程中常用的库,Pandas这个名字来源于计量经济学中的panel data,Pandas提供了对于结构化数据的读取、处理、分析、创建的能力,举一个更加具体生动的例子,Pandas就像是excel软件一样,提供许多功能,帮助我们分析和查看结构化的数据(可以理解为表格),而与excel不同的是,Pandas中的功能更多的通过编程来实现,而excel则是通过在可视化界面当中操作。
在使用之前,需要确保我们已经正确安装了Pandas库,同样可以使用conda或者pip来安装
conda install pandas
# or
pip install pandas
在Anaconda中Pandas不需要专门安装,但目前Pandas刚刚发布3.0版本,如果需要使用,则最好使用虚拟环境并使用上述命令专门安装。
在使用之前,需要先导入Pandas库
import pandas as pd
同时为了方便查看输出,设置Jupyter输出cell中的所有单独一行的变量,而不是只显示最后一行的变量
from IPython.core.interactiveshell import InteractiveShell
InteractiveShell.ast_node_interactivity = "all" # 输出cell所有变量,而不是只输出最后的变量
1.1 Pandas数据结构¶
我们可以说一个结构化数据(表格)有多个列组成,每个列所代表的含义不同,各个列可能有不同的数据类型,这多个列通过某种关系联系起来就构成了表格,比如以下的数据就是一个结构化的数据,我们可以将其理解为一个表格,表格中有两列,分别表示物品的名称和价格
| name | price |
|---|---|
| Phone | 3000 |
| PC | 5000 |
| Laptop | 4500 |
而在Pandas中,每个列就可以认为是一个Series,而整个数据叫做DataFrame,每个DataFrame由多个Series组成。
1.1.1 序列Series¶
Series 是 DataFrame 的组成部分,只能存储一列数据,而非像 DataFrame 可以存储二维数组形式的数据。Series 并且往往由一列索引和一列值组成。参考上面的数据,可以针对name列创建一个Series
name_column = pd.Series(
data = ["Phone","PC","Laptop"], # Series中的数据
name = "name" # Series的名字,可以理解为列名
)
name_column
0 Phone 1 PC 2 Laptop Name: name, dtype: object
同样针对price列也可以创建一个Series
price_column = pd.Series(
data = [3000,5000,4500],
name = 'price'
)
price_column
0 3000 1 5000 2 4500 Name: price, dtype: int64
Series支持许多操作,同时也包含许多方法,这里对一些常用的操作和方法做简单的介绍
print("按照索引取值")
name_column[0] # 按照索引取值
print("按照索引切片")
name_column[:2] # 按照索引切片
print("按照特定条件取值")
price_column[price_column < 4500] # 取出price Series中小于4500的数据
按照索引取值
'Phone'
按照索引切片
0 Phone 1 PC Name: name, dtype: object
按照特定条件取值
0 3000 Name: price, dtype: int64
Series具有一种广播机制,能够自动的将计算广播到Series的所有数据上
temp = price_column * 2 # 将所有的价格翻倍
temp
0 6000 1 10000 2 9000 Name: price, dtype: int64
temp1 = price_column + 100 # 为所有的价格增加100
temp1
0 3100 1 5100 2 4600 Name: price, dtype: int64
1.1.2 数据框Dataframe¶
已经创建好了Series后,我们可以将多个Series组合成一个DataFrame
data = pd.DataFrame(
{
"name":name_column,
"price":price_column
}
)
data
| name | price | |
|---|---|---|
| 0 | Phone | 3000 |
| 1 | PC | 5000 |
| 2 | Laptop | 4500 |
也可以从列表(list)来创建DataFrame,并自己指定索引和列名
list_data = [[1,2,3],[4,5,6]] # 用列表作为 DataFrame 的数据内容
data = pd.DataFrame(list_data, index = ['A', 'B'], columns=["a", "b", "c"])
data
| a | b | c | |
|---|---|---|---|
| A | 1 | 2 | 3 |
| B | 4 | 5 | 6 |
同时也可以从字典(dict)来创建DataFrame
data = pd.DataFrame(
{
"name":["Phone","PC","Laptop"],
"price":[3000,5000,4500]
}
)
data
| name | price | |
|---|---|---|
| 0 | Phone | 3000 |
| 1 | PC | 5000 |
| 2 | Laptop | 4500 |
print(type(data))
print(type(data['name']))
<class 'pandas.core.frame.DataFrame'> <class 'pandas.core.series.Series'>
这里创建的data就是一个DataFrame,而它的每一列都是一个Series
data['name'] # 输出 name 列
0 Phone 1 PC 2 Laptop Name: name, dtype: object
对Series的操作更多是从某一列的角度去操作,但更多的时候,我们需要对结构化数据整体做一些处理,比如选取特定的数据等,这些操作就是对DataFrame的操作,同样的,DataFrame中也有很多选取和操作数据的方法
data.head(2) # head函数用于输出数据的前n行
data.tail(2) # tail函数用于输出数据的最后n行
| name | price | |
|---|---|---|
| 0 | Phone | 3000 |
| 1 | PC | 5000 |
| name | price | |
|---|---|---|
| 1 | PC | 5000 |
| 2 | Laptop | 4500 |
同时可以方便的对DataFrame中的所有列进行查看和更改
print(data.columns) # 输出data中的所有列
temp = data.rename(columns={"name":"item_name"}) # 将name列重命名为item_name
temp
Index(['name', 'price'], dtype='object')
| item_name | price | |
|---|---|---|
| 0 | Phone | 3000 |
| 1 | PC | 5000 |
| 2 | Laptop | 4500 |
data.info() # 输出当前数据的类型、数据量以及所占用内存大小
<class 'pandas.core.frame.DataFrame'> RangeIndex: 3 entries, 0 to 2 Data columns (total 2 columns): # Column Non-Null Count Dtype --- ------ -------------- ----- 0 name 3 non-null object 1 price 3 non-null int64 dtypes: int64(1), object(1) memory usage: 180.0+ bytes
data.describe() # 输出data中数值列的统计描述,包括总数、均值、标准差以及最大最小值以及各个分位数
| price | |
|---|---|
| count | 3.000000 |
| mean | 4166.666667 |
| std | 1040.833000 |
| min | 3000.000000 |
| 25% | 3750.000000 |
| 50% | 4500.000000 |
| 75% | 4750.000000 |
| max | 5000.000000 |
data.index # 输出data的索引
RangeIndex(start=0, stop=3, step=1)
data.values # 输出data的数值矩阵
array([['Phone', 3000],
['PC', 5000],
['Laptop', 4500]], dtype=object)
同样在计算时,DataFrame也是具有广播机制的,能够将相应的操作应用到DataFrame中所有的数据上
import numpy as np
data = pd.DataFrame(
data = {
"column_one": np.random.rand(5),
'column_two':np.random.rand(5)
}
)
data # 原始数据
| column_one | column_two | |
|---|---|---|
| 0 | 0.055179 | 0.903334 |
| 1 | 0.585436 | 0.348394 |
| 2 | 0.899700 | 0.359561 |
| 3 | 0.939682 | 0.448885 |
| 4 | 0.228338 | 0.030038 |
data * 2 # 将所有的数据都乘2
| column_one | column_two | |
|---|---|---|
| 0 | 0.110358 | 1.806668 |
| 1 | 1.170872 | 0.696789 |
| 2 | 1.799400 | 0.719121 |
| 3 | 1.879364 | 0.897770 |
| 4 | 0.456676 | 0.060076 |
data + 100 # 对所有的数据加100
| column_one | column_two | |
|---|---|---|
| 0 | 100.055179 | 100.903334 |
| 1 | 100.585436 | 100.348394 |
| 2 | 100.899700 | 100.359561 |
| 3 | 100.939682 | 100.448885 |
| 4 | 100.228338 | 100.030038 |
1.1.3 文件读写¶
Pandas提供了读写数据文件的能力,我们可以方便的将处理好的数据写入到文件中保存,或者从文件中读取数据
data.to_csv("data/csv_data.csv",index=False) # 将data数据写出到csv_data.csv文件中
使用命令查看csv_data.csv中的内容
%pycat data\csv_data.csv
column_one,column_two 0.05517890062525255,0.9033339210880966 0.5854362060453604,0.34839432486814714 0.89970017095815,0.3595605964154288 0.9396820579673807,0.44888475900152336 0.22833782366645727,0.030037940374700756
test = pd.read_csv("data/csv_data.csv") # 再将csv_data.csv读入
test
| column_one | column_two | |
|---|---|---|
| 0 | 0.055179 | 0.903334 |
| 1 | 0.585436 | 0.348394 |
| 2 | 0.899700 | 0.359561 |
| 3 | 0.939682 | 0.448885 |
| 4 | 0.228338 | 0.030038 |
除了可以用csv格式保存数据之外,也可以将数据保存为excel对应的xls格式,可以更加方便的使用excel来查看和完成后续的步骤。想要导出xls格式,需要安装openpyxl依赖库。可以使用以下命令安装
conda install openpyxl
# or
pip install openpyxl
需要注意:如果导出的Excel为最新的.xlsx 文件,则不需要openpyxl支持。
test.to_excel("data/xls_data.xlsx",index=False)
pd.read_excel("data/xls_data.xlsx") # 对应可以使用read_excel来读取xls文件
| column_one | column_two | |
|---|---|---|
| 0 | 0.055179 | 0.903334 |
| 1 | 0.585436 | 0.348394 |
| 2 | 0.899700 | 0.359561 |
| 3 | 0.939682 | 0.448885 |
| 4 | 0.228338 | 0.030038 |
如果只是想将数据保存到磁盘上,并不对其进行后续的处理,那么可以将其以二进制的形式进行保存,二进制的存储方式可以更快的读取和保存。
test.to_pickle("data/pth_data.pth") # 以pickle的二进制形式进行保存
pd.read_pickle("data/pth_data.pth") # 对应read_pickle来读入二进制数据
| column_one | column_two | |
|---|---|---|
| 0 | 0.055179 | 0.903334 |
| 1 | 0.585436 | 0.348394 |
| 2 | 0.899700 | 0.359561 |
| 3 | 0.939682 | 0.448885 |
| 4 | 0.228338 | 0.030038 |
需要注意:.pth 不是pickle的唯一扩展名。你可以使用.pickle或者pkl作为pickle格式的扩展名。
test.to_pickle("data/pkl_data.pkl") #以pkl为后缀
pd.read_pickle("data/pkl_data.pkl") # 对应read_pickle来读入pkl格式数据
| column_one | column_two | |
|---|---|---|
| 0 | 0.055179 | 0.903334 |
| 1 | 0.585436 | 0.348394 |
| 2 | 0.899700 | 0.359561 |
| 3 | 0.939682 | 0.448885 |
| 4 | 0.228338 | 0.030038 |
1.2 数据框内部的操作¶
1.2.1 新增和删除数据¶
data = pd.DataFrame(
{
"name":["Phone","PC","Laptop"],
"price":[3000,5000,4500]
}
)
data
| name | price | |
|---|---|---|
| 0 | Phone | 3000 |
| 1 | PC | 5000 |
| 2 | Laptop | 4500 |
other_data = pd.DataFrame(
data = {
"name":["TV"],
"price":[3000]
}
)
other_data
| name | price | |
|---|---|---|
| 0 | TV | 3000 |
# 在Pandas1.版本之前,有一个append方法新增变量,但是该方法在Pandas1.0之后取消了,教材中的内容会出现运行异常
new_data = data.append(other_data) # 新增一行
new_data
# 此时将会出现异常
--------------------------------------------------------------------------- AttributeError Traceback (most recent call last) Cell In[70], line 2 1 # 在Pandas1.版本之前,有一个append方法新增变量,但是该方法在Pandas1.0之后取消了,教材中的内容会出现运行异常 ----> 2 new_data = data.append(other_data) # 新增一行 3 new_data File d:\anaconda3\Lib\site-packages\pandas\core\generic.py:6321, in NDFrame.__getattr__(self, name) 6314 if ( 6315 name not in self._internal_names_set 6316 and name not in self._metadata 6317 and name not in self._accessors 6318 and self._info_axis._can_hold_identifiers_and_holds_name(name) 6319 ): 6320 return self[name] -> 6321 return object.__getattribute__(self, name) AttributeError: 'DataFrame' object has no attribute 'append'
#目前常规的操作方法为:
new_data = pd.concat([data, other_data])
new_data
| name | price | |
|---|---|---|
| 0 | Phone | 3000 |
| 1 | PC | 5000 |
| 2 | Laptop | 4500 |
| 0 | TV | 3000 |
new_data.drop_duplicates("price") #删除重复值,默认保留第一个
| name | price | |
|---|---|---|
| 0 | Phone | 3000 |
| 1 | PC | 5000 |
| 2 | Laptop | 4500 |
new_data.drop_duplicates("price",keep='last') # 根据price列删除重复值,保留最后一个重复的值
| name | price | |
|---|---|---|
| 1 | PC | 5000 |
| 2 | Laptop | 4500 |
| 0 | TV | 3000 |
同时也可以新增一列
data.insert(2,"comment",["Good","Bad","Good"])
data
| name | price | comment | |
|---|---|---|---|
| 0 | Phone | 3000 | Good |
| 1 | PC | 5000 | Bad |
| 2 | Laptop | 4500 | Good |
新增一列也可以通过直接对不存在的列赋值的方式
data['selected'] = [True, False, True] # 对原本不存在的 selected 列赋值
data
| name | price | comment | selected | |
|---|---|---|---|---|
| 0 | Phone | 3000 | Good | True |
| 1 | PC | 5000 | Bad | False |
| 2 | Laptop | 4500 | Good | True |
data.drop(columns="price") # 删除price列
| name | comment | selected | |
|---|---|---|---|
| 0 | Phone | Good | True |
| 1 | PC | Bad | False |
| 2 | Laptop | Good | True |
不同列之间可以进行加减运算
data["value"] = [2000, 6000, 1000] # 添加一个 value 列
print(data, end="\n\n")
name price comment selected value 0 Phone 3000 Good True 2000 1 PC 5000 Bad False 6000 2 Laptop 4500 Good True 1000
print(data['price'] - data['value']) # 用 price 列减去 value 列
0 1000 1 -1000 2 3500 dtype: int64
1.2.2 数据索引与切片¶
DataFrame支持多种数据选择的方式
data = pd.DataFrame(
{
"name":["Phone","PC","Laptop"],
"price":[3000,5000,4500]
}
)
data
| name | price | |
|---|---|---|
| 0 | Phone | 3000 |
| 1 | PC | 5000 |
| 2 | Laptop | 4500 |
data['name'] # 选取一列数据
0 Phone 1 PC 2 Laptop Name: name, dtype: object
data[0:1] # 选取第 0 行
| name | price | |
|---|---|---|
| 0 | Phone | 3000 |
data[:2] # 选取前两行
| name | price | |
|---|---|---|
| 0 | Phone | 3000 |
| 1 | PC | 5000 |
data[data['price'] > 4500] # 按照条件选取
| name | price | |
|---|---|---|
| 1 | PC | 5000 |
data.query("price > 4500") # 也可以使用query
| name | price | |
|---|---|---|
| 1 | PC | 5000 |
data[data['price'].isin([4500,5000])] # 按照条件选取,使用isin
| name | price | |
|---|---|---|
| 1 | PC | 5000 |
| 2 | Laptop | 4500 |
data[ (data['price'] > 2500) & (data['name'] != "PC") ] # 多个条件组合
| name | price | |
|---|---|---|
| 0 | Phone | 3000 |
| 2 | Laptop | 4500 |
iloc就是按照index location的意思,按照索引选择数据
data.iloc[0] # 选取第0行
name Phone price 3000 Name: 0, dtype: object
data.iloc[0,0] # 选取第0行的第0列
'Phone'
data.iloc[:,0] # 选取所有行的第0列
0 Phone 1 PC 2 Laptop Name: name, dtype: object
loc就是按照label来选取数据,
data.loc[0,"name"] # 第0行name列
'Phone'
data.loc[:,"price"] # 所有行的price列
0 3000 1 5000 2 4500 Name: price, dtype: int64
可以通过选择多行和多列,从 DataFrame 中选取一块区域的数据。行通过索引选取,列通过列名选取
data.loc[0:1][["name", "price"]] # 前两行的 name 和 price 列
| name | price | |
|---|---|---|
| 0 | Phone | 3000 |
| 1 | PC | 5000 |
可以自己设置 DataFrame 以某一列数据为索引
print(data.index, end="\n\n")
new_data = data.set_index('name') # 设置 DataFrame 以 name 列为索引,代替原来的数字索引
print(new_data.index)
RangeIndex(start=0, stop=3, step=1) Index(['Phone', 'PC', 'Laptop'], dtype='object', name='name')
print(new_data)
price name Phone 3000 PC 5000 Laptop 4500
1.2.3 数据的排序¶
Pandas中同样包含对数据的排序功能
data = pd.DataFrame(
{
"公司":["A","B","C","D"],
"开盘价":[2000,4500,1200,8900],
"收盘价":[2100,4300,980,8950]
}
)
data
| 公司 | 开盘价 | 收盘价 | |
|---|---|---|---|
| 0 | A | 2000 | 2100 |
| 1 | B | 4500 | 4300 |
| 2 | C | 1200 | 980 |
| 3 | D | 8900 | 8950 |
data.sort_values(by="开盘价") # 按照开盘价列排序,默认是升序排序
| 公司 | 开盘价 | 收盘价 | |
|---|---|---|---|
| 2 | C | 1200 | 980 |
| 0 | A | 2000 | 2100 |
| 1 | B | 4500 | 4300 |
| 3 | D | 8900 | 8950 |
data.sort_values(by="收盘价",ascending=False) # 按照收盘价列降序排序
| 公司 | 开盘价 | 收盘价 | |
|---|---|---|---|
| 3 | D | 8900 | 8950 |
| 1 | B | 4500 | 4300 |
| 0 | A | 2000 | 2100 |
| 2 | C | 1200 | 980 |
data.sort_index() # 按照每一行的索引升序进行排序
| 公司 | 开盘价 | 收盘价 | |
|---|---|---|---|
| 0 | A | 2000 | 2100 |
| 1 | B | 4500 | 4300 |
| 2 | C | 1200 | 980 |
| 3 | D | 8900 | 8950 |
1.2.4 统计分析¶
在Pandas中提供了大量的函数可以帮助我们做一些统计分析的工作,比如求最大值、最小值以及求和等
data = pd.DataFrame(
data = {
"价格":[500,200,3000,4000,100],
"月销量":[401,628,205,99,768]
}
)
data.min() # 求各个列的最小值
价格 100 月销量 99 dtype: int64
data.median() # 求各个列的中位数
价格 500.0 月销量 401.0 dtype: float64
data.mean() # 求各个列的平均值
价格 1560.0 月销量 420.2 dtype: float64
data.max() # 求各个列的最大值
价格 4000 月销量 768 dtype: int64
data.std() # 求各个列的标准差
价格 1811.905075 月销量 280.176195 dtype: float64
data.sum() # 求各个列的和
价格 7800 月销量 2101 dtype: int64
data.cumsum() # 累积求和
| 价格 | 月销量 | |
|---|---|---|
| 0 | 500 | 401 |
| 1 | 700 | 1029 |
| 2 | 3700 | 1234 |
| 3 | 7700 | 1333 |
| 4 | 7800 | 2101 |
data.corr() # 计算各个列的相关系数
| 价格 | 月销量 | |
|---|---|---|
| 价格 | 1.000000 | -0.917783 |
| 月销量 | -0.917783 | 1.000000 |
Pandas 中还可以对数据进行统计,统计相同属性出现的个数
data = pd.DataFrame(
data = {
"价格":[500,200,100,100,200, 100],
"名称":['A','B','C','D','E', 'F']
}
)
data.value_counts() # 统计具有相同值的行
价格 名称
100 C 1
D 1
F 1
200 B 1
E 1
500 A 1
Name: count, dtype: int64
data["价格"].value_counts() # 统计价格列的相同值
价格 100 3 200 2 500 1 Name: count, dtype: int64
也可以划分区间,将每个元素归类到一个区间中,方便进行统计
ages = np.array([1,5,10,40,36,12,58,62,77,89,100,18,20,25,30,32])
new_ages = pd.cut(ages, 5) # 将数据划分为等距的 5 个区间
new_ages
[(0.901, 20.8], (0.901, 20.8], (0.901, 20.8], (20.8, 40.6], (20.8, 40.6], ..., (0.901, 20.8], (0.901, 20.8], (20.8, 40.6], (20.8, 40.6], (20.8, 40.6]] Length: 16 Categories (5, interval[float64, right]): [(0.901, 20.8] < (20.8, 40.6] < (40.6, 60.4] < (60.4, 80.2] < (80.2, 100.0]]
stages = pd.cut(ages, 5, labels=['幼儿','少年','青年','中年','老年']) # 划分数据之后打上标签
stages
['幼儿', '幼儿', '幼儿', '少年', '少年', ..., '幼儿', '幼儿', '少年', '少年', '少年'] Length: 16 Categories (5, object): ['幼儿' < '少年' < '青年' < '中年' < '老年']
1.2.5 数据透视表¶
在Pandas中也提供了类似excel中透视表的功能,可以使用pd.pivot_table方法。使用透视表相关的函数可以方便的对数据的行或者列进行聚合统计。
data = pd.DataFrame(
data={
"company":["A","A","B","C","C","B"],
"salary":[8000,5000,4500,3200,7800,9200],
"experience":[5,3,3,1,3,5]
}
)
data
| company | salary | experience | |
|---|---|---|---|
| 0 | A | 8000 | 5 |
| 1 | A | 5000 | 3 |
| 2 | B | 4500 | 3 |
| 3 | C | 3200 | 1 |
| 4 | C | 7800 | 3 |
| 5 | B | 9200 | 5 |
pivot_table函数中有三个比较关键的参数,分别是values,columns以及index,下面结合案例来看三个参数的实际意义
假如想要统计每个公司的平均薪水,
data.pivot_table(index="company",values="salary")
| salary | |
|---|---|
| company | |
| A | 6500.0 |
| B | 6850.0 |
| C | 5500.0 |
pivot_table函数中的index参数表示按照哪一列来统计,而values参数用来指定统计的实际指标值是哪一列,像上面的例子,index为company表示按照company列进行统计,values为salary代表实际统计的列是salry列,而计算的方式是通过aggfunc参数来指定,但aggfunc的默认值是mean,当想要按照其他方式计算时,可以通过修改aggfunc的值来实现,比如统计每个公司的薪水总和。
data.pivot_table(index="company",values="salary",aggfunc="sum")
| salary | |
|---|---|
| company | |
| A | 13000 |
| B | 13700 |
| C | 11000 |
假如想在统计各个公司的平均薪水时,按照工作年限来进行分类
data.pivot_table(index=["company","experience"],values="salary")
| salary | ||
|---|---|---|
| company | experience | |
| A | 3 | 5000.0 |
| 5 | 8000.0 | |
| B | 3 | 4500.0 |
| 5 | 9200.0 | |
| C | 1 | 3200.0 |
| 3 | 7800.0 |
上面代码的结果产生了多重索引,取值时会比较麻烦,假如想要计算完成之后方便的去除每个公司工作三年的平均薪水,可以使用columns参数
res = data.pivot_table(index="company",columns="experience",values="salary")
res
| experience | 1 | 3 | 5 |
|---|---|---|---|
| company | |||
| A | NaN | 5000.0 | 8000.0 |
| B | NaN | 4500.0 | 9200.0 |
| C | 3200.0 | 7800.0 | NaN |
res[3] # 取出所有公司工作三年的平均薪水
company A 5000.0 B 4500.0 C 7800.0 Name: 3, dtype: float64
可以看到columns参数可以指定以某一列填充结果中各个列的标题,同时可以发现上面的结果中有一些位置的值为NaN,比如A公司的工作1年的数据位置,这是由于A公司中没有工作1年的薪水数据,如果想要对无法计算出值来的数据进行默认值填充,可以使用pivot_table的fill_value参数
data.pivot_table(index="company",columns="experience",values="salary",fill_value=0) # 将空缺的数据全部填为0
| experience | 1 | 3 | 5 |
|---|---|---|---|
| company | |||
| A | 0.0 | 5000.0 | 8000.0 |
| B | 0.0 | 4500.0 | 9200.0 |
| C | 3200.0 | 7800.0 | 0.0 |
1.2.6 数据分组和聚合¶
Pandas中同样有对数据进行分组统计的功能。
假如我们有以下数据,我们想要统计每个公司的平均薪水是多少,这时就需要使用到Pandas分组统计的功能。
| company | salary | experience |
|---|---|---|
| A | 8000 | 5 |
| A | 5000 | 3 |
| B | 4500 | 3 |
| C | 3200 | 1 |
| C | 7800 | 3 |
| B | 9200 | 5 |
首先定义数据
data = pd.DataFrame(
data={
"company":["A","A","B","C","C","B"],
"salary":[8000,5000,4500,3200,7800,9200],
"experience":[5,3,3,1,3,5]
}
)
data
| company | salary | experience | |
|---|---|---|---|
| 0 | A | 8000 | 5 |
| 1 | A | 5000 | 3 |
| 2 | B | 4500 | 3 |
| 3 | C | 3200 | 1 |
| 4 | C | 7800 | 3 |
| 5 | B | 9200 | 5 |
按照公司进行分组,然后统计平均薪水
data.groupby(by="company")['salary'].mean()
company A 6500.0 B 6850.0 C 5500.0 Name: salary, dtype: float64
同理我们可以统计已有数据中工作年限的最大值
data.groupby(by="company")['experience'].max()
company A 5 B 5 C 3 Name: experience, dtype: int64
同时可以根据多个字段进行分组并统计需要的信息,比如统计各个公司不同工作年限的薪水均值
data.groupby(by=["company","experience"])['salary'].mean()
company experience
A 3 5000.0
5 8000.0
B 3 4500.0
5 9200.0
C 1 3200.0
3 7800.0
Name: salary, dtype: float64
在Pandas中可以方便的对分组结果进行查看
grouped = data.groupby(by="company") # 获得分组结果对象
grouped.get_group("A") # 输出分组结果中公司A对应的组
| company | salary | experience | |
|---|---|---|---|
| 0 | A | 8000 | 5 |
| 1 | A | 5000 | 3 |
也可以对所有的分组结果进行遍历输出,在遍历时,第一个参数是分组的名字,第二个字段是分组对应的数据
for group_name,group_data in grouped:
print(group_name)
print(group_data)
print()
A company salary experience 0 A 8000 5 1 A 5000 3 B company salary experience 2 B 4500 3 5 B 9200 5 C company salary experience 3 C 3200 1 4 C 7800 3
在有些时候,需要对分组结果一次性做多个统计操作,这时就可以使用agg函数来实现,比如可以同时对各个公司的薪水求最小值、最大值、平均值和总值
data.groupby(by="company")['salary'].agg(['min','max','mean','sum'])
| min | max | mean | sum | |
|---|---|---|---|---|
| company | ||||
| A | 5000 | 8000 | 6500.0 | 13000 |
| B | 4500 | 9200 | 6850.0 | 13700 |
| C | 3200 | 7800 | 5500.0 | 11000 |
同时可以使用agg函数对多个列做不同的操作,比如我们想要按照公司分组,然后统计薪水的最大值以及平均值,并统计工作年限的最大值和最小值
data.groupby(by="company").agg(
{
"salary":["max","mean"],
"experience":["max","min"]
}
)
| salary | experience | |||
|---|---|---|---|---|
| max | mean | max | min | |
| company | ||||
| A | 8000 | 6500.0 | 5 | 3 |
| B | 9200 | 6850.0 | 5 | 3 |
| C | 7800 | 5500.0 | 3 | 1 |
1.2.7 数据转换¶
针对DataFrame和Series都有对应的函数,可以按照我们定义的逻辑来对数据进行转换,在针对Series时,可以通过map函数来实现,而针对DataFrame时可以使用apply函数来实现。
data = pd.DataFrame(
data = {
"company":["A","B","C","D"], # 公司名
"score":[90,50,70,30] # 公司信用评分
}
)
data
| company | score | |
|---|---|---|
| 0 | A | 90 |
| 1 | B | 50 |
| 2 | C | 70 |
| 3 | D | 30 |
假设目前想要对score列进行转换,假设score大于80则为A,大于60小于80则为B,小于60则为C,那么可以对score列使用map函数
# 首先定义转换的逻辑
def get_level(x):
if x > 80:
return "A"
elif 60 < x < 80:
return "B"
elif x < 60:
return "C"
data['score'].map(get_level)
0 A 1 C 2 B 3 C Name: score, dtype: object
map只能应对单列数据的转换,而如果想要在DataFrame维度来对数据做一些转换的操作,就需要使用apply函数
假设目前想要结合name和score列来生成类似company:B, score:50, level:C这样的描述,那么可以使用apply来实现这个逻辑
def process_func(line): # 每次传入的参数是DataFrame的一行
level = get_level(line['score']) # 首先获得score对应的level
line['desc'] = "company{}, score:{}, level:{}".format(line['company'],line['score'],level)
return line
data.apply(process_func,axis=1) # axis=1表明按照横向遍历
| company | score | desc | |
|---|---|---|---|
| 0 | A | 90 | companyA, score:90, level:A |
| 1 | B | 50 | companyB, score:50, level:C |
| 2 | C | 70 | companyC, score:70, level:B |
| 3 | D | 30 | companyD, score:30, level:C |
1.2.8 数据的特殊值处理¶
在数据处理过程中,通常会遇到一些特殊格式的数据,比如数据中尝尝会存在缺失值,或者数据中包含大量的字符串数据,而Pandas同样提供了针对缺失值以及字符串数据的处理功能,能够帮助我们快速的处理和操作这些数据
数据缺失值处理¶
在使用Pandas来读取和处理数据时,常常会在DataFrame中看到NaN字样,这便说明我们要处理的数据中包含了部分缺失值,NaN即Not a Number的缩写,同样Pandas也提供了针对缺失值的处理方法,可以方便的查看数据缺失的统计情况并对缺失值做填充。
首先构造一个有缺失值的DataFrame
import numpy as np
np.random.seed(1)
data = pd.DataFrame(
data = {
"column_one":pd.Series(np.random.random(4),index=['a','b','c','d']),
"column_two":pd.Series(np.random.random(3),index=['a','b','c']),
"column_three":pd.Series(np.random.random(3),index=['a','b','d']),
}
)
data
| column_one | column_two | column_three | |
|---|---|---|---|
| a | 0.417022 | 0.146756 | 0.345561 |
| b | 0.720324 | 0.092339 | 0.396767 |
| c | 0.000114 | 0.186260 | NaN |
| d | 0.302333 | NaN | 0.538817 |
可以通过isna或者isnull函数来查看当前DataFrame或者Series中有哪些数据为NaN,其中isnill函数是isna的别名,两者是等价的
data.isna()
data.isnull() # 等价于isna函数
| column_one | column_two | column_three | |
|---|---|---|---|
| a | False | False | False |
| b | False | False | False |
| c | False | False | True |
| d | False | True | False |
| column_one | column_two | column_three | |
|---|---|---|---|
| a | False | False | False |
| b | False | False | False |
| c | False | False | True |
| d | False | True | False |
isna和isnull同样适用于Series,可以使用这些方法来过滤或者筛选数据,比如选取column_two数据为NaN的数据
data[data['column_two'].isna()]
| column_one | column_two | column_three | |
|---|---|---|---|
| d | 0.302333 | NaN | 0.538817 |
如果要统计每列中缺失值的数量,可以使用如下代码
data.isna().sum()
column_one 0 column_two 1 column_three 1 dtype: int64
在数据处理过程中,如果数据存在缺失值会对处理过程造成影响,因此在数据处理步骤中,一般都需要对缺失值进行处理,而处理方法也有两种:
- 如果存在缺失值的数据较少,可以直接删除掉对应的数据
- 如果存在缺失值的数据较多,可以选择对缺失的数据进行填补
在Pandas中提供了删除存在缺失值数据的方法
data.dropna() # 删除掉所有存在缺失值的行
| column_one | column_two | column_three | |
|---|---|---|---|
| a | 0.417022 | 0.146756 | 0.345561 |
| b | 0.720324 | 0.092339 | 0.396767 |
如果想要对缺失的数据进行填补,同样可以使用Pandas提供的fillna方法
data.fillna(value=0) # 使用fillna将所有缺失值填充成0
| column_one | column_two | column_three | |
|---|---|---|---|
| a | 0.417022 | 0.146756 | 0.345561 |
| b | 0.720324 | 0.092339 | 0.396767 |
| c | 0.000114 | 0.186260 | 0.000000 |
| d | 0.302333 | 0.000000 | 0.538817 |
data.fillna(value={"column_two":data['column_two'].mean(),"column_three":50}) # 在填充时,可以指定对不同列填充不同值
| column_one | column_two | column_three | |
|---|---|---|---|
| a | 0.417022 | 0.146756 | 0.345561 |
| b | 0.720324 | 0.092339 | 0.396767 |
| c | 0.000114 | 0.186260 | 50.000000 |
| d | 0.302333 | 0.141785 | 0.538817 |
字符串数据处理¶
Pandas对字符串的处理提供了很好的支持,针对包含字符串的列,可以通过str函数访问字符串对象,接着使用处理字符串的方法处理数据。
data = pd.Series(
data = ["Ab","a","BB"],
name = "info",
)
data
0 Ab 1 a 2 BB Name: info, dtype: object
可以方便的统计字符串的长度
print(data.str.lower()) # 通过str.lower将所有的字母变为小写
0 ab 1 a 2 bb Name: info, dtype: object
print(data.str.upper()) # 转换为大写
0 AB 1 A 2 BB Name: info, dtype: object
切分字符串
data = pd.Series(
data = ["a_b_c","1_2_3"],
)
print(data)
0 a_b_c 1 1_2_3 dtype: object
print(data.str.split("_"))
0 [a, b, c] 1 [1, 2, 3] dtype: object
data.str.split("_").str.get(1) # 取切分后的第二个元素
0 b 1 2 dtype: object
data.str.split("_",expand=True) # 将其扩展成多列
| 0 | 1 | 2 | |
|---|---|---|---|
| 0 | a | b | c |
| 1 | 1 | 2 | 3 |
判断字符串中是否包含相应的表达式
data = pd.Series(
data = ["a","b","aasd","1sad","23ds"]
)
data
0 a 1 b 2 aasd 3 1sad 4 23ds dtype: object
data.str.contains("1") # 判断是否包含1
data[data.str.contains("1")] # 可用作筛选
0 False 1 False 2 False 3 True 4 False dtype: bool
3 1sad dtype: object
data.str.contains("d") # 可以使用正则表达式,查看字符串中是否包含某些内容如字母“d”
data[data.str.contains("d")] #将含有该字符的数据找出来
0 False 1 False 2 True 3 True 4 True dtype: bool
2 aasd 3 1sad 4 23ds dtype: object
同样可以使用replace来对字符串进行替换操作,同样支持正则表达式
data.str.replace("d","###",regex=True) # 使用正则匹配将所有的选择对象相关部分替换成###
0 a 1 b 2 aas### 3 1sa### 4 23###s dtype: object
Pandas的str对象同样提供了通过正则来提取字符串中特定模式的方法extract
data = pd.Series(
data = ["10 years","2 years","about 7 years","1 month","unknown"]
)
data
0 10 years 1 2 years 2 about 7 years 3 1 month 4 unknown dtype: object
data.str.extract("([0-9]+)") # 提取出所有的数字
| 0 | |
|---|---|
| 0 | 10 |
| 1 | 2 |
| 2 | 7 |
| 3 | 1 |
| 4 | NaN |
1.2.9 可视化数据¶
另外,在Pandas中可以使用plot来实现数据的可视化功能,比如以下代码,这种方式在分析数据时,可以简单的达到可视化数据的要求,而无需增加额外的代码,较为方便。这里使用一个样例数据来展示相关的用法,假设下面为某段时间内公司A和B的股票数据
data = pd.DataFrame(
data={
"A":np.random.randint(0,100,10),
"B":np.random.randint(50,150,10),
},
index = pd.date_range("2020-01-01","2020-01-10",freq="D"),
)
data
| A | B | |
|---|---|---|
| 2020-01-01 | 18 | 144 |
| 2020-01-02 | 84 | 146 |
| 2020-01-03 | 11 | 136 |
| 2020-01-04 | 28 | 63 |
| 2020-01-05 | 29 | 59 |
| 2020-01-06 | 14 | 57 |
| 2020-01-07 | 50 | 113 |
| 2020-01-08 | 68 | 111 |
| 2020-01-09 | 87 | 72 |
| 2020-01-10 | 87 | 107 |
data.plot(); # 直接调用plot方法,默认是折线图
data.plot(y="A",kind="line"); # 只展示A公司的数据,并且通过kind参数指定可视化图的类型为折线图
1.3 数据框之间的操作¶
在数据处理过程中,经常会按照某种方式来融合多份数据到一个DataFrame中,从而进行后续的处理,而Pandas也为这种场景提供了丰富的功能,主要包含以下三个函数:
- concat:将多个
DataFrame横向或者纵向连接,更多的用于当列标签或者行标签相同时的简单合并 - merge:可以按照列或者索引对多个
DataFrame进行融合,类似于SQL中的JOIN - join:
DataFrame对象的方法,常用于按照索引进行合并的情况
1.3.1 concat函数¶
df1 = pd.DataFrame(
data = {
"name":["Jan","Bob"],
"company" : ["A","B"]
}
)
df2 = pd.DataFrame(
data = {
"name":["Alice","James"],
"company" : ["B","C"]
}
)
df3 = pd.DataFrame(
data = {
"salary":[1000,3000],
"experience" : [1,3]
}
)
df1
df2
df3
| name | company | |
|---|---|---|
| 0 | Jan | A |
| 1 | Bob | B |
| name | company | |
|---|---|---|
| 0 | Alice | B |
| 1 | James | C |
| salary | experience | |
|---|---|---|
| 0 | 1000 | 1 |
| 1 | 3000 | 3 |
在使用concat连接DataFrame时,可以通过axis参数设置方向,axis=0代表纵向,axis=1代表水平
pd.concat(objs=[df1,df2],axis=0) # 纵向连接df1和df2
pd.concat(objs=[df1,df3],axis=1) # 横向连接df1和df3
| name | company | |
|---|---|---|
| 0 | Jan | A |
| 1 | Bob | B |
| 0 | Alice | B |
| 1 | James | C |
| name | company | salary | experience | |
|---|---|---|---|---|
| 0 | Jan | A | 1000 | 1 |
| 1 | Bob | B | 3000 | 3 |
concat只能对DataFrame做简单的连接,如果想要按照某列或者某个索引的值来对多个DataFrame做合并,就需要使用merge函数
df1 = pd.DataFrame(
{
"name" : ["Alice","Bob","James","John"],
"company" : ["A","A","B","C"]
}
)
df2 = pd.DataFrame(
{
"name" : ["Alice","Bob","James","Amy"],
"salary" : [3000,2000,1000,5000]
}
)
df1
df2
| name | company | |
|---|---|---|
| 0 | Alice | A |
| 1 | Bob | A |
| 2 | James | B |
| 3 | John | C |
| name | salary | |
|---|---|---|
| 0 | Alice | 3000 |
| 1 | Bob | 2000 |
| 2 | James | 1000 |
| 3 | Amy | 5000 |
1.3.2 merge函数¶
通过merge可以直接合并两个DataFrame,使用以下代码即可,通过on参数指定按照指定列来合并数据
pd.merge(left=df1,right=df2,on="name")
| name | company | salary | |
|---|---|---|---|
| 0 | Alice | A | 3000 |
| 1 | Bob | A | 2000 |
| 2 | James | B | 1000 |
假设两个DataFrame中列名不一致,但是数据是对应的,可以使用下面的代码来合并两个DataFrame
df2.rename(columns={"name":"employee"},inplace=True) # 将df2的name列改为employee
df2
pd.merge(left=df1,right=df2,left_on="name",right_on="employee")
| employee | salary | |
|---|---|---|
| 0 | Alice | 3000 |
| 1 | Bob | 2000 |
| 2 | James | 1000 |
| 3 | Amy | 5000 |
| name | company | employee | salary | |
|---|---|---|---|---|
| 0 | Alice | A | Alice | 3000 |
| 1 | Bob | A | Bob | 2000 |
| 2 | James | B | James | 1000 |
同时在合并时,可以通过merge函数的how参数来指定合并的策略,默认的策略是inner,总共有以下四种策略:
| 合并策略 | 说明 |
|---|---|
| inner | 保留左右数据的交集 |
| outer | 保留左右数据的并集 |
| left | 只保留左边存在的键 |
| right | 只保留右边存在的键 |
pd.merge(left=df1,right=df2,left_on="name",right_on="employee",how="outer") # outer合并方式
pd.merge(left=df1,right=df2,left_on="name",right_on="employee",how="left") # left合并方式
pd.merge(left=df1,right=df2,left_on="name",right_on="employee",how="right") # right合并方式
| name | company | employee | salary | |
|---|---|---|---|---|
| 0 | Alice | A | Alice | 3000.0 |
| 1 | NaN | NaN | Amy | 5000.0 |
| 2 | Bob | A | Bob | 2000.0 |
| 3 | James | B | James | 1000.0 |
| 4 | John | C | NaN | NaN |
| name | company | employee | salary | |
|---|---|---|---|---|
| 0 | Alice | A | Alice | 3000.0 |
| 1 | Bob | A | Bob | 2000.0 |
| 2 | James | B | James | 1000.0 |
| 3 | John | C | NaN | NaN |
| name | company | employee | salary | |
|---|---|---|---|---|
| 0 | Alice | A | Alice | 3000 |
| 1 | Bob | A | Bob | 2000 |
| 2 | James | B | James | 1000 |
| 3 | NaN | NaN | Amy | 5000 |
merge同样支持按照索引来进行合并,只需要指定left_index或者right_index参数即可
df3 = pd.DataFrame(
data = {
"company" : ["A","A","B","C"]
},
index = ["Alice","Bob","James","John"],
)
df4 = pd.DataFrame(
data = {
"salary" : [3000,2000,1000,5000]
},
index = ["Alice","Bob","James","Amy"],
)
df3
df4
pd.merge(left=df3,right=df4,left_index=True,right_index=True) # 两边都按照索引来进行合并
| company | |
|---|---|
| Alice | A |
| Bob | A |
| James | B |
| John | C |
| salary | |
|---|---|
| Alice | 3000 |
| Bob | 2000 |
| James | 1000 |
| Amy | 5000 |
| company | salary | |
|---|---|---|
| Alice | A | 3000 |
| Bob | A | 2000 |
| James | B | 1000 |
1.3.3 join函数¶
在使用索引进行合并时,更加简便的方式是使用join函数,join函数默认按照两边的索引进行合并
df1 = pd.DataFrame(
data = {
"company" : ["A","A","B","C"]
},
index = ["Alice","Bob","James","John"],
)
df2 = pd.DataFrame(
data = {
"salary" : [3000,2000,1000,5000]
},
index = ["Alice","Bob","James","Amy"],
)
df1.join(df2) # 使用索引合并
df3 = pd.DataFrame(
{
"name" : ["Alice","Bob","James","John"],
"company" : ["A","A","B","C"]
}
)
df3.join(df2,on="name") # join同样支持按列进行合并,可以通过on参数指定列
| company | salary | |
|---|---|---|
| Alice | A | 3000.0 |
| Bob | A | 2000.0 |
| James | B | 1000.0 |
| John | C | NaN |
| name | company | salary | |
|---|---|---|---|
| 0 | Alice | A | 3000.0 |
| 1 | Bob | A | 2000.0 |
| 2 | James | B | 1000.0 |
| 3 | John | C | NaN |
1.4 时间序列处理¶
1.4.1 时间序列的基本概念和常用函数¶
使用Pandas也可以很方便的处理时间序列相关的数据,时序数据指的是反映了某一事物、现象等随时间的变化状态或程度。
假如有以下数据描述了不同日期某商品价格的变化。
| time | prices |
|---|---|
| 2021-01-01 | 1000 |
| 2021-01-02 | 2000 |
| 2021-01-03 | 1500 |
| 2021-01-04 | 2500 |
| 2021-01-05 | 500 |
| 2021-01-06 | 1500 |
| 2021-01-07 | 2000 |
| 2021-01-08 | 1900 |
同样可以在Pandas中表示以上数据
ts_data = pd.DataFrame(
{
"time":["2021-01-01","2021-01-02","2021-01-03","2021-01-04","2021-01-05","2021-01-06","2021-01-07","2021-01-08",],
"prices":[1000,2000,1500,2500,500,1500,2000,1900]
}
)
ts_data['time'] = pd.to_datetime(ts_data['time']) # 将time列转为datetime类型方便操作
ts_data
| time | prices | |
|---|---|---|
| 0 | 2021-01-01 | 1000 |
| 1 | 2021-01-02 | 2000 |
| 2 | 2021-01-03 | 1500 |
| 3 | 2021-01-04 | 2500 |
| 4 | 2021-01-05 | 500 |
| 5 | 2021-01-06 | 1500 |
| 6 | 2021-01-07 | 2000 |
| 7 | 2021-01-08 | 1900 |
原本time列的类型为字符串,通过to_datatime转换后为datetime类型,我们可以方便对其进行进行一些操作,比如取出每个日期年月日中的日。
ts_data['time'].dt.day
0 1 1 2 2 3 3 4 4 5 5 6 6 7 7 8 Name: time, dtype: int32
除了使用to_datetime方法外,还可以使用date_range来生成一段时间范围,在使用date_range来生成日期区间的时候,可以自定义freq,也即生成范围的频率,常用的频率符号及其含义可以参考下表,其余符号可以参考官方文档:https://pandas.pydata.org/pandas-docs/stable/user_guide/timeseries.html#timeseries-offset-aliases
| 符号 | 描述 |
|---|---|
| D | Calendar day frequency |
| H | Hour |
| T or min | Minute |
| S | Second |
| M | MonthEnd |
| MS | Monthbegin |
pd.date_range(start="2021-01-01",end="2021-01-08",freq="D")
DatetimeIndex(['2021-01-01', '2021-01-02', '2021-01-03', '2021-01-04',
'2021-01-05', '2021-01-06', '2021-01-07', '2021-01-08'],
dtype='datetime64[ns]', freq='D')
ts_data = pd.DataFrame(
{
"time":pd.date_range(start="2021-01-27",end="2021-02-03",freq="D"),
"prices":[1000,2000,1500,2500,500,1500,2000,1900]
}
)
同样可以使用datetime类型的一些方法
ts_data['time'].dt.date # 输出日期
0 2021-01-27 1 2021-01-28 2 2021-01-29 3 2021-01-30 4 2021-01-31 5 2021-02-01 6 2021-02-02 7 2021-02-03 Name: time, dtype: object
ts_data['time'].dt.day_name()
0 Wednesday 1 Thursday 2 Friday 3 Saturday 4 Sunday 5 Monday 6 Tuesday 7 Wednesday Name: time, dtype: object
将time列作为索引,可以实现一些特定的索引操作
ts_data = ts_data.set_index("time")
ts_data.loc['2021-01'] # 取出1月份数据
| prices | |
|---|---|
| time | |
| 2021-01-27 | 1000 |
| 2021-01-28 | 2000 |
| 2021-01-29 | 1500 |
| 2021-01-30 | 2500 |
| 2021-01-31 | 500 |
ts_data.loc['2021-02'] # 取出2月份数据
| prices | |
|---|---|
| time | |
| 2021-02-01 | 1500 |
| 2021-02-02 | 2000 |
| 2021-02-03 | 1900 |
ts_data.loc[ts_data.index.dayofweek.isin([5,6])] # 只取出每个星期中周末的数据
| prices | |
|---|---|
| time | |
| 2021-01-30 | 2500 |
| 2021-01-31 | 500 |
ts_data.loc["2021-01-27":"2021-01-30"] # 取出2021-01-27到2021-01-30之间的数据
| prices | |
|---|---|
| time | |
| 2021-01-27 | 1000 |
| 2021-01-28 | 2000 |
| 2021-01-29 | 1500 |
| 2021-01-30 | 2500 |
在Pandas提供了shift函数,可以用来方便的前移或者后移数据,且在移动的时候保持索引不变
ts_data.shift(1) # 将所有的数据向后移动一位
| prices | |
|---|---|
| time | |
| 2021-01-27 | NaN |
| 2021-01-28 | 1000.0 |
| 2021-01-29 | 2000.0 |
| 2021-01-30 | 1500.0 |
| 2021-01-31 | 2500.0 |
| 2021-02-01 | 500.0 |
| 2021-02-02 | 1500.0 |
| 2021-02-03 | 2000.0 |
ts_data.shift(-2) # 将所有的数据向前移动两位
| prices | |
|---|---|
| time | |
| 2021-01-27 | 1500.0 |
| 2021-01-28 | 2500.0 |
| 2021-01-29 | 500.0 |
| 2021-01-30 | 1500.0 |
| 2021-01-31 | 2000.0 |
| 2021-02-01 | 1900.0 |
| 2021-02-02 | NaN |
| 2021-02-03 | NaN |
1.4.2 重采样操作¶
同样Pandas也提供了针对时序数据的分组统计功能
resample_data = ts_data.resample("ME") # 按月分组
resample_data.mean() # 计算每月的均值,索引为月末
| prices | |
|---|---|
| time | |
| 2021-01-31 | 1500.0 |
| 2021-02-28 | 1800.0 |
1.4.3 滑动窗口操作¶
使用Pandas提供的rolling函数可以实现对于时序数据滑动窗口相关的统计。
date_index = pd.date_range("2021-01-01","2021-12-31",freq="D") # 生成2021年一整年的数据
data = np.random.randint(1,10,len(date_index)) # 随机生成相同长度的数据
ts_data = pd.Series(data,index=date_index)
r_data = ts_data.rolling("30D") # 以30天作为窗口大小,进行滑动
r_data.mean() # 统计每个窗口的均值
2021-01-01 2.000000
2021-01-02 1.500000
2021-01-03 1.666667
2021-01-04 3.500000
2021-01-05 4.600000
...
2021-12-27 4.166667
2021-12-28 4.200000
2021-12-29 4.133333
2021-12-30 4.333333
2021-12-31 4.166667
Freq: D, Length: 365, dtype: float64
r_data.max() # 统计每个窗口的最大值
2021-01-01 2.0
2021-01-02 2.0
2021-01-03 2.0
2021-01-04 9.0
2021-01-05 9.0
...
2021-12-27 9.0
2021-12-28 9.0
2021-12-29 9.0
2021-12-30 9.0
2021-12-31 9.0
Freq: D, Length: 365, dtype: float64
r_data.std() # 统计每个窗口的标准差
2021-01-01 NaN
2021-01-02 0.707107
2021-01-03 0.577350
2021-01-04 3.696846
2021-01-05 4.037326
...
2021-12-27 2.506314
2021-12-28 2.483046
2021-12-29 2.374191
2021-12-30 2.353769
2021-12-31 2.408080
Freq: D, Length: 365, dtype: float64