索引上的合并
**比如DataFrame中连接键位于其索引中的情况,传入left_index=True
或right_index=True
以说明索引应该被用作连接键:
In [62]: left1 = DataFrame({'key':['a','b','a','a','b','c'],'value':range(6)})
In [63]: right1 = DataFrame({'group_val':[3.5, 7]}, index=['a','b'])
In [64]: left1
Out[64]:
key value
0 a 0
1 b 1
2 a 2
3 a 3
4 b 4
5 c 5
In [65]: right1
Out[65]:
group_val
a 3.5
b 7.0
In [71]: pd.merge(left1,right1,left_on='key',right_index=True)
Out[71]:
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
上面例子中将left1
的key
列与right1
中的行索引进行连接。merge
函数默认是求连接键的交集。可以通过修改how
参数来实现并集。
In [82]: pd.merge(left1,right1,left_on='key',right_index=True,how='outer')
Out[82]:
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
与数学中的交集与并集和合并中的交集并集还有有区别的,在笔记|数据分析之pandas基础----聚合、合并与重塑(一)中已经分析过
如果是对于层次化索引的数据
In [83]: lefth = DataFrame({'key1':['Ohio','Ohio','Ohio','Nevada','Nevada'],'key2':[2000,2001,2002,
...: 2001,2002],'data':np.arange(5.)})
In [84]: righth = DataFrame(np.arange(12).reshape((6,2)),index=[['Nevada','Nevada','Ohio','Ohio','O
...: hio','Ohio'],[2001,2000,2000,2000,2001,2002]],columns=['event1','event2'])
In [85]: lefth
Out[85]:
key1 key2 data
0 Ohio 2000 0.0
1 Ohio 2001 1.0
2 Ohio 2002 2.0
3 Nevada 2001 3.0
4 Nevada 2002 4.0
In [86]: righth
Out[86]:
event1 event2
Nevada 2001 0 1
2000 2 3
Ohio 2000 4 5
2000 6 7
2001 8 9
2002 10 11
这种情况下,你必须以列表的形式指明用作合并键的多个列:
In [89]: pd.merge(lefth,righth,left_on=['key1','key2'],right_index=True)
Out[89]:
key1 key2 data event1 event2
0 Ohio 2000 0.0 4 5
0 Ohio 2000 0.0 6 7
1 Ohio 2001 1.0 8 9
2 Ohio 2002 2.0 10 11
3 Nevada 2001 3.0 0 1
**要将rightf
的层次化索引合并进来需要2列数据,lefth
中的key1,key2
正好可以对其进行合并,所以left_on = ['key1', 'key2']
。
也可以同时合并双方的索引
In [90]: left2 = DataFrame([[1,2],[3,4],[5,6]],index=['a','c','e'], columns=['Ohio','Nevada'])
In [91]: right2 = DataFrame([[7,8],[9,10],[11,12],[13,14]],index=['b','c','d','e'],columns=['Missou
...: ri','Alabama'])
In [92]: left2
Out[92]:
Ohio Nevada
a 1 2
c 3 4
e 5 6
In [93]: right2
Out[93]:
Missouri Alabama
b 7 8
c 9 10
d 11 12
e 13 14
In [94]: pd.merge(left2,right2, how='outer',left_index=True,right_index=True)
Out[94]:
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方法进行合并
join函数可用于合并多个带有相同或相似索引的DataFrame对象而不管它们之间有没有重叠的列
In [95]: left2.join(right2,how='outer')
Out[95]:
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对象
In [96]: another = DataFrame([[7,8],[9,10],[11,12],[16,17]],index=['a','c','e','f'],columns=['New Y
...: ork','Oregon'])
In [97]: left2.join([right2,another])
Out[97]:
Ohio Nevada Missouri Alabama New York Oregon
a 1 2 NaN NaN 7 8
c 3 4 9.0 10.0 9 10
e 5 6 13.0 14.0 11 12
默认是交集,所以a,b,c,d,e
与a,c,e,f
合并后,索引就变成a,c,e
轴向连接
pandas还有另一种合并运算:Numpy的```concatenation````函数:
In [98]: arr = np.arange(12).reshape(3,4)
In [99]: arr
Out[99]:
array([[ 0, 1, 2, 3],
[ 4, 5, 6, 7],
[ 8, 9, 10, 11]])
In [100]: np.concatenate([arr,arr],axis=1)
Out[100]:
array([[ 0, 1, 2, 3, 0, 1, 2, 3],
[ 4, 5, 6, 7, 4, 5, 6, 7],
[ 8, 9, 10, 11, 8, 9, 10, 11]])
这个很好理解,axis=1
合并列。
而对于pandas对象,带有标签的轴使你能够进一步推广数组的连接运算。教材中给出了三个需要思考的点:
- 如果各对象其他轴上的索引不同,那些轴应该做并集还是交集?
- 结果对象中的分组需要各不相同吗?
- 用于连接的轴重要吗?
pandas的concat
函数为这些问题提供了可靠的解决方案。
如下例,假设有三个没有重叠索引的Series:
In [102]: s1 = Series([0,1],index=['a','b'])
In [103]: s2 = Series([2,3,4], index=['c','d','e'])
In [104]: s3 = Series([5,6], index=['f','g'])
我们可以通过concat
函数将三个对象粘合在一起:
In [108]: pd.concat([s1,s2,s3])
Out[108]:
a 0
b 1
c 2
d 3
e 4
f 5
g 6
dtype: int64
concat
函数默认axis=0
,所以会合并行。
从结果中可以看到没有出现重叠的情况,再看下面的例子:
In [115]: pd.concat([s1,s4], axis=1, sort=False)
Out[115]:
0 1
a 0.0 0
b 1.0 5
f NaN 5
g NaN 6
In [117]: pd.concat([s1,s4], axis=1, sort=False, join='inner')
Out[117]:
0 1
a 0 0
b 1 5
因为设置了join=inner
交集,所以f、g
标签消失了。
你可以通过join_axes
指定要在其他轴上使用的索引:
In [118]: pd.concat([s1,s4], axis=1, join_axes=[['a','c','b','e']])
Out[118]:
0 1
a 0.0 0.0
c NaN NaN
b 1.0 5.0
e NaN NaN
如果需要知道合并后的数据来源,我们可以创建一个层次化索引,使用keys
参数可以做到:
In [119]: result = pd.concat([s1, s1, s3], keys = ['one','two','three'])
In [120]: result
Out[120]:
one a 0
b 1
two a 0
b 1
three f 5
g 6
dtype: int64
将result
转为DataFrame
In [121]: result.unstack()
Out[121]:
a b f g
one 0.0 1.0 NaN NaN
two 0.0 1.0 NaN NaN
three NaN NaN 5.0 6.0
如果沿着axis=1对Series进行合并,则keys就会成为DataFrame的列头:
In [129]: pd.concat([s1, s2, s3], keys = ['one','two','three'], axis=1, sort=False)
Out[129]:
one two three
a 0.0 NaN NaN
b 1.0 NaN NaN
c NaN 2.0 NaN
d NaN 3.0 NaN
e NaN 4.0 NaN
f NaN NaN 5.0
g NaN NaN 6.0
这里axis=1
行向合并,keys
也受到了它的影响变成了DataFrame的列头。
同样DataFrame也是如此:
In [130]: df1 = DataFrame(np.arange(6).reshape(3,2), index=['a','b','c'], columns=['one','two'])
In [131]: df2 = DataFrame(5 + np.arange(4).reshape(2, 2), index=['a','c'], columns=['three','four'])
In [133]: pd.concat([df1, df2], axis=1, keys=['level1','level2'], sort=False)
Out[133]:
level1 level2
one two three four
a 0 1 5.0 6.0
b 2 3 NaN NaN
c 4 5 7.0 8.0
如果传入的不是列表而是一个字典,则字典的键就会被当做keys选项的值:
In [138]: pd.concat({'level1':df1, 'level2':df2}, axis=1, sort=False)
Out[138]:
level1 level2
one two three four
a 0 1 5.0 6.0
b 2 3 NaN NaN
c 4 5 7.0 8.0
此外还有两个用于管理层次化索引创建方式的参数:
In [139]: pd.concat([df1,df2], axis=1,sort=False, keys=['level1','level2'],names=['upper','lower'])
...:
Out[139]:
upper level1 level2
lower one two three four
a 0 1 5.0 6.0
b 2 3 NaN NaN
c 4 5 7.0 8.0
concat函数的参数
合并重叠数据
如果遇到比较复杂的两个数据集需要合并,里面会遇到很多重叠的情况,那么用NumPy的where
函数是最佳选择:
In [140]: a = Series([np.nan, 2.5, np.nan, 3.5, 4.5, np.nan],index=['f','e','d','c','b','a'])
In [141]: b = Series(np.arange(len(a), dtype=np.float64), index=['f','e','d','c','b','a'])
In [142]: b[-1] = np.nan
In [143]: a
Out[143]:
f NaN
e 2.5
d NaN
c 3.5
b 4.5
a NaN
dtype: float64
In [144]: b
Out[144]:
f 0.0
e 1.0
d 2.0
c 3.0
b 4.0
a NaN
dtype: float64
比方说遇到这种索引全都重叠的两组数据集,并且merge
函数不能对Series对象进行合并的情况下:
In [155]: np.where(pd.isnull(a),b,a)
Out[155]: array([0. , 2.5, 2. , 3.5, 4.5, nan])
where
函数本来是返回一个数组,判断a
中元素是否为NaN
,是返回b
否则返回a
。正好对两组数据进行了合并,这种情况比较少不过还是要了解下。奇怪的是这里和教材不同,返回了一个ndarray
。
Series有一个combine_first方法,也可以实现一样的功能,而且会对数据对齐:
In [162]: b[:-2].combine_first(a[2:])
Out[162]:
a NaN
b 4.5
c 3.0
d 2.0
e 1.0
f 0.0
Name: b, dtype: float64
同样combine_first
也可以用在DataFrame上:
In [164]: df1 = DataFrame({'a':[1, np.nan, 5, np.nan],'b':[np.nan, 2, np.nan, 6],'c':range(2, 18, 4
...: )})
In [165]: df2 = DataFrame({'a':[5, 4, np.nan, 3, 7],'b':[np.nan, 3, 4, 6, 8]})
In [166]: df1.combine_first(df2)
Out[166]:
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
在两组数据正好可以互相填补的情况下很适合用combine_first
。
总结
今天学习了merge
函数的后半部分功能:
- DataFrame对象行索引的合并
- 也可以对层次化索引进行合并,因为有层次化索引的关系会有两列索引,所以相对应的要提供2列数据来对它合并。
通过join
方法进行合并,它可以对多个相同或者相似索引的DataFrame进行合并。
学习了concat
函数对Series对象的一些合并操作,它能控制Series横向或者纵向合并。可以对合并后的数据添加层次化索引,同样对DataFrame也可以进行这样的合并。