Python 金融交易实用指南(二)(2)https://developer.aliyun.com/article/1523757
DataFrames 中的分组操作
pandas 中的分组操作通常遵循操作的分割-应用-组合过程:
- 首先,数据根据一个或多个键分成组。
- 然后,我们对这些组应用必要的函数来计算所需的结果。
- 最后,我们将它们组合起来构建转换后的数据集。
因此,对单索引 DataFrame 进行分组会构建一个分层 DataFrame。步骤如下:
- 让我们使用
pandas.DataFrame.reset_index(…)
方法从先前的df
DataFrame 中移除所有分层索引:
df = df.reset_index(); df
- 这返回了以下带有整数索引的 DataFrame:
alpha numeric A B 0 a 1 -0.807285 0.170242 1 a 2 0.704596 1.568901 2 a 3 -1.417366 0.573896 3 b 1 1.110121 0.366712 ...
- 让我们使用
pandas.DataFrame.groupby(...)
方法来按alpha
列对A
和B
列进行分组:
grouped = df[['A','B']].groupby(df['alpha']); grouped
- 这产生了以下的
DataFrameGroupBy
对象,随后我们可以对其进行操作:
<pandas.core.groupby.DataFrameGroupBy object at 0x7fd21f24cc18>
- 我们可以使用
DataFrameGroupBy.describe(...)
方法来收集摘要描述性统计信息:
grouped.describe()
- 这产生了以下输出,其中生成了
A
和B
的统计信息,但是按alpha
列分组:
A B alpha a count 3.000000 3.000000 mean -0.506685 0.771013 std 1.092452 0.719863 min -1.417366 0.170242 25% -1.112325 0.372069 50% -0.807285 0.573896 75% -0.051344 1.071398 max 0.704596 1.568901 ...
- 我们可以使用
DataFrameGroupBy.apply(...)
方法应用pandas.DataFrame.unstack(...)
方法,该方法接受不同的函数并将它们应用于grouped
对象的每个组:
grouped.apply(pd.DataFrame.unstack)
- 这产生了以下分层 DataFrame:
alpha a A 0 -0.807285 1 0.704596 2 -1.417366 B 0 0.170242 1 1.568901 2 0.573896 ... dtype: float64
- 还存在
DataFrameGroupBy.agg(...)
方法,它接受函数并使用该方法为每个组的每个列聚合该方法。下一个示例使用mean
方法进行聚合:
grouped[['A', 'B']].agg('mean')
- 输出包含了按
alpha
值分组的A
和B
列的均值:
A B alpha a -0.506685 0.771013 b 0.670435 0.868550 c 0.455688 -0.497468 d -0.786246 0.107246
- 类似的方法是
DataFrameGroupBy.transform(...)
方法,唯一的区别在于 transform 一次只对一列起作用,并返回与系列长度相同的值序列,而 apply 可以返回任何类型的结果:
from scipy import stats grouped[['A', 'B']].transform(stats.zscore)
- 这会为列
A
和B
生成 Z 得分,我们在第二章中解释了这个探索性数据分析:
A B 0 -0.337002 -1.022126 1 1.357964 1.357493 2 -1.020962 -0.335367 3 0.610613 -0.567813 4 -1.410007 1.405598 5 0.799394 -0.837785 6 -1.000000 1.000000 7 1.000000 -1.000000 8 -1.000000 -1.000000 9 1.000000 1.000000
我们现在将学习如何转换 DataFrame 轴索引中的值。
转换 DataFrame 轴索引中的值
让我们首先重新检查我们将在这些示例中使用的 df2
DataFrame:
df2
这包含以下数据:
Columns colA colB colC Index A -2.071652 0.742857 0.632307 B 0.113046 -0.384360 0.414585 C 0.690674 1.511816 2.220732 D 0.184174 -1.069291 -0.994885
我们可以使用 pandas.DataFrame.index
属性重命名索引标签,就像我们之前看到的那样:
df2.index = ['Alpha', 'Beta', 'Gamma', 'Delta']; df2
这会生成以下转换后的 DataFrame:
Columns colA colB colC Alpha -2.071652 0.742857 0.632307 Beta 0.113046 -0.384360 0.414585 Gamma 0.690674 1.511816 2.220732 Delta 0.184174 -1.069291 -0.994885
pandas.Index.map(...)
方法应用于转换索引的函数。
在以下示例中,map
函数取名称的前三个字符并将其设置为新名称:
df2.index = df2.index.map(lambda x : x[:3]); df2
输出如下:
Columns colA colB colC Alp -2.071652 0.742857 0.632307 Bet 0.113046 -0.384360 0.414585 Gam 0.690674 1.511816 2.220732 Del 0.184174 -1.069291 -0.994885
pandas.DataFrame.rename(...)
方法允许我们转换索引名称和列名称,并接受从旧名称到新名称的字典映射:
df2.rename(index={'Alp': 0, 'Bet': 1, 'Gam': 2, 'Del': 3}, columns={'colA': 'A', 'colB': 'B', 'colC': 'C'})
结果 DataFrame 在两个轴上都有新标签:
Columns A B C 0 -2.071652 0.742857 0.632307 1 0.113046 -0.384360 0.414585 2 0.690674 1.511816 2.220732 3 0.184174 -1.069291 -0.994885
通过学习这个课程,我们将学习如何处理 DataFrame 中的缺失数据。
处理 DataFrame 中的缺失数据
缺失数据是数据科学中常见的现象,可能由多种原因导致 - 例如,技术错误,人为错误,市场假期。
过滤掉缺失数据
在处理缺失数据时,第一个选择是删除具有任何缺失数据的所有观察。
此代码块使用 pandas.DataFrame.at[...]
属性修改了 df2
DataFrame,并将一些值设置为 NaN
:
for row, col in [('Bet', 'colA'), ('Bet', 'colB'), ('Bet', 'colC'), ('Del', 'colB'), ('Gam', 'colC')]: df2.at[row, col] = np.NaN df2
修改后的 DataFrame 如下:
Columns colA colB colC Alp -1.721523 -0.425150 1.425227 Bet NaN NaN NaN Gam -0.408566 -1.121813 NaN Del 0.361053 NaN 0.580435
pandas.DataFrame.isnull(...)
方法在 DataFrame 中查找缺失值:
df2.isnull()
结果是一个 DataFrame,其中缺失值为 True
,否则为 False
:
Columns colA colB colC Alp False False False Bet True True True Gam False False True Del False True False
pandas.DataFrame.notnull(...)
方法执行相反操作(检测到非缺失值):
df2.notnull()
输出是以下 DataFrame:
Columns colA colB colC Alp True True True Bet False False False Gam True True False Del True False True
pandas.DataFrame.dropna(...)
方法允许我们删除具有缺失值的行。 附加的 how=
参数控制哪些行被删除。 要删除所有字段都为 NaN
的行,我们执行以下操作:
df2.dropna(how='all')
结果是以下修改后的 DataFrame,其中 Bet
行被移除,因为那是唯一一个所有值都为 NaN
的行:
Columns colA colB colC Alp -1.721523 -0.425150 1.425227 Gam -0.408566 -1.121813 NaN Del 0.361053 NaN 0.580435
将 how=
设置为 any
会删除具有任何 NaN 值的行:
df2.dropna(how='any')
这给我们以下包含所有非 NaN 值的 DataFrame:
Columns colA colB colC Alp -1.721523 -0.42515 1.425227
现在我们将看看如何填充缺失数据。
填充缺失数据
处理缺失数据的第二个选择是使用我们选择的值或使用同一列中的其他有效值来填充缺失值以复制/推断缺失值。
让我们首先重新检查一下 df2
DataFrame:
df2
这产生以下带有一些缺失值的 DataFrame:
Columns colA colB colC Alp -1.721523 -0.425150 1.425227 Bet NaN NaN NaN Gam -0.408566 -1.121813 NaN Del 0.361053 NaN 0.580435
现在,让我们使用 pandas.DataFrame.fillna(...)
方法,使用 method='backfill'
和 inplace=True
参数来使用 backfill
方法从其他值向后填充缺失值并就地更改 DataFrame:
df2.fillna(method='backfill', inplace=True); df2
新的 DataFrame 包含以下内容:
Columns colA colB colC Alp -1.721523 -0.425150 1.425227 Bet -0.408566 -1.121813 0.580435 Gam -0.408566 -1.121813 0.580435 Del 0.361053 NaN 0.580435
(Del,colB)
处的 NaN
值是因为该行后没有观察到值,因此无法执行向后填充。 这可以使用向前填充来修复。
使用函数和映射来转换 DataFrame
pandas DataFrame 的值也可以通过传递函数和字典映射来修改,这些函数和映射作用于一个或多个数据值,并生成新的转换值。
让我们通过添加一个新列 Category
来修改 df2
DataFrame,其中包含离散文本数据:
df2['Category'] = ['HIGH', 'LOW', 'LOW', 'HIGH']; df2
新的 DataFrame 包含以下内容:
Columns colA colB colC Category Alp 1.017961 1.450681 -0.328989 HIGH Bet -0.079838 -0.519025 1.460911 LOW Gam -0.079838 -0.519025 1.460911 LOW Del 0.359516 NaN 1.460911 HIGH
pandas.Series.map(...)
方法接受包含从旧值到新值的映射的字典,并对值进行转换。以下代码片段将 Category
中的文本值更改为单个字符:
df2['Category'] = df2['Category'].map({'HIGH': 'H', 'LOW': 'L'}); df2
更新后的 DataFrame 如下所示:
Columns colA colB colC Category Alp 1.017961 1.450681 -0.328989 H Bet -0.079838 -0.519025 1.460911 L Gam -0.079838 -0.519025 1.460911 L Del 0.359516 NaN 1.460911 H
pandas.DataFrame.applymap(...)
方法允许我们在 DataFrame 中对数据值应用函数。
以下代码应用了 numpy.exp(...)
方法,计算指数:
df2.drop('Category', axis=1).applymap(np.exp)
结果是一个包含原始 DataFrame 值的指数值的 DataFrame(除了 NaN
值):
Columns colA colB colC Alp 2.767545 4.266020 0.719651 Bet 0.923266 0.595101 4.309883 Gam 0.923266 0.595101 4.309883 Del 1.432636 NaN 4.309883
现在我们已经学会了如何转换 DataFrame,我们将看到如何对 DataFrame 中的值进行离散化和分桶。
DataFrame 值的离散化/分桶
实现离散化的最简单方法是创建数值范围,并为落入某个区间的所有值分配一个单独的离散标签。
首先,让我们为我们的使用生成一个随机值 ndarray:
arr = np.random.randn(10); arr
这包括以下内容:
array([ 1.88087339e-01, 7.94570445e-01, -5.97384701e-01, -3.01897668e+00, -5.42185315e-01, 1.10094663e+00, 1.16002554e+00, 1.51491444e-03, -2.21981570e+00, 1.11903929e+00])
pandas.cut(...)
方法可用于离散化这些数值。以下代码使用 bins=
和 labels=[...]
参数将值分为五个离散值,并提供标签:
cat = pd.cut(arr, bins=5, labels=['Very Low', 'Low', 'Med', 'High', 'Very High']); cat
在转换后,我们得到了离散值:
[High, Very High, Med, Very Low, Med, Very High, Very High, High, Very Low, Very High] Categories (5, object): [Very Low < Low < Med < High < Very High]
pandas.qcut(...)
方法类似,但使用四分位数将连续值划分为离散值,以便每个类别具有相同数量的观测值。
以下使用 q=
参数构建了五个离散区间:
qcat = pd.qcut(arr, q=5, labels=['Very Low', 'Low', 'Med', 'High', 'Very High']); qcat
四分位数离散化产生以下类别:
[Med, High, Low, Very Low, Low, High, Very High, Med, Very Low, Very High] Categories (5, object): [Very Low < Low < Med < High < Very High]
以下代码块构建了一个包含原始连续值以及由 cut
和 qcut
生成的类别的 pandas DataFrame:
pd.DataFrame({'Value': arr, 'Category': cat, 'Quartile Category': qcat})
此 DataFrame 允许并列比较:
Category Quartile Category Value 0 High Med 0.188087 1 Very High High 0.794570 2 Med Low -0.597385 3 Very Low Very Low -3.018977 4 Med Low -0.542185 5 Very High High 1.100947 6 Very High Very High 1.160026 7 High Med 0.001515 8 Very Low Very Low -2.219816 9 Very High Very High 1.119039
pandas.Categorical.categories
属性为我们提供了区间范围:
pd.cut(arr, bins=5).categories
在这种情况下,区间/数值范围如下:
Index(['(-3.0232, -2.183]', '(-2.183, -1.347]', '(-1.347, -0.512]', '(-0.512, 0.324]', '(0.324, 1.16]'], dtype='object')
我们也可以检查 qcut
的区间:
pd.qcut(arr, q=5).categories
它们与先前的区间略有不同,并显示如下:
Index(['[-3.019, -0.922]', '(-0.922, -0.216]', '(-0.216, 0.431]', '(0.431, 1.105]', '(1.105, 1.16]'], dtype='object')
现在我们将看到如何对 DataFrame 值进行排列和抽样以生成新的 DataFrame。
对 DataFrame 值进行排列和抽样以生成新的 DataFrame
对可用数据集进行排列以生成新数据集,以及对数据集进行抽样以进行子抽样(减少观测数量)或超抽样(增加观测数量)是统计分析中常见的操作。
首先,让我们生成一个随机值 DataFrame 进行操作:
df = pd.DataFrame(np.random.randn(10,5), index=np.sort(np.random.randint(0, 100, size=10)), columns=list('ABCDE')); df
结果如下:
A B C D E 0 -0.564568 -0.188190 -1.678637 -0.128102 -1.880633 0 -0.465880 0.266342 0.950357 -0.867568 1.504719 29 0.589315 -0.968324 -0.432725 0.856653 -0.683398 ...
当应用于 DataFrame 时,numpy.random.permutation(...)
方法会沿着索引轴随机洗牌,并且可以用于对数据集的行进行置换:
df.loc[np.random.permutation(df.index)]
这产生了以下随机打乱行的 DataFrame:
A B C D E 42 0.214554 1.108811 1.352568 0.238083 -1.090455 0 -0.564568 -0.188190 -1.678637 -0.128102 -1.880633 0 -0.465880 0.266342 0.950357 -0.867568 1.504719 62 -0.266102 0.831051 -0.164629 0.349047 1.874955 ...
我们可以使用 numpy.random.randint(...)
方法在一定范围内生成随机整数,然后使用 pandas.DataFrame.iloc[...]
属性从我们的 DataFrame 中进行随机替换采样(同一观察结果可能会被多次选择
以下代码块随机选择了五行,并进行了替换采样:
df.iloc[np.random.randint(0, len(df), size=5)]
这导致了以下随机子采样的 DataFrame:
A B C D E 54 0.692757 -0.584690 -0.176656 0.728395 -0.434987 98 -0.517141 0.109758 -0.132029 0.614610 -0.235801 29 0.589315 -0.968324 -0.432725 0.856653 -0.683398 35 0.520140 0.143652 0.973510 0.440253 1.307126 62 -0.266102 0.831051 -0.164629 0.349047 1.874955
在接下来的章节中,我们将探索使用 pandas.DataFrames
进行文件操作。
使用 pandas.DataFrames 探索文件操作
pandas 支持将 DataFrames 持久化到纯文本和二进制格式中。常见的文本格式是 CSV 和 JSON 文件,最常用的二进制格式是 Excel XLSX、HDF5 和 pickle。
在本书中,我们专注于纯文本持久化。
CSV 文件
CSV 文件(逗号分隔值 文件)是数据交换标准文件。
Python 金融交易实用指南(二)(4)https://developer.aliyun.com/article/1523759