Pandas (Python Data Analysis Library)

Table of Contents

1. Pandas 简介

Pandas is a software library written for the Python programming language for data manipulation and analysis.

The name is derived from the term "panel data", an econometrics term for multidimensional, structured data sets. Pandas 是“pan(el)-da(ta)-s”的缩写。

后面的代码测试假定引入了以下包:

>>> import numpy as np
>>> import pandas as pd

参考:https://pandas.pydata.org/

2. 数据结构 Series

Series 是 Pandas 中基本的数据结构,和 NumPy 中的数组类似。

2.1. 创建 Series

创建 Series 对象的简单办法是使用一个 Python 列表初始化它。如:

>>> s = pd.Series([1, 2, 3, 4])     # 创建Series对象,也可写为 s = pd.Series(np.arange(1, 5))
>>> s
0    1
1    2
2    3
3    4
dtype: int64

上面的输出中包含两列,第 1 列不是 Series 对象,而是索引标签(index label),第 2 列是 Series 对象的值。如果我们创建 Series 对象时不指定索引,则会自动创建以 0 开始,每次增加 1 的索引。

把索引标签放入 [] 中可访问 Series 对象中的元素,如果 [] 中是索引标签的列表则会基于这些索引值返回新的 Series 对象。如:

>>> s[1]            # 访问Series对象中的元素
2
>>> s[[1,3]]        # 返回另一个Series对象,它的索引标签为1和3。
1    2
3    4
dtype: int64
>>> s[[1,3]][1]
2
>>> s[[1,3]][3]
4

创建 Series 时,可以通过 index 参数明确地指定索引标签(不一定是整数),如:

>>> s = pd.Series([1, 2, 3, 4],
                  index = ['a', 'b', 'c', 'd'])       # 创建Series时,通过index明确指定索引标签
>>> s
a    1
b    2
c    3
d    4
dtype: int64
>>> s['a']
1
>>> s[['a', 'd']]
a    1
d    4
dtype: int64

此外,还可以从 Python Dictionary 直接创建 Series 对象,如:

>>> s = pd.Series({'a': 1, 'b': 2, 'c': 3, 'd': 4})
>>> s
a    1
b    2
c    3
d    4
dtype: int64

Pandas 比较智能,尽管在创建 Series 时指定的索引标签不是整数,我们还是可以通过以 0 开始的整数索引来访问它们。如:

>>> s = pd.Series([1, 2, 3, 4],
                  index = ['a', 'b', 'c', 'd'])
>>> s[2]            # 索引标签不是整数,但也可以通过以0开始的整数索引来访问它们
3
>>> s[[1, 2]]
b    2
c    3
dtype: int64

2.2. 通过.loc[](by label)和.iloc[](by location)访问 Series

前面介绍过,把索引标签(index label)或者 0 开始的整数索引放到 [] 中可以访问到 Series 中的元素。如:

>>> s2 = pd.Series([1, 2, 3, 4],
                   index = ['a', 'b', 'c', 'd'])
>>> s2['b']            # 通过索引标签访问Series中的元素
2
>>> s2[['b', 'd']]     # 指定索引标签数组可以得到另一个Series
b    2
d    4
dtype: int64
>>> s2[1]              # 通过0开始的整数索引访问Series中的元素
2
>>> s2[[1, 3]]         # 指定整数索引数组可以得到另一个Series
b    2
d    4
dtype: int64

但当 Series 的索引标签是数字,但不是以 0 开始的连续整数时会带来困惑。如:

>>> s3 = pd.Series([1, 2, 3], index=[10, 11, 12])
>>> s3[10]
1
>>> s3[0]            # 尽管指定的是整数索引,但却无法访问对应位置中的Series元素
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/usr/local/lib/python2.7/site-packages/pandas/core/series.py", line 623, in __getitem__
    result = self.index.get_value(self, key)
  File "/usr/local/lib/python2.7/site-packages/pandas/core/indexes/base.py", line 2560, in get_value
    tz=getattr(series.dtype, 'tz', None))
  File "pandas/_libs/index.pyx", line 83, in pandas._libs.index.IndexEngine.get_value
  File "pandas/_libs/index.pyx", line 91, in pandas._libs.index.IndexEngine.get_value
  File "pandas/_libs/index.pyx", line 139, in pandas._libs.index.IndexEngine.get_loc
  File "pandas/_libs/hashtable_class_helper.pxi", line 811, in pandas._libs.hashtable.Int64HashTable.get_item
  File "pandas/_libs/hashtable_class_helper.pxi", line 817, in pandas._libs.hashtable.Int64HashTable.get_item
KeyError: 0

为了解决上面困惑,可以通过 .loc[] 明确指定为“label-based lookup”,通过 .iloc[] 明确指定为“position-based lookup”。如:

>>> s3.loc[10]         # label-based lookup
1
>>> s3.iloc[0]         # position-based lookup
1
>>> s3.loc[[10, 12]]   # label-based lookup
10    1
12    3
dtype: int64
>>> s3.iloc[[0, 2]]    # position-based lookup
10    1
12    3
dtype: int64

2.3. Series 常用属性和方法

下面是 Series 常用属性和方法的一些测试:

>>> s = pd.Series([0, 1, 1, 2, 3, 4, 5, 6, 7, np.nan])
>>> s.size                      # 属性size返回Series中元素个数
10
>>> s.count()                   # 方法count()返回Series中除NaN外的元素个数
9
>>> s.unique()                  # 方法unique()返回Series中“去重”以后的元素
array([  0.,   1.,   2.,   3.,   4.,   5.,   6.,   7.,  nan])
>>> s.value_counts()   # 方法value_counts()可统计每个“非NaN值”出现的个数
1.0    2
7.0    1
6.0    1
5.0    1
4.0    1
3.0    1
2.0    1
0.0    1
dtype: int64
>>> s.head()           # 方法head()返回Series中前5个元素,通过参数可定制个数
0    0.0
1    1.0
2    1.0
3    2.0
4    3.0
dtype: float64
>>> s.tail()           # 方法tail()返回Series中最后5个元素,通过其参数可定制个数
5    4.0
6    5.0
7    6.0
8    7.0
9    NaN
dtype: float64

下面是一些基本的统计函数:

>>> s.min()             # min() 求最小值(忽略NaN元素)
0.0
>>> s.max()             # max() 求最大值(忽略NaN元素)
7.0
>>> s.median()          # median() 求中位数(忽略NaN元素)
3.0
>>> s.sum()             # sum() 求和(忽略NaN元素)
29.0
>>> s.mean()            # mean() 求均值(忽略NaN元素)
3.2222222222222223
>>> s.std()             # std() 求标准差(忽略NaN元素)
2.438123139721299
>>> s.var()             # var() 求方差(忽略NaN元素)
5.9444444444444446

Series 完整的属性和方法可参考:http://pandas.pydata.org/pandas-docs/stable/generated/pandas.Series.html

2.4. 修改,增加,删除元素

下面是对 Series 对象修改,增加,删除元素的例子:

>>> s = pd.Series(np.random.randn(3), index=['a', 'b', 'c'])
>>> s
a    0.071455
b   -0.987037
c    1.202520
dtype: float64
>>> s['c'] = 100          # 修改元素(如果索引标签下元素不存在,则是增加元素)
>>> s
a      0.071455
b     -0.987037
c    100.000000
dtype: float64
>>> s['d'] = 200          # 增加元素(如果索引标签下元素存在,则是修改元素)
>>> s
a      0.071455
b     -0.987037
c    100.000000
d    200.000000
dtype: float64
>>> del(s['b'])           # del函数可以删除Series中元素
>>> s
a      0.071455
c    100.000000
d    200.000000
dtype: float64

2.5. Boolean Selection

把布尔表达式应用于 Series,相当于应用布尔表达式到 Series 中的每个元素,并返回一个新的 Series(其元素类似为布尔值,反映前面布尔表达式对每个元素的真假)。如:

>>> s = pd.Series(np.arange(0, 10))
>>> s > 5
0    False
1    False
2    False
3    False
4    False
5    False
6     True
7     True
8     True
9     True
dtype: bool

如果把布尔表达式数组放入 Series 的 [] 操作符中,则会返回布尔表达式数组中为 True 的对应元素组成的新 Series 对象。如:

>>> s[[False, False, False, False, False, False, True, True, True, True]]
6    6
7    7
8    8
9    9
dtype: int64
>>> logicalResults = s > 5
>>> s[logicalResults]             # 或直接写为 s[s > 5]
6    6
7    7
8    8
9    9
dtype: int64

下面用多个布尔表达式数组进行选择的例子:

>>> s[(s > 5) & (s < 8)]          # 不要写为 s[s > 5 and s < 8] ,会报错
6    6
7    7
dtype: int64

2.6. 算术操作

Series 上的算术操作是对 Series 中每个元素分别进行算术操作。如:

>>> s1 = pd.Series([1, 2, 3, 4])
>>> s1 * 4               # s1中每个元素乘以4
0     4
1     8
2    12
3    16
dtype: int64
>>> s2 = pd.Series([1, 2, 3, 4])
>>> s1 + s2              # s1和s2中对应元素分别相加
0    2
1    4
2    6
3    8
dtype: int64

对两个 Series 对象进行算法操作时,如果两个 Series 对象的索引标签相同,但顺序不同时,会“对齐”索引标签。如:

>>> s3 = pd.Series([1, 2, 4, 3], index=['a', 'b', 'd', 'c'])
>>> s4 = pd.Series([4, 3, 2, 1], index=['d', 'c', 'b', 'a'])  # 索引标签和上面相同,但顺序不同
>>> s3 + s4             # 索引标签相同的元素对应相加
a    2
b    4
c    6
d    8
dtype: int64

如果两个 Series 对象的索引标签有不同,则仅存在于某个 Series 对象的索引标签对应的值为 NaN。如:

>>> s5 = pd.Series([1, 2, 3], index=['a', 'b', 'c'])
>>> s6 = pd.Series([1, 2, 3], index=['a', 'b', 'd'])
>>> s5 + s6
a    2.0
b    4.0
c    NaN
d    NaN
dtype: float64

如果两个 Series 对象的索引标签有不同,且存在重复索引标签。则对重复的索引标签进行“交叉联接(笛卡尔积)”。如:

>>> s7 = pd.Series([1, 2, 3], index=['a', 'a', 'b'])   # 两个 'a'
>>> s8 = pd.Series([1, 2, 3], index=['a', 'a', 'c'])   # 两个 'a'
>>> s7 + s8                                            # 结果四个 'a'
a    2.0
a    3.0
a    3.0
a    4.0
b    NaN
c    NaN
dtype: float64
>>> s9 = pd.Series([1, 2, 3], index=['a', 'a', 'a'])   # 三个 'a'
>>> s7 + s9                                            # 结果六个 'a'
a    2.0
a    3.0
a    4.0
a    3.0
a    4.0
a    5.0
b    NaN
dtype: float64

2.7. Reindexing a Series

有时,我们需要重新设置 Series 的索引标签。如下面例子:

>>> s1 = pd.Series([1, 2, 3, 4])
>>> s1
0    1
1    2
2    3
3    4
dtype: int64
>>> s2 = pd.Series([5, 6, 7, 8])
>>> s2
0    5
1    6
2    7
3    8
dtype: int64
>>> combined = pd.concat([s1, s2])   # concat两个Series为combined
>>> combined
0    1
1    2
2    3
3    4
0    5
1    6
2    7
3    8
dtype: int64

你很可能想要重置 combined 的索引标签,这可以通过设置 index 属性的实现,如:

>>> combined.index = np.arange(0, len(combined))   # 重置combined的索引标签
>>> combined
0    1
1    2
2    3
3    4
4    5
5    6
6    7
7    8
dtype: int64

2.7.1. reindex

reindex 方法可以重置 Series 的索引标签,不过它不会修改原 Series,而是基于原 Series 创建一个新的 Series。

>>> np.random.seed(123456)
>>> s1 = pd.Series(np.random.randn(4), ['a', 'b', 'c', 'd'])
>>> s1
a    0.469112
b   -0.282863
c   -1.509059
d   -1.135632
dtype: float64
>>> s2 = s1.reindex(['a', 'c', 'g'])    # 标签索引'a'和'c'在s1中存在,'g'在s1中不存在
>>> s2
a    0.469112
c   -1.509059
g         NaN
dtype: float64
>>> s2['a'] = 100                       # 修改s2,不会影响s1(reindex不会影响原Series)
>>> s1['a']
0.46911229990718628

通过上面例子可知, reindex() 的参数中指定的索引标签,如果索引标签在原 Series 中的存在(如'a'和'c'),则直接选择出来放入新 Series 中;如果不存在(如'g'),则在新 Series 中的值为 NaN。

缺失索引标签的值默认为 NaN,我们可以通过 reindex() 的参数 fill_value 来设置为其它值。如:

>>> s3 = s1.reindex(['a', 'c', 'g'], fill_value=0)   # 把缺失索引标签的值设为0(而非默认值NaN)
>>> s3
a    0.469112
c   -1.509059
g    0.000000
dtype: float64

还可以通过 method='ffill' 来定制缺失索引标签的值为“上一个非 NaN 值”;通过 method='bfill' 来定制缺失索引标签的值为“下一个非 NaN 值”。如:

>>> s1 = pd.Series(['red', 'green', 'blue'], index=[0, 3, 5])  # s1中的索引标签1,2,4,6是缺失的
>>> s1
0      red
3    green
5     blue
dtype: object
>>> s1.reindex(np.arange(0,7))
0      red
1      NaN
2      NaN
3    green
4      NaN
5     blue
6      NaN
dtype: object
>>> s1.reindex(np.arange(0,7), method='ffill')    # 缺失索引标签的值“上一个非NaN值”
0      red
1      red
2      red
3    green
4    green
5     blue
6     blue
dtype: object
>>> s1.reindex(np.arange(0,7), method='bfill')     # 缺失索引标签的值“下一个非NaN值”
0      red
1    green
2    green
3    green
4     blue
5     blue
6      NaN
dtype: object

2.8. 切片操作

Series 的切片操作和的 NumPy 数组的切片操作基本类似。下面仅介绍几个简单例子:

>>> s = pd.Series(np.arange(100, 110), index=np.arange(10, 20))
>>> s
10    100
11    101
12    102
13    103
14    104
15    105
16    106
17    107
18    108
19    109
dtype: int64
>>> s[0:6:2]           # 从索引0开始,到6(不包含),步进为2
10    100
12    102
14    104
dtype: int64
>>> s[:5]              # 前5个,和s.head(5)相同
10    100
11    101
12    102
13    103
14    104
dtype: int64
>>> s[::-1]            # reverse the Series
19    109
18    108
17    107
16    106
15    105
14    104
13    103
12    102
11    101
10    100
dtype: int64

3. 数据结构 DataFrame

DataFrame 是 Pandas 中基本的数据结构,它表达的是二维数组。

3.1. 创建 DataFrame

首先,准备两个 Series 对象(temps1 和 temps2):

>>> dates = pd.date_range('2014-07-01', '2014-07-06')
>>> temps1 = pd.Series([80, 82, 85, 90, 83, 87], index = dates)
>>> temps2 = pd.Series([70, 75, 69, 83, 79, 77], index = dates)
>>> temps1
2014-07-01    80
2014-07-02    82
2014-07-03    85
2014-07-04    90
2014-07-05    83
2014-07-06    87
Freq: D, dtype: int64
>>> temps2
2014-07-01    70
2014-07-02    75
2014-07-03    69
2014-07-04    83
2014-07-05    79
2014-07-06    77
Freq: D, dtype: int64

基于上面两个 Series,可以创建 DataFrame 对象,如:

>>> temps_df = pd.DataFrame(
                            {'Missoula': temps1,
                             'Philadelphia': temps2})    # 基于两个Series对象创建DataFrame对象
>>> temps_df
            Missoula  Philadelphia
2014-07-01        80            70
2014-07-02        82            75
2014-07-03        85            69
2014-07-04        90            83
2014-07-05        83            79
2014-07-06        87            77

下面是给 DataFrame 对象增加一列的例子:

>>> temps_df['Difference'] = temps1 - temps2
>>> temps_df
            Missoula  Philadelphia  Difference
2014-07-01        80            70          10
2014-07-02        82            75           7
2014-07-03        85            69          16
2014-07-04        90            83           7
2014-07-05        83            79           4
2014-07-06        87            77          10

可以通过 NumPy 数组来创建 DataFrame 对象。如:

>>> df = pd.DataFrame(np.array([[10, 11, 12], [20, 21, 22]]),
                      columns=['a', 'b', 'c'])
>>> df
    a   b   c
0  10  11  12
1  20  21  22

上面例子中,直接用 Python 数组也行,如:

>>> df = pd.DataFrame([[10, 11, 12], [20, 21, 22]],
                      columns=['a', 'b', 'c'])
>>> df
    a   b   c
0  10  11  12
1  20  21  22

3.2. 选择行、列、元素

3.2.1. 选择行

下面是选择 DataFrame 对象中某些行的例子:

>>> temps_df.iloc[[0, 2]]                          # 通过iloc[]选择第1行和第3行
            Missoula  Philadelphia  Difference
2014-07-01        80            70          10
2014-07-03        85            69          16
>>> temps_df.loc[[pd.to_datetime('2014-07-01'), pd.to_datetime('2014-07-03')]] # 通过loc[]选择
            Missoula  Philadelphia  Difference
2014-07-01        80            70          10
2014-07-03        85            69          16
>>> temps_df.head()                                # 通过head()选择前5行
            Missoula  Philadelphia  Difference
2014-07-01        80            70          10
2014-07-02        82            75           7
2014-07-03        85            69          16
2014-07-04        90            83           7
2014-07-05        83            79           4
>>> temps_df[:5]                                   # 通过切片操作选择前5行,和head()相同
            Missoula  Philadelphia  Difference
2014-07-01        80            70          10
2014-07-02        82            75           7
2014-07-03        85            69          16
2014-07-04        90            83           7
2014-07-05        83            79           4
>>> temps_df[temps_df.Difference >= 10]            # 通过"Boolean Selection"选择满足要求的行
            Missoula  Philadelphia  Difference
2014-07-01        80            70          10
2014-07-03        85            69          16
2014-07-06        87            77          10
>>> temps_df[(temps_df.Difference >= 10) & (temps_df.Philadelphia <= 75)] # "Boolean Selection"
            Missoula  Philadelphia  Difference
2014-07-01        80            70          10
2014-07-03        85            69          16

3.2.2. 选择列

下面是选择 DataFrame 对象中某些列的例子:

>>> temps_df[['Missoula', 'Difference']]
            Missoula  Difference
2014-07-01        80          10
2014-07-02        82           7
2014-07-03        85          16
2014-07-04        90           7
2014-07-05        83           4
2014-07-06        87          10

3.2.3. 通过.at[](by label)和.iat[](by location)访问某行某列

>>> temps_df.at[pd.to_datetime('2014-07-03'), 'Philadelphia']
69
>>> temps_df.iat[2, 1]                         # 通过 .iat[] 访问第3行第2列
69

3.3. 增加删除列

给 DataFrame 增加一列,很简单:

>>> temps_df['column_name'] = ......    # 如果'column_name'存在,是修改列;否则是增加列

要删除 DataFrame 的列,可以使用表 1 所示的办法。

Table 1: 删除 DataFrame 列的不同方式
方式 说明
del 关键字 原地地修改 DataFrame
pop() 方法 原地地修改 DataFrame,被删除的列保存在返回值中
drop(labels, axis=1) 方法 默认不是原地修改,可通过参数 inplace=True 指定为原地修改(不指定 axis=1,默认是删除行)

下面是 del, pop(), drop() 的使用例子:

>>> df1 = pd.DataFrame(np.array([[10, 11, 12], [20, 21, 22]]),
                       columns=['a', 'b', 'c'])
>>> del df1['a']              # 用关键字删除列'a'
>>> df1                       # del修改了df1(删除了列'a')
    b   c
0  11  12
1  21  22
>>> tmp1 = df1.pop('b')       # 用pop()方法删除列'b',被删除的列保存在返回值中
>>> df1                       # pop()方法修改了df1(删除了列'b')
    c
0  12
1  22
>>> tmp1
0    11
1    21
Name: b, dtype: int64
>>> tmp2 = df1.drop(['c'], axis=1)
>>> tmp2
Empty DataFrame
Columns: []
Index: [0, 1]
>>> df1                       # 默认,drop并没有修改df1
    c
0  12
1  22

3.4. 增加删除行

往 DataFrame 对象中增加行有下面办法:
1、通过 append() 方法(不是原地修改);
2、通过 pd.concat() 方法(不是原地修改);
3、通过 .loc[] 属性(原地修改)。

下面是 append() 方法和 pd.concat() 方法的使用例子:

>>> df1 = pd.DataFrame([[1, 2], [3, 4]], columns=list('AB'))
>>> df2 = pd.DataFrame([[5, 6], [7, 8]], columns=list('AB'))
>>> df1.append(df2)           # 结果在返回值中,append并不会修改df1
   A  B
0  1  2
1  3  4
0  5  6
1  7  8
>>> df1.append(df2, ignore_index=True)  # 指定ignore_index=True,索引标签会重新生成
   A  B
0  1  2
1  3  4
2  5  6
3  7  8
>>> pd.concat([df1, df2], ignore_index=True)  # pd.concat()实例
   A  B
0  1  2
1  3  4
2  5  6
3  7  8

下面是通过修改 .loc[] 属性增加行的例子:

>>> df1 = pd.DataFrame([[1, 2], [3, 4]], columns=list('AB'))
>>> df1
   A  B
0  1  2
1  3  4
>>> df1.loc[2] = [5, 6]        # 增加了一行
>>> df1
   A  B
0  1  2
1  3  4
2  5  6

通过 drop() 可以删除 DataFrame 中的行。如:

>>> df = pd.DataFrame(np.arange(12).reshape(3,4),
                      columns=['A', 'B', 'C', 'D'])
>>> df
   A  B   C   D
0  0  1   2   3
1  4  5   6   7
2  8  9  10  11
>>> df.drop([1, 2], inplace=True)   # 指定inplace=True后会原地修改
>>> df
   A  B   C   D
0  0  1   2   3

3.5. 查看数据的基本信息

3.5.1. info

使用 info (Series 对象没有这个方法,DataFrame 对象有这个方法)可以方便地显示数据的基本情况,比如有多少行(列)数据,哪些列存在 NaN 数据等。如:

>>> df = pd.DataFrame({ 'object': ['a', 'b', np.nan],
...                     'numeric': [1, 2, 3],
...                     'categorical': pd.Categorical(['d','e','f'])
...                   })
>>> df
  categorical  numeric object
0           d        1      a
1           e        2      b
2           f        3    NaN
>>> df.info()                                    # 显示数据的基本情况
<class 'pandas.core.frame.DataFrame'>
RangeIndex: 3 entries, 0 to 2                    # 共3条记录(3行)
Data columns (total 3 columns):                  # 共3列
categorical    3 non-null category               # 第1列类型为catagore,没有NaN值
numeric        3 non-null int64                  # 第2列类型为int64,没有NaN值
object         2 non-null object                 # 第3列类型为object,有一个NaN值
dtypes: category(1), int64(1), object(1)
memory usage: 235.0+ bytes

3.5.2. describe

使用 describe (Series 和 DataFrame 对象都有这个方法)可以方便地显示数据的基本统计信息。

下面是 Series 为数字类型时, describe() 的输出例子:

>>> s = pd.Series([1, 2, 3])
>>> s.describe()
count    3.0
mean     2.0
std      1.0
min      1.0
25%      1.5
50%      2.0
75%      2.5
max      3.0
dtype: float64

下面是 Series 为 categorical 类型时, describe() 的输出例子:

>>> s = pd.Series(['a', 'a', 'b', 'c'])
>>> s.describe()
count     4
unique    3
top       a
freq      2
dtype: object

下面是 Series 为时间戳类型时, describe() 的输出例子:

>>> s = pd.Series([np.datetime64("2000-01-01"),
                   np.datetime64("2010-01-01"),
                   np.datetime64("2010-01-01")
                  ])
>>> s.describe()
count                       3
unique                      2
top       2010-01-01 00:00:00
freq                        2
first     2000-01-01 00:00:00
last      2010-01-01 00:00:00
dtype: object

下面是 DataFrame 对象, describe() 的输出例子:

>>> df = pd.DataFrame({ 'object': ['a', 'b', 'c'],
                        'numeric': [1, 2, 3],
                        'categorical': pd.Categorical(['d','e','f'])
                       })
>>> df.describe()                  # 默认,describe()只会输出数字类型的列的统计信息
       numeric
count      3.0
mean       2.0
std        1.0
min        1.0
25%        1.5
50%        2.0
75%        2.5
max        3.0
>>> df.describe(include='all')     # 通过指定include='all',describe()会输出所有列的统计信息
       categorical  numeric object
count            3      3.0      3
unique           3      NaN      3
top              f      NaN      c
freq             1      NaN      1
mean           NaN      2.0    NaN
std            NaN      1.0    NaN
min            NaN      1.0    NaN
25%            NaN      1.5    NaN
50%            NaN      2.0    NaN
75%            NaN      2.5    NaN
max            NaN      3.0    NaN
>>> df.categorical.describe()      # 输出某一列(这里是'categorical'列)的统计信息
count     3
unique    3
top       f
freq      1
Name: categorical, dtype: object

3.6. 读 CSV 文件为 DataFrame

通过 pd.read_csv() 可以读取 CSV 文件为 DataFrame。

假设有文件 file1,其内容为:

col1, col2, col3
a, b, 1
a, b, 2
c, d, 3
e, f, 4

下面是通过 pd.read_csv() 可以读取 file1 为 DataFrame 的例子:

>>> pd.read_csv('file1', sep=',')    # 默认分隔符为逗号,所以sep=','可省略
  col1  col2   col3
0    a     b      1
1    a     b      2
2    c     d      3
3    e     f      4

4. Tidying Up 数据

4.1. 处理 NaN 值

后面例子中使用将使用下面 DataFrame:

>>> df = pd.DataFrame([[10, 11, 12],
                       [40, np.nan, 42],
                       [50, np.nan, 52],
                       [30, 31, 32]], columns=['a', 'b', 'c'])
>>> df
    a     b   c
0  10  11.0  12
1  40   NaN  42
2  50   NaN  52
3  30  31.0  32

4.1.1. 用固定值填充 NaN 值(fillna)

使用方法 fillna 可以用指定值填充 NaN 值。如:

>>> df.fillna(0)          # NaN值填充为0
    a     b   c
0  10  11.0  12
1  40   0.0  42
2  50   0.0  52
3  30  31.0  32

4.1.2. 用插值数据填充 NaN 值(interpolate)

使用方法 interpolate 可以用插值数据(默认是线性插值)填充 NaN 值。如:

>>> df
    a     b   c
0  10  11.0  12
1  40   NaN  42
2  50   NaN  52
3  30  31.0  32
>>> df.interpolate()             # 默认按列进行插值
    a          b   c
0  10  11.000000  12
1  40  17.666667  42
2  50  24.333333  52
3  30  31.000000  32
>>> df.interpolate(axis=1)       # 指定axis=1,让其按行进行插值
      a     b     c
0  10.0  11.0  12.0
1  40.0  41.0  42.0
2  50.0  51.0  52.0
3  30.0  31.0  32.0

4.1.3. 删除包含 NaN 的行(dropna)

使用方法 dropna 可以删除包含 NaN 的行,指定参数 axis=1 可删除包含 NaN 的列。如:

>>> df
    a     b   c
0  10  11.0  12
1  40   NaN  42
2  50   NaN  52
3  30  31.0  32
>>> df.dropna()           # 删除包含NaN的行
    a     b   c
0  10  11.0  12
3  30  31.0  32
>>> df.dropna(axis=1)     # 指定参数axis=1可删除包含NaN的列
    a   c
0  10  12
1  40  42
2  50  52
3  30  32

4.2. 删除重复数据(drop_duplicates)

使用方法 drop_duplicates 可以删除重复的行。

>>> data = pd.DataFrame({'a': ['x'] * 3 + ['y'] * 4,
                         'b': [1, 1, 2, 3, 3, 4, 4]})
>>> data
   a  b
0  x  1
1  x  1
2  x  2
3  y  3
4  y  3
5  y  4
6  y  4
>>> data.drop_duplicates()    # 使用drop_duplicates删除重复行,默认保留重复行的第一行
   a  b
0  x  1
2  x  2
3  y  3
5  y  4
>>> data.drop_duplicates(keep='last')   # 使用 keep='last' 可保留重复行的最后一行
   a  b
1  x  1
2  x  2
4  y  3
6  y  4

5. Reshaping 数据

重塑(Reshaping)数据是转换数据形式,使其更适合于进一步的分析。

关于 pivot, pivot_table, stack, unstack 区别的形象例子可参考:Reshaping in Pandas - Pivot, Pivot-Table, Stack and Unstack explained with Pictures, by NIKOLAY GROZEV

5.1. pivot

下面通过实例来介绍 pivot 的作用。

>>> df = pd.DataFrame({'foo': ['one','one','one','two','two','two'],
                       'bar': ['A', 'B', 'C', 'A', 'B', 'C'],
                       'baz': [1, 2, 3, 4, 5, 6]})
>>> df
  bar  baz  foo
0   A    1  one
1   B    2  one
2   C    3  one
3   A    4  two
4   B    5  two
5   C    6  two
>>> df.pivot(index='foo', columns='bar', values='baz')
     A   B   C
one  1   2   3
two  4   5   6

5.2. 数据透视表(Pivot Table)

A pivot table is a table that summarizes data in another table, and is made by applying an operation such as sorting, averaging, or summing to data in the first table.

MS Excel 中“数据透视表”功能强大(注:微软为词“PivotTable”申请了专利),可以方便地生成报表,这里 https://www.zhihu.com/question/22484899 有一个例子。Oracle Database 11g 通过操作符 PIVOTUNPIVOT 提供了类似功能。

在 Pandas 中使用方法 pivot_table 可以创建数据透视表。

5.2.1. pivot_table

下面通过例子介绍 pivot_table 的使用。

假设有下面 CSV 数据(文件名为"sales-funnel.csv"),记录着产品的销售信息:

Account,Name,Rep,Manager,Product,Quantity,Price,Status
714466,Trantow-Barrows,Craig Booker,Debra Henley,CPU,1,30000,presented
714466,Trantow-Barrows,Craig Booker,Debra Henley,Software,1,10000,presented
714466,Trantow-Barrows,Craig Booker,Debra Henley,Maintenance,2,5000,pending
737550,"Fritsch, Russel and Anderson",Craig Booker,Debra Henley,CPU,1,35000,declined
146832,Kiehn-Spinka,Daniel Hilton,Debra Henley,CPU,2,65000,won
218895,Kulas Inc,Daniel Hilton,Debra Henley,CPU,2,40000,pending
218895,Kulas Inc,Daniel Hilton,Debra Henley,Software,1,10000,presented
412290,Jerde-Hilpert,John Smith,Debra Henley,Maintenance,2,5000,pending
740150,Barton LLC,John Smith,Debra Henley,CPU,1,35000,declined
141962,Herman LLC,Cedric Moss,Fred Anderson,CPU,2,65000,won
163416,Purdy-Kunde,Cedric Moss,Fred Anderson,CPU,1,30000,presented
239344,Stokes LLC,Cedric Moss,Fred Anderson,Maintenance,1,5000,pending
239344,Stokes LLC,Cedric Moss,Fred Anderson,Software,1,10000,presented
307599,"Kassulke, Ondricka and Metz",Wendy Yule,Fred Anderson,Maintenance,3,7000,won
688981,Keeling LLC,Wendy Yule,Fred Anderson,CPU,5,100000,won
729833,Koepp Ltd,Wendy Yule,Fred Anderson,CPU,2,65000,declined
729833,Koepp Ltd,Wendy Yule,Fred Anderson,Monitor,2,5000,presented

首先,把它读取为 DataFrame 对象:

>>> df = pd.read_csv('sales-funnel.csv')
>>> df        # 太宽了,会折行显示
    Account                          Name            Rep        Manager  \
0    714466               Trantow-Barrows   Craig Booker   Debra Henley
1    714466               Trantow-Barrows   Craig Booker   Debra Henley
2    714466               Trantow-Barrows   Craig Booker   Debra Henley
3    737550  Fritsch, Russel and Anderson   Craig Booker   Debra Henley
4    146832                  Kiehn-Spinka  Daniel Hilton   Debra Henley
5    218895                     Kulas Inc  Daniel Hilton   Debra Henley
6    218895                     Kulas Inc  Daniel Hilton   Debra Henley
7    412290                 Jerde-Hilpert     John Smith   Debra Henley
8    740150                    Barton LLC     John Smith   Debra Henley
9    141962                    Herman LLC    Cedric Moss  Fred Anderson
10   163416                   Purdy-Kunde    Cedric Moss  Fred Anderson
11   239344                    Stokes LLC    Cedric Moss  Fred Anderson
12   239344                    Stokes LLC    Cedric Moss  Fred Anderson
13   307599   Kassulke, Ondricka and Metz     Wendy Yule  Fred Anderson
14   688981                   Keeling LLC     Wendy Yule  Fred Anderson
15   729833                     Koepp Ltd     Wendy Yule  Fred Anderson
16   729833                     Koepp Ltd     Wendy Yule  Fred Anderson

        Product  Quantity   Price     Status
0           CPU         1   30000  presented
1      Software         1   10000  presented
2   Maintenance         2    5000    pending
3           CPU         1   35000   declined
4           CPU         2   65000        won
5           CPU         2   40000    pending
6      Software         1   10000  presented
7   Maintenance         2    5000    pending
8           CPU         1   35000   declined
9           CPU         2   65000        won
10          CPU         1   30000  presented
11  Maintenance         1    5000    pending
12     Software         1   10000  presented
13  Maintenance         3    7000        won
14          CPU         5  100000        won
15          CPU         2   65000   declined
16      Monitor         2    5000  presented

下面进行一些简单尝试,把"Name"设置为索引:

>>> pd.pivot_table(df, index=["Name"])

将得到:

                                Account    Price      Quantity 
 Name                                                          
 Barton LLC                     740150.0   35000.0    1.000000 
 Fritsch, Russel and Anderson   737550.0   35000.0    1.000000 
 Herman LLC                     141962.0   65000.0    2.000000 
 Jerde-Hilpert                  412290.0   5000.0     2.000000 
 Kassulke, Ondricka and Metz    307599.0   7000.0     3.000000 
 Keeling LLC                    688981.0   100000.0   5.000000 
 Kiehn-Spinka                   146832.0   65000.0    2.000000 
 Koepp Ltd                      729833.0   35000.0    2.000000 
 Kulas Inc                      218895.0   25000.0    1.500000 
 Purdy-Kunde                    163416.0   30000.0    1.000000 
 Stokes LLC                     239344.0   7500.0     1.000000 
 Trantow-Barrows                714466.0   15000.0    1.333333 

说明:如果目标数据集位置上对应多个值,默认会其会求其平均值。例如,“Trantow-Barrows”的“Price”一列为什么是“15000.0”呢,这是因为“15000.0=(30000+10000+5000)/3”。

也可以把"Name", "Rep", "Manager"都设置为索引,即:

>>> pd.pivot_table(df, index=["Name", "Rep", "Manager"])

将得到:

                                                                Account    Price      Quantity 
 Name                           Rep             Manager                                        
 Barton LLC                     John Smith      Debra Henley    740150.0   35000.0    1.000000 
 Fritsch, Russel and Anderson   Craig Booker    Debra Henley    737550.0   35000.0    1.000000 
 Herman LLC                     Cedric Moss     Fred Anderson   141962.0   65000.0    2.000000 
 Jerde-Hilpert                  John Smith      Debra Henley    412290.0   5000.0     2.000000 
 Kassulke, Ondricka and Metz    Wendy Yule      Fred Anderson   307599.0   7000.0     3.000000 
 Keeling LLC                    Wendy Yule      Fred Anderson   688981.0   100000.0   5.000000 
 Kiehn-Spinka                   Daniel Hilton   Debra Henley    146832.0   65000.0    2.000000 
 Koepp Ltd                      Wendy Yule      Fred Anderson   729833.0   35000.0    2.000000 
 Kulas Inc                      Daniel Hilton   Debra Henley    218895.0   25000.0    1.500000 
 Purdy-Kunde                    Cedric Moss     Fred Anderson   163416.0   30000.0    1.000000 
 Stokes LLC                     Cedric Moss     Fred Anderson   239344.0   7500.0     1.000000 
 Trantow-Barrows                Craig Booker    Debra Henley    714466.0   15000.0    1.333333 

如果我们把"Manager", "Rep"设置为索引,即:

>>> pd.pivot_table(df, index=["Manager", "Rep"])    # 注意顺序,index=["Rep", "Manager"]的输出会不同

将得到:

                                 Account    Price          Quantity 
 Manager         Rep                                                
 Debra Henley  
               
               
               
               
 Craig Booker    720237.0   20000.000000   1.250000 
 Daniel Hilton   194874.0   38333.333333   1.666667 
 John Smith      576220.0   20000.000000   1.500000 
 Fred Anderson 
               
               
 Cedric Moss     196016.5   27500.000000   1.250000 
 Wendy Yule      614061.5   44250.000000   3.000000 

上面表格已经初步展现出了有价值的报表信息。如果不关心“Account”(它是原始含义是销售员的 ID,这里取平均了显然无用)和“Quantity”列,可以通过“values”域显式地定义我们关心的列,实现移除那些不关心的列。如:

>>> pd.pivot_table(df, index=["Manager","Rep"], values=["Price"])

将得到:

                                 Price        
 Manager         Rep                          
 Debra Henley  
               
               
               
               
 Craig Booker    20000.000000 
 Daniel Hilton   38333.333333 
 John Smith      20000.000000 
 Fred Anderson 
               
               
 Cedric Moss     27500.000000 
 Wendy Yule      44250.000000 

“Price”列默认动计算数据的平均值,但是也可以对该列元素进行求和(指定 aggfunc=np.sum 即可)等其它运算。如:

>>> pd.pivot_table(df, index=["Manager","Rep"], values=["Price"], aggfunc=np.sum)

将得到:

                                 Price  
 Manager         Rep                    
 Debra Henley  
               
               
               
               
 Craig Booker    80000  
 Daniel Hilton   115000 
 John Smith      40000  
 Fred Anderson 
               
               
 Cedric Moss     110000 
 Wendy Yule      177000 

aggfunc 可以接收函数列表(多个函数)。如:

>>> pd.pivot_table(df, index=["Manager","Rep"], values=["Price"] ,aggfunc=[np.mean,len])

将得到:

                                 mean           len   
                                 Price          Price 
 Manager         Rep                                  
 Debra Henley  
               
               
               
               
 Craig Booker    20000.000000   4     
 Daniel Hilton   38333.333333   3     
 John Smith      20000.000000   2     
 Fred Anderson 
               
               
 Cedric Moss     27500.000000   4     
 Wendy Yule      44250.000000   4     

如果我们想通过不同产品来分析销售情况,可以通过设置 columns=["Product"] 来实现。如:

>>> pd.pivot_table(df, index=["Manager","Rep"], values=["Price"],
                   columns=["Product"], aggfunc=[np.sum], fill_value=0)

将得到:

                                 sum                                       
                                 Price                                     
                 Product         CPU      Maintenance   Monitor   Software 
 Manager         Rep                                                       
 Debra Henley  
               
               
               
               
 Craig Booker    65000    5000          0         10000    
 Daniel Hilton   105000   0             0         10000    
 John Smith      35000    5000          0         0        
 Fred Anderson 
               
               
 Cedric Moss     95000    5000          0         10000    
 Wendy Yule      165000   7000          5000      0        

说明 1: fill_value=0 的作用是把 NaN 值填充为 0。
说明 2: “columns”参数和“values”参数的区别在于:columns 提供一种方法来分割你所关心的实际值;聚合函数会应用到 values 指定的列中。

如果我们把“Quantity”添加到“values”列表中,可以得到更具体的信息。如:

>>> pd.pivot_table(df,index=["Manager","Rep"], values=["Price","Quantity"],
                   columns=["Product"], aggfunc=[np.sum], fill_value=0)

将得到:

                                 sum                                                                                     
                                 Price                                       Quantity                                    
                 Product         CPU      Maintenance   Monitor   Software   CPU        Maintenance   Monitor   Software 
 Manager         Rep                                                                                                     
 Debra Henley    Craig Booker    65000    5000          0         10000      2          2             0         1        
                 Daniel Hilton   105000   0             0         10000      4          0             0         1        
                 John Smith      35000    5000          0         0          1          2             0         0        
 Fred Anderson   Cedric Moss     95000    5000          0         10000      3          1             0         1        
                 Wendy Yule      165000   7000          5000      0          7          3             2         0        

我们也可以将不同的项目设置为“index”来得到不同的报表展示。比如,将“Product”从“columns”中移除,并添加到“index”变量中:

>>> pd.pivot_table(df,index=["Manager","Rep","Product"], values=["Price","Quantity"],
                   aggfunc=[np.sum], fill_value=0)

将得到:

                                               sum               
                                               Price    Quantity 
 Manager         Rep             Product                         
 Debra Henley  
               
               
               
               
               
               
               
               
               
               
               
               
 Craig Booker  
               
               
               
               
 CPU           65000    2        
 Maintenance   5000     2        
 Software      10000    1        
 Daniel Hilton 
               
               
 CPU           105000   4        
 Software      10000    1        
 John Smith    
               
               
 CPU           35000    1        
 Maintenance   5000     2        
 Fred Anderson 
               
               
               
               
               
               
               
               
               
               
 Cedric Moss   
               
               
               
               
 CPU           95000    3        
 Maintenance   5000     1        
 Software      10000    1        
 Wendy Yule    
               
               
               
               
 CPU           165000   7        
 Maintenance   7000     3        
 Monitor       5000     2        

上面的数据展示比前一个更加实用。通过设置 margins=True 可以得到“总和”统计数据。如:

>>> pd.pivot_table(df,index=["Manager","Rep","Product"], values=["Price","Quantity"],
                   aggfunc=[np.sum], fill_value=0, margins=True)

将得到(比上表多了最后一行):

                                               sum               
                                               Price    Quantity 
 Manager         Rep             Product                         
 Debra Henley  
               
               
               
               
               
               
               
               
               
               
               
               
 Craig Booker  
               
               
               
               
 CPU           65000    2        
 Maintenance   5000     2        
 Software      10000    1        
 Daniel Hilton 
               
               
 CPU           105000   4        
 Software      10000    1        
 John Smith    
               
               
 CPU           35000    1        
 Maintenance   5000     2        
 Fred Anderson 
               
               
               
               
               
               
               
               
               
               
 Cedric Moss   
               
               
               
               
 CPU           95000    3        
 Maintenance   5000     1        
 Software      10000    1        
 Wendy Yule    
               
               
               
               
 CPU           165000   7        
 Maintenance   7000     3        
 Monitor       5000     2        
 All                                           522000   30       

下面,我们从更高的管理者角度来分析销售情况。

>>> pd.pivot_table(df,index=["Manager","Status"], values=["Price"],
                   aggfunc=[np.sum], fill_value=0, margins=True)

将得到:

                             sum    
                             Price  
 Manager         Status             
 Debra Henley  
               
               
               
               
               
               
 declined    70000  
 pending     50000  
 presented   50000  
 won         65000  
 Fred Anderson 
               
               
               
               
               
               
 declined    65000  
 pending     5000   
 presented   45000  
 won         172000 
 All                         522000 

通过向 aggfunc 传递一个 dictionary,可以为“values”中的不同列执行不同的聚合函数。 这是一个实用的功能。例如,我们对"Quantity"执行聚合函数 len,而对"Price"执行聚合函数 np.sum:

>>> pd.pivot_table(df,index=["Manager","Status"],
                   columns=["Product"],
                   values=["Quantity","Price"],
                   aggfunc={"Quantity":len,"Price":np.sum},fill_value=0)

将得到:

                             Price                                       Quantity                                    
                 Product     CPU      Maintenance   Monitor   Software   CPU        Maintenance   Monitor   Software 
 Manager         Status                                                                                              
 Debra Henley  
               
               
               
               
               
               
 declined    70000    0             0         0          2          0             0         0        
 pending     40000    10000         0         0          1          2             0         0        
 presented   30000    0             0         20000      1          0             0         2        
 won         65000    0             0         0          1          0             0         0        
 Fred Anderson 
               
               
               
               
               
               
 declined    65000    0             0         0          1          0             0         0        
 pending     0        5000          0         0          0          1             0         0        
 presented   30000    0             5000      10000      1          0             1         1        
 won         165000   7000          0         0          2          1             0         0        

我们也可以对“values”中的某一列执行多个聚合函数。如对"Price"执行两个聚合函数(np.sum 和 np.mean):

>>> table1 = pd.pivot_table(df,index=["Manager","Status"],
                            columns=["Product"],
                            values=["Quantity","Price"],
                            aggfunc={"Quantity":len,"Price":[np.sum,np.mean]},fill_value=0)
>>> table1

将得到:

                             Price                                                                                  Quantity                               
                             mean                                       sum                                         len                                    
                 Product     CPU     Maintenance   Monitor   Software   CPU      Maintenance   Monitor   Software   CPU   Maintenance   Monitor   Software 
 Manager         Status                                                                                                                                    
 Debra Henley  
               
               
               
               
               
               
 declined    35000   0             0         0          70000    0             0         0          2     0             0         0        
 pending     40000   5000          0         0          40000    10000         0         0          1     2             0         0        
 presented   30000   0             0         10000      30000    0             0         20000      1     0             0         2        
 won         65000   0             0         0          65000    0             0         0          1     0             0         0        
 Fred Anderson 
               
               
               
               
               
               
 declined    65000   0             0         0          65000    0             0         0          1     0             0         0        
 pending     0       5000          0         0          0        5000          0         0          0     1             0         0        
 presented   30000   0             5000      10000      30000    0             5000      10000      1     0             1         1        
 won         82500   7000          0         0          165000   7000          0         0          2     1             0         0        

我们可以使用标准的 DataFrame 函数来对透视表结果进行过滤选择。如,按"Manager"进行过滤:

>>> table1.query('Manager == ["Debra Henley"]')

将得到:

                             Price                                                                                 Quantity                               
                             mean                                       sum                                        len                                    
                 Product     CPU     Maintenance   Monitor   Software   CPU     Maintenance   Monitor   Software   CPU   Maintenance   Monitor   Software 
 Manager         Status                                                                                                                                   
 Debra Henley  
               
               
               
               
               
               
 declined    35000   0             0         0          70000   0             0         0          2     0             0         0        
 pending     40000   5000          0         0          40000   10000         0         0          1     2             0         0        
 presented   30000   0             0         10000      30000   0             0         20000      1     0             0         2        
 won         65000   0             0         0          65000   0             0         0          1     0             0         0        

又如,按"Status"进行过滤:

>>> table1.query('Status == ["pending","won"]')

将得到:

                             Price                                                                                  Quantity                               
                             mean                                       sum                                         len                                    
                 Product     CPU     Maintenance   Monitor   Software   CPU      Maintenance   Monitor   Software   CPU   Maintenance   Monitor   Software 
 Manager         Status                                                                                                                                    
 Debra Henley  
               
               
 pending     40000   5000          0         0          40000    10000         0         0          1     2             0         0        
 won         65000   0             0         0          65000    0             0         0          1     0             0         0        
 Fred Anderson 
               
               
 pending     0       5000          0         0          0        5000          0         0          0     1             0         0        
 won         82500   7000          0         0          165000   7000          0         0          2     1             0         0        

本节的实例摘自:
Pandas Pivot Table Explained, by Chris Moffitt
Pandas透视表(pivot_table)详解(译文)

5.2.2. 何时使用透视表

何时使用透视表?经验法则是:当你使用多个 grouby 时,那么你需要评估此时使用透视表是否是一种更好的选择。

5.2.3. pivot VS. pivot_table

首先,回顾一下前面介绍的 pivot 使用例子,在该例子中使用 pivot_table 也能得到相同的输出:

>>> df = pd.DataFrame({'foo': ['one','one','one','two','two','two'],
                       'bar': ['A', 'B', 'C', 'A', 'B', 'C'],
                       'baz': [1, 2, 3, 4, 5, 6]})
>>> df
  bar  baz  foo
0   A    1  one
1   B    2  one
2   C    3  one
3   A    4  two
4   B    5  two
5   C    6  two
>>> df.pivot(index='foo', columns='bar', values='baz')        # 使用 pivot
     A  B  C
one  1  2  3
two  4  5  6
>>> df.pivot_table(index='foo', columns='bar', values='baz')  # 使用 pivot_table
     A  B  C
one  1  2  3
two  4  5  6

但很多情况下,pivot 和 pivot_table 是不相同的。

不同点一: 在 pivot 函数中,通过参数“values”指定的列的类型可以是任意类型;而在 pivot_table 函数中,通过参数“values”指定的列的类型要求是数字类型(当 aggfunc='count'时,可以不是数字类型)。 如:

>>> df = pd.DataFrame({'foo': ['one','one','one','two','two','two'],
...                    'bar': ['A', 'B', 'C', 'A', 'B', 'C'],
...                    'baz': ['a1', 'a2', 'a3', 'a4', 'a5', 'a6']})
>>> df.pivot(index='foo', columns='bar', values='baz')
      A   B   C
one  a1  a2  a3
two  a4  a5  a6
>>> df.pivot_table(index='foo', columns='bar', values='baz')                   # pivot_table报错,因为values指定的列('baz')的类型不是数字
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
......
    raise DataError('No numeric types to aggregate')
pandas.core.base.DataError: No numeric types to aggregate
>>> df.pivot_table(index='foo', columns='bar', values='baz', aggfunc='count')  # 指定aggfunc='count'后,“values”指定的列的类型可以不是数字
     A  B  C
one  1  1  1
two  1  1  1

不同点二: 如果结果集某位置上的值不唯一,就不能使用 pivot;如果结果集某位置上的值不唯一,使用 pivot_table 则默认会求其平均值。 如:

>>> df = pd.DataFrame({'foo': ['one','one','one','two','two','two','two'],
                       'bar': ['A', 'B', 'C', 'A', 'B', 'C', 'C'],
                       'baz': [1, 2, 3, 4, 5, 6, 7]})
>>> df                         # 和本节前面例子相比,DataFrame多了最后一行
  bar  baz  foo
0   A    1  one
1   B    2  one
2   C    3  one
3   A    4  two
4   B    5  two
5   C    6  two
6   C    7  two                # 这行是新增加的,但它导致结果集位置['two', 'C']上的值不唯一了
>>> df.pivot(index='foo', columns='bar', values='baz')       # 不能使用pivot,因为结果集位置上有多个值时(在['two', 'C']处有两个值6和7)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/usr/local/lib/python3.6/site-packages/pandas/core/frame.py", line 4382, in pivot
    return pivot(self, index=index, columns=columns, values=values)
  File "/usr/local/lib/python3.6/site-packages/pandas/core/reshape/reshape.py", line 389, in pivot
    return indexed.unstack(columns)
  File "/usr/local/lib/python3.6/site-packages/pandas/core/series.py", line 2224, in unstack
    return unstack(self, level, fill_value)
  File "/usr/local/lib/python3.6/site-packages/pandas/core/reshape/reshape.py", line 474, in unstack
    fill_value=fill_value)
  File "/usr/local/lib/python3.6/site-packages/pandas/core/reshape/reshape.py", line 116, in __init__
    self._make_selectors()
  File "/usr/local/lib/python3.6/site-packages/pandas/core/reshape/reshape.py", line 154, in _make_selectors
    raise ValueError('Index contains duplicate entries, '
ValueError: Index contains duplicate entries, cannot reshape
>>> df.pivot_table(index='foo', columns='bar', values='baz')  # 可以使用pivot_table,对多个值处理的默认行为是“求均值”,6.5=(6+7)/2
       A    B    C
one  1.0  2.0  3.0
two  4.0  5.0  6.5
>>> df.pivot_table(index='foo', columns='bar', values='baz', aggfunc=np.max)  # 指定aggfunc=np.max后,多个值处理的方法为取最大值,7=max(6,7)
bar  A  B  C
foo
one  1  2  3
two  4  5  7

5.3. stack

下面通过实例来介绍 stack 的作用。

首先,我们测试 DataFrame 只有一列的情况。

>>> df = pd.DataFrame({'a': [1, 2]}, index={'one', 'two'})
>>> df
     a
one  1
two  2
>>> stacked1 = df.stack()       # stack()会产生层次化索引(hierarchical index)
>>> stacked1
one  a    1
two  a    2
dtype: int64
>>> stacked1[('one', 'a')]      # 元组 ('one', 'a') 是层次化索引
1
>>> stacked1[('two', 'a')]
2

现在,我们测试 DataFrame 有两列的情况。

>>> df = pd.DataFrame({'a': [1, 2],
                       'b': [3, 4]},
                      index={'one', 'two'})
>>> df
     a  b
one  1  3
two  2  4
>>> stacked2 = df.stack()        # stack()会产生层次化索引(hierarchical index)
>>> stacked2
one  a    1
     b    3
two  a    2
     b    4
dtype: int64
>>> stacked2[('one', 'a')]
1
>>> stacked2[('one', 'b')]
3
>>> stacked2[('two', 'a')]
2
>>> stacked2[('two', 'b')]
4

6. Comparison with SQL

7. Tips

7.1. 设置显示的行宽

有时,输出数据比较宽,导致数据会换行显示。可以通过下面设置控制显示行宽:

>>> pd.set_option('display.width', 200)        # 设置行宽为200,默认为80

8. 参考

Learning pandas, by Michael Heydt, 2015(本文很多例子摘自于该书)
Pandas Documentation: http://pandas.pydata.org/pandas-docs/stable/tutorials.html

Author: cig01

Created: <2017-01-01 Sun>

Last updated: <2017-12-29 Fri>

Creator: Emacs 27.1 (Org mode 9.4)