数据合并、连接和拼接 Merge, join, and concatenate

对于Series DataFrame和Panel对象,pandas提供了丰富的对象之间的连接、合并操作

专业术语-中英文对照

英语 汉语
merge 合并
join 连接
concat 拼接
index 索引
list 列表
dict 字典

pandas axis解释

官方:It specifies the axis along which the means are computed. 沿着轴操作。

默认axis=0,也就是竖轴,操作的结果是行数的增加或减少

axis=1,也就是横轴,操作的结果每一列属性增加或减少

拼接操作 Concatenating objects

pandas.concat()方法实现了所有的拼接操作,即沿着一条轴将多个对象堆叠到一起。

提醒:所有使用concat()进行行拼接(axis=1)的操作,都可以转化为使用merge()完成。

concat()拼接时根据两个对象的index进行拼接,可以使用reset_index(),得到两个DataFrame,使用pd.merge(),on=index得到的列。

在讲解复杂的concat操作之前,先看一个简单的示例:


In [ ]:

pandas中concat对DataFrame操作, 由axis和join两个参数共同控制得到结果。

axis=0, 结果是行数等于两个子DataFrame的行数之和(结果的索引是两个子DF 索引的罗列,即使子DF的索引重复也无所谓)。而每一行的列数 由join参数控制,如果join=‘outer’,会对两个子DataFrame的列求并集,得到结果的每一行属性;如果join=‘inner’,会对两个子DataFrame的列求交集,得到结果的每一行属性

axis=1, 结果中每一行的属性个数等于两个子DF中的属性相加。而行数由join参数控制,具体地,对子DF的索引进行outer或inner。


In [1]:
import pandas as pd
import numpy as np

In [2]:
df1 = pd.DataFrame({'A':['A0','A1','A2','A3'],
                    'B':['B0','B1','B2','B3'],
                    'C':['C0','C1','C2','C3'],
                    'D':['D0','D1','D2','D3']},
                   index=[0,1,2,3])

In [3]:
df1


Out[3]:
A B C D
0 A0 B0 C0 D0
1 A1 B1 C1 D1
2 A2 B2 C2 D2
3 A3 B3 C3 D3

In [4]:
df2 = pd.DataFrame({'A': ['A4', 'A5', 'A6', 'A7'],
                   'B': ['B4', 'B5', 'B6', 'B7'],
                   'C': ['C4', 'C5', 'C6', 'C7'],
                   'D': ['D4', 'D5', 'D6', 'D7']},
                   index=[4, 5, 6, 7])

In [5]:
df2


Out[5]:
A B C D
4 A4 B4 C4 D4
5 A5 B5 C5 D5
6 A6 B6 C6 D6
7 A7 B7 C7 D7

In [6]:
df3 = pd.DataFrame({'A': ['A8', 'A9', 'A10', 'A11'],
                   'B': ['B8', 'B9', 'B10', 'B11'],
                   'C': ['C8', 'C9', 'C10', 'C11'],
                   'D': ['D8', 'D9', 'D10', 'D11']},
                   index=[8, 9, 10, 11])

In [7]:
df3


Out[7]:
A B C D
8 A8 B8 C8 D8
9 A9 B9 C9 D9
10 A10 B10 C10 D10
11 A11 B11 C11 D11

In [8]:
frames = [df1, df2, df3] #产生一个list对象

In [9]:
results = pd.concat(frames)

In [10]:
results


Out[10]:
A B C D
0 A0 B0 C0 D0
1 A1 B1 C1 D1
2 A2 B2 C2 D2
3 A3 B3 C3 D3
4 A4 B4 C4 D4
5 A5 B5 C5 D5
6 A6 B6 C6 D6
7 A7 B7 C7 D7
8 A8 B8 C8 D8
9 A9 B9 C9 D9
10 A10 B10 C10 D10
11 A11 B11 C11 D11

使用join='inner' 查看结果 ,和outer一样!


In [11]:
res = pd.concat(frames, join='inner')
res


Out[11]:
A B C D
0 A0 B0 C0 D0
1 A1 B1 C1 D1
2 A2 B2 C2 D2
3 A3 B3 C3 D3
4 A4 B4 C4 D4
5 A5 B5 C5 D5
6 A6 B6 C6 D6
7 A7 B7 C7 D7
8 A8 B8 C8 D8
9 A9 B9 C9 D9
10 A10 B10 C10 D10
11 A11 B11 C11 D11

修改df3的index,看看结果


In [12]:
df5 = pd.DataFrame({'A': ['A8', 'A9', 'A10', 'A11'],
                   'B': ['B8', 'B9', 'B10', 'B11'],
                   'C': ['C8', 'C9', 'C10', 'C11'],
                   'D': ['D8', 'D9', 'D10', 'D11']},
                   index=[2, 3, 10, 11])
df5


Out[12]:
A B C D
2 A8 B8 C8 D8
3 A9 B9 C9 D9
10 A10 B10 C10 D10
11 A11 B11 C11 D11

In [13]:
df1


Out[13]:
A B C D
0 A0 B0 C0 D0
1 A1 B1 C1 D1
2 A2 B2 C2 D2
3 A3 B3 C3 D3

In [14]:
results2 = pd.concat([df1,df2,df5])
results2


Out[14]:
A B C D
0 A0 B0 C0 D0
1 A1 B1 C1 D1
2 A2 B2 C2 D2
3 A3 B3 C3 D3
4 A4 B4 C4 D4
5 A5 B5 C5 D5
6 A6 B6 C6 D6
7 A7 B7 C7 D7
2 A8 B8 C8 D8
3 A9 B9 C9 D9
10 A10 B10 C10 D10
11 A11 B11 C11 D11

修改df5,使得某几行和df2一样,再看看结果


In [15]:
df5 = pd.DataFrame({'A': ['A2', 'A9', 'A10', 'A11'],
                   'B': ['B2', 'B9', 'B10', 'B11'],
                   'C': ['C2', 'C9', 'C10', 'C11'],
                   'D': ['D2', 'D9', 'D10', 'D11']},
                   index=[2, 3, 10, 11])
df5


Out[15]:
A B C D
2 A2 B2 C2 D2
3 A9 B9 C9 D9
10 A10 B10 C10 D10
11 A11 B11 C11 D11

In [16]:
results3 = pd.concat([df1,df2,df5])
results3


Out[16]:
A B C D
0 A0 B0 C0 D0
1 A1 B1 C1 D1
2 A2 B2 C2 D2
3 A3 B3 C3 D3
4 A4 B4 C4 D4
5 A5 B5 C5 D5
6 A6 B6 C6 D6
7 A7 B7 C7 D7
2 A2 B2 C2 D2
3 A9 B9 C9 D9
10 A10 B10 C10 D10
11 A11 B11 C11 D11

如同numpy.concatenate()方法, pandas.concat方法接收一个列表或者字典对象然后进行拼接操作,注意列表或字典中每个对象要是同构的。拼接的本质就是沿着一条轴并且根据对另一条轴上进行的操作 将列表中的对象堆叠。

concat方法的参数:

pd.concat(objs, axis=0, join='outer', join_axes=None, ignore_index=False, keys=None, levels=None, names=None, verify_integrity=False)

  • objs: Series、DataFrame或Panel对象构成的列表或字典。如果传入一个字典对象,它的键值就作为最后结果的键值参数,除非显示说明结果的键值参数。
  • axis: 轴,取值范围是{0,1,...},默认值为0。concat操作时沿着的那条轴
  • join: 取值范围是{'inner', 'outer'},即内连接(交集)和外连接(并集)。 默认是'outer'(外连接)。连接操作决定了如何处理除axis之外的那条轴。
  • join_axes: 拼接操作得到的结果,它的索引取值。
  • keys: 序列对象,默认是None。 构建层次化索引时使用传入的keys作为最外层的索引。如果想要传入多级keys,需要以元组形式传入。
  • levels: 序列构成的列表,默认是None。用于构建MultiIndex。
  • names:列表,默认是None。层次索引中每个level的名字。
  • verify_integrity: 布尔变量,默认是False。用于检查新拼接的轴是否包含重复值。这个操作很耗时。
  • ignore_index: 布尔变量,默认是False。如果值为True,则不使用拼接轴现有的索引值,拼接结果的索引值将会设置为0,1,...,n-1。
  • copy: 布尔变量,默认是True。 如果取值为False,则不会复制不必要的数据。

如果不结合例子来看,上面介绍的参数真没法解释。

先来看一下keys参数 用于构建层次化索引时起到的作用。


In [17]:
#层次化索引, 用处是给列表中每个对象一个map标记,这样在结果中还能方便的调用每个子Series或DataFrame
result = pd.concat(frames, keys=['x','y','z'])

In [18]:
result


Out[18]:
A B C D
x 0 A0 B0 C0 D0
1 A1 B1 C1 D1
2 A2 B2 C2 D2
3 A3 B3 C3 D3
y 4 A4 B4 C4 D4
5 A5 B5 C5 D5
6 A6 B6 C6 D6
7 A7 B7 C7 D7
z 8 A8 B8 C8 D8
9 A9 B9 C9 D9
10 A10 B10 C10 D10
11 A11 B11 C11 D11

注意到结果中的索引是层次化的。


In [19]:
result.ix['y'] #查看df2


Out[19]:
A B C D
4 A4 B4 C4 D4
5 A5 B5 C5 D5
6 A6 B6 C6 D6
7 A7 B7 C7 D7

注意:concat()方法在拼接使要复制所有的数据,因此对于它的性能你要容忍。为了方便起见,如果对多个数据集拼接,可以使用列表解析式。


In [20]:
frames = [process_your_file(f) for f in files]
result = pd.concat(frames)


---------------------------------------------------------------------------
NameError                                 Traceback (most recent call last)
<ipython-input-20-96969eee2810> in <module>()
----> 1 frames = [process_your_file(f) for f in files]
      2 result = pd.concat(frames)

NameError: name 'files' is not defined

对其它轴进行逻辑运算 Set logic on the other axes

如果是对DataFrame或Panel对象进行拼接操作,你可以同时对非拼接轴进行逻辑运算。由于Series只有一个轴,所以此功能不适用于Series对象。

有三种方式能够对非拼接轴进行逻辑运算:

  • join='outer',取并集,这也是默认的操作,这个操作绝会有信息损失。
  • join='inner',取交集。
  • 利用join_axes参数。

针对上面三种方法,各举一例说明:


In [21]:
df4 = pd.DataFrame({'B': ['B2', 'B3', 'B6', 'B7'],
                   'D': ['D2', 'D3', 'D6', 'D7'],
                   'F': ['F2', 'F3', 'F6', 'F7']},
                   index=[2, 3, 6, 7])

In [22]:
result = pd.concat([df1, df4], axis=1) #这里axis=1

解释

axis=1,即拼接轴是横轴,先将df1的列和df4的列名进行堆叠,即每一行有7列。由于join='outer', 故将df1和df4的索引取取并集,得到{0,1,2,3,6,7},共6行。

还要注意结果的索引值是排好序的。


In [23]:
df4 = pd.DataFrame({'B': ['B2', 'B3', 'B6', 'B7'],
                   'D': ['D2', 'D3', 'D6', 'D7'],
                   'F': ['F2', 'F3', 'F6', 'F7']},
                   index=[3, 2, 7, 6]) #注意这里的索引值序列
df4


Out[23]:
B D F
3 B2 D2 F2
2 B3 D3 F3
7 B6 D6 F6
6 B7 D7 F7

In [24]:
result = pd.concat([df1, df4], axis=1) 
result #结果中的索引值已排序


Out[24]:
A B C D B D F
0 A0 B0 C0 D0 NaN NaN NaN
1 A1 B1 C1 D1 NaN NaN NaN
2 A2 B2 C2 D2 B3 D3 F3
3 A3 B3 C3 D3 B2 D2 F2
6 NaN NaN NaN NaN B7 D7 F7
7 NaN NaN NaN NaN B6 D6 F6

再来看看join='inner'的例子:


In [25]:
result = pd.concat([df1, df4], axis=1, join='inner')
result


Out[25]:
A B C D B D F
3 A3 B3 C3 D3 B2 D2 F2
2 A2 B2 C2 D2 B3 D3 F3

解释

axis=1,即拼接轴是横轴,先将df1的列和df4的列名进行堆叠,即每一行有7列。由于join='inner', 故将df1和df4的索引取取并集,得到{2,3},共2行。

还要注意结果的索引值没有排序。

最后,看一下使用 join_axes参数的例子:


In [26]:
result = pd.concat([df1, df4], axis=1, join_axes=[df1.index])

解释

axis=1,即拼接轴是横轴,先将df1的列和df4的列名进行堆叠,即每一行有7列。由于join_axes=[df1.index],故结果的索引取值和df1相同,{0,1,2,3}。

使用append()方法进行 行拼接

如果只想对Series或DataFrame对象进行行拼接(axis=0),推荐使用append()方法。 append()方法实际上就是沿着索引轴(axis=0)进行拼接的concat()。

注意:

如果是对DataFrame对象进行append操作,要注意他们的索引值交集必须为空!即每个DataFrame对象的索引值都不相同。列名不作要求。

其实,对DataFrame进行append操作时是可以忽略索引的,result = df1.append(df4, ignore_index=True)


In [27]:
result = df1.append(df2)


In [28]:
result = df1.append(df4)
#由于df1和df2的索引值交集不为空,导致最后result的索引值有重复!

append()方法可以一次拼接多个对象。


In [29]:
result = df1.append([df2,df3])

append()操作并不是直接对df1对象进行操作,而是在df1副本的基础上进行拼接操作。

拼接操作时忽略索引值

大多数情况下,索引值都是默认生成的一些无意义的id,此时,两个DataFrame对象很可能有重复的索引值,但他们并没有实质物理含义,此时,在进行行拼接操作时我们可以忽略索引,使用ignore_index参数即可。

在进行列拼接时,就不要使用ignore_index参数了,因为99.9999%的数据列名都是有意义的。


In [30]:
df1


Out[30]:
A B C D
0 A0 B0 C0 D0
1 A1 B1 C1 D1
2 A2 B2 C2 D2
3 A3 B3 C3 D3

In [31]:
df4


Out[31]:
B D F
3 B2 D2 F2
2 B3 D3 F3
7 B6 D6 F6
6 B7 D7 F7

In [32]:
result = pd.concat([df1, df4],ignore_index=True)
result


Out[32]:
A B C D F
0 A0 B0 C0 D0 NaN
1 A1 B1 C1 D1 NaN
2 A2 B2 C2 D2 NaN
3 A3 B3 C3 D3 NaN
4 NaN B2 NaN D2 F2
5 NaN B3 NaN D3 F3
6 NaN B6 NaN D6 F6
7 NaN B7 NaN D7 F7

In [33]:
df1


Out[33]:
A B C D
0 A0 B0 C0 D0
1 A1 B1 C1 D1
2 A2 B2 C2 D2
3 A3 B3 C3 D3

In [34]:
df4


Out[34]:
B D F
3 B2 D2 F2
2 B3 D3 F3
7 B6 D6 F6
6 B7 D7 F7

In [35]:
result2 = pd.concat([df1, df4],axis=1, ignore_index=True) #99.9999%不推荐大家列拼接时使用ignore_index
result2


Out[35]:
0 1 2 3 4 5 6
0 A0 B0 C0 D0 NaN NaN NaN
1 A1 B1 C1 D1 NaN NaN NaN
2 A2 B2 C2 D2 B3 D3 F3
3 A3 B3 C3 D3 B2 D2 F2
6 NaN NaN NaN NaN B7 D7 F7
7 NaN NaN NaN NaN B6 D6 F6

DataFrame.append方法也有ignore_index参数哦


In [36]:
result = df1.append(df4, ignore_index=True)

同时对Series和DataFrame对象进行拼接操作

很简单,原因就是Series会被隐式转为DataFrame对象,同时保持列名相同


In [37]:
s1 = pd.Series(['X0','X1','X2','X3','X4'],name='X')

In [38]:
s1


Out[38]:
0    X0
1    X1
2    X2
3    X3
4    X4
Name: X, dtype: object

In [39]:
result = pd.concat([df1,s1],axis=1)
result


Out[39]:
A B C D X
0 A0 B0 C0 D0 X0
1 A1 B1 C1 D1 X1
2 A2 B2 C2 D2 X2
3 A3 B3 C3 D3 X3
4 NaN NaN NaN NaN X4

如果Series的列没有名字,会默认生成数字用于列名。


In [40]:
s2 = pd.Series(['_0', '_1', '_2', '_3'])
result = pd.concat([df1, s2, s2, s2], axis=1)

对Series对象进行拼接时使用keys参数

concat()方法中keys参数除了构建层次化索引外,另一个很常见的使用情景是:对多个Series对象进行拼接操作,我们希望重新制定得到的DataFrame对象中的列名,而不是使用Series中的名字。


In [41]:
s3 = pd.Series([0, 1, 2, 3], name='foo')
s4 = pd.Series([0, 1, 2, 3])
s5 = pd.Series([0, 1, 4, 5])

In [42]:
pd.concat([s3,s4,s5],axis=1)


Out[42]:
foo 0 1
0 0 0 0
1 1 1 1
2 2 2 4
3 3 3 5

通过keys参数,来设置生成的DataFrame的列名


In [43]:
pd.concat([s3,s4,s5],axis=1,keys=['red','blue','yellos'])


Out[43]:
red blue yellos
0 0 0 0
1 1 1 1
2 2 2 4
3 3 3 5

回顾一下我们的第一个例子:


In [44]:
result = pd.concat(frames, keys=['x', 'y', 'z'])

可以用字典代替上面concat方法中的frames和keys:


In [46]:
pieces = {'x':df1, 'y':df2, 'z':df3}
result = pd.concat(pieces)
result


Out[46]:
A B C D
x 0 A0 B0 C0 D0
1 A1 B1 C1 D1
2 A2 B2 C2 D2
3 A3 B3 C3 D3
y 4 A4 B4 C4 D4
5 A5 B5 C5 D5
6 A6 B6 C6 D6
7 A7 B7 C7 D7
z 8 A8 B8 C8 D8
9 A9 B9 C9 D9
10 A10 B10 C10 D10
11 A11 B11 C11 D11


In [48]:
result = pd.concat(pieces, keys=['z', 'y']) #只对'z','y'对应的DataFrame进行拼接
result


Out[48]:
A B C D
z 8 A8 B8 C8 D8
9 A9 B9 C9 D9
10 A10 B10 C10 D10
11 A11 B11 C11 D11
y 4 A4 B4 C4 D4
5 A5 B5 C5 D5
6 A6 B6 C6 D6
7 A7 B7 C7 D7

对DataFrame对象增加行数

你可以使用append()方法接收一个Series或者列表 对DataFrame对象增加一行,虽然这种方法效率不高,因为要重新生成一个新的DataFrame存放结果。

也可以传递一个Series构成的列表或者字典构成的列表,来增加几行!


In [49]:
s2 = pd.Series(['X0', 'X1', 'X2', 'X3'], index=['A', 'B', 'C', 'D'])
s2


Out[49]:
A    X0
B    X1
C    X2
D    X3
dtype: object

In [50]:
df1


Out[50]:
A B C D
0 A0 B0 C0 D0
1 A1 B1 C1 D1
2 A2 B2 C2 D2
3 A3 B3 C3 D3

In [52]:
result = df1.append(s2, ignore_index=True) #推荐忽略现有的索引值
result


Out[52]:
A B C D
0 A0 B0 C0 D0
1 A1 B1 C1 D1
2 A2 B2 C2 D2
3 A3 B3 C3 D3
4 X0 X1 X2 X3

In [53]:
dicts = [{'A': 1, 'B': 2, 'C': 3, 'X': 4},
        {'A': 5, 'B': 6, 'C': 7, 'Y': 8}]

In [54]:
result = df1.append(dicts, ignore_index=True)

数据库风格的DataFrame 连接/合并操作

pandas提供了非常高效的DataFrame连接(join)操作,并且使用风格和SQL非常类似。相对于其他的开源实现,pandas无疑是相当高效的, 这要归功于对DataFrame对象的优秀的算法设计和高效的内部数据部署。

如果你之前有过SQL开发经验而首次使用pandas,你可能想先了解二者的对比

pandas仅提供了一个merge()方法,就能够对DataFrame对象进行类似关系数据库的连接(join)操作,先看merge()参数

merge(left, right, how='inner', on=None, left_on=None, right_on=None, left_index=False, right_index=False, sort=True, suffixes=('_x', '_y'), copy=True, indicator=False)

  • left: 参与操作的DataFrame对象
  • right: 参与操作的另一个DataFrame对象
  • on: 进行连接操作用到的列名,可以是一列,也可以是多列。这个列名必须在left和right中都存在! 如果on参数没有赋值,并且left_index和right_index都是False,pandas会将left和right都含有的列作为on的值
  • left_on: 取left中某一列名作为keys,可以是列名或者长度和DataFrame对象一行长度相等的数组
  • right_on: 取right中某一列名作为keys, 可以是列名或者长度和DataFrame对象一行长度相等的数组
  • left_index: 如果取值是True,使用left中的索引作为连接操作的keys
  • right_index: 意思同上,
  • how: 取值范围{'left', 'right', 'outer', 'inner'},默认是inner
  • sort: 是否对结果按照连接keys进行排序,默认是True,如果顺序不重要,可以设置为False,会提高效率
  • suffixes: 元组。如果left和right中除keys之外,也有列名一样,会用suffixes参数区别, 默认格式('_x', '_y').
  • copy:是否copy数据,默认为True,这样不会改变left和right
  • indicator:在pandas 0.17.0中加入的参数,如果设置为True,能够显示结果中的key来自left还是right,

merger方法的返回的对象类型同left

merge()方法是pandas中的全局方法,你可以通过DataFrame对象调用它。

和merge()方法很相似的是DataFrame.join()方法,它内部使用merge方法来实现 索引与索引、索引与列 的拼接操作,默认是对索引进行拼接,而merge方法默认是对列进行拼接,无疑,对列进行拼接更常见。除非你要对索引进行拼接,通常都是使用merge()方法。

基本的数学知识:关系代数

对数据库操作熟悉的同学肯定对拼接操作不陌生,下面几种情况是必须知道的:

  • 一对一拼接(one-to-one join): 比如对两个DF对象的索引 进行拼接操作,注意此时索引值必须是唯一的
  • 多对一拼接 (many-to-one join): 对一个对象的索引(唯一的)和另一个对象的一列或几列进行拼接操作
  • 多对多拼接 (many-to-many join): 对两个对象的列 进行拼接操作

注意: 显然, 多对多拼接时最常用的,并且在多对多拼接过程中会丢弃索引值。

大家有必要花时间理解多对多拼接操作的情况。在SQL/关系代数中,如果连接时的键值(key)在两个表中值不唯一,拼接结果是两个表的笛卡尔积。

看一个简单的例子来理解笛卡尔积。


In [56]:
left = pd.DataFrame({'key': ['K0', 'K1', 'K2', 'K3'],
                     'A': ['A0', 'A1', 'A2', 'A3'],
                     'B': ['B0', 'B1', 'B2', 'B3']
        
    })

In [57]:
right = pd.DataFrame({'key': ['K0', 'K2', 'K1', 'K3'],
                      'C': ['C0', 'C1', 'C2', 'C3'],
                      'D': ['D0', 'D1', 'D2', 'D3']
    })

In [58]:
pd.merge(left, right, on='key')


Out[58]:
A B key C D
0 A0 B0 K0 C0 D0
1 A1 B1 K1 C2 D2
2 A2 B2 K2 C1 D1
3 A3 B3 K3 C3 D3

解释:

拼接的键值是'key', left中key这一列的取值是{K0, k1, K2, K3},right中key这一列的取值是{K0, K1, K2, K3}, 由于how参数默认值是'inner', 即内连接,所以笛卡尔积是{K0, K1, K2, K3}


In [ ]:

再看一个复杂点的例子, 多个列名作为key


In [59]:
left = pd.DataFrame({
        'key1':['K0', 'K0', 'K1', 'K2'],
        'key2':['K0', 'K1', 'K0', 'K1'],
        'A':['A0', 'A1', 'A2', 'A3'],
        'B':['B0', 'B1', 'B2', 'B3']
    })

In [60]:
right = pd.DataFrame({'key1': ['K0', 'K1', 'K1', 'K2'],
                      'key2': ['K0', 'K0', 'K0', 'K0'],
                       'C': ['C0', 'C1', 'C2', 'C3'],
                       'D': ['D0', 'D1', 'D2', 'D3']})

In [62]:
pd.merge(left, right, on=['key1', 'key2'])


Out[62]:
A B key1 key2 C D
0 A0 B0 K0 K0 C0 D0
1 A2 B2 K1 K0 C1 D1
2 A2 B2 K1 K0 C2 D2

解释:

此时拼接操作的键值是['key1', 'key2'],在left中['key1','key2']的取值是{['K0','K0'], ['K0', 'K1'], ['K1', 'K0'], ['K2', 'K1']},在right中['key1', 'key2']的取值是{['K0', 'K0'], ['K1', 'K0'], ['K2', 'K0']}, 默认进行内连接,所以取二者的交集{['K0', 'K0'], ['K1', 'K0']}, 并且left中['K1', 'K0']出现了两次,所以拼接结果是3行。


In [ ]:

merge方法中的参数how决定了哪些键值(keys)会被保留到结果中。注意如果on参数接收的键值在left和right中都没有出现,拼接结果是NA。

下面总结了how参数在SQL中对应的操作

参数how取值 SQL拼接操作 操作描述
left LEFT OUTER JOIN 只使用来自left中的键值
right RIGHT OUTER JOIN 只使用来自right中的键值
outer FULL OUTER JOIN 使用left、right中键值的并集
inner INNER JOIN 使用left、right中键值的交集

In [68]:
result = pd.merge(left, right, how='left', on=['key1', 'key2'])

解释

on的参数是['key1', 'key2'],所以拼接的键值是['key1', 'key2'],how参数是'left',所以进行的是左拼接,只关注left中键值取值。

过程:left中第一行键值是['K0', 'K1'],发现right有此取值,拼接后得到result中的第一行;

left中第二行键值是['K0', 'K1'],发现right中没有此取值,仍进行拼接得到result中第二行,注意'C' 'D'取值NaN

left中第三行键值是['K1', 'K0'], 发现right中有此取值切出现了两次,分别进行拼接得到result中第三行、第四行

left中第四行取值是['K2', 'K1'], 发现right中没有此取值,仍进行拼接得到result中第五行,'C', 'D'取值NaN


In [ ]:


In [69]:
result = pd.merge(left, right, how='right', on=['key1', 'key2'])


In [ ]:


In [70]:
result = pd.merge(left, right, how='outer', on=['key1', 'key2'])


In [71]:
result = pd.merge(left, right, how='inner', on=['key1', 'key2'])

merge方法中indicator参数说明

0.17.0版本才有的参数

merge方法在0.17.0版本中新增了一个参数indicator。这个参数取值True,False或字符串。如果取值True,会在拼接结果中多出一列'_merge', 显示 每一行的键值来自left还是right。

_merge值 含义
left_only 键值仅来自left
right_only 键值仅来自right
both 键值来自left和both

In [77]:
df1 = pd.DataFrame({'col1':[0,1], 'col_left':['a','b']})
df2 = pd.DataFrame({'col1':[1,2,2],'col_right':[2,2,2]})

In [78]:
df1


Out[78]:
col1 col_left
0 0 a
1 1 b

In [79]:
df2


Out[79]:
col1 col_right
0 1 2
1 2 2
2 2 2

In [80]:
pd.merge(df1, df2, on='col1', how='outer', indicator=True)


Out[80]:
col1 col_left col_right _merge
0 0.0 a NaN left_only
1 1.0 b 2.0 both
2 2.0 NaN 2.0 right_only
3 2.0 NaN 2.0 right_only

上面提到indicator参数取值也可以是字符串,没啥特别的,就是在拼接结果中用接收的字符串作为'_merge'的列名罢了。


In [81]:
pd.merge(df1, df2, on='col1', how='outer', indicator='indicator_column')


Out[81]:
col1 col_left col_right indicator_column
0 0.0 a NaN left_only
1 1.0 b 2.0 both
2 2.0 NaN 2.0 right_only
3 2.0 NaN 2.0 right_only

对索引进行拼接

DataFrame.join方法的作用是对两个DataFrame对象进行基于索引值的拼接操作。下面是一个简单的例子:

把索引也看做是一列,再来看下面的操作就就没啥了


In [82]:
left = pd.DataFrame({'A': ['A0', 'A1', 'A2'],
                       'B': ['B0', 'B1', 'B2']},
                       index=['K0', 'K1', 'K2'])
  

right = pd.DataFrame({'C': ['C0', 'C2', 'C3'],
                         'D': ['D0', 'D2', 'D3']},
                         index=['K0', 'K2', 'K3'])
  

result = left.join(right)


In [83]:
result = left.join(right, how='outer')


In [84]:
result = left.join(right, how='inner')

merge方法中提供了left_index, right_index参数同样实现了基于索引的拼接操作。


In [85]:
result = pd.merge(left, right, left_index=True, right_index=True, how='outer')


In [86]:
result = pd.merge(left, right, left_index=True, right_index=True, how='inner')

除拼接键值之外,重复的列名用suffixes参数区别

很多情况下,left和right中除了拼接键值之外,还会有其他列名也相同,为了在结果中加以区分,这个时候suffixes参数就派上用场了。好消息是suffixes有默认值,即使不为它赋值在结果中也能够区分。

举例如下:


In [87]:
left = pd.DataFrame({'k': ['K0', 'K1', 'K2'], 'v': [1, 2, 3]})

In [88]:
right = pd.DataFrame({'k': ['K0', 'K0', 'K3'], 'v': [4, 5, 6]})

In [90]:
result = pd.merge(left, right, on='k')

默认情况下:


In [91]:
result = pd.merge(left, right, on='k', suffixes=['_l', '_r']) #为suffixes参数赋值

DataFrame.join方法也提供了lsuffix和rsuffix参数来实现同样的功能:


In [92]:
left = left.set_index('k')
right = right.set_index('k')

In [93]:
result = left.join(right, lsuffix='_l', rsuffix='_r')

拼接多个DataFrame对象

DataFrame.join方法能够接收DataFrame对象构成的元组或列表,对列表内所有DF对象进行基于索引的拼接。Panel.join方法也有此功能。


In [94]:
right2 = pd.DataFrame({'v': [7, 8, 9]}, index=['K1', 'K1', 'K2'])

In [96]:
left.join([right, right2])


Out[96]:
v_x v_y v
K0 1.0 4.0 NaN
K0 1.0 5.0 NaN
K1 2.0 NaN 7.0
K1 2.0 NaN 8.0
K2 3.0 NaN 9.0
K3 NaN 6.0 NaN

合并 有序数据

v0.18.0新增了一个ordered_merge()方法 对时间序列或者其他有序数据进行合并。并且ordered_merge提供了fill_method参数来填充缺失值。


In [103]:
left = pd.DataFrame({'k': ['K0', 'K1', 'K1', 'K2'],
                     'lv': [1, 2, 3, 4],
                     's': ['a', 'b', 'c', 'd']})

right = pd.DataFrame({'k': ['K1', 'K2', 'K4'],
                     'rv': [1, 2, 3]})
left


Out[103]:
k lv s
0 K0 1 a
1 K1 2 b
2 K1 3 c
3 K2 4 d

In [104]:
right


Out[104]:
k rv
0 K1 1
1 K2 2
2 K4 3

In [102]:
pd.ordered_merge(left, right, fill_method='ffill', left_by='s')


Out[102]:
k lv s rv
0 K0 1.0 a NaN
1 K1 1.0 a 1.0
2 K2 1.0 a 2.0
3 K4 1.0 a 3.0
4 K1 2.0 b 1.0
5 K2 2.0 b 2.0
6 K4 2.0 b 3.0
7 K1 3.0 c 1.0
8 K2 3.0 c 2.0
9 K4 3.0 c 3.0
10 K1 NaN d 1.0
11 K2 4.0 d 2.0
12 K4 4.0 d 3.0

使用合并方法填充NaN


In [105]:
df1 = pd.DataFrame([[np.nan, 3., 5.], [-4.6, np.nan, np.nan],
                    [np.nan, 7., np.nan]])
df1


Out[105]:
0 1 2
0 NaN 3.0 5.0
1 -4.6 NaN NaN
2 NaN 7.0 NaN

In [106]:
df2 = pd.DataFrame([[-42.6, np.nan, -8.2], [-5., 1.6, 4]],
                       index=[1, 2])
df2


Out[106]:
0 1 2
1 -42.6 NaN -8.2
2 -5.0 1.6 4.0

先使用combine_first方法:


In [108]:
df1.combine_first(df2)


Out[108]:
0 1 2
0 NaN 3.0 5.0
1 -4.6 NaN -8.2
2 -5.0 7.0 4.0

In [109]:
df1


Out[109]:
0 1 2
0 NaN 3.0 5.0
1 -4.6 NaN NaN
2 NaN 7.0 NaN

df1和df2列名完全相同,索引值也相似。

由于df2中不存在索引值=0,所以df1中第一行的值原封不动;

对于df1中不等于NaN的值,也不需要改变;

对于df1中等于NaN的值,用df2中同索引同列名的值替换, 得到结果。

注意combine_first()方法不改变df1的值。

如果想改变df1的值,使用update()方法。


In [110]:
df1.update(df2)

In [111]:
df1


Out[111]:
0 1 2
0 NaN 3.0 5.0
1 -42.6 NaN -8.2
2 -5.0 1.6 4.0

In [ ]: