当前位置:首页 > 话题广场 > 攻略专题 > 手游攻略

100034错误代码专题之数据!数据!数据!没有黏土我无法造出砖来!pandas数据分析

数据!数据!数据!没有黏土,我就做不了砖头!——福尔摩斯

本章介绍了pandas,这是一个侧重于表数据的数据分析库。

pandas是一个强大的工具,它不仅提供了许多实用类和函数,而且很好地封装了来自其他软件包的功能。该工具提供了一个用户接口,能够让用户方便且高效地实现数据分析,特别是金融分析。

本章介绍如下基本数据结构:

本章的组织如下。

DataFrame类

本节首先用简单的小数据集来探索pandas中DataFrame类的基本特性和功能;然后说明如何将NumPy中的ndarray对象转换为DataFrame对象。

基本分析与基本可视化

这两节介绍基本分析与可视化功能(后续的章节将深入介绍这些主题)。

Series类

本节短小精悍,介绍了pandas的Series类。从某种意义上讲,它是DataFrame的一种特例,只有一列数据。

GroupBy操作

DataFrame的优势之一是根据一列或者多列来分组数据。这一节探索pandas的分组能力。

复杂选择

这一节介绍如何使用(复杂)条件,轻松地从DataFrame对象中选择数据。

联接、连接和合并

将不同数据集合而为一是数据分析中的重要操作。pandas提供了实现这一任务的不同选项,本节将对此进行介绍。

性能特征

pandas往往提供实现相同目标的多个选项,这符合Python的常规。本节简单介绍潜在的性能差异。

5.1 DataFrame类

pandas(以及本章)的核心是DataFrame,它是设计用于高效处理表格数据(也就是以列进行组织的数据)的类。为此,DataFrame提供了列标签以及数据集中各行(记录)的索引功能,这与关系数据库的一个表或者Excel电子表格类似。

本节介绍pandas中 DataFrame类的一些根本特性。该类非常复杂且强大,这里只能介绍其中的一小部分功能。后续章节将提供更多示例,揭示其不同方面的特征。

5.1.1 使用DataFrame类的第一步

从最根本的层面上看,DataFrame类设计用来管理具有索引和标签的数据,这些数据与来自SQL数据库表或者电子表格应用中工作表的数据没有太多的不同。考虑如下代码创建的DataFrame对象:

In [1]: import pandas as pd ❶In [2]: df = ([10, 20, 30, 40], ❷ columns=['numbers'], ❸ index=['a', 'b', 'c', 'd']) ❹In [3]: df ❺Out[3]: numbers A 10 b 20 c 30 d 40

❶ 导入pandas。

❷ 定义列表对象形式的数据。

❸ 指定列标签。

❹ 指定索引值/标签。

❺ 显示DataFrame对象的数据以及列和索引标签。

这个简单的例子已经说明了DataFrame在存储数据上的主要特性,具体如下。

  • 数据本身可以用不同组成及类型(列表、元组、ndarray和字典对象都是候选者)展现。
  • 数据以列的方式被组织,可以自定义列名(标签)。
  • 索引可以采用不同的格式(如数值、字符串、时间信息)。

使用这种DataFrame对象总体上相当方便和高效,例如,当您想要进行扩大现有对象等工作时,我推荐你使用DataFrame对象。相比之下,常规的ndarray对象更专门化,也更受限制。与此同时,DataFrame对象往往在计算上和ndarray对象一样高效。下面是简单的例子,说明了DataFrame对象的典型操作:

In [4]: df.index ❶Out[4]: Index(['a', 'b', 'c', 'd'], dtype='object')In [5]: df.columns ❷Out[5]: Index(['numbers'], dtype='object')In [6]: df.loc['c']❸Out[6]: numbers 30 Name: c, dtype: int64In [7]: df.loc[['a', 'd']] ❹Out[7]: numbers a 10 d 40In [8]: df.iloc[1:3] ❺Out[8]: numbers b 20 c 30In [9]: df.sum() ❻Out[9]: numbers 100 dtype: int64In [10]: df.apply(lambda x: x ** 2) ❼Out[10]: numbers a 100 b 400 c 900 d 1600In [11]: df ** 2 ❽Out[11]: numbers a 100 b 400 c 900 d 1600

❶ index属性和Index对象。

❷ columns属性和Index对象。

❸ 选择对应于索引c的值。

❹ 选择对应于索引a和b的两个值。

❺ 通过索引位置选择第二行和第三行。

❻ 计算单列总和。

❼ 使用apply方法,以向量化的方式计算平方值。

❽ 和ndarray对象一样,直接应用向量化。

与NumPy的ndarray对象不同,可以在两个维度上同时扩增DataFrame对象:

In [12]: df['floats'] = , 2.5, 3.5, 4.5) ❶In [13]: dfOut[13]: numbers floats a 10 1.5 b 20 2.5 c 30 3.5 d 40 4.5In [14]: df['floats'] ❷Out[14]: a 1.5 b 2.5 c 3.5 d 4.5 Name: floats, dtype: float64

❶ 添加一个新列,该包含以元组形式提供的浮点数对象。

❷ 选择该列并显示其数据和索引标签。

也可以使用整个DataFrame对象来定义一个新列。在这种情况下,索引自动对齐:

In [15]: df['names'] = (['Yves', 'Sandra', 'Lilli', 'Henry'], index=['d', 'a', 'b', 'c']) ❶In [16]: dfOut[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) ❶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 JilIn [18]: df = df.append(({'numbers': 100, 'floats': 5.75, 'names': 'Jil'}, index=['y',])) ❷In [19]: dfOut[19]: numbers floats names a 10 1.50 Sandra b 20 2.50 Lilli c 30 3.50 Henry d 40 4.50 Yves y 100 5.75 JilIn [20]: df = df.append(({'names': 'Liz'}, index=['z',]), sort=False) ❸In [21]: dfOut[21]: numbers floats names A 10.0 1.50 Sandra b 20.0 2.50 Lilli c 30.0 3.50 Henry d 40.0 4.50 Yves y 100.0 5.75 Jil z NaN NaN LizIn [22]: df.dtypes ❹Out[22]: numbers float64 floats float64 names object dtype: object

❶ 通过一个字典对象附加新行;这是一个临时操作,操作期间索引信息丢失。

❷ 根据带索引信息的DataFrame对象附加行;原始索引信息保留。

❸ 向DataFrame对象附加不完整的数据行,生成NaN值。

❹ 返回各列的不同dtype,这与结构化ndarray对象类似。

尽管此时有遗漏的值,但大部的分方法调用仍然可以正常工作:

In [23]: df[['numbers', 'floats']].mean() ❶Out[23]: numbers 40.00 Floats 3.55 dtype: float64In [24]: df[['numbers', 'floats']].std() ❷Out[24]: numbers 35.355339 Floats 1.662077 dtype: float64

❶ 计算指定两列的均值(忽略带NaN值的行)。

❷ 计算指定两列的标准差(忽略带NaN值的行)。

5.1.2 使用DataFrame类的第二步

本节的例子基于一个包含标准正态分布随机数的ndarray对象。该例探索管理时间序列数据的更多功能,如DatetimeIndex:

In [25]: import numpy as npIn [26]: np.random.seed(100)In [27]: a = np.random.standard_normal((9, 4))In [28]: aOut[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 = (a) ❶In [30]: dfOut[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函数使用的参数。表中,“类似数组”意味着和ndarray对象类似的数据结构——如列表对象。“索引”是pandas的Index类的一个实例。

表5-1 DataFrame函数参数

和结构数组一样,我们已经了解,DataFrame对象可以通过指定一个具有合适数量元素的列表来直接定义列名。下面的例子说明,我们可以随时定义/更改DataFrame对象的属性:

In [31]: df.columns = ['No1', 'No2', 'No3', 'No4'] ❶In [32]: dfOut[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.455947In [33]: df['No2'].mean() ❷Out[33]: 0.7010330941456459

❶ 通过列表对象指定列标签。

❷ 选择一列很容易。

为了高效处理金融事件序列数据,我们还必须很好地处理时间索引。这也可以视为pandas的一个重要优势。例如,假定分为4列的9个数据项对应于2019年1月开始的月底数据。然后,用date_range生成一个DatetimeIndex对象:

In [34]: dates = ('2019-1-1', periods=9, freq='M') ❶In [35]: datesOut[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函数参数

下列代码将刚刚创建的DatetimeIndex对象定义为相关的索引对象,并建立原始数据集的一个时间序列:

In [36]: df.index = datesIn [37]: dfOut[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有多种选择。表6-3列出了其所有选项。

表5-3 date_range函数的频率参数值(略)

在某些情况下,以ndarray对象的形式访问原始数据是有好处的。values属性提供直接访问的方式:

In [38]: df.valuesOut[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]])

数组和DataFrame

通常可以从一个ndarray对象生成DataFrame对象。也可以通过NumPy的np.array函数或DataFrame类的values属性,来从DataFrame对象生成ndarray对象。

5.2 基本分析

和NumPy的ndarray类一样,pandas的DataFrame类有多个便利的内建方法。首先考虑info和describe方法:

In [40]: df.info() ❶ <class ';> 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 bytesIn [41]: df.describe() ❷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 [43]: df.sum() ❶Out[43]: No1 -1.351906 No2 6.309298 No3 2.602739 No4 -3.490089 dtype: float64In [44]: df.mean()Out[44]: No1 -0.150212 No2 0.701033 No3 0.289193 No4 -0.387788 dtype: float64In [45]: df.mean(axis=0) ❷Out[45]: No1 -0.150212 No2 0.701033 No3 0.289193 No4 -0.387788 dtype: float64In [46]: df.mean(axis=1) ❸Out[46]: 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: float64In [47]: df.cumsum() ❹Out[47]: 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 [48]: np.mean(df) ❶Out[48]: No1 -0.150212 No2 0.701033 No3 0.289193 No4 -0.387788 dtype: float64In [49]: np.log(df) ❷Out[49]: 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 NaNIn [50]: np.sqrt(abs(df)) ❸Out[50]: 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.675238In [51]: np.sqrt(abs(df)).sum() ❹Out[51]: No1 7.384345 No2 7.075190 No3 6.397719 No4 7.538440 dtype: float64In [52]: 100 * df + 100 ❺Out[52]: 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通用函数适用于ndarray对象的所有情况下,都可以将这些函数应用到包含相同数据的pandas的DataFrame对象。

pandas有相当强的容错能力,它可以捕捉错误,在对应数学运算失败时放入NaN值。不仅如此,正如前面已经展示的,在许多情况下,它还可以将这些不完整的数据集当成完整数据集来使用。这种做法很方便,因为现实中不完整数据集往往比人们预想的要多。

5.3 基本可视化

如果数据存储在DataFrame对象中,那么数据图表的绘制通常只需要一行代码(见图5-1)。

图5-1 DataFrame对象的折线图

In [53]: from pylab import plt, mpl ❶ ('seaborn') ❶ m[';] = 'serif' ❶ %matplotlib inlineIn [54]: df.cumsum().plot(lw=2.0, figsize=(10, 6)); ❷

❶ 定制绘图样式。

❷ 绘制4列累计总和的折线图。

基本上,pandas提供了专为DataFrame对象设计的matplotplib(参见第7章)包装器。plot()方法的参数如表5-4所示。

表5-4 plot方法参数(略)

再举个例子,考虑相同数据的柱状图(见图5-2):

图5-2 DataFrame对象的柱状图

In [55]: df.(figsize=(10, 6), rot=15); ❶# df.plot(kind='bar', figsize=(10, 6)) ❷

❶ ()绘制柱状图。

❷ 替代语法:使用kind参数改变绘图类型。

5.4 Series类

迄今为止,本章主要介绍了pandas的DataFrame类。Series是pandas自带的另一个重要的类。它的特点是只有一列数据。从这个意义上讲,它是DataFrame类的特例,两者之间有许多共同特性和功能,但不完全相同。从多列的DataFrame对象上选取一列,可以得到Series对象:

In [56]: type(df)Out[56]: [57]: S = (0, 15, 7), name='series')In [58]: SOut[58]: 0 0.0 1 2.5 2 5.0 3 7.5 4 10.0 5 12.5 6 15.0 Name: series, dtype: float64In [59]: type(S)Out[59]: [60]: s = df['No1']In [61]: sOut[61]: 2019-01-31 -1.749765 2019-02-28 0.981321 2019-03-31 -0.189496 2019-04-30 -0.583595 2019-05-31 -0.531280 2019-06-30 1.618982 2019-07-31 0.184519 2019-08-31 -0.326238 2019-09-30 -0.756352 Freq: M, Name: No1, dtype: float64In [62]: type(s)Out[62]:

DataFrame的主要方法也可用于Series对象,我们以mean和plot方法为例(见图5-3):

In [63]: s.mean()Out[63]: -0.319458In [64]: s.plot(lw=2.0, figsize=(10, 6));

图5-3 Series对象的折线图

5.5 GroupBy操作

pandas具备强大而灵活的分组功能,工作方式类似于SQL中的分组和Microsoft Excel中的透视表。为了进行分组,我们添加一列,表示对应索引数据所属的季度:

In [65]: df['Quarter'] = ['Q1', 'Q1', 'Q1', 'Q2', 'Q2', 'Q2', 'Q3', 'Q3', 'Q3'] dfOut[65]: No1 No2 No3 No4 Quarter 2019-01-31 -1.749765 0.342680 1.153036 -0.252436 Q1 2019-02-28 0.981321 0.514219 0.221180 -1.070043 Q1 2019-03-31 -0.189496 0.255001 -0.458027 0.435163 Q1 2019-04-30 -0.583595 0.816847 0.672721 -0.104411 Q2 2019-05-31 -0.531280 1.029733 -0.438136 -1.118318 Q2 2019-06-30 1.618982 1.541605 -0.251879 -0.842436 Q2 2019-07-31 0.184519 0.937082 0.731000 1.361556 Q3 2019-08-31 -0.326238 0.055676 0.222400 -1.443217 Q3 2019-09-30 -0.756352 0.816454 0.750445 -0.455947 Q3

现在,我们可以根据Quarter列分组,并输出单独组的统计量:

In [66]: groups = df.groupby('Quarter') ❶In [67]: grou() ❷Out[67]: Quarter Q1 3 Q2 3 Q3 3 dtype: int64In [68]: grou() ❸Out[68]: No1 No2 No3 No4Quarter Q1 -0.319314 0.370634 0.305396 -0.295772 Q2 0.168035 1.129395 -0.005765 -0.688388 Q3 -0.299357 0.603071 0.567948 -0.179203In [69]: grou() ❹Out[69]: No1 No2 No3 No4 Quarter Q1 0.981321 0.514219 1.153036 0.435163 Q2 1.618982 1.541605 0.672721 -0.104411 Q3 0.184519 0.937082 0.750445 1.361556In [70]: grou([min, max]).round(2) ❺Out[70]: No1 No2 No3 No4 Min max min max min max min max Quarter Q1 -1.75 0.98 0.26 0.51 -0.46 1.15 -1.07 0.44 Q2 -0.58 1.62 0.82 1.54 -0.44 0.67 -1.12 -0.10 Q3 -0.76 0.18 0.06 0.94 0.22 0.75 -1.44 1.36

❶ 根据Quarter列分组。

❷ 给出每个分组的行数。

❸ 给出每的列均值。

❹ 给出每列的最大值。

❺ 给出每列的最小值和最大值。

分组也可以在多列上进行。为此,我们添加另一列,表示索引日期的月份是奇数还是偶数:

In [71]: df['Odd_Even'] = ['Odd', 'Even', 'Odd', 'Even', 'Odd', 'Even', 'Odd', 'Even', 'Odd']In [72]: groups = df.groupby(['Quarter', 'Odd_Even'])In [73]: grou()Out[73]: Quarter Odd_Even Q1 Even 1 Odd 2 Q2 Even 2 Odd 1 Q3 Even 1 Odd 2 dtype: int64In [74]: groups[['No1', 'No4']].aggregate([sum, np.mean])Out[74]: No1 No4 Sum mean sum mean Quarter Odd_Even Q1 Even 0.981321 0.981321 -1.070043 -1.070043 Odd -1.939261 -0.969631 0.182727 0.091364 Q2 Even 1.035387 0.517693 -0.946847 -0.473423 Odd -0.531280 -0.531280 -1.118318 -1.118318 Q3 Even -0.326238 -0.326238 -1.443217 -1.443217 Odd -0.571834 -0.285917 0.905609 0.452805

对pandas和DataFrame对象使用的介绍到此结束,下面将这组工具应用到现实世界的金融数据中。

5.6 复杂选择

数据选择往往是通过列值上的条件公式实现的,也可以通过符合逻辑的方式来组合多个条件。考虑如下数据集。

In [75]: data = np.random.standard_normal((10, 2)) ❶In [76]: df = (data, columns=['x', 'y']) ❷In [77]: df.info() ❷ <class ';> RangeIndex: 10 entries, 0 to 9 Data columns (total 2 columns): X 10 non-null float64 Y 10 non-null float64 dtypes: float64(2) memory usage: 240.0 bytesIn [78]: df.head() ❸Out[78]: x y 0 1.189622 -1.690617 1 -1.356399 -1.232435 2 -0.544439 -0.668172 3 0.007315 -0.612939 4 1.299748 -1.733096In [79]: df.tail() ❹Out[79]: x y 5 -0.983310 0.357508 6 -1.613579 1.470714 7 -1.188018 -0.549746 8 -0.940046 -0.827932 9 0.108863 0.507810

❶ 包含标准正态分布随机数的ndarray对象。

❷ 包含相同随机数的DataFrame对象。

❸ 通过head方法取得前5行。

❹ 通过tail方法取得最后5行。

下面的代码说明了Python比较运算符和逻辑运算符在两列值上的应用:

In [80]: df['x'] > 0.5 ❶Out[80]: 0 True 1 False 2 False 3 False 4 True 5 False 6 False 7 False 8 False 9 False Name: x, dtype: boolIn [81]: (df['x'] > 0) & (df['y'] < 0) ❷Out[81]: 0 True 1 False 2 False 3 True 4 True 5 False 6 False 7 False 8 False 9 False dtype: boolIn [82]: (df['x'] > 0) | (df['y'] < 0) ❸Out[82]: 0 True 1 True 2 True 3 True 4 True 5 False 6 False 7 True 8 True 9 True dtype: bool

❶ 检查x列的值是否大于0.5。

❷ 检查是否x列的值为正且y列的值为负。

❸ 检查是否x列的值为正或y列的值为负。

利用得到的布尔型Series对象,就可以很容易地实现复杂数据(行)的选择。另外,也可以使用query方法并以字符串对象的形式来传递条件:

In [83]: df[df['x'] > 0] ❶Out[83]: x y 0 1.189622 -1.690617 3 0.007315 -0.612939 4 1.299748 -1.733096 9 0.108863 0.507810In [84]: df.query('x > 0') ❶Out[84]: x y 0 1.189622 -1.690617 3 0.007315 -0.612939 4 1.299748 -1.733096 9 0.108863 0.507810In [85]: df[(df['x'] > 0) & (df['y'] < 0)] ❷Out[85]: x y 0 1.189622 -1.690617 3 0.007315 -0.612939 4 1.299748 -1.733096In [86]: df.query('x > 0 & y < 0') ❷Out[86]: x y 0 1.189622 -1.690617 3 0.007315 -0.612939 4 1.299748 -1.733096In [87]: df[ > 0) | < 0)] ❸Out[87]: x y 0 1.189622 -1.690617 1 -1.356399 -1.232435 2 -0.544439 -0.668172 3 0.007315 -0.612939 4 1.299748 -1.733096 7 -1.188018 -0.549746 8 -0.940046 -0.827932 9 0.108863 0.507810

❶ 所有第x列的值大于0.5的行。

❷ 所有第x列的值为正且y的列值为负的行。

❸ 所有第x列的值为正或y的列值为负的行(各列通过对应属性访问)。

比较运算符也可以一次性应用到整个DataFrame对象上:

In [88]: df > 0 ❶Out[88]: x y 0 True False 1 False False 2 False False 3 True False 4 True False 5 False True 6 False True 7 False False 8 False False 9 True TrueIn [89]: df[df > 0] ❷Out[89]: x y 0 1.189622 NaN 1 NaN NaN 2 NaN NaN 3 0.007315 NaN 4 1.299748 NaN 5 NaN 0.357508 6 NaN 1.470714 7 NaN NaN 8 NaN NaN 9 0.108863 0.507810

❶ DataFrame对象中的哪些值为正?

❷ 选择所有满足要求的值,并将其他值设为NaN。

5.7 联接、连接和合并

本节简单介绍连接DataFrame对象形式的两个简单数据集的不同方法。这两个简单数据集如下所示:

In [90]: df1 = (['100', '200', '300', '400'], index=['a', 'b', 'c', 'd'], columns=['A',])In [91]: df1Out[91]: A a 100 b 200 c 300 d 400In [92]: df2 = (['200', '150', '50'], index=['f', 'b', 'd'], columns=['B',])In [93]: df2Out[93]: B f 200 b 150 d 50

5.7.1 联接

联接(Concatenation)或者附加(Appending)本质上指的是将一个DataFrame对象中的行添加到另一个DataFrame对象上,这可通过append方法或者函数完成。需要认真考虑的是索引值的处理方法:

In [94]: d(df2, sort=False) ❶Out[94]: A B a 100 NaN b 200 NaN c 300 NaN d 400 NaN f NaN 200 b NaN 150 d NaN 50In [95]: d(df2, ignore_index=True, sort=False) ❷Out[95]: A B 0 100 NaN 1 200 NaN 2 300 NaN 3 400 NaN 4 NaN 200 5 NaN 150 6 NaN 50In [96]: ((df1, df2), sort=False) ❸Out[96]: A B a 100 NaN b 200 NaN c 300 NaN d 400 NaN f NaN 200 b NaN 150 d NaN 50In [97]: ((df1, df2), ignore_index=True, sort=False) ❹Out[97]: A B 0 100 NaN 1 200 NaN 2 300 NaN 3 400 NaN 4 NaN 200 5 NaN 150 6 NaN 50

❶ 将df2的数据附加到df1中作为新行。

❷ 完成同样的工作,但忽略索引。

❸ 与第一个附加操作效果相同。

❹ 与第二个附加操作效果相同。

5.7.2 连接

连接两个数据集时,DataFrame对象的顺序很重要。只有第一个DataFrame对象的索引值被使用,这种默认行为称为左连接(left join):

In [98]: d(df2) ❶Out[98]: A B a 100 NaN b 200 150 c 300 NaN d 400 50In [99]: d(df1) ❷Out[99]: B A f 200 NaN b 150 200 d 50 400

❶ df1的索引值有意义。

❷ df2的索引值有意义。

共有4种不同的连接方法,每种方法都导致索引值和对应数据行的不同处理行为:

In [100]: d(df2, how='left') ❶Out[100]: A B a 100 NaN b 200 150 c 300 NaN d 400 50In [101]: d(df2, how='right') ❷Out[101]: A B f NaN 200 b 200 150 d 400 50In [102]: d(df2, how='inner') ❸Out[102]: A B b 200 150 d 400 50In [103]: d(df2, how='outer') ❹Out[103]: A B a 100 NaN b 200 150 c 300 NaN d 400 50 f NaN 200

❶ 左连接是默认操作。

❷ 右连接相当于颠倒了DataFrame对象的顺序。

❸ 内连接只保留在两个索引中都存在的索引值。

❹ 外连接保留两个索引中的所有索引值。

连接也可以基于空的DataFrame对象。在这种情况下,列按顺序被创建,这与左连接类似:

In [104]: df = ()In [105]: df['A'] = df1['A'] ❶In [106]: dfOut[106]: A a 100 b 200 c 300 d 400In [107]: df['B'] = df2 ❷In [108]: dfOut[108]: A B a 100 NaN b 200 150 c 300 NaN d 400 50

❶ df1为第一列A。

❷ df2为第二列B。

使用字典来组合数据集可以得到类似于外连接的结果,因为列是同时创建的:

In [109]: df = ({'A': df1['A'], 'B': df2['B']})In [110]: dfOut[110]: A B a 100 NaN b 200 150 c 300 NaN d 400 50 f NaN 200

❶ DataFrame对象的各列用作字典对象的值

5.7.3 合并

连接操作根据待连接的DataFrame对象的索引进行,而合并操作通常在两个数据集共享的某列上进行。为此,在两个原始的DataFrame对象上添加一个新列C:

In [111]: c = ([250, 150, 50], index=['b', 'd', 'c']) df1['C'] = c df2['C'] = cIn [112]: df1Out[112]: A C a 100 NaN b 200 250.0 c 300 50.0 d 400 150.0In [113]: df2Out[113]: B C f 200 NaN b 150 250.0 d 50 150.0

默认情况下,这种合并操作根据单一共享列C进行。但也存在其他可能,例如外合并:

In [114]: (df1, df2) ❶Out[114]: A C B 0 100 NaN 200 1 200 250.0 150 2 400 150.0 50In [115]: (df1, df2, on='C') ❶Out[115]: A C B 0 100 NaN 200 1 200 250.0 150 2 400 150.0 50In [116]: (df1, df2, how='outer')Out[116]: A C B 0 100 NaN 200 1 200 250.0 150 2 300 50.0 NaN 3 400 150.0 50

❶ 默认合并根据C列进行。

❷ 也可以进行外合并,保留所有数据行。

可用的合并操作类型还有很多,下面的代码演示了其中的几种:

In [117]: (df1, df2, left_on='A', right_on='B')Out[117]: A C_x B C_y 0 200 250.0 200 NaNIn [118]: (df1, df2, left_on='A', right_on='B', how='outer')Out[118]: A C_x B C_y 0 100 NaN NaN NaN 1 200 250.0 200 NaN 2 300 50.0 NaN NaN 3 400 150.0 NaN NaN 4 NaN NaN 150 250.0 5 NaN NaN 50 150.0In [119]: (df1, df2, left_index=True, right_index=True)Out[119]: A C_x B C_y b 200 250.0 150 250.0 d 400 150.0 50 150.0In [120]: (df1, df2, on='C', left_index=True)Out[120]: A C B f 100 NaN 200 b 200 250.0 150 d 400 150.0 50In [121]: (df1, df2, on='C', right_index=True)Out[121]: A C B a 100 NaN 200 b 200 250.0 150 d 400 150.0 50In [122]: (df1, df2, on='C', left_index=True, right_index=True)Out[122]: A C B b 200 250.0 150 d 400 150.0 50

5.8 性能特征

本章的许多例子说明,用pandas实现同一个目标往往有多种选择。本节以按元素加总两列数值为例来这些选择进行对比。首先用NumPy生成数据集:

In [123]: data = np.random.standard_normal((1000000, 2)) ❶In [124]: da ❶Out[124]: 16000000In [125]: df = (data, columns=['x', 'y']) ❷In [126]: df.info() ❷ <class ';> RangeIndex: 1000000 entries, 0 to 999999 Data columns (total 2 columns): X 1000000 non-null float64 Y 1000000 non-null float64 dtypes: float64(2) memory usage: 15.3 MB

❶ 包含随机数的ndarray对象。

❷ 包含随机数的DataFrame对象。

其次,实现手边任务的某选择性能还不错:

In [127]: %time res = df['x'] + df['y'] ❶ CPU times: user 7.35 ms, sys: 7.43 ms, total: 14.8 ms Wall time: 7.48 msIn [128]: res[:3]Out[128]: 0 0.387242 1 -0.969343 2 -0.863159 dtype: float64In [129]: %time res = df.sum(axis=1) ❷ CPU times: user 130 ms, sys: 30.6 ms, total: 161 ms Wall time: 101 msIn [130]: res[:3]Out[130]: 0 0.387242 1 -0.969343 2 -0.863159 dtype: float64In [131]: %time res = df.values.sum(axis=1) ❸ CPU times: user 50.3 ms, sys: 2.75 ms, total: 53.1 ms Wall time: 27.9 msIn [132]: res[:3]Out[132]: array([ 0.3872424 , -0.96934273, -0.86315944])In [133]: %time res = np.sum(df, axis=1) ❹ CPU times: user 127 ms, sys: 15.1 ms, total: 142 ms Wall time: 73.7 msIn [134]: res[:3]Out[134]: 0 0.387242 1 -0.969343 2 -0.863159 dtype: float64In [135]: %time res = np.sum, axis=1) ❺ CPU times: user 49.3 ms, sys: 2.36 ms, total: 51.7 ms Wall time: 26.9 msIn [136]: res[:3]Out[136]: array([ 0.3872424 , -0.96934273, -0.86315944])

❶ 直接使用列(Series对象)是最快的方法。

❷ 调用DataFrame对象的sum方法来计算总和。

❸ 调用ndarray对象的sum方法来计算总和。

❹ 调用DataFrame对象的np.sum方法来计算总和。

❺ 调用ndarray对象的np.sum方法来计算总和。

最后,还有两种选择可以计算元素和,它们分别基于eval和apply方法[1]:

The application of the eval() method requires the numexpr package to be in [137]: %time res = df.eval('x + y') ❶ CPU times: user 25.5 ms, sys: 17.7 ms, total: 43.2 ms Wall time: 22.5 msIn [138]: res[:3]Out[138]: 0 0.387242 1 -0.969343 2 -0.863159 dtype: float64In [139]: %time res = df.apply(lambda row: row['x'] + row['y'], axis=1) CPU times: user 19.6 s, sys: 83.3 ms, total: 19.7 s Wall time: 19.9 sIn [140]: res[:3]Out[140]: 0 0.387242 1 -0.969343 2 -0.863159 dtype: float64

❶ eval是专门用于计算(复杂)数值表达式的方法,可以直接处理数据列。

❷ 最慢的选择是逐行使用apply方法,这就像在Python级别上循环访问所有行。

明智的选择

pandas常常为实现统一目标提供多个选择。如果不确定使用哪一个,那么在时间是关键因素的情况下,比较各种选择,选择性能最好的一种。在上面这个简单的例子里,不同选择的执行时间相差好几个数量级。

5.9 结语

pandas是强大的数据分析工具,已经成为所谓的PyData栈的核心软件包。它的DataFrame类特别适合于处理任何类型的表格数据。这些对象上的大部分操作是向量化的,这不仅可以得到和NumPy类似的简洁代码,而且还可以得到高性能。此外,pandas能够很方便地处理不完整数据集(NumPy做不到这一点)。pandas和DataFrame类将是本书后续多个章节的核心,在必要时将使用并介绍它们的更多特性。

5.10 延伸阅读

pandas是一个开源项目,你既可以阅读在线文档,也可以下载其PDF版本[2]。官网上还提供了一些附加资源。

至于NumPy,我们建议的pandas参考书如下。

  • McKinney, Wes (2017). Python for Data Analysis. Sebastopol, CA: O’Reilly.
  • VanderPlas, Jake (2016). Python Data Science Handbook. Sebastopol, CA: O’Reilly.

[1] 要想使用eval方法需要安装numexpr软件包。——原注

[2] 在本书编写期间,PDF文档共有2500页。——原注

本文摘自《Python金融大数据分析 第2版》[德] 伊夫·希尔皮斯科(Yves Hilpisch) 著,姚军 译

  • 金融科技算法交易量化金融教程书籍
  • 详细讲解使用Python分析处理金融大数据的专业图书
  • 将人工智能应用于金融开发的实战指南,金融应用开发领域从业人员的常备读物

Python已成为数据驱动AI、金融优先选择的编程语言。现在,一些大型的投资银行和对冲资金均使用Python及其生态系统来构建核心交易与风险管理系统。在本书中,作者向开发人员和量化分析人员介绍了使用Python程序库与工具,完成金融数据科学、算法交易和计算金融任务的方法。

Python与金融:Python交互式金融分析与程序开发入门。基本知识:学习Python数据类型与结构、NumPy、pandas及其DataFrame类、面向对象编程。金融数据科学:探索用于金融时间序列数据、I/O操作、推断统计学和机器学习的Python技术与程序库。算法交易:使用Python来验证和部署自动算法交易策略。衍生品分析:开发灵活、强大的Python期权、衍生品定价和风险管理程序库。

1.《100034错误代码专题之数据!数据!数据!没有黏土我无法造出砖来!pandas数据分析》援引自互联网,旨在传递更多网络信息知识,仅代表作者本人观点,与本网站无关,侵删请联系页脚下方联系方式。

2.《100034错误代码专题之数据!数据!数据!没有黏土我无法造出砖来!pandas数据分析》仅供读者参考,本网站未对该内容进行证实,对其原创性、真实性、完整性、及时性不作任何保证。

3.文章转载时请保留本站内容来源地址,https://www.lu-xu.com/gl/2081487.html

上一篇

亮相建党百年庆祝大会的新装备,与两栖作战有关

下一篇

【2018msi季中邀请赛】LOL-MSI:季中冠军赛六强出炉,对抗赛首战RNG对决DK

【100034错误代码】专题英雄联盟手游pada masa ini akaun anda dihadkan.tidak dapat log masuk解决方法

【100034错误代码】专题英雄联盟手游pada masa ini akaun anda dihadkan.tidak dapat log masuk解决方法

100034错误代码相关介绍,在登陆英雄联盟手游时,极少数玩家会遇到“pada masa ini akaun anda di dapat log masuk”报错,这句话的意思是目前你的账户无法登陆,那么造成这个问题的原因是什么呢...

100034错误代码,干货看这篇!英雄联盟手游登陆失败问题有哪些?所有问题解决方法大全

100034错误代码,干货看这篇!英雄联盟手游登陆失败问题有哪些?所有问题解决方法大全

100034错误代码相关介绍,Lol英雄联盟手游着陆失败的问题是什么?在英雄联盟手游中,今天部分外服已经开始了公考。由于尚未在中国大陆举行公考,很多国内玩家出去玩外服体验游戏,在下载注册注册的过程中,玩家们遇到了各种登陆失败的问题...

100034错误代码,干货看这篇!践行强军目标 争做强军先锋

100034错误代码,干货看这篇!践行强军目标 争做强军先锋

100034错误代码相关介绍,中央军委决定表彰全军践踏强军目标标兵。根据评选表彰工作的安排,经过各主要单位和军委机关有关部门的推荐、审查组的审查,产生了表彰候选人。 为充分发扬民主,广泛听取意见,接受官兵监督,现将其中可公示的12...

【100034错误代码】专题践行强军目标 争做强军先锋——全军践行强军目标标兵候选对象简要事迹

【100034错误代码】专题践行强军目标 争做强军先锋——全军践行强军目标标兵候选对象简要事迹

100034错误代码相关介绍,(9500.163.com) 践行强军目标 争做强军先锋 ——全军践行强军目标标兵候选对象简要事迹 (9501.163.com) 中央军委决定表彰全军践行强军目标标兵。 达到当天最大量API KEY...

100034错误代码,干货看这篇!热带地区类鼻疽感染引起重症肺炎伴多器官功能损伤1例

100034错误代码,干货看这篇!热带地区类鼻疽感染引起重症肺炎伴多器官功能损伤1例

100034错误代码相关介绍,患者男,37岁,“发烧4天,胸敏、气体促进呕吐1天”,2016年11月01日入院,4天前感冒后发烧,最高体温达到39.8。 外院诊断“社区获得性肺炎”,予“阿奇霉素”抗感染及“布洛芬”解热等治疗。治疗...

100034错误代码,干货看这篇!自噬在年龄相关性黄斑变性和视网膜脱离中的研究进展

100034错误代码,干货看这篇!自噬在年龄相关性黄斑变性和视网膜脱离中的研究进展

100034错误代码相关介绍,本文总结了年龄相关黄斑变性(AGE-Related Macular Degeneration,AMD)和视网膜脱离(retinal detachment,RD)中的作用,以及自动变异对这些疾病的影响。...

关于100034错误代码我想说“爷青回”!如何抢先体验《英雄联盟》手游?这份攻略送给你

  • 关于100034错误代码我想说“爷青回”!如何抢先体验《英雄联盟》手游?这份攻略送给你
  • 关于100034错误代码我想说“爷青回”!如何抢先体验《英雄联盟》手游?这份攻略送给你
  • 关于100034错误代码我想说“爷青回”!如何抢先体验《英雄联盟》手游?这份攻略送给你

100034错误代码看这里!“爷青回”!如何抢先体验《英雄联盟》手游?这份攻略送给你

  • 100034错误代码看这里!“爷青回”!如何抢先体验《英雄联盟》手游?这份攻略送给你
  • 100034错误代码看这里!“爷青回”!如何抢先体验《英雄联盟》手游?这份攻略送给你
  • 100034错误代码看这里!“爷青回”!如何抢先体验《英雄联盟》手游?这份攻略送给你