数据合并

索引上的合并

**比如DataFrame中连接键位于其索引中的情况,传入left_index=Trueright_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

上面例子中将left1key列与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,ea,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函数的参数
WechatIMG34.png

合并重叠数据

如果遇到比较复杂的两个数据集需要合并,里面会遇到很多重叠的情况,那么用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也可以进行这样的合并。