文章目录
在许多应用中,数据可能分散在许多文件或数据库中,存储的形式也不利于分析。本章关注可以聚合、合并、重塑数据的方法。
首先,我会介绍 pandas 的层次化索引,它广泛用于以上操作。然后,我深入介绍了一些特殊的数据操作。在第 14 章,你可以看到这些工具的多种应用。
8.1 层次化索引
层次化索引是pandas的一个重要功能,它可以让你在一个轴上拥有多层索引。抽象点说,它能够让你以低纬度的形式,处理高纬度的数据。我们先来看一个简单的例子:创建一个Series,并用一个由列表或数组组成的列表作为索引:
In [9]: data = 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]: data
Out[10]:
a 1 -0.204708
2 0.478943
3 -0.519439
b 1 -0.555730
3 1.965781
c 1 1.393406
2 0.092908
d 2 0.281746
3 0.769023
dtype: float64
看到的结果是经过美化的multiindex索引的Series格式。索引之间的间隔表示直接使用上面的标签
In [11]: data.index
Out[11]:
MultiIndex(levels=[['a', 'b', 'c', 'd'], [1, 2, 3]],
labels=[[0, 0, 0, 1, 1, 2, 2, 3, 3], [0, 1, 2, 0, 2, 0, 1, 1, 2]])
对于一个层次化索引,可以只使用外层进行索引
In [12]: data['b']
Out[12]:
1 -0.555730
3 1.965781
dtype: float64
In [13]: data['b':'c']
Out[13]:
b 1 -0.555730
3 1.965781
c 1 1.393406
2 0.092908
dtype: float64
In [14]: data.loc[['b', 'd']]
Out[14]:
b 1 -0.555730
3 1.965781
d 2 0.281746
3 0.769023
dtype: float64
也可以进行内层索引
In [15]: data.loc[:, 2]
Out[15]:
a 0.478943
c 0.092908
d 0.281746
dtype: float64
注意这里使用内层索引的方法和DataFrame格式,进行列索引的方式是一致的。其实两层的索引本质上可以看成二维数据,外层的索引是行,内层的索引是列。所以索引的方式也和二维数据索引的方式是一致的。
层次化索引在数据重塑和基于分组的操作(比如透视表生产)中扮演着重要的角色。
可以通过unstack方法将这段数据重新安排到DataFrame中
In [16]: data.unstack()
Out[16]:
1 2 3
a -0.204708 0.478943 -0.519439
b -0.555730 NaN 1.965781
c 1.393406 0.092908 NaN
d NaN 0.281746 0.769023
unstack的逆运算是stack
In [17]: data.unstack().stack()
Out[17]:
a 1 -0.204708
2 0.478943
3 -0.519439
b 1 -0.555730
3 1.965781
c 1 1.393406
2 0.092908
d 2 0.281746
3 0.769023
dtype: float64
对于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]: frame
Out[19]:
Ohio Colorado
Green Red Green
a 1 0 1 2
2 3 4 5
b 1 6 7 8
2 9 10 11
各层索引都可以有其自己的名字,如果指定了名称,它们就会显示在控制台输出中:
In [20]: frame.index.names = ['key1', 'key2']
In [21]: frame.columns.names = ['state', 'color']
In [22]: frame
Out[22]:
state Ohio Colorado
color Green Red Green
key1 key2
a 1 0 1 2
2 3 4 5
b 1 6 7 8
2 9 10 11
有了部分列索引,因此可以轻松选取列分组:
In [23]: frame['Ohio']
Out[23]:
color Green Red
key1 key2
a 1 0 1
2 3 4
b 1 6 7
2 9 10
可以单独创建MultiIndex然后复用。上面那个DataFrame中的(带有分级名称)列可以这样创建:
MultiIndex.from_arrays([['Ohio', 'Ohio', 'Colorado'], ['Green', 'Red', 'Green']],
names=['state', 'color'])
重排与分级排序
如果你想对多层索引,调整其顺序,可以使用swaplevel。
swaplevel接受两个级别编号或名称,并返回一个互换了级别的新对象(数据不变)
In [24]: frame.swaplevel('key1', 'key2')
Out[24]:
state Ohio Colorado
color Green Red Green
key2 key1
1 a 0 1 2
2 a 3 4 5
1 b 6 7 8
2 b 9 10 11
多层索引进行索引排序时,可以指定排序的索引,比如指定对内层索引进行排序。交换级别时,常常也会用到sort_index,这样最终结果就是按照指定顺序进行字母排序了:
In [25]: frame.sort_index(level=1)
Out[25]:
state Ohio Colorado
color Green Red Green
key1 key2
a 1 0 1 2
b 1 6 7 8
a 2 3 4 5
b 2 9 10 11
In [26]: frame.swaplevel(0, 1).sort_index(level=0)
Out[26]:
state Ohio Colorado
color Green Red Green
key2 key1
1 a 0 1 2
b 6 7 8
2 a 3 4 5
b 9 10 11
根据级别汇总统计
许多对DataFrame和Series的描述性统计可以都有一个level选项,它用于指定某条轴上汇总的级别。
再以上面那个DataFrame为例,我们可以根据行或列上的级别来进行求和:
In [27]: frame.sum(level='key2')
Out[27]:
state Ohio Colorado
color Green Red Green
key2
1 6 8 10
2 12 14 16
In [28]: frame.sum(level='color', axis=1)
Out[28]:
color Green Red
key1 key2
a 1 2 1
2 8 4
b 1 14 7
2 20 10
注意如果没有指定行或者列的求和的级别,则会整个行或者列进行求和
把DataFrame的列设置为索引
人们经常想把列作为行的索引,或者希望将行索引变成列。
DataFrame的set_index函数可以把一个或多个列转换为行索引,并创建一个新的DataFrame
In [29]: 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]})
In [30]: frame
Out[30]:
a b c d
0 0 7 one 0
1 1 6 one 1
2 2 5 one 2
3 3 4 two 0
4 4 3 two 1
5 5 2 two 2
6 6 1 two 3
In [31]: frame2 = frame.set_index(['c', 'd'])
In [32]: frame2
Out[32]:
a b
c d
one 0 0 7
1 1 6
2 2 5
two 0 3 4
1 4 3
2 5 2
3 6 1
默认情况下这些列会从DataFrame中移出,但是也可以设置不挪出,使用关键字drop = False
In [33]: frame.set_index(['c', 'd'], drop=False)
Out[33]:
a b c d
c d
one 0 0 7 one 0
1 1 6 one 1
2 2 5 one 2
two 0 3 4 two 0
1 4 3 two 1
2 5 2 two 2
3 6 1 two 3
reset_index和set_index正好相反,它可以把行索引转换为列
In [34]: frame2.reset_index()
Out[34]:
c d a b
0 one 0 0 7
1 one 1 1 6
2 one 2 2 5
3 two 0 3 4
4 two 1 4 3
5 two 2 5 2
6 two 3 6 1
注意reset_index默认把所有的行索引都转换为列,但是也可以指定要转换的行索引
In [34]: frame2.reset_index(['d'])
Out[34]:
d a b
c
one 0 0 7
one 1 1 6
one 2 2 5
two 0 3 4
two 1 4 3
two 2 5 2
two 3 6 1
8.2 合并数据集
pandas 对象中的数据可以通过一些方式进行合并:
pandas.merge可根据一个或多个键将不同DataFrame中的行连接起来。SQL 或其他关系型数据库的用户对此应该会比较熟悉,因为它实现的就是数据库的连接操作。
pandas.concat可以沿着一条轴将多个对象堆叠到一起。
实例方法combine_first可以将重复数据拼接在一起,用一个对象中的值填充另一个对象中的缺失值。
我将分别对它们进行讲解,并给出一些例子。本书剩余部分的示例中将经常用到它们。
数据库风格的DataFrame合并
数据集的合并(merge)和链接(join)是通过一个或多个键将行链接起来。这些运算是关系型数据库的核心。pandas的merge是对数据运用这些算法的主要切入点
以一个简单的例子开始
In [35]: df1 = pd.DataFrame({'key': ['b', 'b', 'a', 'c', 'a', 'a', 'b'],
....: 'data1': range(7)})
In [36]: df2 = pd.DataFrame({'key': ['a', 'b', 'd'],
....: 'data2': range(3)})
In [37]: df1
Out[37]:
data1 key
0 0 b
1 1 b
2 2 a
3 3 c
4 4 a
5 5 a
6 6 b
In [38]: df2
Out[38]:
data2 key
0 0 a
1 1 b
2 2 d
这是一种多对一的合并方式,df1中有多个被标记为a或b的行,而df2中key列的每个值则仅对应一行。对这些对象调用merge即可得到:
In [39]: pd.merge(df1, df2)
Out[39]:
data1 key data2
0 0 b 1
1 1 b 1
2 6 b 1
3 2 a 0
4 4 a 0
5 5 a 0
注意:这里没有指定用哪个列进行连接,如果没有指定merge就会将重叠列的列名当做键。不过最好明确指定
In [40]: pd.merge(df1, df2, on='key')
Out[40]:
data1 key data2
0 0 b 1
1 1 b 1
2 6 b 1
3 2 a 0
4 4 a 0
5 5 a 0
如果两个对象的链接列,列名不同,则需要分别进行指定
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 rkey
0 0 b 1 b
1 1 b 1 b
2 6 b 1 b
3 2 a 0 a
4 4 a 0 a
5 5 a 0 a
注意:使用merge默认是内连接,如上面的结果只含有a或b的行,没有含有c及d的行,这是因为使用merge默认是内连接,取的是两个连接列的交集。如果不想要这种结果需要指定连接方式,其他连接方式分别为left(左连接),right(右连接),out(外连接)。
In [44]: pd.merge(df1, df2, how='outer')
Out[44]:
data1 key data2
0 0.0 b 1.0
1 1.0 b 1.0
2 6.0 b 1.0
3 2.0 a 0.0
4 4.0 a 0.0
5 5.0 a 0.0
6 3.0 c NaN
7 NaN d 2.0
表 8-1 对这些选项进行了总结。
多对多的合并有些不直观。看下面的例子:
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 [47]: df1
Out[47]:
data1 key
0 0 b
1 1 b
2 2 a
3 3 c
4 4 a
5 5 b
In [48]: df2
Out[48]:
data2 key
0 0 a
1 1 b
2 2 a
3 3 b
4 4 d
In [49]: pd.merge(df1, df2, on='key', how='left')
Out[49]:
data1 key data2
0 0 b 1.0
1 0 b 3.0
2 1 b 1.0
3 1 b 3.0
4 2 a 0.0
5 2 a 2.0
6 3 c NaN
7 4 a 0.0
8 4 a 2.0
9 5 b 1.0
10 5 b 3.0
多对多连接产生的是行的笛卡尔积。由于左边的DataFrame有 3 个"b"行,右边的有 2 个,所以最终结果中就有 6 个"b"行。连接方式只影响出现在结果中的不同的键的值:
In [50]: pd.merge(df1, df2, how='inner')
Out[50]:
data1 key data2
0 0 b 1
1 0 b 3
2 1 b 1
3 1 b 3
4 5 b 1
5 5 b 3
6 2 a 0
7 2 a 2
8 4 a 0
9 4 a 2
要根据多个列进行连接,只需要传入一个由列名组成的列表就可以了
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 rval
0 foo one 1.0 4.0
1 foo one 1.0 5.0
2 foo two 2.0 NaN
3 bar one 3.0 6.0
4 bar two NaN 7.0
注意,在进行列-列的连接时,DataFrame的行索引会丢失
对于合并列,有个需要处理的问题就是重复列名,
merge有一个关键字suffixes,用于指定附加到左右两个DataFrame对象的重叠列名上的字符串:
In [54]: pd.merge(left, right, on='key1')
Out[54]:
key1 key2_x lval key2_y rval
0 foo one 1 one 4
1 foo one 1 one 5
2 foo two 2 one 4
3 foo two 2 one 5
4 bar one 3 one 6
5 bar one 3 two 7
In [55]: pd.merge(left, right, on='key1', suffixes=('_left', '_right'))
Out[55]:
key1 key2_left lval key2_right rval
0 foo one 1 one 4
1 foo one 1 one 5
2 foo two 2 one 4
3 foo two 2 one 5
4 bar one 3 one 6
5 bar one 3 two 7
表 8-2 merge函数的参数
索引上的合并
有时候,DataFrame中的连接键位于其索引中,在这种情况下,可以传入left_index=True或right_index(或两个都传)以表明索引应该用作连接键
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 [58]: left1
Out[58]:
key value
0 a 0
1 b 1
2 a 2
3 a 3
4 b 4
5 c 5
In [59]: right1
Out[59]:
group_val
a 3.5
b 7.0
In [60]: pd.merge(left1, right1, left_on='key', right_index=True)
Out[60]:
key value group_val
0 a 0 3.5
2 a 2 3.5
3 a 3 3.5
1 b 1 7.0
4 b 4 7.0
由于merge默认是取他们的交集,可以通过参数how取他们的并集
In [61]: pd.merge(left1, right1, left_on='key', right_index=True, how='outer')
Out[61]:
key value group_val
0 a 0 3.5
2 a 2 3.5
3 a 3 3.5
1 b 1 7.0
4 b 4 7.0
5 c 5 NaN
这种情况下,必须要指定用作合并的列
In [66]: pd.merge(lefth, righth, left_on=['key1', 'key2'], right_index=True)
Out[66]:
data key1 key2 event1 event2
0 0.0 Ohio 2000 4 5
0 0.0 Ohio 2000 6 7
1 1.0 Ohio 2001 8 9
2 2.0 Ohio 2002 10 11
3 3.0 Nevada 2001 0 1
In [67]: pd.merge(lefth, righth, left_on=['key1', 'key2'],
....: right_index=True, how='outer')
Out[67]:
data key1 key2 event1 event2
0 0.0 Ohio 2000 4.0 5.0
0 0.0 Ohio 2000 6.0 7.0
1 1.0 Ohio 2001 8.0 9.0
2 2.0 Ohio 2002 10.0 11.0
3 3.0 Nevada 2001 0.0 1.0
4 4.0 Nevada 2002 NaN NaN
4 NaN Nevada 2000 2.0 3.0
同时使用合并双方的索引也没问题
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 [70]: left2
Out[70]:
Ohio Nevada
a 1.0 2.0
c 3.0 4.0
e 5.0 6.0
In [71]: right2
Out[71]:
Missouri Alabama
b 7.0 8.0
c 9.0 10.0
d 11.0 12.0
e 13.0 14.0
In [72]: pd.merge(left2, right2, how='outer', left_index=True, right_index=True)
Out[72]:
Ohio Nevada Missouri Alabama
a 1.0 2.0 NaN NaN
b NaN NaN 7.0 8.0
c 3.0 4.0 9.0 10.0
d NaN NaN 11.0 12.0
e 5.0 6.0 13.0 14.0
DataFrame 还有一个便捷的join连接方法,它能较为方便的实现按照索引进行连接,它还可以较为方便的合并多个带有相同或相似索引的DataFrame对象,但要求没有重叠的列,对于上面的例子,我们可以这样连接
In [73]: left2.join(right2, how='outer')
Out[73]:
Ohio Nevada Missouri Alabama
a 1.0 2.0 NaN NaN
b NaN NaN 7.0 8.0
c 3.0 4.0 9.0 10.0
d NaN NaN 11.0 12.0
e 5.0 6.0 13.0 14.0
join默认的连接方式是左连接,保留左边表的行索引。它还支持在调用的DataFrame的列上,连接传递的DataFrame索引:
关键字on 用来指定左边的对象的连接键的列名
In [58]: left1
Out[58]:
key value
0 a 0
1 b 1
2 a 2
3 a 3
4 b 4
5 c 5
In [59]: right1
Out[59]:
group_val
a 3.5
b 7.0
In [74]: left1.join(right1, on='key')
Out[74]:
key value group_val
0 a 0 3.5
1 b 1 7.0
2 a 2 3.5
3 a 3 3.5
4 b 4 7.0
5 c 5 NaN
注意on只能指定调用join()方法的DataFrame,而传入join()方法的DataFrame还是用行索引进行连接。
最后,对于简单的索引合并,你还可以向join传入一组DataFrame,下一节会介绍更为通用的concat函数,也能实现此功能:
In [75]: another = pd.DataFrame([[7., 8.], [9., 10.], [11., 12.], [16., 17.]],
....: index=['a', 'c', 'e', 'f'],
....: columns=['New York',
'Oregon'])
In [76]: another
Out[76]:
New York Oregon
a 7.0 8.0
c 9.0 10.0
e 11.0 12.0
f 16.0 17.0
In [77]: left2.join([right2, another])
Out[77]:
Ohio Nevada Missouri Alabama New York Oregon
a 1.0 2.0 NaN NaN 7.0 8.0
c 3.0 4.0 9.0 10.0 9.0 10.0
e 5.0 6.0 13.0 14.0 11.0 12.0
In [78]: left2.join([right2, another], how='outer')
Out[78]:
Ohio Nevada Missouri Alabama New York Oregon
a 1.0 2.0 NaN NaN 7.0 8.0
b NaN NaN 7.0 8.0 NaN NaN
c 3.0 4.0 9.0 10.0 9.0 10.0
d NaN NaN 11.0 12.0 NaN NaN
e 5.0 6.0 13.0 14.0 11.0 12.0
f NaN NaN NaN NaN 16.0 17.0
轴向连接
在数据处理过程中,经常会遇到多个表进行拼接合并的需求,在Pandas中有多个拼接合并的方法,每种方法都有自己擅长的拼接方式,本文对pd.concat()进行详细讲解,希望对你有帮助。pd.concat()函数可以沿着指定的轴将多个dataframe或者series拼接到一起,这一点和另一个常用的pd.merge()函数不同,pd.merge()解决数据库样式的左右拼接,不能解决上下拼接。
基本语法
pd.concat(
objs,
axis=0,
join='outer',
ignore_index=False,
keys=None,
levels=None,
names=None,
verify_integrity=False,
copy=True)
参数含义
竖向堆叠
concat函数默认为竖向堆叠,
并且合并之后不改变每个DataFrame子对象的index值, 因此我们可以在合并之后的DataFrame中看到index的值0和1重复了两次。如果希望重新设置合并之后的DataFrame对象的index值, 可以添加ignore_index=True参数:
构建需要的数据表
import pandas as pd
df1 = pd.DataFrame({'A':['A{}'.format(i) for i in range(0,4)],
'B':['B{}'.format(i) for i in range(0,4)],
'C':['C{}'.format(i) for i in range(0,4)]
})
df2 = pd.DataFrame({'A':['A{}'.format(i) for i in range(4,8)],
'B':['B{}'.format(i) for i in range(4,8)],
'C':['C{}'.format(i) for i in range(4,8)]
})
df3 = pd.DataFrame({'A':['A{}'.format(i) for i in range(8,12)],
'B':['B{}'.format(i) for i in range(8,12)],
'C':['C{}'.format(i) for i in range(8,12)]
})
对数据进行连接
result = pd.concat([df1, df2, df3])
A B C
0 A0 B0 C0
1 A1 B1 C1
2 A2 B2 C2
3 A3 B3 C3
0 A4 B4 C4
1 A5 B5 C5
2 A6 B6 C6
3 A7 B7 C7
0 A8 B8 C8
1 A9 B9 C9
2 A10 B10 C10
3 A11 B11 C11
传入也可以是字典(这样可以标注哪一部分是属于哪个表的,类似于行标签了)
frames = {'df1':df1, 'df2':df2,'df3':df3}
result = pd.concat(frames)
A B C
df1 0 A0 B0 C0
1 A1 B1 C1
2 A2 B2 C2
3 A3 B3 C3
df2 0 A4 B4 C4
1 A5 B5 C5
2 A6 B6 C6
3 A7 B7 C7
df3 0 A8 B8 C8
1 A9 B9 C9
2 A10 B10 C10
3 A11 B11 C11
如果两个要合并的列并不完全相同,相同的列会进行合并,不相同的列只出现在本身对象所在的部分,非本身对象所在的部分为NA
df4 = pd.DataFrame({'C':['C{}'.format(i) for i in range(3,9)],
'E':['E{}'.format(i) for i in range(3,9)],
'F':['CF{}'.format(i) for i in range(3,9)]})
df4
C E F
0 C3 E3 CF3
1 C4 E4 CF4
2 C5 E5 CF5
3 C6 E6 CF6
4 C7 E7 CF7
5 C8 E8 CF8
pd.concat([df1,df4])
A B C E F
0 A0 B0 C0 NaN NaN
1 A1 B1 C1 NaN NaN
2 A2 B2 C2 NaN NaN
3 A3 B3 C3 NaN NaN
0 NaN NaN C3 E3 CF3
1 NaN NaN C4 E4 CF4
2 NaN NaN C5 E5 CF5
3 NaN NaN C6 E6 CF6
4 NaN NaN C7 E7 CF7
5 NaN NaN C8 E8 CF8
横向拼接
当参数设置为axis = 1时,concat就是行连接,然后将两个表进行合并
#再构建一个表
df4 = pd.DataFrame({'C':['C{}'.format(i) for i in range(3,9)],
'E':['E{}'.format(i) for i in range(3,9)],
'F':['F{}'.format(i) for i in range(3,9)]
})
pd.concat([df1,df4], axis=1)
A B C C E F
0 A0 B0 C0 C3 E3 F3
1 A1 B1 C1 C4 E4 F4
2 A2 B2 C2 C5 E5 F5
3 A3 B3 C3 C6 E6 F6
4 NaN NaN NaN C7 E7 F7
5 NaN NaN NaN C8 E8 F8
注意横向拼接是按照行标签进行拼接,行标签不同的不会拼接在一起(纵向拼接是按照列标签进行拼接的)
concat函数默认结果是拼接的并集,如果不想显示不能拼接的部分,可以使用参数join = ‘inner’
# join='inner' 取交集
pd.concat([df1, df4], axis=1, join='inner')
A B C C E F
0 A0 B0 C0 C3 E3 F3
1 A1 B1 C1 C4 E4 F4
2 A2 B2 C2 C5 E5 F5
3 A3 B3 C3 C6 E6 F6
# join='outer' 和 默认值相同
pd.concat([df1, df4], axis=1, join='outer')
A B C C E F
0 A0 B0 C0 C3 E3 F3
1 A1 B1 C1 C4 E4 F4
2 A2 B2 C2 C5 E5 F5
3 A3 B3 C3 C6 E6 F6
4 NaN NaN NaN C7 E7 F7
5 NaN NaN NaN C8 E8 F8
增加区分组件
前面提到的keys参数可以用来给合并后的表增加key来区分不同的表数据来源
1、可以直接用key参数实现
pd.concat([df1,df2,df3], keys=['x', 'y', 'z'])
A B C
x 0 A0 B0 C0
1 A1 B1 C1
2 A2 B2 C2
3 A3 B3 C3
y 0 A4 B4 C4
1 A5 B5 C5
2 A6 B6 C6
3 A7 B7 C7
z 0 A8 B8 C8
1 A9 B9 C9
2 A10 B10 C10
3 A11 B11 C11
2、传入字典来增加分组键
frames = {'df1':df1, 'df2':df2,'df3':df3}
result = pd.concat(frames)
A B C
df1 0 A0 B0 C0
1 A1 B1 C1
2 A2 B2 C2
3 A3 B3 C3
df2 0 A4 B4 C4
1 A5 B5 C5
2 A6 B6 C6
3 A7 B7 C7
df3 0 A8 B8 C8
1 A9 B9 C9
2 A10 B10 C10
3 A11 B11 C11
合并重叠数据
还有一种数据组合问题不能用简单的合并(merge)或连接(concatenation)运算来处理。比如说,你可能有索引全部或部分重叠的两个数据集。只有当对象A为NA时,需要对象B同索引下得数据进行补充。这种情况可以使用combine_first方法进行处理
In [108]: a = pd.Series([np.nan, 2.5, np.nan, 3.5, 4.5, np.nan],
.....: index=['f', 'e', 'd', 'c', 'b', 'a'])
In [109]: b = pd.Series(np.arange(len(a), dtype=np.float64),
.....: index=['f', 'e', 'd', 'c', 'b', 'a'])
In [110]: b[-1] = np.nan
In [111]: a
Out[111]:
f NaN
e 2.5
d NaN
c 3.5
b 4.5
a NaN
dtype: float64
In [112]: b
Out[112]:
f 0.0
e 1.0
d 2.0
c 3.0
b 4.0
a NaN
dtype: float64
In [114]: b[:-2].combine_first(a[2:])
Out[114]:
a NaN
b 4.5
c 3.0
d 2.0
e 1.0
f 0.0
dtype: 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 [117]: df1
Out[117]:
a b c
0 1.0 NaN 2
1 NaN 2.0 6
2 5.0 NaN 10
3 NaN 6.0 14
In [118]: df2
Out[118]:
a b
0 5.0 NaN
1 4.0 3.0
2 NaN 4.0
3 3.0 6.0
4 7.0 8.0
In [119]: df1.combine_first(df2)
Out[119]:
a b c
0 1.0 NaN 2.0
1 4.0 2.0 6.0
2 5.0 4.0 10.0
3 3.0 6.0 14.0
4 7.0 8.0 NaN
8.3 重塑和透视
有许多用于重新排列表格型数据的基础运算。这些函数也称作重塑(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'))
In [121]: data
Out[121]:
number one two three
state
Ohio 0 1 2
Colorado 3 4 5
对该数据使用stack方法,即可把列转换为行,得到一个Series(原来的行标签标称外层的行标签,原来的列标签变成里层的行标签)
In [122]: result = data.stack()
In [123]: result
Out[123]:
state number
Ohio one 0
two 1
three 2
Colorado one 3
two 4
three 5
dtype: int64
反过来对于一个拥有层次化索引的Series数据,可以使用unstack将其重排为一个DataFrame
In [124]: result.unstack()
Out[124]:
number one two three
state
Ohio 0 1 2
Colorado 3 4 5
默认情况下,unstack是把最内层的行标签转换为列标签。也可以通过传入分层级别的编号或名称即可对其它级别进行unstack操作:(传入的编号或名称是要转换为列标签的行标签的编号或名称)
In [125]: result.unstack(0)
Out[125]:
state Ohio Colorado
number
one 0 3
two 1 4
three 2 5
In [126]: result.unstack('state')
Out[126]:
state Ohio Colorado
number
one 0 3
two 1 4
three 2 5
如果不是所有的级别值都能在各分组中找到的话,unstack将会引入缺失值
In [127]: s1 = pd.Series([0, 1, 2, 3], index=['a', 'b', 'c', 'd'])
In [128]: s2 = pd.Series([4, 5, 6], index=['c', 'd', 'e'])
In [129]: data2 = pd.concat([s1, s2], keys=['one', 'two'])
In [130]: data2
Out[130]:
one a 0
b 1
c 2
d 3
two c 4
d 5
e 6
dtype: int64
In [131]: data2.unstack()
Out[131]:
a b c d e
one 0.0 1.0 2.0 3.0 NaN
two NaN NaN 4.0 5.0 6.0
对应的stack默认会滤除缺失值,所以stack和unstack是可以逆转的
In [132]: data2.unstack()
Out[132]:
a b c d e
one 0.0 1.0 2.0 3.0 NaN
two NaN NaN 4.0 5.0 6.0
In [133]: data2.unstack().stack()
Out[133]:
one a 0.0
b 1.0
c 2.0
d 3.0
two c 4.0
d 5.0
e 6.0
dtype: float64
In [134]: data2.unstack().stack(dropna=False)
Out[134]:
one a 0.0
b 1.0
c 2.0
d 3.0
e NaN
two a NaN
b NaN
c 4.0
d 5.0
e 6.0
dtype: float64
在对DataFrame进行unstack操作时,作为旋转轴的级别将会成为结果中的最低级别
In [135]: df = pd.DataFrame({'left': result, 'right': result + 5},
.....: columns=pd.Index(['left', 'right'], name='side'))
In [136]: df
Out[136]:
side left right
state number
Ohio one 0 5
two 1 6
three 2 7
Colorado one 3 8
two 4 9
three 5 10
In [137]: df.unstack('state')
Out[137]:
side left right
state Ohio Colorado Ohio Colorado
number
one 0 3 5 8
two 1 4 6 9
three 2 5 7 10
当调用stack,我们可以指明轴的名字:
In [138]: df.unstack('state').stack('side')
Out[138]:
state Colorado Ohio
number side
one left 3 0
right 8 5
two left 4 1
right 9 6
three left 5 2
right 10 7