Python 强制对包含重复键的列进行一对一合并

Python 强制对包含重复键的列进行一对一合并,python,pandas,dataframe,join,merge,Python,Pandas,Dataframe,Join,Merge,我有两个数据帧,df1: | ID | Invoice | ------------------------- | X\191 | 4 | | R\192 | 4 | | 733 | 1 | | X215 | 3 | | BL000002 | 3 | df2: 我应该把它们一对一地合并,得到: | ID | Invoice | ID

我有两个数据帧,df1:

| ID        | Invoice   |
-------------------------
| X\191     | 4         |
| R\192     | 4         |
| 733       | 1         |
| X215      | 3         |
| BL000002  | 3         |
df2:

我应该把它们一对一地合并,得到:

| ID        | Invoice   | ID        |
-------------------------------------
| X\191     | 4         | X191      |
| X\192     | 4         |           |
| 733       | 1         |           |
| X215      | 3         | X215      |
| BL000002  | 3         | BL000002  |
但当我进行外部合并时,会得到重复的值

import pandas as pd
dict1 = {"ID": ["X\\191","R\\192","733","X215","BL000002"], "Inv": [4,4,1,3,3]}
df1 = pd.DataFrame.from_dict(dict1)

dict2 = {"ID": ["X191","X215","BL000002"], "Inv": [4,3,3]}
df2 = pd.DataFrame.from_dict(dict2)

some_df = pd.merge(df1, df2, on = 'Inv', how='outer')
输出如下所示:

    ID_x    Inv    ID_y
X\191       4      X191
X\192       4      X191
733         1       NaN
X215        3      X215
X215        3  BL000002
BL000002    3      X215
BL000002    3  BL000002
我如何合并,使它一对一地连接,而不是混合和匹配

我不能在合并中使用任何其他列,因为它们在实际数据中会有所不同

编辑和解释
我很抱歉。我还不够清楚。列ID不一致。我不能保证它是否总是一个子串。但发票金额必须相同。这是人类在一年多的时间里输入的,大约有1.5万行。我需要对它们进行排序,以便具有相同发票值的数据框彼此相邻,以便在其中一个数据框(最初是excel工作表)缺少某些内容时更容易手动验证。

我认为一个简单的列表查找就可以做到这一点:

df1['new_id'] = df1.apply(lambda row: row['ID'] if row['ID'] in df2['ID'].tolist() else "", axis=1)

     ID  Invoice new_id
0  X191        4   X191
1  X192        4       
2  X212        1       
3  X215        3   X215
4  X319        3   X319
找到要删除的内容后,您可以执行以下操作(我假定ID具有
\
,“@”):

请尝试以下内容:

您正在寻找熊猫。请按合并。它允许您在一个键上组合两个数据帧,在本例中为时间,而无需要求它们完全匹配。您可以选择一个方向来确定匹配的优先级,但在这种情况下,很明显您需要最接近的匹配

>>> pd.merge_asof(df2.sort_values('Inv'), df1.sort_values('Inv'), on='Inv', direction='nearest')
  ID_x  Inv  ID_y 
0  215    3  X319
1  319    3  X319
2  191    4  X192
看,借自@
ALollz

您需要一个额外的基于
cumcount
的列:

u = df1.assign(Cnt=df1.groupby('Inv').cumcount())
v = df2.assign(Cnt=df2.groupby('Inv').cumcount())
u.merge(v, on=['Inv', 'Cnt'], how='left').drop('Cnt', 1)

       ID_x  Inv      ID_y
0     X\191    4      X191
1     R\192    4       NaN
2       733    1       NaN
3      X215    3      X215
4  BL000002    3  BL000002

也许可以尝试这样的事情:
>pd.merge(df1,df2,左上'Inv',右上'Inv',how='outer')
你为什么要加入
Inv
?您是否应该加入
ID
?@Mortz否,因为我已经提到我不能使用ID。在实际数据集中,我已经将ID分组,在一个组中,我有
“发票价值”、“日期”、“发票号”
除“发票价值”之外的所有内容“是两个
df
s之间的变量。@IhorVoronin我试过了。我得到了同样的结果。你是如何得到不同的结果的?@clmno,看来数据帧现在已经被编辑了!为了更好地了解情况,我编辑了这个问题。请看一看。基本上从你的答案来看,我不能依赖于ID,因为它们几乎从来都不一样(可以是子字符串,但我不能确定)。你不能在df1中预处理ID吗?是的,我可以。我该怎么做呢?实际df将有一个
供应商ID
,我将使用该ID对其进行分组。那么我现在剩下的就是
发票价值
。我也许可以在df1中构建一个更好的
发票号
(这就是我在问题中所说的ID),您应该查看ID值,可以打印更多的ID,并查看找到了哪些标点符号,因为现在我可以看到,ID值中有``个,会有更多个,一旦找到它们,您就可以执行我在回答中添加的命令。,非常感谢。但是coldspeed的答案很酷,并且按照我想要的方式解决了这个问题。事实上,我对他/她的解决方案感到敬畏嘿,coldspeed,你没有漏掉一个括号吗
?这看起来像答案。我正在实际数据集上运行它。过一会儿就会回来。你在
u.merge(v),on=['Inv',Cnt',how='left')上有一个拼写错误。drop('Cnt',1)
应该是
u.merge(v,on=['Inv Cnt',Cnt',how='left')。drop('Cnt',1)
@clmno感谢您捕获它。解决方案有效,但我在写答案时出现了复制粘贴错误。
>>> pd.merge_asof(df2.sort_values('Inv'), df1.sort_values('Inv'), on='Inv', direction='nearest')
  ID_x  Inv  ID_y 
0  215    3  X319
1  319    3  X319
2  191    4  X192
u = df1.assign(Cnt=df1.groupby('Inv').cumcount())
v = df2.assign(Cnt=df2.groupby('Inv').cumcount())
u.merge(v, on=['Inv', 'Cnt'], how='left').drop('Cnt', 1)

       ID_x  Inv      ID_y
0     X\191    4      X191
1     R\192    4       NaN
2       733    1       NaN
3      X215    3      X215
4  BL000002    3  BL000002