Professional Documents
Culture Documents
Pandas
Pandas
Pandas
Pandas 有以下五大特点:
数据结构
Series:一维数组,与 Numpy 中的一维 array 类似。二者与 Python 基本的数据结构 List 也很
相近。Series 如今能保存不同种数据类型,字符串、boolean 值、数字等都能保存在 Series
中。它由一组数据(各种 Numpy 数据类型)以及一组与之相关的数据标签(即索引)组
成。
Time- Series:以时间为索引的 Series。
DataFrame:二维的表格型数据结构。是一个表格型的数据结构,它含有一组有序的列,每
列可以是不同的值类型(数值、字符串、布尔型值)。 DataFrame 既有行索引也有列索
引,它可以被看做由 Series 组成的字典(共同用一个索引)。Panel :三维的数组,可以
理解为 DataFrame 的容器。
Panel4D:是像 Panel 一样的 4 维数据容器。
PanelND:拥有 factory 集合,可以创建像 Panel4D 一样 N 维命名容器的模块。
DataFrame 构造方法如下:
pandas.DataFrame( data, index, columns, dtype, copy)
参数说明:
data:一组数据(ndarray、series, map, lists, dict 等类型)。
index:索引值,或者可以称为行标签。
columns:列标签,默认为 RangeIndex (0, 1, 2, …, n) 。
dtype:数据类型。
copy:拷贝数据,默认为 False。
>>> data = {
'华东科技': [1.91, 1.90, 1.86, 1.84],
'长安汽车': [11.27, 11.14, 11.28, 11.71],
'西藏矿业': [7.89, 7.79, 7.61, 7.50],
'重庆啤酒': [50.46, 50.17, 50.28, 50.28]
}
>>> pd.DataFrame(data)
基本操作
DataFrame 几乎被打造成了一个全能小怪兽:它有字典的影子,有 NumPy 数组的性
能,甚至继承了 NumPy 数组的很多属性和方法;它可以在一个结构内存储和处理多种不同
类型的数据;它看起来像是二维的结构,却能处理更高维度的数据;它可以处理包括日期
时间在内的任意类型的数据,它能读写几乎所有的数据格式;它提供了多到不可胜数的方
法,并派生出无穷的操作技巧。
先构造一个关于多只股票在同一天的开盘价、收盘价、交易量等信息的 DataFrame 对
象,并以 stack 命名,演示相关操作
>>> import numpy as np
>>> data = np.array([
[10.70, 11.95, 10.56, 11.71, 789.10, 68771048],
[7.28, 7.59, 7.17, 7.50, 57.01, 7741802],
[48.10, 50.59, 48.10, 50.28, 223.06, 4496598],
[66.70, 69.28, 66.66, 68.92, 1196.14, 17662768],
[7.00, 7.35, 6.93, 7.11, 783.15, 109975919],
[2.02, 2.10, 2.01, 2.08, 56.32, 27484360]
])
>>> colnames = ['开盘价','最高价','最低价','收盘价','成交额','成交量']
>>> idx = ['000625.SZ','000762.SZ','600132.SH','600009.SH','600126.SH','000882.SZ']
>>> stock = pd.DataFrame(data, columns=colnames, index=idx)
>>> stock
1、数据预览
显示开始的 5 行和最后的 5 行
查看均值、方差、极值的统计概要
>>> stock.describe()
转置
>>> stock.T
排序
2、数据选择
行选择
列选择
行列选择
条件选择
返回行、列标签
>>>stock.axes
3、改变数据结构
重新索引
DataFrame 的 reindex()方法可以重新定义行标签或列标签,并返回一个新的对象,原有
的数据结构不会被改变。重新索引既可以删除已有的行或列,也可以增加新的行或列。如
果不指定填充值,新增的行或列的值默认为 NaN。
>>> idx=['000762.SZ','000625.SZ','000955.sz']
>>> colnames=['开盘价','收盘价','成交额','成交量','涨跌幅']
>>> stock.reindex(index=idx, columns=colnames)
>>> stock.reindex(index=idx, columns=colnames, fill_value=0)
删除行或列
DataFrame 的 drop()方法可以删除指定轴的指定项,返回一个新的对象,原有的数据结
构不会被改变。
>>> stock.drop(['000762.SZ', '600132.SH'], axis=0) # 删除指定行
>>> stock.drop(['成交额', '最高价', '最低价'], axis=1) # 删除指定列
行扩展
列扩展
直接对新列赋值,即可实现列扩展。赋值时,数据长度必须要和
DataFrame 的长度匹配。这里需要特别说明一点,其他改变数据结构的操作都
是返回新的数据结构,原有的数据结构不会被改变,而赋值操作改变了原有的
数据结构。
>>> stock['涨跌幅'] = [0.02, 0.03, 0.05, 0.01, 0.02, 0.03]
>>> stock
4、改变数据类型
类似于 NumPy 数组,Series 对象提供了 astype()的方法用来改变数据类型。
不过 astype()只是返回了一个新的 Series 对象,并没有真正改变原有的
Series 对象。DataFrame 对象没有提供改变某一列数据类型的方法,如果要这
样做,可以对这一列重新赋值。
>>> stock['涨跌幅'].dtype
>>> stock['涨跌幅'] = stock['涨跌幅'].astype('float32').values
>>> stock['涨跌幅'].dtype
5、广播与矢量化运算
Pandas 是基于 NumPy 数组的扩展,继承了 NumPy 数组的广播和矢量化特性。
不管是 Series 对象内部,还是 Series 对象之间,甚至是 DataFrame 对象之间,
所有的运算都支持广播和矢量化。此外,NumPy 数组的数学和统计函数,几乎
都可以应用在 Pandas 的各种数据据结构上。
>>> stock
>>> stock['成交量'] /= 2 # 成交量减半
>>> stock
>>> stock['最高价'] += stock['最低价'] # 最高价加上最低价
>>> stock
>>> stock['开盘价'] = (stock['开盘价']-stock['开盘价'].mean())/stock['开盘价'].std() # 开盘价
标准化:去中心化(减开盘价均值),再除以开盘价的标准差
>>> stock
6、行列级广播函数
NumPy 数组的大部分数学函数和统计函数都是广播函数,可以被隐式地映
射到数组的各个元素上。NumPy 数组也支持自定义广播函数。Pandas 的 apply()
函数类似于 NumPy 的自定义广播函数功能,可以将函数映射到 DataFrame 对象
的特定行或者列上,也就是以行或者列的一维数组作为函数的输入参数,而不
是以行或者列的各个元素作为函数的输入参数。这是 Pandas 的 apply()函数有
别于 NumPy 自定义广播函数的地方。
>>> stock
>>> f = lambda x:(x-x.min())/(x.max()-x.min()) # 定义归一化函数
>>> stock.apply(f, axis=0) # 0 轴(行方向)归一化
>>> stock.apply(f, axis=1) # 1 轴(列方向)归一化
高级应用
DataFrame 作为数据分析的利器,其作用体现在两个方面:一是用来暂存
形式复杂的数据,二是提供高效的处理手段。前一节的基本操作,偏重于暂存
数据,本节的高级应用,则偏重于如何高效地处理数据。
1、分组
分组与聚合是数据处理中最常见的应用场景。比如,对于多只股票多个交
易日的成交量分析,需要按股票和交易日两种分类方式进行统计
>>> data = {
'日期': ['2020-03-11','2020-03-11','2020-03-11','2020-03-11','2020-03-11','2020-03-12','2020-03-
12','2020-03-12','2020-03-12','2020-03-12','2020-03-13','2020-03-13','2020-03-13','2020-03-
13','2020-03-13'],
'代码':
['000625.SZ','000762.SZ','600132.SH','600009.SH','000882.SZ','000625.SZ','000762.SZ','600132.S
H','600009.SH','000882.SZ','000625.SZ','000762.SZ','600132.SH','600009.SH','000882.SZ'],
'成交额':
[422.08,73.65,207.04,510.59,63.28,471.78,59.2,156.82,853.83,52.84,789.1,57.01,223.06,1196.14
,56.32],
'成交量':
[37091400,9315300,4127800,7233100,28911100,42471700,7724200,3143100,12350400,24828
900,68771048,7741802,4496598,17662768,27484360]
}
>>> vo = pd.DataFrame(data)
>>> vo
使用 groupby()函数按照日期分组,返回的分组结果是一个迭代器,遍历
这个迭代器,可以得到三个由组名(日期)和该组的 DataFrame 组成的元组。
>>> for name, df in vo.groupby('日期'):
print('组名:%s'%name)
print('-------------------------------------------')
print(df)
print()
使用 groupby()函数按照股票代码分组,返回的分组结果是一个迭代器,
遍历这个迭代器,可以得到五个由组名(股票代码)和该组的 DataFrame 组成
的元组。
>>> for name, df in vo.groupby('代码'):
print('组名:%s'%name)
print('-------------------------------------------')
print(df)
print()
2、聚合
理解了分组,接下来就可以根据分组做点什么了。比如,统计所有股票每
天的成交总额和成交总量等等。
可以直接应用在分组结果上的函数包括:计数(count)、求和(sum)、
均值(mean)、中位数(median)、有效值的乘积(prod)、方差和标准差
(var、std)、最大值和最小值(min 和 max)、第一个和最后一个有效值
(first 和 last)等。
如果要对分组结果实施自定义的函数,或者对分组结果做更多的统计分析,
这时候就要使用聚合函数 agg()了。
>>> def scope(x): # 返回最大值和最小值之差(波动幅度)的函数
>>> vo.groupby('代码').agg(scope) # 统计每一只股票成交额和成交量的波动幅度
>>> vo.groupby('代码').agg(['mean', scope]) # 统计成交额和成交量的均值和波动幅度
聚合函数还可以对不同的列实施不同的函数操作。比如,下面的代码对成
交额实施均值操作,对成交量实施自定义的波动幅度函数。
>>> vo.groupby('代码').agg({'成交额':'mean', '成交量':scope})
3、层次化索引
在讨论分组和聚合的例子中,日期-股票代码-成交额和成交量,这样的数
据结构俨然已经是三维的了,却依然可以用 DataFrame 暂存和处理。这样的数
据结构,尽管可以通过分组获得多个二维的 DataFrame 对象,但毕竟无法直接
索引或者选择。层次化索引可以很好地解决这个问题,为 DataFrame 处理更高
维数据指明了方向。
这里仍然直接使用日期字符串做索引,正确的做法是使用日期索引对象。
我们将在讨论时间序列时,正式介绍 DatetimeIndex 类的使用。
>>> dt = ['2020-03-11', '2020-03-12','2020-03-13']
>>> sc = ['000625.SZ','000762.SZ','600132.SH','600009.SH','600126.SH']
>>> cn = ['成交额', '成交量']
>>> idx = pd.MultiIndex.from_product([dt, sc], names=['日期', '代码'])
>>> data = np.array([
[422.08, 37091400],
[73.65, 9315300],
[207.04, 4127800],
[510.59, 7233100],
[63.28, 28911100],
[471.78, 42471700],
[59.2, 7724200],
[156.82, 3143100],
[853.83, 12350400],
[52.84, 24828900],
[789.1, 68771048],
[57.01, 7741802],
[223.06, 4496598],
[1196.14, 17662768],
[56.32, 27484360]
])
>>> vom1 = pd.DataFrame(data, index=idx, columns=cn)
>>> vom1
4、表级广播函数
行列级广播函数 apply()可以把一个计算函数映射到 DataFrame 对象的行或
者列上,并以行或者列的一维数组作为计算函数的输入参数。类似 apply(),
表级广播函数 pipe()可以把一个计算函数映射到 DataFrame 对象的每一个元素
上,并以每一个元素作为计算函数的第一个输入参数。
>>> def scale(x, k): # 对 x 进行缩放,缩放系数为 k
>>> vom1.pipe(scale, 0.2) # 对 vom1 所有数据执行缩放函数,缩放系数 0.2
作为广播函数,pipe()并无新意,不过,pipe()函数将 DataFrame 对象作
为首个参数,这为链式调用提供了可能性。链式调用作为一种流行的编码风格,
以其代码的简洁和易读,受到了很多语言和程序员的追捧。
>>> def adder(x, dx):
return x+dx
>>> vom1.pipe(scale, 0.2).pipe(adder, 5) # 链式调用
5 日期时间索引对象
Pandas 对于时间日期类型的数据也有很好的支持,提供了很多非常实用的
方法,可以非常方便地生成、转换日期时间索引对象。DatetimeIndex 类是索
引数组的一种,也是最常用的日期时间序列生成和转换工具,可以由日期时间
字符串列表直接生成日期时间索引对象,也可以将字符串类型的索引对象、
Series 对象转换成日期时间索引对象。
>>> pd.DatetimeIndex(['2020-03-10', '2020-03-11', '2020-03-12'])
>>> idx = pd.Index(['2020-03-10', '2020-03-11', '2020-03-12'])
>>> sdt = pd.Series(['2020-03-10', '2020-03-11', '2020-03-12'])
>>> idx
>>> sdt
>>> pd.DatetimeIndex(idx)
>>> pd.DatetimeIndex(sdt)
转换函数 pd.to_datetime()的功能类似于 DatetimeIndex 类,也可以将各
种格式的日期时间字符串转换为日期时间索引对象。
>>> pd.to_datetime(['2020-03-10', '2020-03-11', '2020-03-12', '2020-03-13'])
DatetimeIndex(['2020-03-10', '2020-03-11', '2020-03-12', '2020-03-13'], dtype='datetime64[ns]',
freq=None)
>>> pd.to_datetime(idx)
>>> pd.to_datetime(std)
给定起止时间、序列长度或者分割步长,date_range()也可以快速创建日
期时间索引对象。分割步长使用 L、S、T、H、D、M 表示毫秒、秒、分钟、小时、
天,月等,还可以加上数字,比如 3H 表示分割步长为 3 小时。
>>> pd.date_range(start='2020-05-12', end='2020-05-18')
>>> pd.date_range(start='2020-05-12 08:00:00', periods=6, freq='3H')
>>> pd.date_range(start='08:00:00', end='9:00:00', freq='15T')
6 数据可视化
Pandas 的可视化是基于 Matplotlib 的一个封装,并且封装得不够彻底,很
多地方仍然离不开 Matplotlib。比如,脱离 ipython 或 jupyter 的环境,必须
要使用 pyplot.show()才能显示绘图结果,解决中文显示问题也必须要显式地
导入 matplotlib.pyplot 包,除非你手动修改 Matplotlib 的字体配置文件。因
此,使用 Pandas 的可视化功能之前,需要导入模块并设置默认字体。本节的所
有示例,均假定已经运行了以下代码。
Pandas 的可视化 API 提供了绘制折线图、柱状图、箱型图、直方图、散点
图、饼图等功能。对于 Series 对象和 DataFrame 对象的数据可视化,通常以横
轴表示索引,以纵轴表示数据。
对于 DataFrame 对象的数据可视化,也是以横轴表示索引,多列数据可以
绘制在画布(figure)的同一个子图(axes)上,也可以绘制在同一张画布的
多个子图(axes)上。
DataFrame 对象的 plot()方法同时绘制了 4 列数据,自动生成了图例,显
然比直接使用 Matplotlib 要简洁得多。
>>> data = np.random.randn(10,4)
>>> idx = pd.date_range('08:00:00', periods=10, freq='H')
>>> df = pd.DataFrame(data, index=idx, columns=list('ABCD'))
>>> df.plot()
>>> plt.show()
读写 Excel 文件
读写 HDF 文件
转换 ndarray 数组
使用 to_numpy() 函数,将 DataFrame 对象转换为 NumPy ndarray 数组,并将其返回。
函数的语法格式如下:
DataFrame.to_numpy(dtype=None, copy=False)
参数说明如下:
dtype:可选参数,表示数据类型;
copy:布尔值参数,默认值为 Fales,表示返回值不是其他数组的视图。
info = pd.DataFrame({"P": [2, 3], "Q": [4.0, 5.8]})
info['R'] = pd.date_range('2020-12-23', periods=2) #给 info 添加 R 列
print(info)
n=info.to_numpy() #将其转化为 numpy 数组
print(n)
print(type(n))
import pandas as pd
#创建 DataFrame 对象
info = pd.DataFrame([[17, 62, 35],[25, 36, 54],[42, 20, 15],[48, 62, 76]],
columns=['x', 'y', 'z'])
print('DataFrame\n----------\n', info)
#转换 DataFrame 为数组 array
arr = info.to_numpy()
print('\nNumpy Array\n----------\n', arr)