DataFrame合并操作是高性能的内存中连接操作,它将两个DataFrame合并在一起,merge函数类似于SQL中join的用法,可以将不同数据集依照某些字段(属性)进行合并操作,得到一个新的数据集merge定义如下:
def merge(left, right, how='inner', on=None, left_on=None, right_on=None,
left_index=False, right_index=False, sort=False,
suffixes=('_x', '_y'), copy=True, indicator=False,
validate=None):
merge方法的参数非常多,本文讲解其中比较重要的几个
编号 | 参数 | 含义 |
---|---|---|
1 | how | 连接方式, 有left, right, inner, outer四个可选值, 默认是inner |
2 | on | 列名或者索引标签列表 |
3 | left_index | 如果为 True,则 使用左侧DataFrame中的索引(行标签)作为其连接键 |
4 | right_index | 则 使用右侧DataFrame中的索引(行标签)作为其连接键 |
5 | left_on | 左侧DataFrame中的合并作用列 |
6 | right_on | 右侧DataFrame中的合并作用列 |
7 | sort | 按照字典顺序通过连接键对结果DataFrame进行排序 |
两个DataFrame合并,总要有所依据,比如下面这两分数据
import pandas as pd
data1 = [
{'语文': 90, '数学': 92, 'id': 1},
{'语文': 98, '数学': 87, 'id': 2},
{'语文': 87, '数学': 90, 'id': 3},
{'语文': 90, '数学': 98, 'id': 4},
]
left_df = pd.DataFrame(data1, index=['小明', '小红', '小刚', '小丽'])
left_df.index.name = '姓名'
left_df.columns.name = '科目'
data2 = [
{'英语': 89, '物理': 98, 'id': 1},
{'英语': 90, '物理': 90, 'id': 2},
{'英语': 90, '物理': 97, 'id': 3},
{'英语': 98, '物理': 94, 'id': 5},
]
right_df = pd.DataFrame(data2, index=['小明', '小红', '小刚', '小王'])
right_df.index.name = '姓名'
right_df.columns.name = '科目'
现在,我们考虑合并两份数据,以常理推断,合并后,小明,小红,小刚他们3个有4个学科的成绩, 小丽只有语文,数学的成绩, 小王只有英语和物理的成绩,那么问题来了,我们需要告诉程序以哪一列做连接的依据,通过观察数据,我们可以用id这一列做连接合并的依据,id相同的数据被放到同一行上
df = pd.merge(left_df, right_df, on='id')
print(df)
程序输出结果
科目 id 数学 语文 物理 英语
0 1 92 90 98 89
1 2 87 98 90 90
2 3 90 87 97 90
看着最终的结果,有两处我很不满意,一是合并后,学生的姓名都不见了,二是少了两个学生的信息,为了解决这两个问题,需要其他参数配合
之所以缺少两个学生的信息,是因为参数how默认等于inner,做的是内连接,以id列为连接合并依据时,内连接只会保留两个数据集都有数据,想要保留两个数据集全部的数据,需要使用外连接
df = pd.merge(left_df, right_df, how='outer', on='id')
print(df)
合并后的数据
科目 id 数学 语文 物理 英语
0 1 92.0 90.0 98.0 89.0
1 2 87.0 98.0 90.0 90.0
2 3 90.0 87.0 97.0 90.0
3 4 98.0 90.0 NaN NaN
4 5 NaN NaN 94.0 98.0
经过1.2小节的处理,已经得到所有学生的考试成绩,但是学生的姓名都丢失了,这种情况,需要设置left_index 和 right_index
df = pd.merge(left_df, right_df, how='outer', on='id', left_index=True, right_index=True)
print(df)
设置left_index,将使用左侧DataFrame中的索引(行标签)作为其连接键, 这样就保留了左侧DataFrame的索引,最终的合并结果是
科目 id 数学 语文 物理 英语
姓名
小丽 4 98.0 90.0 NaN NaN
小刚 3 90.0 87.0 97.0 90.0
小明 1 92.0 90.0 98.0 89.0
小王 5 NaN NaN 94.0 98.0
小红 2 87.0 98.0 90.0 90.0
现在,我使用姓名列作为连接合并的依据,两个数据集中都有id列,这两个id列会如何处理呢?
df = pd.merge(left_df, right_df, how='inner', on='姓名')
print(df)
输出结果
科目 id_x 数学 语文 id_y 物理 英语
姓名
小明 1 92 90 1 98 89
小红 2 87 98 2 90 90
小刚 3 90 87 3 97 90
两个数据集的id列并没有合并,而是都保留到了新的DataFrame中并有了新的列名
how有4个可选值,inner,left, right, outer,代表了4种不同的合并方式,下面用4个例子逐一解释他们的作用
how参数默认是inner
df = pd.merge(left_df, right_df, how='inner', on='id', left_index=True, right_index=True)
print(df)
以id列连接合并,how参数值是inner, 两个数据集都存在的id是1, 2, 3, 因此最终的结果只有3个学生的数据
科目 id 数学 语文 物理 英语
姓名
小明 1 92 90 98 89
小红 2 87 98 90 90
小刚 3 90 87 97 90
outer是外连接,最终的结果会包含左右两个数据集的全部数据,以id列作为连接合并的依据,相同id的数据会融合到一起,不同id缺失的数据使用np.nan填充
df = pd.merge(left_df, right_df, how='outer', on='id', left_index=True, right_index=True)
print(df)
程序输出结果
科目 id 数学 语文 物理 英语
姓名
小丽 4 98.0 90.0 NaN NaN
小刚 3 90.0 87.0 97.0 90.0
小明 1 92.0 90.0 98.0 89.0
小王 5 NaN NaN 94.0 98.0
小红 2 87.0 98.0 90.0 90.0
仍然以id列作为连接合并的依据,how参数值为left时,将保留全部左侧的数据,右侧DataFrame的数据只保留那些与左侧DataFrame有相同id的数据, 因此最终只有小明, 小红, 小刚, 小丽这4个人的数据,而没有小王的数据,如果how设置为right,那么最终结果将没有小丽的数据
df = pd.merge(left_df, right_df, how='left', on='id', left_index=True, right_index=True)
print(df)
print('*'*20)
df = pd.merge(left_df, right_df, how='right', on='id', left_index=True, right_index=True)
print(df)
程序输出结果
科目 id 数学 语文 物理 英语
姓名
小明 1 92 90 98.0 89.0
小红 2 87 98 90.0 90.0
小刚 3 90 87 97.0 90.0
小丽 4 98 90 NaN NaN
********************
科目 id 数学 语文 物理 英语
姓名
小明 1.0 92.0 90.0 98 89
小红 2.0 87.0 98.0 90 90
小刚 3.0 90.0 87.0 97 90
小王 NaN NaN NaN 94 98
left_on和right_on需要一起使用,是一种替代on参数的方法,前面的例子中,或使用id或使用姓名作为连接合并的依据列,这样做有一个前提条件,两个数据集必须同时有相同名称的列,否则无法使用on参数,比如下面的数据
import pandas as pd
data1 = [
{'语文': 90, '数学': 92, 'id': 1},
{'语文': 98, '数学': 87, 'id': 2},
{'语文': 87, '数学': 90, 'id': 3},
{'语文': 90, '数学': 98, 'id': 4},
]
left_df = pd.DataFrame(data1, index=['小明', '小红', '小刚', '小丽'])
left_df.index.name = '姓名'
left_df.columns.name = '科目'
data2 = [
{'英语': 89, '物理': 98, 'stu_id': 1},
{'英语': 90, '物理': 90, 'stu_id': 2},
{'英语': 90, '物理': 97, 'stu_id': 3},
{'英语': 98, '物理': 94, 'stu_id': 5},
]
right_df = pd.DataFrame(data2, index=['小明', '小红', '小刚', '小王'])
right_df.index.name = 'name'
right_df.columns.name = '科目'
唯一可以标识一个学生身份的学号id,在不同的数据集中有不同的列名,而且index.name也不相同了,这时,就无法使用on这个参数来指定合并时所依据的列,对于这种情况,则可以使用left_on, right_on设置两个数据集分别起作用的列名
df = pd.merge(left_df, right_df, left_on='id', right_on='stu_id', left_index=True)
print(df)
程序最终输出结果
科目 id 数学 语文 stu_id 物理 英语
name
小明 1 92 90 1 98 89
小红 2 87 98 2 90 90
小刚 3 90 87 3 97 90
按照字典顺序通过连接键对结果DataFrame进行排序
为了演示效果,修改左侧数据集
import pandas as pd
data1 = [
{'语文': 87, '数学': 90, 'id': 3},
{'语文': 98, '数学': 87, 'id': 2},
{'语文': 90, '数学': 92, 'id': 1},
{'语文': 90, '数学': 98, 'id': 4},
]
left_df = pd.DataFrame(data1, index=['小明', '小红', '小刚', '小丽'])
left_df.index.name = '姓名'
left_df.columns.name = '科目'
data2 = [
{'英语': 89, '物理': 98, 'stu_id': 1},
{'英语': 90, '物理': 90, 'stu_id': 2},
{'英语': 90, '物理': 97, 'stu_id': 3},
{'英语': 98, '物理': 94, 'stu_id': 5},
]
right_df = pd.DataFrame(data2, index=['小明', '小红', '小刚', '小王'])
right_df.index.name = 'name'
right_df.columns.name = '科目'
df = pd.merge(left_df, right_df, left_on='id', right_on='stu_id', left_index=True)
print(df)
学生的信息没有发生变化,我只修改了学生信息在列表中的顺序,程序输出结果
科目 id 数学 语文 stu_id 物理 英语
name
小刚 3 90 87 3 97 90
小红 2 87 98 2 90 90
小明 1 92 90 1 98 89
最终的结果里,id列是从到小排序的,如果我们想让id列从小到大排序,需要设置sort参数为True
df = pd.merge(left_df, right_df, left_on='id', right_on='stu_id', left_index=True, sort=True)
print(df)
程序最终输出结果
科目 id 数学 语文 stu_id 物理 英语
name
小刚 3 90 87 3 97 90
小红 2 87 98 2 90 90
小明 1 92 90 1 98 89
QQ交流群: 211426309