Python Pandas基础使用
- Python Pandas基础使用
- 数据类型
- Series
- DataFrame
- Panel
- 创建
- Series
- DataFrame
- 基本使用
- 描述性统计
- 重新索引
- 迭代
- 排序
- 处理文本数据
- 选项和定制
- 索引和选择数据
- 统计函数
- 窗口函数
- 聚合函数
- 缺失数据
- GroupBy
- 合并/连接
- 日期功能
- Timedelta
Python Pandas基础使用
记录一下Python Pandas使用,将网上的文章汇总于此篇文章,用于后续查缺补漏(文章内容主要来源于:极客教程)。
Pandas 可以从各种文件格式比如 CSV、JSON、SQL、Microsoft Excel 导入数据。
Pandas 可以对各种数据进行运算操作,比如归并、再成形、选择,还有数据清洗和数据加工特征。
Pandas 广泛应用在学术、金融、统计学等各个数据分析领域。
数据类型
Pandas 的主要数据结构是 Series (一维数据)与 DataFrame(二维数据)。
- Series 是一种类似于一维数组的对象,它由一组数据(各种 Numpy 数据类型)以及一组与之相关的数据标签(即索引)组成。
- DataFrame 是一个表格型的数据结构,它含有一组有序的列,每列可以是不同的值类型(数值、字符串、布尔型值)。DataFrame 既有行索引也有列索引,它可以被看做由 Series 组成的字典(共同用一个索引)。
维度与描述:
最好的理解这些数据结构的方式是将高维数据结构看作是低维数据结构的容器。例如,DataFrame是Series的容器,Panel是DataFrame的容器。
数据结构 | 维度 | 描述 |
---|---|---|
Series | 1 | 一维带标签的同质数组,大小不可变。 |
Data Frames | 2 | 通用的二维带标签,大小可变的表格结构,其中列的类型可能不同。 |
Panel | 3 | 通用的三维带标签,大小可变的数组。 |
构建和处理二维或多维数组是一项繁琐的任务,用户在编写函数时需要考虑数据集的方向,但使用Pandas数据结构可以减少用户的心力。
例如,对于表格数据(DataFrame),更有语义意义的是考虑行(索引)和列,而不是轴0和轴1。
可变性:
所有Pandas数据结构都是可变的(可以更改),除了Series之外,所有其他结构的大小都是可变的。Series的大小是不可变的。
注意 - DataFrame被广泛使用,是最重要的数据结构之一。Panel的使用较少。
Series
Series是一个一维数组-like的结构,具有同质数据。例如,以下系列是一组整数10、23、56,…
10 | 23 | 56 | 17 | 52 | 61 | 73 | 90 | 26 | 72 |
---|---|---|---|---|---|---|---|---|---|
关键要点:
- 同质数据
- 尺寸不可变
- 数据值可变
DataFrame
DataFrame是一个二维数组,包含异质数据。例如,
Name | Age | Gender | Rating |
---|---|---|---|
Steve | 32 | Male | 3.45 |
Lia | 28 | Female | 4.6 |
Vin | 45 | Male | 3.9 |
Katie | 38 | Female | 2.78 |
该表格代表了一个组织中销售团队的数据以及他们的整体绩效评级。数据以行和列的方式呈现,每列代表一个属性,每行代表一个人。
列的数据类型
四列的数据类型如下:
Column | Type |
---|---|
Name | String |
Age | Integer |
Gender | String |
Rating | Float |
关键点:
- 异构数据
- 大小可变
- 数据可变
Panel
Panel是一个具有异构数据的三维数据结构。用图形表示面板是困难的。但可以将面板看作是DataFrame的容器。
关键点:
- 异构数据
- 大小可变
- 数据可变
创建
Series
可以使用以下构造函数创建pandas Series-
pandas.Series( data, index, dtype, copy)
构造函数的参数如下:
序号 | 参数和描述 |
---|---|
1 | data 数据可以是ndarray、列表、常量等形式 |
2 | index 索引值必须唯一且可哈希,长度与数据相同。如果没有传递索引,则默认为 np.arrange(n) |
3 | dtype 数据类型。如果为None,则会推断数据类型 |
4 | copy 复制数据。默认为False |
一系列可以使用各种输入来创建,如−
- 数组
- 字典
- 标量值或常数
常见的几种创建方法:
- 创建一个空系列
- 从ndarray创建Series
- 从字典创建一个系列
- 从标量创建 Series
import pandas as pd
import numpy as np# 1. 创建一个空系列
s = pd.Series()
print s
# 输出如下
Series([], dtype: float64)# 2. 从ndarray创建Series
data = np.array(['a','b','c','d'])
s = pd.Series(data)
print s0 a
1 b
2 c
3 d
dtype: objectdata = np.array(['a','b','c','d'])
s = pd.Series(data,index=[100,101,102,103])
print s100 a
101 b
102 c
103 d
dtype: object# 3. 从字典创建一个系列
data = {'a' : 0., 'b' : 1., 'c' : 2.}
s = pd.Series(data)
print sa 0.0
b 1.0
c 2.0
dtype: float64data = {'a' : 0., 'b' : 1., 'c' : 2.}
s = pd.Series(data,index=['b','c','d','a'])
print sb 1.0
c 2.0
d NaN
a 0.0
dtype: float64# 4. 从标量创建 Series
s = pd.Series(5, index=[0, 1, 2, 3])
print s0 5
1 5
2 5
3 5
dtype: int64
常见的几种使用方法:
- 从Series中按位置访问数据
- 使用标签(索引)检索数据
import pandas as pd
import numpy as np# 1. 从Series中按位置访问数据
s = pd.Series([1,2,3,4,5],index = ['a','b','c','d','e'])
#retrieve the first element
print s[0]1#retrieve the first three element
print s[:3]a 1
b 2
c 3
dtype: int64#retrieve the last three element
print s[-3:]c 3
d 4
e 5
dtype: int64# 2. 使用标签(索引)检索数据
s = pd.Series([1,2,3,4,5],index = ['a','b','c','d','e'])
#retrieve a single element
print s['a']1#retrieve multiple elements
print s[['a','c','d']]a 1
c 3
d 4
dtype: int64#retrieve multiple elements
print s['f']…
KeyError: 'f'
DataFrame
可以使用以下构造器创建pandas DataFrame:
pandas.DataFrame( data, index, columns, dtype, copy)
构造函数的参数如下所示:
序号 | 参数和描述 |
---|---|
1 | 数据(data) 数据可以是各种形式,如ndarray、series、map、lists、dict、constants和另一个DataFrame。 |
2 | 索引(index) 用于行标签的索引,如果没有传递索引,则默认为np.arange(n)。 |
3 | 列标签(columns) 用于列标签的可选默认语法是np.arange(n),仅当没有传递索引时才成立。 |
4 | 数据类型(dtype) 每列的数据类型。 |
5 | 复制(copy) 如果默认值为False,则用于复制数据的命令(或其他命令)。 |
常见的几种创建方法:
- 创建一个空的DataFrame
- 从列表创建DataFrame
- 从字典的ndarrays /列表创建DataFrame
- 从字典列表创建DataFrame
- 从Series字典创建DataFrame
import pandas as pd# 1. 创建一个空的DataFrame
df = pd.DataFrame()
print dfEmpty DataFrame
Columns: []
Index: []# 2. 从列表创建DataFrame
data = [1,2,3,4,5]
df = pd.DataFrame(data)
print df0
0 1
1 2
2 3
3 4
4 5data = [['Alex',10],['Bob',12],['Clarke',13]]
df = pd.DataFrame(data,columns=['Name','Age'])
print dfName Age
0 Alex 10
1 Bob 12
2 Clarke 13data = [['Alex',10],['Bob',12],['Clarke',13]]
df = pd.DataFrame(data,columns=['Name','Age'],dtype=float)
print dfName Age
0 Alex 10.0
1 Bob 12.0
2 Clarke 13.0# 3. 从字典的ndarrays /列表创建DataFrame
data = {'Name':['Tom', 'Jack', 'Steve', 'Ricky'],'Age':[28,34,29,42]}
df = pd.DataFrame(data)
print dfAge Name
0 28 Tom
1 34 Jack
2 29 Steve
3 42 Rickydata = {'Name':['Tom', 'Jack', 'Steve', 'Ricky'],'Age':[28,34,29,42]}
df = pd.DataFrame(data, index=['rank1','rank2','rank3','rank4'])
print dfAge Name
rank1 28 Tom
rank2 34 Jack
rank3 29 Steve
rank4 42 Ricky# 4. 从字典列表创建DataFrame
data = [{'a': 1, 'b': 2},{'a': 5, 'b': 10, 'c': 20}]
df = pd.DataFrame(data)
print dfa b c
0 1 2 NaN
1 5 10 20.0data = [{'a': 1, 'b': 2},{'a': 5, 'b': 10, 'c': 20}]
df = pd.DataFrame(data, index=['first', 'second'])
print dfa b c
first 1 2 NaN
second 5 10 20.0data = [{'a': 1, 'b': 2},{'a': 5, 'b': 10, 'c': 20}]
#With two column indices, values same as dictionary keys
df1 = pd.DataFrame(data, index=['first', 'second'], columns=['a', 'b'])
#With two column indices with one index with other name
df2 = pd.DataFrame(data, index=['first', 'second'], columns=['a', 'b1'])
print df1
print df2#df1 outputa b
first 1 2
second 5 10
#df2 outputa b1
first 1 NaN
second 5 NaN# 5. 从Series字典创建DataFrame
d = {'one' : pd.Series([1, 2, 3], index=['a', 'b', 'c']),'two' : pd.Series([1, 2, 3, 4], index=['a', 'b', 'c', 'd'])}
df = pd.DataFrame(d)
print dfone two
a 1.0 1
b 2.0 2
c 3.0 3
d NaN 4
常见的几种使用方法:
- 列选择
- 列相加
- 列删除
- 按标签选择
- 根据整数位置进行选择
- 选择行
- 添加行
- 删除行
import pandas as pd# 1. 列选择
d = {'one' : pd.Series([1, 2, 3], index=['a', 'b', 'c']),'two' : pd.Series([1, 2, 3, 4], index=['a', 'b', 'c', 'd'])}
df = pd.DataFrame(d)
print df ['one']a 1.0
b 2.0
c 3.0
d NaN
Name: one, dtype: float64# 2. 列相加
d = {'one' : pd.Series([1, 2, 3], index=['a', 'b', 'c']),'two' : pd.Series([1, 2, 3, 4], index=['a', 'b', 'c', 'd'])}
df = pd.DataFrame(d)
# Adding a new column to an existing DataFrame object with column label by passing new series
print ("Adding a new column by passing as Series:")
df['three']=pd.Series([10,20,30],index=['a','b','c'])
print dfprint ("Adding a new column using the existing columns in DataFrame:")
df['four']=df['one']+df['three']
print dfAdding a new column by passing as Series:one two three
a 1.0 1 10.0
b 2.0 2 20.0
c 3.0 3 30.0
d NaN 4 NaNAdding a new column using the existing columns in DataFrame:one two three four
a 1.0 1 10.0 11.0
b 2.0 2 20.0 22.0
c 3.0 3 30.0 33.0
d NaN 4 NaN NaN# 3. 列删除
d = {'one' : pd.Series([1, 2, 3], index=['a', 'b', 'c']), 'two' : pd.Series([1, 2, 3, 4], index=['a', 'b', 'c', 'd']), 'three' : pd.Series([10,20,30], index=['a','b','c'])}
df = pd.DataFrame(d)
print ("Our dataframe is:")
print df# using del function
print ("Deleting the first column using DEL function:")
del df['one']
print df# using pop function
print ("Deleting another column using POP function:")
df.pop('two')
print dfOur dataframe is:one three two
a 1.0 10.0 1
b 2.0 20.0 2
c 3.0 30.0 3
d NaN NaN 4Deleting the first column using DEL function:three two
a 10.0 1
b 20.0 2
c 30.0 3
d NaN 4Deleting another column using POP function:three
a 10.0
b 20.0
c 30.0
d NaN# 4. 按标签选择
d = {'one' : pd.Series([1, 2, 3], index=['a', 'b', 'c']), 'two' : pd.Series([1, 2, 3, 4], index=['a', 'b', 'c', 'd'])}
df = pd.DataFrame(d)
print df.loc['b']one 2.0
two 2.0
Name: b, dtype: float64# 5. 根据整数位置进行选择
d = {'one' : pd.Series([1, 2, 3], index=['a', 'b', 'c']),'two' : pd.Series([1, 2, 3, 4], index=['a', 'b', 'c', 'd'])}
df = pd.DataFrame(d)
print df.iloc[2]one 3.0
two 3.0
Name: c, dtype: float64# 6. 选择行
d = {'one' : pd.Series([1, 2, 3], index=['a', 'b', 'c']), 'two' : pd.Series([1, 2, 3, 4], index=['a', 'b', 'c', 'd'])}
df = pd.DataFrame(d)
print df[2:4]one two
c 3.0 3
d NaN 4# 7. 添加行
df = pd.DataFrame([[1, 2], [3, 4]], columns = ['a','b'])
df2 = pd.DataFrame([[5, 6], [7, 8]], columns = ['a','b'])
df = df.append(df2)
print dfa b
0 1 2
1 3 4
0 5 6
1 7 8# 8. 删除行
df = pd.DataFrame([[1, 2], [3, 4]], columns = ['a','b'])
df2 = pd.DataFrame([[5, 6], [7, 8]], columns = ['a','b'])
df = df.append(df2)
# Drop rows with label 0
df = df.drop(0)
print dfa b
1 3 4
1 7 8
CSV读/写:
import pandas as pd# 读取 csv文件
df = pd.read_csv('nba.csv')# 保存 dataframe
df.to_csv('site.csv')
JSON读/写:
nested_list.json 文件内容:
{"school_name": "ABC primary school","class": "Year 1","students": [{"id": "A001","name": "Tom","math": 60,"physics": 66,"chemistry": 61},{"id": "A002","name": "James","math": 89,"physics": 76,"chemistry": 51},{"id": "A003","name": "Jenny","math": 79,"physics": 90,"chemistry": 78}]
}
import pandas as pd
import json# JSON读取,方式一:
df = pd.read_json('sites.json')# 方式二:
# 使用 Python JSON 模块载入数据
with open('nested_list.json','r') as f:data = json.loads(f.read())# 展平数据(复杂的JSON结构可以使用.json_normalize方法)
df_nested_list = pd.json_normalize(data, record_path =['students'])
print(df_nested_list)
基本使用
Series
的基本功能:
Sr.No. | 属性或方法 & 说明 |
---|---|
1 | axes 返回行轴标签的列表 |
2 | dtype 返回对象的dtype。 |
3 | empty 返回True,如果_series——是空的。 |
4 | ndim 返回基础数据的维数的数量,按定义1。 |
5 | size 返回基础数据中元素的数量。 |
6 | values 返回级数作为ndarray。 |
7 | head() 返回前n行。 |
8 | tail() 返回最后n行。 |
DataFrame
的基本功能:
编号 | 属性或方法 & 描述 |
---|---|
1 | T 将行和列进行转置。 |
2 | axes 返回一个列表,其中包含行轴标签和列轴标签作为唯一成员。 |
3 | dtypes 返回此对象中的数据类型。 |
4 | empty 如果NDFrame完全为空[没有任何项目],则为True;如果任何轴的长度为0,则为True。 |
5 | ndim 轴/数组维度的数量。 |
6 | shape 返回一个表示DataFrame维度的元组。 |
7 | size NDFrame中元素的数量。 |
8 | values NDFrame的Numpy表示。 |
9 | head() 返回前n行。 |
10 | tail() 返回最后n行。 |
描述性统计
让我们现在了解Python Pandas中描述性统计的函数。以下表格列出了重要的函数:
序号 | 函数 | 描述 |
---|---|---|
1 | count() | 非空值的观察数量 |
2 | sum() | 值的总和 |
3 | mean() | 值的平均值 |
4 | median() | 值的中位数 |
5 | mode() | 值的众数 |
6 | std() | 值的标准差 |
7 | min() | 最小值 |
8 | max() | 最大值 |
9 | abs() | 绝对值 |
10 | prod() | 值的乘积 |
11 | cumsum() | 累计求和 |
12 | cumprod() | 累计乘积 |
注意 - 由于DataFrame是一种异构数据结构,通用操作不适用于所有函数。
- 像**sum(),cumsum()**这样的函数,对于数字和字符(或字符串)数据元素都可以正常工作而不会出错。虽然通常情况下很少使用字符聚合,但这些函数不会引发任何异常。
- 像 abs(),cumprod() 这样的函数,在DataFrame包含字符或字符串数据时会抛出异常,因为不能执行此类操作
汇总数据:
describe() 函数计算有关DataFrame列的统计摘要。
import pandas as pd
import numpy as np#Create a Dictionary of series
d = {'Name':pd.Series(['Tom','James','Ricky','Vin','Steve','Smith','Jack','Lee','David','Gasper','Betina','Andres']),'Age':pd.Series([25,26,25,23,30,29,23,34,40,30,51,46]),'Rating':pd.Series([4.23,3.24,3.98,2.56,3.20,4.6,3.8,3.78,2.98,4.80,4.10,3.65])
}#Create a DataFrame
df = pd.DataFrame(d)
print df.describe()
它的 输出 如下−
Age Rating
count 12.000000 12.000000
mean 31.833333 3.743333
std 9.232682 0.661628
min 23.000000 2.560000
25% 25.000000 3.230000
50% 29.500000 3.790000
75% 35.500000 4.132500
max 51.000000 4.800000
该函数提供了 平均值、标准差 和 IQR值 。函数排除字符列,并给出数值列的摘要信息。 ‘include’ 是用于传递需要考虑进行摘要的列的必要信息的参数。接受值的列表,默认为’number’。
- object - 总结字符串列
- number - 总结数值列
- all - 总结所有列在一起(不应将其作为列表值传递)
现在,在程序中使用以下语句并检查输出 –
import pandas as pd
import numpy as np#Create a Dictionary of series
d = {'Name':pd.Series(['Tom','James','Ricky','Vin','Steve','Smith','Jack','Lee','David','Gasper','Betina','Andres']),'Age':pd.Series([25,26,25,23,30,29,23,34,40,30,51,46]),'Rating':pd.Series([4.23,3.24,3.98,2.56,3.20,4.6,3.8,3.78,2.98,4.80,4.10,3.65])
}#Create a DataFrame
df = pd.DataFrame(d)
print df.describe(include=['object'])
它的 输出 如下−
Name
count 12
unique 12
top Ricky
freq 1
现在,使用以下语句并检查输出−
import pandas as pd
import numpy as np#Create a Dictionary of series
d = {'Name':pd.Series(['Tom','James','Ricky','Vin','Steve','Smith','Jack','Lee','David','Gasper','Betina','Andres']),'Age':pd.Series([25,26,25,23,30,29,23,34,40,30,51,46]),'Rating':pd.Series([4.23,3.24,3.98,2.56,3.20,4.6,3.8,3.78,2.98,4.80,4.10,3.65])
}#Create a DataFrame
df = pd.DataFrame(d)
print df. describe(include='all')
它的输出如下:
Age Name Rating
count 12.000000 12 12.000000
unique NaN 12 NaN
top NaN Ricky NaN
freq NaN 1 NaN
mean 31.833333 NaN 3.743333
std 9.232682 NaN 0.661628
min 23.000000 NaN 2.560000
25% 25.000000 NaN 3.230000
50% 29.500000 NaN 3.790000
75% 35.500000 NaN 4.132500
max 51.000000 NaN 4.800000
重新索引
重新索引 可以更改DataFrame的行标签和列标签。重新索引意味着将数据调整为与给定的一组标签相匹配。
索引可以通过多种操作来实现,例如:
- 重新排序现有数据以匹配新的标签集。
- 在标签位置上插入缺失值(NA),以对应没有该标签的数据。
示例:
import pandas as pd
import numpy as npN=20df = pd.DataFrame({'A': pd.date_range(start='2016-01-01',periods=N,freq='D'),'x': np.linspace(0,stop=N-1,num=N),'y': np.random.rand(N),'C': np.random.choice(['Low','Medium','High'],N).tolist(),'D': np.random.normal(100, 10, size=(N)).tolist()
})#reindex the DataFrame
df_reindexed = df.reindex(index=[0,2,5], columns=['A', 'C', 'B'])print df_reindexed
它的 输出 如下:
A C B
0 2016-01-01 Low NaN
2 2016-01-03 High NaN
5 2016-01-06 Low NaN
- 重新索引以与其他对象对齐
您可能希望将一个对象的轴重新索引,使其标签与另一个对象相同。请考虑以下示例以更好地理解。
示例:
import pandas as pd
import numpy as npdf1 = pd.DataFrame(np.random.randn(10,3),columns=['col1','col2','col3'])
df2 = pd.DataFrame(np.random.randn(7,3),columns=['col1','col2','col3'])df1 = df1.reindex_like(df2)
print df1
它的输出如下所示:
col1 col2 col3
0 -2.467652 -1.211687 -0.391761
1 -0.287396 0.522350 0.562512
2 -0.255409 -0.483250 1.866258
3 -1.150467 -0.646493 -0.222462
4 0.152768 -2.056643 1.877233
5 -1.155997 1.528719 -1.343719
6 -1.015606 -1.245936 -0.295275
注意 - 这里,DataFrame df1 被修改并像 df2 一样重新索引。列名应匹配,否则会为整个列标签添加 NAN。
- 在重新索引时填充
reindex() 可以接受一个可选参数 method,它是一种填充方法,取值如下:
- pad/ffill - 前向填充值
- bfill/backfill - 后向填充值
- nearest - 从最近的索引值填充
示例:
import pandas as pd
import numpy as npdf1 = pd.DataFrame(np.random.randn(6,3),columns=['col1','col2','col3'])
df2 = pd.DataFrame(np.random.randn(2,3),columns=['col1','col2','col3'])# Padding NAN's
print df2.reindex_like(df1)# Now Fill the NAN's with preceding Values
print ("Data Frame with Forward Fill:")
print df2.reindex_like(df1,method='ffill')
它的 输出 如下所示−
col1 col2 col3
0 1.311620 -0.707176 0.599863
1 -0.423455 -0.700265 1.133371
2 NaN NaN NaN
3 NaN NaN NaN
4 NaN NaN NaN
5 NaN NaN NaNData Frame with Forward Fill:col1 col2 col3
0 1.311620 -0.707176 0.599863
1 -0.423455 -0.700265 1.133371
2 -0.423455 -0.700265 1.133371
3 -0.423455 -0.700265 1.133371
4 -0.423455 -0.700265 1.133371
5 -0.423455 -0.700265 1.133371
注意 - 最后四行是填充的。
- 重新索引时填充的限制
limit参数在重新索引时提供了更多的填充控制。limit指定连续匹配的最大计数。让我们通过以下示例来理解:
示例:
import pandas as pd
import numpy as npdf1 = pd.DataFrame(np.random.randn(6,3),columns=['col1','col2','col3'])
df2 = pd.DataFrame(np.random.randn(2,3),columns=['col1','col2','col3'])# Padding NAN's
print df2.reindex_like(df1)# Now Fill the NAN's with preceding Values
print ("Data Frame with Forward Fill limiting to 1:")
print df2.reindex_like(df1,method='ffill',limit=1)
它的 输出 如下:
col1 col2 col3
0 0.247784 2.128727 0.702576
1 -0.055713 -0.021732 -0.174577
2 NaN NaN NaN
3 NaN NaN NaN
4 NaN NaN NaN
5 NaN NaN NaNData Frame with Forward Fill limiting to 1:col1 col2 col3
0 0.247784 2.128727 0.702576
1 -0.055713 -0.021732 -0.174577
2 -0.055713 -0.021732 -0.174577
3 NaN NaN NaN
4 NaN NaN NaN
5 NaN NaN NaN
注意 −注意,只有第7行由前面的第6行填充。然后,行保持不变。
- 重命名
rename()方法允许你基于某个映射(字典或Series)或任意函数来重新标记一个轴。
让我们考虑以下示例来了解这个。
import pandas as pd
import numpy as npdf1 = pd.DataFrame(np.random.randn(6,3),columns=['col1','col2','col3'])
print df1print ("After renaming the rows and columns:")
print df1.rename(columns={'col1' : 'c1', 'col2' : 'c2'},
index = {0 : 'apple', 1 : 'banana', 2 : 'durian'})
它的 输出 如下所示:
col1 col2 col3
0 0.486791 0.105759 1.540122
1 -0.990237 1.007885 -0.217896
2 -0.483855 -1.645027 -1.194113
3 -0.122316 0.566277 -0.366028
4 -0.231524 -0.721172 -0.112007
5 0.438810 0.000225 0.435479After renaming the rows and columns:c1 c2 col3
apple 0.486791 0.105759 1.540122
banana -0.990237 1.007885 -0.217896
durian -0.483855 -1.645027 -1.194113
3 -0.122316 0.566277 -0.366028
4 -0.231524 -0.721172 -0.112007
5 0.438810 0.000225 0.435479
rename()方法提供了一个名为 inplace 的参数,默认为False,会复制底层数据。将 inplace=True 传递给该方法,可以原地重命名数据。
迭代
要遍历DataFrame的行,我们可以使用以下函数:
- iteritems() - 遍历(键,值)对
- iterrows() - 以(索引,系列)对的形式遍历行
- itertuples() - 以namedtuples的形式遍历行
Example(迭代DataFrame
每一行):
concated_df = pd.DataFrame({'station_id_c':[1,2], 'win_s_max':[1.2, 2.3]# 此处省略其它...})# 转换为pojofor i, r in concated_df.iterrows():station_id_c = r['station_id_c']observe_time = r['observe_time']win_s_max = r['win_s_max']win_s_inst_max = r['win_s_inst_max']pojo = SurfStationHistoryDay()pojo.station_id_c = station_id_cpojo.observe_time = observe_timepojo.win_s_max = win_s_maxpojo.win_s_inst_max = win_s_inst_maxresult_list.append(pojo)
排序
Pandas提供了两种排序方法。它们是−
- 标签排序
- 按数值排序
排序函数:
- sort_index():按照索引排序
- 通过将轴参数设为0或1,可以对列标签进行排序。默认情况下,axis=0,按行排序。
- sort_values():按照数值排序
- by=[‘column_name1’,‘column_name2’ ]
- kind=‘mergesort’
处理文本数据
在本章中,我们将讨论基本Series/Index的字符串操作。在后续的章节中,我们将学习如何在DataFrame上应用这些字符串函数。
Pandas提供了一组字符串函数,使得对字符串数据的操作变得很容易。最重要的是,这些函数会忽略(或排除)缺失/NaN值。
几乎所有这些方法都适用于Python字符串函数(参考:https://docs.python.org/3/library/stdtypes.html#string-methods)。因此,将Series对象转换为字符串对象,然后执行操作。
现在让我们看看每个操作的执行情况。
序号 | 函数与描述 |
---|---|
1 | lower() 将Series/Index中的字符串转换为小写。 |
2 | upper() 将Series/Index中的字符串转换为大写。 |
3 | len() 计算字符串的长度。 |
4 | strip() 帮助去除Series/Index中每个字符串两侧的空白(包括换行符)。 |
5 | split(‘ ‘) 使用给定的模式拆分每个字符串。 |
6 | cat(sep=’ ‘) 使用给定的分隔符连接Series/Index元素。 |
7 | get_dummies() 返回具有One-Hot编码值的DataFrame。 |
8 | contains(pattern) 对于每个元素,如果子字符串包含在元素中,则返回布尔值True,否则返回False。 |
9 | replace(a,b) 替换值 a 为值 b 。 |
10 | repeat(value) 重复每个元素指定次数。 |
11 | count(pattern) 返回每个元素中模式出现的次数。 |
12 | startswith(pattern) 如果Series/Index中的元素以模式开头,则返回True。 |
13 | endswith(pattern) 如果Series/Index中的元素以pattern结尾,则返回true。 |
14 | find(pattern) 返回pattern第一次出现的位置。 |
15 | findall(pattern) 返回pattern所有出现的位置的列表。 |
16 | swapcase 交换大小写。 |
17 | islower() 检查Series/Index中每个字符串中的所有字符是否都为小写。返回布尔值。 |
18 | isupper() 检查Series/Index中每个字符串中的所有字符是否都为大写。返回布尔值。 |
19 | isnumeric() 检查Series/Index中每个字符串的所有字符是否都是数字。返回布尔值。 |
让我们现在创建一个Series并看看上面的所有函数是如何工作的。
让我们现在创建一个Series并看看上面的所有函数是如何工作的。
import pandas as pd
import numpy as nps = pd.Series(['Tom', 'William Rick', 'John', 'Alber@t', np.nan, '1234','SteveSmith'])print s
它的 输出 如下:
0 Tom
1 William Rick
2 John
3 Alber@t
4 NaN
5 1234
6 Steve Smith
dtype: object
lower():
import pandas as pd
import numpy as nps = pd.Series(['Tom', 'William Rick', 'John', 'Alber@t', np.nan, '1234','SteveSmith'])print s.str.lower()
这是它的 输出 结果-
0 tom
1 william rick
2 john
3 alber@t
4 NaN
5 1234
6 steve smith
dtype: object
其它函数省略…
选项和定制
Pandas提供了API来定制其行为的一些方面,其中显示功能被广泛使用。
该API由五个相关函数组成。它们是:
- get_option()
- set_option()
- reset_option()
- describe_option()
- option_context()
让我们现在了解这些函数如何操作。
常用参数:
Sr.No | 参数和描述 |
---|---|
1 | display.max_rows 显示要显示的最大行数 |
2 | display.max_columns 显示要显示的最大列数 |
3 | display.expand_frame_repr 显示要拉伸页面的DataFrame |
4 | display.max_colwidth 显示最大列宽 |
5 | display.precision 显示十进制数的精度 |
- get_option(param)
get_option接受一个参数并将其值返回为下面的输出所示:
显示默认值。解释器读取此值并将行显示为此值作为显示上限。
import pandas as pd
print pd.get_option("display.max_rows")
它的 输出 如下:
60
显示默认的数据值数量。 解释器读取此值,并将以此值作为显示的上限来显示行。
import pandas as pd
print pd.get_option("display.max_columns")
它的输出如下:
20
这里,60和20是默认的配置参数值。
- set_option(param,value)
set_option接受两个参数,并将参数的值设置如下:
使用 set_option() ,我们可以更改默认显示的行数。
import pandas as pdpd.set_option("display.max_rows",80)print pd.get_option("display.max_rows")
它的输出如下所示−
80
使用 set_option() ,我们可以更改默认显示的行数。
import pandas as pdpd.set_option("display.max_columns",30)print pd.get_option("display.max_columns")
它的 输出 如下:
30
- reset_option()
重置选项 接受一个参数,并将值设置为默认值。
使用reset_option(),我们可以将值更改回默认的显示行数。
import pandas as pdpd.reset_option("display.max_rows")
print pd.get_option("display.max_rows")
它的 输出 如下:
60
- describe_option(param)
describe_option 打印参数的描述。
使用reset_option(),我们可以将该值更改回默认要显示的行数。
import pandas as pd
pd.describe_option("display.max_rows")
它的 输出 如下-
display.max_rows : intIf max_rows is exceeded, switch to truncate view. Depending on'large_repr', objects are either centrally truncated or printed asa summary view. 'None' value means unlimited.In case python/IPython is running in a terminal and `large_repr`equals 'truncate' this can be set to 0 and pandas will auto-detectthe height of the terminal and print a truncated object which fitsthe screen height. The IPython notebook, IPython qtconsole, orIDLE do not run in a terminal and hence it is not possible to docorrect auto-detection.[default: 60] [currently: 60]
- option_context()
option_context上下文管理器用于在 with语句 中临时设置选项。当退出 with块 时,选项值会自动恢复。
使用option_context(),我们可以临时设置值。
import pandas as pd
with pd.option_context("display.max_rows",10):print(pd.get_option("display.max_rows"))print(pd.get_option("display.max_rows"))
它的 输出 如下:
10
10
看一下第一个和第二个打印语句之间的区别。第一个语句打印的是由 option_context() 设置的值,在 with context 内部是临时的。在 with context 之后,第二个打印语句打印的是配置的值。
索引和选择数据
在这一章中,我们将讨论如何切片和切块日期以及通常获取pandas对象的子集。
Python和NumPy的索引操作符“[]”和属性操作符“.”快速而轻松地访问Pandas数据结构的各种用例。然而,由于要访问的数据类型事先未知,直接使用标准操作符在优化方面存在一些局限性。对于生产代码,我们建议您利用本章中介绍的优化的pandas数据访问方法。
Pandas现在支持三种类型的多轴索引;这三种类型在下表中列出 –
序号 | 索引和描述 |
---|---|
1 | .loc() 基于标签 |
2 | .iloc() 基于整数 |
3 | .ix() 标签和整数都能用 |
.loc()
:Pandas提供了各种纯粹基于标签的索引方法。在切片时,起始边界也被包含在内。整数是有效的标签,但它们指的是标签,而不是位置。
.loc() 具有多个访问方法,如−
- 单个标量标签
- 标签列表
- 切片对象
- 布尔数组
loc 通过’,’分隔两个单个/列表/范围运算符。第一个表示行,第二个表示列。
# import the pandas library and aliasing as pd
import pandas as pd
import numpy as npdf = pd.DataFrame(np.random.randn(8, 4),
index = ['a','b','c','d','e','f','g','h'], columns = ['A', 'B', 'C', 'D'])# 1. 选取所有行,其中的A列
df.loc[:,'A']# 2. 选取所有行,其中的A、C列
df.loc[:,['A','C']]# 3. 选其中几行, 所有列
df.loc['a':'h']# 4. 获取其中'a'行,判断是否大于0,返回一个bool数组
print df.loc['a']>0
.iloc()
:Pandas提供了各种方法来实现纯整数索引。与Python和Numpy一样,这些方法都是从0开始的索引。
各种访问方法如下所示:
- 整数
- 整数列表
- 一段数值范围
import pandas as pd
import numpy as npdf = pd.DataFrame(np.random.randn(8, 4), columns = ['A', 'B', 'C', 'D'])# Integer slicing
print df.iloc[:4]
print df.iloc[1:5, 2:4]
其输出结果如下:
A B C D
0 0.699435 0.256239 -1.270702 -0.645195
1 -0.685354 0.890791 -0.813012 0.631615
2 -0.783192 -0.531378 0.025070 0.230806
3 0.539042 -1.284314 0.826977 -0.026251C D
1 -0.813012 0.631615
2 0.025070 0.230806
3 0.826977 -0.026251
4 1.423332 1.130568
.ix()
除了纯标签和整数基础外,Pandas还提供了一种混合方法来使用.ix()操作符进行选择和子集化对象。
import pandas as pd
import numpy as npdf = pd.DataFrame(np.random.randn(8, 4), columns = ['A', 'B', 'C', 'D'])# Integer slicing
print df.ix[:4]
它的 输出 如下:
A B C D
0 0.699435 0.256239 -1.270702 -0.645195
1 -0.685354 0.890791 -0.813012 0.631615
2 -0.783192 -0.531378 0.025070 0.230806
3 0.539042 -1.284314 0.826977 -0.026251
import pandas as pd
import numpy as npdf = pd.DataFrame(np.random.randn(8, 4), columns = ['A', 'B', 'C', 'D'])
# Index slicing
print df.ix[:,'A']
它的 输出 如下:
0 0.699435
1 -0.685354
2 -0.783192
3 0.539042
4 -1.044209
5 -1.415411
6 1.062095
7 0.994204
Name: A, dtype: float64
符号的使用:
使用多轴索引从Pandas对象中获取值使用以下符号表示:
对象 | 索引器 | 返回类型 |
---|---|---|
Series(序列) | s.loc[indexer] | 标量值 |
DataFrame(数据帧) | df.loc[row_index,col_index] | Series对象 |
Panel(面板) | p.loc[item_index,major_index, minor_index] | p.loc[item_index,major_index, minor_index] |
属性访问:
可以使用属性运算符’.’来选择列。
import pandas as pd
import numpy as np
df = pd.DataFrame(np.random.randn(8, 4), columns = ['A', 'B', 'C', 'D'])print df.A
这个输出为
0 -0.478893
1 0.391931
2 0.336825
3 -1.055102
4 -0.165218
5 -0.328641
6 0.567721
7 -0.759399
Name: A, dtype: float64
统计函数
统计方法有助于理解和分析数据的行为。现在我们将学习一些可以应用于Pandas对象上的统计函数。
- 百分比变化
Series、DataFrames和Panel都具有 pct_change() 函数。该函数将每个元素与其前面的元素进行比较,并计算出变化百分比。
import pandas as pd
import numpy as np
s = pd.Series([1,2,3,4,5,4])
print s.pct_change()df = pd.DataFrame(np.random.randn(5, 2))
print df.pct_change()
它的 输出 如下:
0 NaN
1 1.000000
2 0.500000
3 0.333333
4 0.250000
5 -0.200000
dtype: float640 1
0 NaN NaN
1 -15.151902 0.174730
2 -0.746374 -1.449088
3 -3.582229 -3.165836
4 15.601150 -1.860434
默认情况下, pct_change() 操作是基于列进行的;如果要逐行应用相同的操作,则使用 axis=1() 参数。
- Covariance
协方差应用于系列数据。Series对象有一个cov方法,用于计算系列对象之间的协方差。NA将被自动排除。
import pandas as pd
import numpy as np
s1 = pd.Series(np.random.randn(10))
s2 = pd.Series(np.random.randn(10))
print s1.cov(s2)
它的 输出 如下:
-0.12978405324
当在DataFrame上应用协方差方法时,计算 cov 所有列之间的协方差。
import pandas as pd
import numpy as np
frame = pd.DataFrame(np.random.randn(10, 5), columns=['a', 'b', 'c', 'd', 'e'])
print frame['a'].cov(frame['b'])
print frame.cov()
它的 输出 如下:
-0.58312921152741437a b c d e
a 1.780628 -0.583129 -0.185575 0.003679 -0.136558
b -0.583129 1.297011 0.136530 -0.523719 0.251064
c -0.185575 0.136530 0.915227 -0.053881 -0.058926
d 0.003679 -0.523719 -0.053881 1.521426 -0.487694
e -0.136558 0.251064 -0.058926 -0.487694 0.960761
注意 - 在第一条语句中,观察 a 列和 b 列之间的 cov ,返回的值与DataFrame中的cov相同。
- 相关性
相关性显示任意两个数值数组(序列)之间的线性关系。有多种方法可以计算相关性,如pearson(默认)、spearman和kendall。
import pandas as pd
import numpy as np
frame = pd.DataFrame(np.random.randn(10, 5), columns=['a', 'b', 'c', 'd', 'e'])print frame['a'].corr(frame['b'])
print frame.corr()
它的 输出 如下:
-0.383712785514a b c d e
a 1.000000 -0.383713 -0.145368 0.002235 -0.104405
b -0.383713 1.000000 0.125311 -0.372821 0.224908
c -0.145368 0.125311 1.000000 -0.045661 -0.062840
d 0.002235 -0.372821 -0.045661 1.000000 -0.403380
e -0.104405 0.224908 -0.062840 -0.403380 1.000000
如果DataFrame中存在非数字列,则会自动排除。
- 数据排名
数据排名为数组中的每个元素产生排名。在出现并列的情况下,分配平均排名。
import pandas as pd
import numpy as nps = pd.Series(np.random.np.random.randn(5), index=list('abcde'))
s['d'] = s['b'] # so there's a tie
print s.rank()
它的 输出 如下所示−
a 1.0
b 3.5
c 2.0
d 3.5
e 5.0
dtype: float64
排名(Rank)参数可选地接受一个升序参数,默认值为true;当为false时,数据被反向排名,较大的值被分配较小的排名。
排名支持不同的决胜方法,使用method参数指定 –
- 平均(average) - 组内并列的平均排名
- 最小(min) - 组内最低的排名
- 最大(max) - 组内最高的排名
- 首位(first) - 按照数组中出现的顺序分配排名
窗口函数
对于处理数值数据,Pandas提供了几种变体,如滚动、扩展和指数移动权重,用于窗口统计。其中包括 求和、均值、中位数、方差、协方差、相关系数 等等。
现在我们将学习如何在DataFrame对象上应用每一种函数。
.rolling()
函数
此函数可以应用于数据系列。指定 window=n 参数,并在其之上应用适当的统计函数。
import pandas as pd
import numpy as npdf = pd.DataFrame(np.random.randn(10, 4),index = pd.date_range('1/1/2000', periods=10),columns = ['A', 'B', 'C', 'D'])
print df.rolling(window=3).mean()
它的 输出 如下:
A B C D
2000-01-01 NaN NaN NaN NaN
2000-01-02 NaN NaN NaN NaN
2000-01-03 0.434553 -0.667940 -1.051718 -0.826452
2000-01-04 0.628267 -0.047040 -0.287467 -0.161110
2000-01-05 0.398233 0.003517 0.099126 -0.405565
2000-01-06 0.641798 0.656184 -0.322728 0.428015
2000-01-07 0.188403 0.010913 -0.708645 0.160932
2000-01-08 0.188043 -0.253039 -0.818125 -0.108485
2000-01-09 0.682819 -0.606846 -0.178411 -0.404127
2000-01-10 0.688583 0.127786 0.513832 -1.067156
注意 − 由于窗口大小为3,因此前两个元素为null,从第三个元素开始,值将为前 n , n-1 和 n-2 元素的平均值。因此,我们还可以应用上面提到的各种函数。
实际应用案例:
# 逐1小时降水数据滚动计算逐3小时降水
hour_df['PRE_3h'] = hour_df['PRE_1h'].rolling('3h').sum()# 最大3小时降水值
pre_max_3h = hour_df['PRE_3h'].max() if hour_df['PRE_3h'].dropna().shape[0] > 0 else None
.expanding()
函数
此函数可以应用于一系列数据。指定 min_periods=n 参数并在其上应用适当的统计函数。
import pandas as pd
import numpy as npdf = pd.DataFrame(np.random.randn(10, 4),index = pd.date_range('1/1/2000', periods=10),columns = ['A', 'B', 'C', 'D'])
print df.expanding(min_periods=3).mean()
它的 输出 如下−
A B C D
2000-01-01 NaN NaN NaN NaN
2000-01-02 NaN NaN NaN NaN
2000-01-03 0.434553 -0.667940 -1.051718 -0.826452
2000-01-04 0.743328 -0.198015 -0.852462 -0.262547
2000-01-05 0.614776 -0.205649 -0.583641 -0.303254
2000-01-06 0.538175 -0.005878 -0.687223 -0.199219
2000-01-07 0.505503 -0.108475 -0.790826 -0.081056
2000-01-08 0.454751 -0.223420 -0.671572 -0.230215
2000-01-09 0.586390 -0.206201 -0.517619 -0.267521
2000-01-10 0.560427 -0.037597 -0.399429 -0.376886
ewm()
函数
ewm 函数应用于一系列的数据。您可以通过指定com、span或 halflife 参数,并在其上应用适当的统计函数来赋予数据指数权重。
import pandas as pd
import numpy as npdf = pd.DataFrame(np.random.randn(10, 4),index = pd.date_range('1/1/2000', periods=10),columns = ['A', 'B', 'C', 'D'])
print df.ewm(com=0.5).mean()
它的 输出 如下所示:
A B C D
2000-01-01 1.088512 -0.650942 -2.547450 -0.566858
2000-01-02 0.865131 -0.453626 -1.137961 0.058747
2000-01-03 -0.132245 -0.807671 -0.308308 -1.491002
2000-01-04 1.084036 0.555444 -0.272119 0.480111
2000-01-05 0.425682 0.025511 0.239162 -0.153290
2000-01-06 0.245094 0.671373 -0.725025 0.163310
2000-01-07 0.288030 -0.259337 -1.183515 0.473191
2000-01-08 0.162317 -0.771884 -0.285564 -0.692001
2000-01-09 1.147156 -0.302900 0.380851 -0.607976
2000-01-10 0.600216 0.885614 0.569808 -1.110113
窗口函数主要用于通过平滑曲线来图形化地找出数据中的趋势。如果每天的数据有很大的变化并且有很多数据点可用,那么取样并绘制图表就是一种方法,应用窗口计算并绘制结果图表是另一种方法。通过这些方法,我们可以平滑曲线或趋势。
聚合函数
rolling()
缺失数据
常用处理特殊数据方法:
isnull()
:返回bool数组notnull()
:返回bool数组fillna()
dropna()
:replace()
GroupBy
任何 groupby 操作都涉及对原始对象的以下操作。它们是 –
- 拆分 对象
- 应用 函数
- 合并 结果
在许多情况下,我们将数据拆分为不同的组,并对每个子集应用一些功能。在应用功能中,我们可以执行以下操作 –
- 聚合 - 计算摘要统计量
- 变换 - 执行一些组特定的操作
- 筛选 - 根据某些条件丢弃数据
常用函数:
groupby()
:按参数分组groupby('Team').groups
:查看分组for name,group in grouped:
:遍历分组,name为分组名,group为分组列表grouped.agg([np.sum, np.mean, np.std])
:分组后聚合操作grouped.transform(lambda x: (x - x.mean()) / x.std()*10)
:对组或列进行转换会返回一个与正在分组的对象大小相同的对象df.groupby('Team').filter(lambda x: len(x) >= 3)
:过滤根据定义的条件筛选数据并返回数据的子集
合并/连接
常用函数:
merge()
- `concat()
merge()
Pandas提供了一个单一的函数 merge ,作为DataFrame对象之间所有标准数据库连接操作的入口点。
pd.merge(left, right, how='inner', on=None, left_on=None, right_on=None,
left_index=False, right_index=False, sort=True)
在这里,我们使用了以下参数−
- left − 一个DataFrame对象。
- right − 另一个DataFrame对象。
- on − 要连接的列(名称)。必须在左侧和右侧的DataFrame对象中找到。
- left_on − 从左侧DataFrame中用作键的列。可以是列名称或与DataFrame长度相等的数组。
- right_on − 从右侧DataFrame中用作键的列。可以是列名称或与DataFrame长度相等的数组。
- left_index − 如果 True, 则使用左侧DataFrame的索引(行标签)作为其连接键。对于具有MultiIndex(分层)的DataFrame,级数的数量必须与右侧DataFrame的连接键数量匹配。
- right_index − 对于右侧DataFrame,与 left_index 的用法相同。
- how − 一个’left’、’right’、’outer’、’inner’之一。默认为inner。下面每种方法都有详细描述。
- sort − 按字典顺序对连接键对应的结果DataFrame进行排序。默认为True,将其设置为False将在许多情况下大大提高性能。
concat()
pd.concat(objs,axis=0,join='outer',join_axes=None,
ignore_index=False)
- objs − 这是一系列或映射的Series、DataFrame或Panel对象。
- axis − {0, 1, …},默认为0。这是要沿着连接的轴。
- join − {‘inner’, ‘outer’},默认为‘outer’。如何处理其他轴上的索引。对于并集使用Outer,对于交集使用Inner。
- ignore_index − 布尔型, 默认为False。如果为True,则不使用连接轴上的索引值。 结果轴将被标记为0,…,n-1。
- join_axes − 这是索引对象的列表。特定的索引用于其他(n-1)轴,而不执行内部/外部集合逻辑。
日期功能
常用函数:
pd.date_range()
:通过指定周期和频率,我们可以创建日期序列。默认情况下,日期范围的频率是天。pd.bdate_range()
:表示营业日期范围。与date_range()不同,它排除星期六和星期日。
示例:
import pandas as pdprint pd.date_range('1/1/2011', periods=5,freq='M')DatetimeIndex(['2011-01-31', '2011-02-28', '2011-03-31', '2011-04-30', '2011-05-31'],dtype='datetime64[ns]', freq='M')start = pd.datetime(2011, 1, 1)
end = pd.datetime(2011, 1, 5)
print pd.date_range(start, end)DatetimeIndex(['2011-01-01', '2011-01-02', '2011-01-03', '2011-01-04', '2011-01-05'],dtype='datetime64[ns]', freq='D')
一些字符串别名被赋予了常见的时间序列频率。我们将这些别名称为偏移别名。
别名 | 描述 | 别名 | 描述 |
---|---|---|---|
B | 工作日频率 | BQS | 季度开始的工作日频率 |
D | 日历日频率 | A | 年度结束频率 |
W | 每周频率 | BA | 工作年度结束频率 |
M | 月末频率 | BAS | 工作年度开始频率 |
SM | 半月末频率 | BH | 工作小时频率 |
BM | 工作月末频率 | H | 小时频率 |
MS | 月初频率 | T, min | 分钟频率 |
SMS | SMS半月初频率 | S | 秒钟频率 |
BMS | 工作月初频率 | L, ms | 毫秒 |
Q | 季度末频率 | U, us | 微秒 |
BQ | 工作季度末频率 | N | 纳秒 |
QS | 季度开始频率 |
Timedelta
Timedeltas是时间差,以不同的单位表示,例如天、小时、分钟、秒。它们可以是正数也可以是负数。
创建:
# 1. 字符串
pd.Timedelta('2 days 2 hours 15 minutes 30 seconds')# 2. 整数
pd.Timedelta(6,unit='h')# 3. 数据偏移量,如-周、天、小时、分钟、秒、毫秒、微秒、纳秒,也可以用于构建。
pd.Timedelta(days=2)
操作:
import pandas as pds = pd.Series(pd.date_range('2012-1-1', periods=3, freq='D'))
td = pd.Series([ pd.Timedelta(days=i) for i in range(3) ])
df = pd.DataFrame(dict(A = s, B = td))# 1. 加法操作
df['C']=df['A']+df['B']# 2. 减法操作
df['D']=df['C']-df['B']# 3. 实际应用中,将时间索引作操作
pre_df.index = pre_df.index - pd.Timedelta(hours=20)