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
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 所示的办法。
方式 | 说明 |
---|---|
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 通过操作符 PIVOT
和 UNPIVOT
提供了类似功能。
在 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