与Wild Cards合并

时间:2017-08-01 16:16:05

标签: python pandas join merge

我正在尝试使用带有通配符的多个列合并两个pandas数据帧。

考虑数据集,其中result是所需合并的结果:

left=pd.DataFrame({'Type':['ABC','ADEC','OOO','DOG','MOT'], 'ID':[22,44,23,21,55]})
right=pd.DataFrame({'Type':['ABC','ADE*','*','DOG'], 'ID':[22,'*','23','2*'], 'Value': [0,1,1,0]})
result=pd.DataFrame({'Type':['ABC','ADEC','OOO','DOG','MOT'], 'ID':[22,44,23,21,55],'Value': [0,1,1,0,'NaN']})

哪个给:

left
   ID  Type
0  22   ABC
1  44  ADEC
2  23   OOO  
3  21   DOG
4  55   MOT

right
   ID  Type  Value
0  22   ABC      0
1   *  ADE*      1
2  23     *      1
3  2*   DOG      0

result
   ID  Type Value
0  22   ABC     0
1  44  ADEC     1
2  23   OOO     1
3  21   DOG     0
4  55   MOT   NaN

我尝试使用以下方法完成此任务:

pd.merge(left=left, right=right, left_on=['Type', 'ID'], right_on ['Type','ID'], how='left')

但最终得到了:

pd.merge(left=left, right=right, left_on=['Type', 'ID'], right_on= ['Type','ID'], how='left')
   ID  Type  Value
0  22   ABC    0.0
1  44  ADEC    NaN
2  23   OOO    NaN
3  21   DOG    NaN
4  55   MOT    NaN

非常感谢任何帮助。谢谢!

1 个答案:

答案 0 :(得分:4)

import pandas as pd

left = pd.DataFrame(
    {'Type': ['ABC', 'ADEC', 'OOO', 'DOG', 'MOT'], 'ID': [22, 44, 23, 21, 55]})
right = pd.DataFrame({'Type': ['ABC', 'ADE*', '*', 'DOG'],
                      'ID': [22, '*', '23', '2*'], 'Value': [0, 1, 1, 0]},
                     index=list('ABCD'))
expected = pd.DataFrame({'Type': ['ABC', 'ADEC', 'OOO', 'DOG', 'MOT'], 'ID': [
                      22, 44, 23, 21, 55], 'Value': [0, 1, 1, 0, 'NaN']})

data = {}
for col in ['ID', 'Type']:
    right[col] = right[col].astype(str).str.replace('*','.')
    left[col] = left[col].astype(str)
    data[col] = (right[col].apply(lambda pat: left.loc[left[col].str.match(pat), col])
                 .stack().to_frame(col))
    data[col].index = data[col].index.droplevel(level=1)

expanded = (data['ID']
            .join(data['Type'])
            .join(right['Value']))

result = pd.merge(left, expanded, how='left')


print(result)

产量

   ID  Type  Value
0  22   ABC    0.0
1  44  ADEC    1.0
2  23   OOO    1.0
3  21   DOG    0.0
4  55   MOT    NaN

如果您将*更改为.,则可以将right中的值视为正则表达式模式。 然后,您可以使用str.match(pat)来测试right中的模式是否与left中的字符串匹配。例如,

In [297]: right
Out[297]: 
   ID  Type  Value
A  22   ABC      0
B   .  ADE.      1
C  23     .      1
D  2.   DOG      0

In [298]: left
Out[298]: 
   ID  Type
0  22   ABC
1  44  ADEC
2  23   OOO
3  21   DOG
4  55   MOT

In [271]: right['ID'].apply(lambda pat: left.loc[left['ID'].str.match(pat), 'ID'])
Out[271]: 
     0    1    2    3    4
A   22  NaN  NaN  NaN  NaN
B   22   44   23   21   55
C  NaN  NaN   23  NaN  NaN
D   22  NaN   23   21  NaN

此DataFrame为right中的每一行显示left['ID']中的哪些值与模式匹配。例如,在最后一行中,模式为2.,与22中的2321left['ID']匹配。

如果我们stack这个DataFrame,我们会看到一个系列列出了通配符的所有可能扩展:

In [299]: right['ID'].apply(lambda pat: left.loc[left['ID'].str.match(pat), 'ID']).stack()
Out[299]: 
A  0    22
B  0    22
   1    44
   2    23
   3    21
   4    55
C  2    23
D  0    22
   2    23
   3    21
dtype: object

Type也可以这样做。将两个结果连接在一起以获取DataFrame,其中列出了通配符的每个有效扩展:

In [301]: expanded = (data['ID']
                      .join(data['Type'])
                      .join(right['Value']))
Out[301]: 
   ID  Type  Value
A  22   ABC      0
B  22  ADEC      1
B  44  ADEC      1
B  23  ADEC      1
B  21  ADEC      1
B  55  ADEC      1
C  23   ABC      1
C  23  ADEC      1
C  23   OOO      1
C  23   DOG      1
C  23   MOT      1
D  22   DOG      0
D  23   DOG      0
D  21   DOG      0

现在可以通过leftexpanded的左合并获得所需的结果:

result = pd.merge(left, expanded, how='left')

PS:我将right改为index=list('ABCD')而不是通常的[0,1,2,3] left,以便rightlet this : Bool? = nil this != nil 上的索引值不会发生 与我们希望行匹配的方式一致。我这样做是为了防止开发错误地利用这个的解决方案 巧合。