Python 金融编程第二版(二)(2)https://developer.aliyun.com/article/1559403
DataFrame 类
本节涵盖了DataFrame
类的一些基本方面。这个类非常复杂和强大,这里只能展示其中一小部分功能。后续章节提供更多例子并揭示不同的方面。
使用 DataFrame 类的第一步
从相当基本的角度来看,DataFrame
类被设计用来管理带索引和标签的数据,与SQL
数据库表或电子表格应用程序中的工作表并没有太大的不同。考虑以下创建DataFrame
对象的示例:
In [1]: import pandas as pd ![1](https://gitee.com/OpenDocCN/ibooker-quant-zh/raw/master/docs/py-fin-2e/img/1.png) In [2]: df = pd.DataFrame([10, 20, 30, 40], ![2](https://gitee.com/OpenDocCN/ibooker-quant-zh/raw/master/docs/py-fin-2e/img/2.png) columns=['numbers'], ![3](https://gitee.com/OpenDocCN/ibooker-quant-zh/raw/master/docs/py-fin-2e/img/3.png) index=['a', 'b', 'c', 'd']) ![4](https://gitee.com/OpenDocCN/ibooker-quant-zh/raw/master/docs/py-fin-2e/img/4.png) In [3]: df ![5](https://gitee.com/OpenDocCN/ibooker-quant-zh/raw/master/docs/py-fin-2e/img/5.png) Out[3]: numbers a 10 b 20 c 30 d 40
导入pandas
。
将数据定义为list
对象。
指定列标签。
指定索引值/标签。
显示DataFrame
对象的数据以及列和索引标签。
这个简单的例子已经展示了当涉及到存储数据时DataFrame
类的一些主要特性:
数据
数据本身可以以不同的形状和类型提供(list
、tuple
、ndarray
和dict
对象都是候选对象)。
标签
数据以列的形式组织,可以具有自定义名称。
索引
存在可以采用不同格式(例如,数字、字符串、时间信息)的索引。
与此类DataFrame
对象一起工作通常非常方便和高效,例如,与常规的ndarray
对象相比,当您想要像扩大现有对象一样时,后者更为专业和受限。以下是展示在DataFrame
对象上进行典型操作的简单示例:
In [4]: df.index ![1](https://gitee.com/OpenDocCN/ibooker-quant-zh/raw/master/docs/py-fin-2e/img/1.png) Out[4]: Index(['a', 'b', 'c', 'd'], dtype='object') In [5]: df.columns ![2](https://gitee.com/OpenDocCN/ibooker-quant-zh/raw/master/docs/py-fin-2e/img/2.png) Out[5]: Index(['numbers'], dtype='object') In [6]: df.loc['c'] ![3](https://gitee.com/OpenDocCN/ibooker-quant-zh/raw/master/docs/py-fin-2e/img/3.png) Out[6]: numbers 30 Name: c, dtype: int64 In [7]: df.loc[['a', 'd']] ![4](https://gitee.com/OpenDocCN/ibooker-quant-zh/raw/master/docs/py-fin-2e/img/4.png) Out[7]: numbers a 10 d 40 In [8]: df.iloc[1:3] ![5](https://gitee.com/OpenDocCN/ibooker-quant-zh/raw/master/docs/py-fin-2e/img/5.png) Out[8]: numbers b 20 c 30 In [9]: df.sum() ![6](https://gitee.com/OpenDocCN/ibooker-quant-zh/raw/master/docs/py-fin-2e/img/6.png) Out[9]: numbers 100 dtype: int64 In [10]: df.apply(lambda x: x ** 2) ![7](https://gitee.com/OpenDocCN/ibooker-quant-zh/raw/master/docs/py-fin-2e/img/7.png) Out[10]: numbers a 100 b 400 c 900 d 1600 In [11]: df ** 2 ![8](https://gitee.com/OpenDocCN/ibooker-quant-zh/raw/master/docs/py-fin-2e/img/8.png) Out[11]: numbers a 100 b 400 c 900 d 1600
index
属性和Index
对象。
columns
属性和Index
对象。
选择与索引c
对应的值。
选择与索引a
和d
对应的两个值。
通过索引位置选择第二行和第三行。
计算单列的总和。
使用apply()
方法以向量化方式计算平方。
直接应用向量化,就像使用ndarray
对象一样。
与NumPy
的ndarray
对象相反,可以在两个维度上扩大DataFrame
对象:
In [12]: df['floats'] = (1.5, 2.5, 3.5, 4.5) ![1](https://gitee.com/OpenDocCN/ibooker-quant-zh/raw/master/docs/py-fin-2e/img/1.png) In [13]: df Out[13]: numbers floats a 10 1.5 b 20 2.5 c 30 3.5 d 40 4.5 In [14]: df['floats'] ![2](https://gitee.com/OpenDocCN/ibooker-quant-zh/raw/master/docs/py-fin-2e/img/2.png) Out[14]: a 1.5 b 2.5 c 3.5 d 4.5 Name: floats, dtype: float64
使用提供的float
对象作为tuple
对象添加新列。
选择此列并显示其数据和索引标签。
整个DataFrame
对象也可以用来定义新列。在这种情况下,索引会自动对齐:
In [15]: df['names'] = pd.DataFrame(['Yves', 'Sandra', 'Lilli', 'Henry'], index=['d', 'a', 'b', 'c']) ![1](https://gitee.com/OpenDocCN/ibooker-quant-zh/raw/master/docs/py-fin-2e/img/1.png) In [16]: df Out[16]: numbers floats names a 10 1.5 Sandra b 20 2.5 Lilli c 30 3.5 Henry d 40 4.5 Yves
基于DataFrame
对象创建另一个新列。
数据附加工作方式类似。但是,在以下示例中,我们看到通常应避免的副作用——索引被简单的范围索引替换:
In [17]: df.append({'numbers': 100, 'floats': 5.75, 'names': 'Jil'}, ignore_index=True) ![1](https://gitee.com/OpenDocCN/ibooker-quant-zh/raw/master/docs/py-fin-2e/img/1.png) Out[17]: numbers floats names 0 10 1.50 Sandra 1 20 2.50 Lilli 2 30 3.50 Henry 3 40 4.50 Yves 4 100 5.75 Jil In [18]: df = df.append(pd.DataFrame({'numbers': 100, 'floats': 5.75, 'names': 'Jil'}, index=['y',])) ![2](https://gitee.com/OpenDocCN/ibooker-quant-zh/raw/master/docs/py-fin-2e/img/2.png) In [19]: df Out[19]: floats names numbers a 1.50 Sandra 10 b 2.50 Lilli 20 c 3.50 Henry 30 d 4.50 Yves 40 y 5.75 Jil 100 In [20]: df = df.append(pd.DataFrame({'names': 'Liz'}, index=['z',])) ![3](https://gitee.com/OpenDocCN/ibooker-quant-zh/raw/master/docs/py-fin-2e/img/3.png) In [21]: df Out[21]: floats names numbers a 1.50 Sandra 10.0 b 2.50 Lilli 20.0 c 3.50 Henry 30.0 d 4.50 Yves 40.0 y 5.75 Jil 100.0 z NaN Liz NaN In [22]: df.dtypes ![4](https://gitee.com/OpenDocCN/ibooker-quant-zh/raw/master/docs/py-fin-2e/img/4.png) Out[22]: floats float64 names object numbers float64 dtype: object
通过dict
对象添加新行;这是一个临时操作,在此期间索引信息会丢失。
这基于具有索引信息的DataFrame
对象附加行;原始索引信息被保留。
这将不完整的数据行附加到DataFrame
对象中,导致NaN
值。
单列的不同dtypes
;这类似于带有NumPy
的记录数组。
尽管现在存在缺失值,但大多数方法调用仍将起作用。例如:
In [23]: df[['numbers', 'floats']].mean() ![1](https://gitee.com/OpenDocCN/ibooker-quant-zh/raw/master/docs/py-fin-2e/img/1.png) Out[23]: numbers 40.00 floats 3.55 dtype: float64 In [24]: df[['numbers', 'floats']].std() ![2](https://gitee.com/OpenDocCN/ibooker-quant-zh/raw/master/docs/py-fin-2e/img/2.png) Out[24]: numbers 35.355339 floats 1.662077 dtype: float64
对指定的两列求平均值(忽略具有NaN
值的行)。
对指定的两列计算标准差(忽略具有NaN
值的行)。
DataFrame 类的第二步
本小节中的示例基于具有标准正态分布随机数的ndarray
对象。它探索了进一步的功能,如使用DatetimeIndex
来管理时间序列数据。
In [25]: import numpy as np In [26]: np.random.seed(100) In [27]: a = np.random.standard_normal((9, 4)) In [28]: a Out[28]: array([[-1.74976547, 0.3426804 , 1.1530358 , -0.25243604], [ 0.98132079, 0.51421884, 0.22117967, -1.07004333], [-0.18949583, 0.25500144, -0.45802699, 0.43516349], [-0.58359505, 0.81684707, 0.67272081, -0.10441114], [-0.53128038, 1.02973269, -0.43813562, -1.11831825], [ 1.61898166, 1.54160517, -0.25187914, -0.84243574], [ 0.18451869, 0.9370822 , 0.73100034, 1.36155613], [-0.32623806, 0.05567601, 0.22239961, -1.443217 ], [-0.75635231, 0.81645401, 0.75044476, -0.45594693]])
尽管可以更直接地构造DataFrame
对象(如前所示),但通常使用ndarray
对象是一个很好的选择,因为pandas
将保留基本结构,并且“只”会添加元信息(例如,索引值)。它还代表了金融应用和一般科学研究的典型用例。例如:
In [29]: df = pd.DataFrame(a) ![1](https://gitee.com/OpenDocCN/ibooker-quant-zh/raw/master/docs/py-fin-2e/img/1.png) In [30]: df Out[30]: 0 1 2 3 0 -1.749765 0.342680 1.153036 -0.252436 1 0.981321 0.514219 0.221180 -1.070043 2 -0.189496 0.255001 -0.458027 0.435163 3 -0.583595 0.816847 0.672721 -0.104411 4 -0.531280 1.029733 -0.438136 -1.118318 5 1.618982 1.541605 -0.251879 -0.842436 6 0.184519 0.937082 0.731000 1.361556 7 -0.326238 0.055676 0.222400 -1.443217 8 -0.756352 0.816454 0.750445 -0.455947
从ndarray
对象创建DataFrame
对象。
表 5-1 列出了DataFrame
函数接受的参数。在表中,“array-like”意味着类似于ndarray
对象的数据结构,例如list
。Index
是pandas
Index
类的一个实例。
表 5-1. DataFrame 函数的参数
参数 | 格式 | 描述 |
data |
ndarray /dict /DataFrame |
DataFrame 的数据;dict 可以包含Series ,ndarray ,list 等 |
index |
Index /array-like |
要使用的索引;默认为range(n) |
columns |
Index /array-like |
要使用的列标题;默认为range(n) |
dtype |
dtype ,默认为None |
要使用/强制的数据类型;否则,它会被推断 |
copy |
bool ,默认为None |
从输入复制数据 |
与结构化数组一样,正如我们已经看到的那样,DataFrame
对象具有可以直接通过分配具有正确数量元素的list
来定义的列名。这说明您可以在需要时定义/更改DataFrame
对象的属性:
In [31]: df.columns = ['No1', 'No2', 'No3', 'No4'] ![1](https://gitee.com/OpenDocCN/ibooker-quant-zh/raw/master/docs/py-fin-2e/img/1.png) In [32]: df Out[32]: No1 No2 No3 No4 0 -1.749765 0.342680 1.153036 -0.252436 1 0.981321 0.514219 0.221180 -1.070043 2 -0.189496 0.255001 -0.458027 0.435163 3 -0.583595 0.816847 0.672721 -0.104411 4 -0.531280 1.029733 -0.438136 -1.118318 5 1.618982 1.541605 -0.251879 -0.842436 6 0.184519 0.937082 0.731000 1.361556 7 -0.326238 0.055676 0.222400 -1.443217 8 -0.756352 0.816454 0.750445 -0.455947 In [33]: df['No2'].mean() ![2](https://gitee.com/OpenDocCN/ibooker-quant-zh/raw/master/docs/py-fin-2e/img/2.png) Out[33]: 0.70103309414564585
通过list
对象指定列标签。
现在选择列变得很容易。
要高效处理金融时间序列数据,必须能够处理时间索引。这也可以被视为pandas
的一项重要优势。例如,假设我们的四个列中的九个数据条目对应于从 2019 年 1 月开始的每月末数据。然后,可以使用date_range()
函数生成DatetimeIndex
对象,如下所示:
In [34]: dates = pd.date_range('2019-1-1', periods=9, freq='M') ![1](https://gitee.com/OpenDocCN/ibooker-quant-zh/raw/master/docs/py-fin-2e/img/1.png) In [35]: dates Out[35]: DatetimeIndex(['2019-01-31', '2019-02-28', '2019-03-31', '2019-04-30', '2019-05-31', '2019-06-30', '2019-07-31', '2019-08-31', '2019-09-30'], dtype='datetime64[ns]', freq='M')
创建一个DatetimeIndex
对象。
表 5-2 列出了date_range
函数的参数。
表 5-2。date_range
函数的参数
参数 | 格式 | 描述 |
start |
string /datetime |
生成日期的左边界 |
end |
string /datetime |
生成日期的右边界 |
periods |
integer /None |
期数(如果start 或end 为None ) |
freq |
string /DateOffset |
频率字符串,例如,5D 代表 5 天 |
tz |
string /None |
本地化索引的时区名称 |
normalize |
bool ,默认为None |
规范化start 和end 为午夜 |
name |
string ,默认为None |
结果索引的名称 |
以下代码将刚刚创建的DatetimeIndex
对象定义为相关的索引对象,从而使原始数据集生成时间序列:
In [36]: df.index = dates In [37]: df Out[37]: No1 No2 No3 No4 2019-01-31 -1.749765 0.342680 1.153036 -0.252436 2019-02-28 0.981321 0.514219 0.221180 -1.070043 2019-03-31 -0.189496 0.255001 -0.458027 0.435163 2019-04-30 -0.583595 0.816847 0.672721 -0.104411 2019-05-31 -0.531280 1.029733 -0.438136 -1.118318 2019-06-30 1.618982 1.541605 -0.251879 -0.842436 2019-07-31 0.184519 0.937082 0.731000 1.361556 2019-08-31 -0.326238 0.055676 0.222400 -1.443217 2019-09-30 -0.756352 0.816454 0.750445 -0.455947
在使用date_range
函数生成DatetimeIndex
对象时,频率参数freq
有多种选择。表 5-3 列出了所有选项。
表 5-3。date_range
函数的频率参数值
别名 | 描述 |
B |
工作日频率 |
C |
自定义工作日频率(实验性的) |
D |
日历日频率 |
W |
周频率 |
M |
月度末频率 |
BM |
工作月末频率 |
MS |
月初频率 |
BMS |
工作月初频率 |
Q |
季度末频率 |
BQ |
工作季度末频率 |
QS |
季度初频率 |
BQS |
工作季度初频率 |
A |
年度末频率 |
BA |
工作年度末频率 |
AS |
年度初频率 |
BAS |
工作年度初频率 |
H |
每小时频率 |
T |
分钟频率 |
S |
每秒频率 |
L |
毫秒 |
U |
微秒 |
在某些情况下,以ndarray
对象的形式访问原始数据集是值得的。例如,values
属性直接提供了对它的访问。
In [38]: df.values Out[38]: array([[-1.74976547, 0.3426804 , 1.1530358 , -0.25243604], [ 0.98132079, 0.51421884, 0.22117967, -1.07004333], [-0.18949583, 0.25500144, -0.45802699, 0.43516349], [-0.58359505, 0.81684707, 0.67272081, -0.10441114], [-0.53128038, 1.02973269, -0.43813562, -1.11831825], [ 1.61898166, 1.54160517, -0.25187914, -0.84243574], [ 0.18451869, 0.9370822 , 0.73100034, 1.36155613], [-0.32623806, 0.05567601, 0.22239961, -1.443217 ], [-0.75635231, 0.81645401, 0.75044476, -0.45594693]]) In [39]: np.array(df) Out[39]: array([[-1.74976547, 0.3426804 , 1.1530358 , -0.25243604], [ 0.98132079, 0.51421884, 0.22117967, -1.07004333], [-0.18949583, 0.25500144, -0.45802699, 0.43516349], [-0.58359505, 0.81684707, 0.67272081, -0.10441114], [-0.53128038, 1.02973269, -0.43813562, -1.11831825], [ 1.61898166, 1.54160517, -0.25187914, -0.84243574], [ 0.18451869, 0.9370822 , 0.73100034, 1.36155613], [-0.32623806, 0.05567601, 0.22239961, -1.443217 ], [-0.75635231, 0.81645401, 0.75044476, -0.45594693]])
数组和数据框
通常情况下,您可以从ndarray
对象中生成DataFrame
对象。但是,您也可以通过使用DataFrame
类的values
属性或NumPy
的np.array()
函数轻松地从DataFrame
生成ndarray
对象。
基本分析
像NumPy
的ndarray
对象一样,pandas
的DataFrame
类内置了许多便利方法。作为入门,考虑info()
方法和+describe()。
In [40]: df.info() ![1](https://gitee.com/OpenDocCN/ibooker-quant-zh/raw/master/docs/py-fin-2e/img/1.png) <class 'pandas.core.frame.DataFrame'> DatetimeIndex: 9 entries, 2019-01-31 to 2019-09-30 Freq: M Data columns (total 4 columns): No1 9 non-null float64 No2 9 non-null float64 No3 9 non-null float64 No4 9 non-null float64 dtypes: float64(4) memory usage: 360.0 bytes In [41]: df.describe() ![2](https://gitee.com/OpenDocCN/ibooker-quant-zh/raw/master/docs/py-fin-2e/img/2.png) Out[41]: No1 No2 No3 No4 count 9.000000 9.000000 9.000000 9.000000 mean -0.150212 0.701033 0.289193 -0.387788 std 0.988306 0.457685 0.579920 0.877532 min -1.749765 0.055676 -0.458027 -1.443217 25% -0.583595 0.342680 -0.251879 -1.070043 50% -0.326238 0.816454 0.222400 -0.455947 75% 0.184519 0.937082 0.731000 -0.104411 max 1.618982 1.541605 1.153036 1.361556
提供有关数据、列和索引的元信息。
为每列提供有用的摘要统计信息(针对数值数据)。
此外,您可以轻松地按列或按行获取和累积和,平均值,如下所示:
In [42]: df.sum() ![1](https://gitee.com/OpenDocCN/ibooker-quant-zh/raw/master/docs/py-fin-2e/img/1.png) Out[42]: No1 -1.351906 No2 6.309298 No3 2.602739 No4 -3.490089 dtype: float64 In [43]: df.mean() ![2](https://gitee.com/OpenDocCN/ibooker-quant-zh/raw/master/docs/py-fin-2e/img/2.png) Out[43]: No1 -0.150212 No2 0.701033 No3 0.289193 No4 -0.387788 dtype: float64 In [44]: df.mean(axis=0) ![2](https://gitee.com/OpenDocCN/ibooker-quant-zh/raw/master/docs/py-fin-2e/img/2.png) Out[44]: No1 -0.150212 No2 0.701033 No3 0.289193 No4 -0.387788 dtype: float64 In [45]: df.mean(axis=1) ![3](https://gitee.com/OpenDocCN/ibooker-quant-zh/raw/master/docs/py-fin-2e/img/3.png) Out[45]: 2019-01-31 -0.126621 2019-02-28 0.161669 2019-03-31 0.010661 2019-04-30 0.200390 2019-05-31 -0.264500 2019-06-30 0.516568 2019-07-31 0.803539 2019-08-31 -0.372845 2019-09-30 0.088650 Freq: M, dtype: float64 In [46]: df.cumsum() ![4](https://gitee.com/OpenDocCN/ibooker-quant-zh/raw/master/docs/py-fin-2e/img/4.png) Out[46]: No1 No2 No3 No4 2019-01-31 -1.749765 0.342680 1.153036 -0.252436 2019-02-28 -0.768445 0.856899 1.374215 -1.322479 2019-03-31 -0.957941 1.111901 0.916188 -0.887316 2019-04-30 -1.541536 1.928748 1.588909 -0.991727 2019-05-31 -2.072816 2.958480 1.150774 -2.110045 2019-06-30 -0.453834 4.500086 0.898895 -2.952481 2019-07-31 -0.269316 5.437168 1.629895 -1.590925 2019-08-31 -0.595554 5.492844 1.852294 -3.034142 2019-09-30 -1.351906 6.309298 2.602739 -3.490089
逐列求和。
逐列平均值。
逐行平均值。
逐列累积和(从第一个索引位置开始)。
DataFrame
对象也按预期理解NumPy
通用函数:
In [47]: np.mean(df) ![1](https://gitee.com/OpenDocCN/ibooker-quant-zh/raw/master/docs/py-fin-2e/img/1.png) Out[47]: No1 -0.150212 No2 0.701033 No3 0.289193 No4 -0.387788 dtype: float64 In [48]: np.log(df) ![2](https://gitee.com/OpenDocCN/ibooker-quant-zh/raw/master/docs/py-fin-2e/img/2.png) /Users/yves/miniconda3/envs/base/lib/python3.6/site-packages/ipykernel_launcher.py:1: RuntimeWarning: invalid value encountered in log """Entry point for launching an IPython kernel. Out[48]: No1 No2 No3 No4 2019-01-31 NaN -1.070957 0.142398 NaN 2019-02-28 -0.018856 -0.665106 -1.508780 NaN 2019-03-31 NaN -1.366486 NaN -0.832033 2019-04-30 NaN -0.202303 -0.396425 NaN 2019-05-31 NaN 0.029299 NaN NaN 2019-06-30 0.481797 0.432824 NaN NaN 2019-07-31 -1.690005 -0.064984 -0.313341 0.308628 2019-08-31 NaN -2.888206 -1.503279 NaN 2019-09-30 NaN -0.202785 -0.287089 NaN In [49]: np.sqrt(abs(df)) ![3](https://gitee.com/OpenDocCN/ibooker-quant-zh/raw/master/docs/py-fin-2e/img/3.png) Out[49]: No1 No2 No3 No4 2019-01-31 1.322787 0.585389 1.073795 0.502430 2019-02-28 0.990616 0.717091 0.470297 1.034429 2019-03-31 0.435311 0.504977 0.676777 0.659669 2019-04-30 0.763934 0.903796 0.820196 0.323127 2019-05-31 0.728890 1.014757 0.661918 1.057506 2019-06-30 1.272392 1.241614 0.501876 0.917843 2019-07-31 0.429556 0.968030 0.854986 1.166857 2019-08-31 0.571173 0.235958 0.471593 1.201340 2019-09-30 0.869685 0.903578 0.866282 0.675238 In [50]: np.sqrt(abs(df)).sum() ![4](https://gitee.com/OpenDocCN/ibooker-quant-zh/raw/master/docs/py-fin-2e/img/4.png) Out[50]: No1 7.384345 No2 7.075190 No3 6.397719 No4 7.538440 dtype: float64 In [51]: 100 * df + 100 ![5](https://gitee.com/OpenDocCN/ibooker-quant-zh/raw/master/docs/py-fin-2e/img/5.png) Out[51]: No1 No2 No3 No4 2019-01-31 -74.976547 134.268040 215.303580 74.756396 2019-02-28 198.132079 151.421884 122.117967 -7.004333 2019-03-31 81.050417 125.500144 54.197301 143.516349 2019-04-30 41.640495 181.684707 167.272081 89.558886 2019-05-31 46.871962 202.973269 56.186438 -11.831825 2019-06-30 261.898166 254.160517 74.812086 15.756426 2019-07-31 118.451869 193.708220 173.100034 236.155613 2019-08-31 67.376194 105.567601 122.239961 -44.321700 2019-09-30 24.364769 181.645401 175.044476 54.405307
逐列平均值。
逐元素自然对数;会发出警告,但计算会继续进行,导致多个NaN
值。
绝对值的逐元素平方根 …
… 以及结果的逐列平均值。
数值数据的线性变换。
NumPy 通用函数
通常情况下,您可以将NumPy
通用函数应用于pandas
的DataFrame
对象,只要它们可以应用于包含相同类型数据的ndarray
对象。
pandas
相当容错,以捕获错误并在相应的数学运算失败时仅放置NaN
值。不仅如此,正如之前简要展示的那样,您还可以在许多情况下像处理完整数据集一样处理这些不完整数据集。这非常方便,因为现实往往被不完整的数据集所表征,这比人们希望的更常见。
Python 金融编程第二版(二)(4)https://developer.aliyun.com/article/1559405