目录
一、层次化索引二、合并数据集三、重塑和轴向旋转一、层次化索引
层次化索引(hierarchical indexing)是pandas的一项重要功能,它使你能在一个轴上拥有多个(两个以上)索引级别。抽象点说,它使你能以低维度形式处理高维度数据。import pandas as pdimport numpy as npdata = pd.Series(np.random.randn(9), index=[['a', 'a', 'a', 'b', 'b', 'c', 'c', 'd', 'd'], [1, 2, 3, 1, 3, 1, 2, 2, 3]])In [10]: dataOut[10]: a 1 -0.2047082 0.4789433 -0.519439b 1 -0.5557303 1.965781c 1 1.3934062 0.092908d 2 0.2817463 0.769023dtype: float64In [12]: data['b']Out[12]: 1 -0.5557303 1.965781dtype: float64In [15]: data.loc[:, 2]Out[15]: a 0.478943c 0.092908d 0.281746dtype: float64
层次化索引在数据重塑和基于分组的操作(如透视表生成)中扮演着重要的角色。
In [16]: data.unstack()Out[16]: 1 2 3a -0.204708 0.478943 -0.519439b -0.555730 NaN 1.965781c 1.393406 0.092908 NaNd NaN 0.281746 0.769023data.unstack().stack()
对于一个DataFrame
,每条轴都可以有分层索引:
In [18]: frame = pd.DataFrame(np.arange(12).reshape((4, 3)), index=[['a', 'a', 'b', 'b'], [1, 2, 1, 2]], columns=[['Ohio', 'Ohio', 'Colorado'], ['Green', 'Red', 'Green']])In [19]: frameOut[19]: OhioColoradoGreen Red Greena 10 1 223 4 5b 16 7 829 10 11
各层都可以有名字(可以是字符串,也可以是别的Python对象)。
In [20]: frame.index.names = ['key1', 'key2']In [21]: frame.columns.names = ['state', 'color']In [22]: frameOut[22]: stateOhioColoradocolorGreen Red Greenkey1 key2 a 1 0 1 22 3 4 5b 1 6 7 82 9 10 11
可以单独创建MultiIndex
然后复用。上面那个DataFrame
中的(带有分级名称)列可以这样创建:
MultiIndex.from_arrays([['Ohio', 'Ohio', 'Colorado'], ['Green', 'Red', 'Green']], names=['state', 'color'])
swaplevel
接受两个级别编号或名称,并返回一个互换了级别的新对象(但数据不会发生变化):
In [24]: frame.swaplevel('key1', 'key2')Out[24]: stateOhioColoradocolorGreen Red Greenkey2 key1 1 a 0 1 22 a 3 4 51 b 6 7 82 b 9 10 11
而sort_index则根据单个级别中的值对数据进行排序。交换级别时,常常也会用到sort_index,这样最终结果就是按照指定顺序进行字母排序了:
In [25]: frame.sort_index(level=1)Out[25]: stateOhioColoradocolorGreen Red Greenkey1 key2 a 1 0 1 2b 1 6 7 8a 2 3 4 5b 2 9 10 11In [26]: frame.swaplevel(0, 1).sort_index(level=0)Out[26]: stateOhioColoradocolorGreen Red Greenkey2 key1 1 a 0 1 2b 6 7 82 a 3 4 5b 9 10 11
许多对DataFrame
和Series
的描述和汇总统计都有一个level
选项,它用于指定在某条轴上求和的级别。
In [27]: frame.sum(level='key2')Out[27]: state OhioColoradocolor Green Red Greenkey21 6 8 102 12 14 16In [28]: frame.sum(level='color', axis=1)Out[28]: colorGreen Redkey1 key2 a 1 2 12 8 4b 1 14 72 20 10
这其实是利用了pandas
的groupby
功能。
人们经常想要将DataFrame
的一个或多个列当做行索引来用,或者可能希望将行索引变成DataFrame
的列。
frame = pd.DataFrame({'a': range(7), 'b': range(7, 0, -1), 'c': ['one', 'one', 'one', 'two', 'two', 'two', 'two'], 'd': [0, 1, 2, 0, 1, 2, 3]})frame2 = frame.set_index(['c', 'd'])frame.set_index(['c', 'd'], drop=False)frame2.reset_index() # 功能跟set_index刚好相反,层次化索引的级别会被转移到列里面
二、合并数据集
pandas
对象中的数据可以通过一些方式进行合并:pandas.merge
可根据一个或多个键将不同DataFrame
中的行连接起来,相当于数据库的join操作。pandas.concat
可以沿着一条轴将多个对象堆叠到一起。实例方法combine_first
可以将重复数据拼接在一起,用一个对象中的值填充另一个对象中的缺失值。 多对一的合并df1 = pd.DataFrame({'key': ['b', 'b', 'a', 'c', 'a', 'a', 'b'], 'data1': range(7)})df2 = pd.DataFrame({'key': ['a', 'b', 'd'], 'data2': range(3)})# 注意,如果没有指定要用哪个列进行连接,merge就会将重叠列的列名当做键。In [39]: pd.merge(df1, df2)Out[39]: data1 key data200 b111 b126 b132 a044 a055 a0
如果两个对象的列名不同,也可以分别进行指定:
In [41]: df3 = pd.DataFrame({'lkey': ['b', 'b', 'a', 'c', 'a', 'a', 'b'], 'data1': range(7)})In [42]: df4 = pd.DataFrame({'rkey': ['a', 'b', 'd'], 'data2': range(3)})In [43]: pd.merge(df3, df4, left_on='lkey', right_on='rkey')Out[43]: data1 lkey data2 rkey00 b1 b11 b1 b26 b1 b32 a0 a44 a0 a55 a0 a
默认情况下,merge
做的是“内连接”;结果中的键是交集。其他方式还有”left”、”right”以及”outer”。外连接求取的是键的并集,组合了左连接和右连接的效果。
多对多的合并
In [45]: df1 = pd.DataFrame({'key': ['b', 'b', 'a', 'c', 'a', 'b'], 'data1': range(6)})In [46]: df2 = pd.DataFrame({'key': ['a', 'b', 'a', 'b', 'd'], 'data2': range(5)})In [49]: pd.merge(df1, df2, on='key', how='left') # 左连接,保留c,删除dOut[49]: data1 key data20 0 b 1.01 0 b 3.02 1 b 1.03 1 b 3.04 2 a 0.05 2 a 2.06 3 c NaN7 4 a 0.08 4 a 2.09 5 b 1.0105 b 3.0
要根据多个键进行合并,传入一个由列名组成的列表即可
In [51]: left = pd.DataFrame({'key1': ['foo', 'foo', 'bar'], 'key2': ['one', 'two', 'one'], 'lval': [1, 2, 3]})In [52]: right = pd.DataFrame({'key1': ['foo', 'foo', 'bar', 'bar'], 'key2': ['one', 'one', 'one', 'two'], 'rval': [4, 5, 6, 7]})In [53]: pd.merge(left, right, on=['key1', 'key2'], how='outer')Out[53]: key1 key2 lval rval0 foo one 1.0 4.01 foo one 1.0 5.02 foo two 2.0 NaN3 bar one 3.0 6.04 bar two NaN 7.0
对于合并运算需要考虑的最后一个问题是对重复列名的处理。虽然你可以手工处理列名重叠的问题(查看前面介绍的重命名轴标签),但merge有一个更实用的suffixes选项,用于指定附加到左右两个DataFrame对象的重叠列名上的字符串:
In [54]: pd.merge(left, right, on='key1')Out[54]: key1 key2_x lval key2_y rval0 foo one1 one41 foo one1 one52 foo two2 one43 foo two2 one54 bar one3 one65 bar one3 two7In [55]: pd.merge(left, right, on='key1', suffixes=('_left', '_right'))Out[55]: key1 key2_left lval key2_right rval0 foo one1 one41 foo one1 one52 foo two2 one43 foo two2 one54 bar one3 one65 bar one3 two7
merge函数的参数: on:用于连接的列名left_on/right_on:左侧/右侧DataFrame中用作连接键的列left_index/right_index:将左侧/右侧的行索引用作其连接键how:inner、outer、left、right,DataFrame连接方式
索引上的合并
In [56]: left1 = pd.DataFrame({'key': ['a', 'b', 'a', 'a', 'b', 'c'], 'value': range(6)})In [57]: right1 = pd.DataFrame({'group_val': [3.5, 7]}, index=['a', 'b'])In [60]: pd.merge(left1, right1, left_on='key', right_index=True)Out[60]: key value group_val0 a0 3.52 a2 3.53 a3 3.51 b1 7.04 b4 7.0In [61]: pd.merge(left1, right1, left_on='key', right_index=True, how='outer') # 通过外连接的方式得到它们的并集
层次化索引上的合并
In [62]: lefth = pd.DataFrame({'key1': ['Ohio', 'Ohio', 'Ohio', 'Nevada', 'Nevada'],....: 'key2': [2000, 2001, 2002, 2001, 2002],....: 'data': np.arange(5.)})In [63]: righth = pd.DataFrame(np.arange(12).reshape((6, 2)),....: index=[['Nevada', 'Nevada', 'Ohio', 'Ohio', 'Ohio', 'Ohio'],....:[2001, 2000, 2000, 2000, 2001, 2002]],....: columns=['event1', 'event2'])In [64]: lefthOut[64]: data key1 key20 0.0 Ohio 20001 1.0 Ohio 20012 2.0 Ohio 20023 3.0 Nevada 20014 4.0 Nevada 2002In [65]: righthOut[65]: event1 event2Nevada 2001 0 12000 2 3Ohio 2000 4 52000 6 72001 8 920021011# 必须以列表的形式指明用作合并键的多个列(注意用how=’outer’对重复索引值的处理)In [66]: pd.merge(lefth, righth, left_on=['key1', 'key2'], right_index=True)Out[66]: data key1 key2 event1 event20 0.0 Ohio 2000 4 50 0.0 Ohio 2000 6 71 1.0 Ohio 2001 8 92 2.0 Ohio 200210113 3.0 Nevada 2001 0 1In [67]: pd.merge(lefth, righth, left_on=['key1', 'key2'], right_index=True, how='outer')Out[67]: data key1 key2 event1 event20 0.0 Ohio 20004.05.00 0.0 Ohio 20006.07.01 1.0 Ohio 20018.09.02 2.0 Ohio 2002 10.0 11.03 3.0 Nevada 20010.01.04 4.0 Nevada 2002NaNNaN4 NaN Nevada 20002.03.0
DataFrame
还有一个便捷的join
实例方法,它能更为方便地实现按索引合并。它还可用于合并多个带有相同或相似索引的DataFrame
对象,但要求没有重叠的列。
In [68]: left2 = pd.DataFrame([[1., 2.], [3., 4.], [5., 6.]], index=['a', 'c', 'e'], columns=['Ohio', 'Nevada'])In [69]: right2 = pd.DataFrame([[7., 8.], [9., 10.], [11., 12.], [13, 14]], index=['b', 'c', 'd', 'e'], columns=['Missouri', 'Alabama'])In [72]: pd.merge(left2, right2, how='outer', left_index=True, right_index=True)In [73]: left2.join(right2, how='outer') # 效果等价于上面这行代码Out[73]: Ohio Nevada Missouri Alabamaa 1.02.0 NaNNaNb NaNNaN 7.08.0c 3.04.0 9.010.0d NaNNaN11.012.0e 5.06.013.014.0
DataFrame
的join
方法默认使用的是左连接,保留左边表的行索引
对于简单的索引合并,你还可以向join
传入一组DataFrame
:
another = pd.DataFrame([[7., 8.], [9., 10.], [11., 12.], [16., 17.]], index=['a', 'c', 'e', 'f'], columns=['New York', 'Oregon'])In [77]: left2.join([right2, another])Out[77]: Ohio Nevada Missouri Alabama New York Oregona 1.02.0 NaNNaN 7.08.0c 3.04.0 9.010.0 9.0 10.0e 5.06.013.014.011.0 12.0In [78]: left2.join([right2, another], how='outer')Out[78]: Ohio Nevada Missouri Alabama New York Oregona 1.02.0 NaNNaN 7.08.0b NaNNaN 7.08.0 NaNNaNc 3.04.0 9.010.0 9.0 10.0d NaNNaN11.012.0 NaNNaNe 5.06.013.014.011.0 12.0f NaNNaN NaNNaN16.0 17.0
另一种数据合并运算也被称作连接(concatenation)、绑定(binding)或堆叠(stacking)。NumPy
的concatenation
函数可以用NumPy
数组来做:
arr = np.arange(12).reshape((3, 4))np.concatenate([arr, arr], axis=1)
对于pandas
对象,带有标签的轴使你能够进一步推广数组的连接运算。需要考虑: 如果对象在其它轴上的索引不同,我们应该合并这些轴的不同元素还是只使用交集?连接的数据集是否需要在结果对象中可识别?连接轴中保存的数据是否需要保留?许多情况下,DataFrame
默认的整数标签最好在连接时删掉。 默认情况下,concat
是在axis=0
上工作的,最终产生一个新的Series
。
In [82]: s1 = pd.Series([0, 1], index=['a', 'b'])In [83]: s2 = pd.Series([2, 3, 4], index=['c', 'd', 'e'])In [84]: s3 = pd.Series([5, 6], index=['f', 'g'])In [85]: pd.concat([s1, s2, s3])Out[85]: a 0b 1c 2d 3e 4f 5g 6dtype: int64
如果传入axis=1
,则结果就会变成一个DataFrame
(axis=1
是列):
In [86]: pd.concat([s1, s2, s3], axis=1)Out[86]: 0 1 2a 0.0 NaN NaNb 1.0 NaN NaNc NaN 2.0 NaNd NaN 3.0 NaNe NaN 4.0 NaNf NaN NaN 5.0g NaN NaN 6.0In [87]: s4 = pd.concat([s1, s3])In [89]: pd.concat([s1, s4], axis=1) # 默认交集Out[89]: 0 1a 0.0 0b 1.0 1f NaN 5g NaN 6In [90]: pd.concat([s1, s4], axis=1, join='inner') # 交集Out[90]: 0 1a 0 0b 1 1
可以通过join_axes指定要在其它轴上使用的索引:
In [91]: pd.concat([s1, s4], axis=1, join_axes=[['a', 'c', 'b', 'e']])Out[91]: 0 1a 0.0 0.0c NaN NaNb 1.0 1.0e NaN NaN
假设你想要在连接轴上创建一个层次化索引。使用keys参数即可达到这个目的:
In [92]: result = pd.concat([s1, s1, s3], keys=['one','two', 'three'])In [93]: resultOut[93]: one a 0b 1two a 0b 1three f 5g 6dtype: int64In [94]: result.unstack()Out[94]: a b f gone 0.0 1.0 NaN NaNtwo 0.0 1.0 NaN NaNthree NaN NaN 5.0 6.0
如果沿着axis=1对Series进行合并,则keys就会成为DataFrame的列头:
In [95]: pd.concat([s1, s2, s3], axis=1, keys=['one','two', 'three'])Out[95]: one two threea 0.0 NaN NaNb 1.0 NaN NaNc NaN 2.0 NaNd NaN 3.0 NaNe NaN 4.0 NaNf NaN NaN 5.0g NaN NaN 6.0
同样的逻辑也适用于DataFrame对象:
In [96]: df1 = pd.DataFrame(np.arange(6).reshape(3, 2), index=['a', 'b', 'c'], columns=['one', 'two'])In [97]: df2 = pd.DataFrame(5 + np.arange(4).reshape(2, 2), index=['a', 'c'], columns=['three', 'four'])In [100]: pd.concat([df1, df2], axis=1, keys=['level1', 'level2'])Out[100]: level1level2one two three foura0 1 5.0 6.0b2 3 NaN NaNc4 5 7.0 8.0In [101]: result['level1']Out[102]: onetwoa01b23c45In [102]: result['level1'].loc['a']Out[102]: one 0two 1Name: a, dtype: int32
如果传入的不是列表而是一个字典,则字典的键就会被当做keys选项的值:
In [101]: pd.concat({'level1': df1, 'level2': df2}, axis=1)Out[101]: level1level2one two three foura0 1 5.0 6.0b2 3 NaN NaNc4 5 7.0 8.0
用names参数命名创建的轴级别:pd.concat([df1, df2], axis=1, keys=['level1', 'level2'], names=['upper', 'lower'])
若DataFrame
的行索引不包含任何相关数据,传入ignore_index=True
即可:
In [103]: df1 = pd.DataFrame(np.random.randn(3, 4), columns=['a', 'b', 'c', 'd'])In [104]: df2 = pd.DataFrame(np.random.randn(2, 3), columns=['b', 'd', 'a'])In [107]: pd.concat([df1, df2], ignore_index=True)Out[107]: a b c d0 1.246435 1.007189 -1.296221 0.2749921 0.228913 1.352917 0.886429 -2.0016372 -0.371843 1.669025 -0.438570 -0.5397413 -1.021228 0.476985 NaN 3.2489444 0.302614 -0.577087 NaN 0.124121
合并重叠数据
a = pd.Series([np.nan, 2.5, np.nan, 3.5, 4.5, np.nan], index=['f', 'e', 'd', 'c', 'b', 'a'])b = pd.Series(np.arange(len(a), dtype=np.float64), index=['f', 'e', 'd', 'c', 'b', 'a'])b[-1] = np.nanIn [113]: np.where(pd.isnull(a), b, a)Out[113]: array([ 0. , 2.5, 2. , 3.5, 4.5, nan])
Series
有一个combine_first
方法,实现的也是一样的功能,还带有pandas
的数据对齐:
In [114]: b[:-2].combine_first(a[2:])Out[114]: a NaNb 4.5c 3.0d 2.0e 1.0f 0.0dtype: float64
对于DataFrame,combine_first自然也会在列上做同样的事情,因此你可以将其看做:用传递对象中的数据为调用对象的缺失数据“打补丁”:
In [115]: df1 = pd.DataFrame({'a': [1., np.nan, 5., np.nan],.....: 'b': [np.nan, 2., np.nan, 6.],.....: 'c': range(2, 18, 4)})In [116]: df2 = pd.DataFrame({'a': [5., 4., np.nan, 3., 7.],.....: 'b': [np.nan, 3., 4., 6., 8.]})In [119]: bine_first(df2)Out[119]: a bc0 1.0 NaN 2.01 4.0 2.0 6.02 5.0 4.0 10.03 3.0 6.0 14.04 7.0 8.0 NaN
三、重塑和轴向旋转
有许多用于重新排列表格型数据的基础运算。这些函数也称作重塑(reshape)或轴向旋转(pivot)运算。
层次化索引为DataFrame
数据的重排任务提供了一种具有良好一致性的方式。主要功能有二:stack
:将数据的列“旋转”为行。unstack
:将数据的行“旋转”为列。 重塑层次化索引:
In [120]: data = pd.DataFrame(np.arange(6).reshape((2, 3)),.....: index=pd.Index(['Ohio','Colorado'], name='state'),.....: columns=pd.Index(['one', 'two', 'three'],.....: name='number'))
对该数据使用stack
方法即可将列转换为行,得到一个Series
;对于一个层次化索引的Series
,你可以用unstack
将其重排为一个DataFrame
:
In [122]: result = data.stack()In [123]: resultOut[123]: statenumberOhioone 0two 1three2Colorado one 3two 4three5dtype: int64In [124]: result.unstack()Out[124]: number one two threestateOhio 0 12Colorado 3 45
默认情况下,unstack
操作的是最内层(stack
也是如此)。传入分层级别的编号或名称即可对其它级别进行unstack
操作:
In [125]: result.unstack(0)Out[125]: state Ohio Coloradonumberone 0 3two 1 4three2 5In [126]: result.unstack('state')Out[126]: state Ohio Coloradonumberone 0 3two 1 4three2 5
如果不是所有的级别值都能在各分组中找到的话,则unstack
操作可能会引入缺失数据。stack
默认会滤除缺失数据,因此该运算是可逆的:
In [132]: data2.unstack()Out[132]: a b c d eone 0.0 1.0 2.0 3.0 NaNtwo NaN NaN 4.0 5.0 6.0In [133]: data2.unstack().stack()Out[133]: one a 0.0b 1.0c 2.0d 3.0two c 4.0d 5.0e 6.0dtype: float64In [134]: data2.unstack().stack(dropna=False)Out[134]: one a 0.0b 1.0c 2.0d 3.0e NaNtwo a NaNb NaNc 4.0d 5.0e 6.0dtype: float64
DataFrame
的pivot
方法可以实现将数据从长格式旋转为宽格式,melt
函数可以实现将数据从宽格式旋转为长格式,两者操作可逆。
In[157]: df = pd.DataFrame({'key':['foo','bar','baz'], 'A':[1,2,3], 'B':[4,5,6], 'C':[7,8,9]})In[158]: dfOut[158]:ABCkey0147foo1258bar2369baz# 当使用pandas.melt,我们必须指明哪些列是分组指标。下面使用key作为唯一的分组指标:In[159]: melted = pd.melt(df,['key'])In[160]: meltedOut[160]:key variable value0 foo A11 bar A22 baz A33 foo B44 bar B55 baz B66 foo C77 bar C88 baz C9
使用pivot
,可以重塑回原来的样子:reshaped = melted.pivot('key','variable','value')
注意,pivot
其实就是用set_index
创建层次化索引,再用unstack
重塑:
melted.set_index(['key', 'variable']).unstack('variable')valuevariableABCkeybar258baz369foo147
现在你已经掌握了pandas数据导入、清洗、重塑,我们可以进一步学习matplotlib
数据可视化。我们在稍后会回到pandas
,学习更高级的分析。