在具有不同连接类型的多个列上合并Python数据帧

时间:2017-02-01 09:00:48

标签: python dataframe merge

我一直在寻找合并查询的帮助,但我不确定用最简洁的方式来表达我的问题,所以我们无法找到任何直接帮助的东西。

我希望使用左连接合并三列上的两个表,但是在第三个字段中有一些空条目,在这种情况下,我认为我实际上想要一个外连接 - 所以如果有一个匹配前两列,无论第三列中是否匹配,数据都将被连接。

df_A:
Competitor    Product    Type    
A             P1         X
A             P2         X
A             P2         Y
B             P1         X
B             P1         Y 

df_B:
Competitor    Product    Type    Value    
A             P1         X       £5
A             P2         X       £10
A             P2         Y       £12
B             P1                 £15

我想使用Competitor,Product和Type字段对这两个表进行合并。但是,' Type'第二个表中的字段并不总是被填充,在这个例子中,我希望Value应用于表A中的所有类型,即:

Competitor    Product    Type   Value   
A             P1         X      £5
A             P2         X      £10
A             P2         Y      £12
B             P1         X      £15
B             P1         Y      £15

我可以使用代码成功合并前两列:

df_merge=pd.merge(df_A,df_B,how='left',on=['Competitor','Product'])

但是如果我添加第三列' Type',这只会填充所有列中具有匹配项的值,即:

Competitor    Product    Type   Value   
A             P1         X      £5
A             P2         X      £10
A             P2         Y      £12
B             P1         
B             P1         

有没有办法组合连接类型或任何其他方式来获得此解决方案?

1 个答案:

答案 0 :(得分:3)

您可以在['Competitor','Product']上合并:

df_merged = pd.merge(df_A, df_B, how='left', on=['Competitor','Product'])

然后选择类型相同的行 或者Type_y是通配符值:

mask = (df_merged['Type'] == df_merged['Type_y']) | (df_merged['Type_y'] == '')
result = df_merged.loc[mask, ['Competitor','Product','Type','Value']]

例如,

import pandas as pd

df_A = pd.DataFrame({'Competitor': ['A', 'A', 'A', 'B', 'B'],
 'Product': ['P1', 'P2', 'P2', 'P1', 'P1'],
 'Type': ['X', 'X', 'Y', 'X', 'Y']})

df_B = pd.DataFrame({'Competitor': ['A', 'A', 'A', 'B'],
 'Product': ['P1', 'P2', 'P2', 'P1'],
 'Type': ['X', 'X', 'Y', ''],
 'Value': ['£5', '£10', '£12', '£15']},)

df_merged = pd.merge(df_A, df_B, how='left', on=['Competitor','Product'], 
                     suffixes=('','_y'))
mask = (df_merged['Type'] == df_merged['Type_y']) | (df_merged['Type_y'] == '')
result = df_merged.loc[mask, ['Competitor','Product','Type','Value']]

print(result)

产量

  Competitor Product Type Value
0          A      P1    X    £5
1          A      P2    X   £10
4          A      P2    Y   £12
5          B      P1    X   £15
6          B      P1    Y   £15

可以使用

进行批评
pd.merge(df_A, df_B, how='left', on=['Competitor','Product'])

是它可以生成许多不必要的行 - Type_x不等于Type_y的所有行。如果df_Adf_B很大,则可能导致过度使用内存。

为了解决这个问题,我们可以通过将df_B分成两部分来更简约地使用内存:带有通配符值的行和没有通配符的行:

is_wild = pd.isnull(df_B['Type'])
df_notwild, df_wild = df_B.loc[~is_wild], df_B.loc[is_wild]

然后分别合并这两个部分。如果没有通配符值,我们可以 所有列的内部合并。当有通配符时,我们只想右键合并 ['Competitor','Product']

df_merged1 = pd.merge(df_A, df_notwild, how='inner')
df_merged2 = pd.merge(df_A, df_wild, how='right', on=['Competitor','Product'],
                      suffixes=('','_y')).drop('Type_y', axis=1)

然后可以连接两个DataFrame以形成所需的结果:

result = pd.concat([df_merged1, df_merged2], ignore_index=True)

因此,为了节省内存,

import numpy as np
import pandas as pd

df_A = pd.DataFrame({'Competitor': ['A', 'A', 'A', 'B', 'B'],
 'Product': ['P1', 'P2', 'P2', 'P1', 'P1'],
 'Type': ['X', 'X', 'Y', 'X', 'Y']})

df_B = pd.DataFrame({'Competitor': ['A', 'A', 'A', 'B'],
 'Product': ['P1', 'P2', 'P2', 'P1'],
 'Type': ['X', 'X', 'Y', np.nan],
 'Value': ['£5', '£10', '£12', '£15']},)

is_wild = pd.isnull(df_B['Type'])
df_notwild, df_wild = df_B.loc[~is_wild], df_B.loc[is_wild]

df_merged1 = pd.merge(df_A, df_notwild, how='inner')
df_merged2 = pd.merge(df_A, df_wild, how='right', on=['Competitor','Product'],
                      suffixes=('','_y')).drop('Type_y', axis=1)

result = pd.concat([df_merged1, df_merged2], ignore_index=True)
print(result)

产生与上面第一种方法相同的结果。