我尝试通过匹配键合并两个DataFrame,但每个DataFrame中的键可能会出现多次( n )次。内部联接为所有 n ^ 2 对键提供行 - 相反,我想要 n 行。
对于某些上下文:想象一下图书馆的签入/签出方案,其中可以签入书籍,记录在df1中,或签出,并记录在df2中。每本书都有一个唯一的密钥,但可以多次登记/退房。此外,由于数据集仅跨越特定时间窗口,因此某些书籍可能具有签到记录但不能签出(在记录数据之前已经签出的书籍),或者签出记录但不是办理登机手续(尚未归还的书籍)。我的目标是创建一个新的DataFrame,其中只包含签到和相应签出的行。
所以,最后得到我的问题:
1)如何进行内部联接,首次登记与首次结账,第二次结账时再次结账等?默认情况下,它会提供所有组合 - 因此,如果有 n 签到和 n 签出,我会获得 n ^ 2 行我想要的 n 行。(现在,让我们忽略在第一次办理登机手续之前退房的可能性,或者不等数量的签到/检查 - 以下是一个简单的例子。
df1 = pd.DataFrame({'ID': ['A1', 'A2','A2', 'A3'], 'DATE': [1, 1,2, 2]})
df2 = pd.DataFrame({'ID': ['A2', 'A3', 'A2', 'A4'], 'DATE': [3, 5, 5, 7]})
df = pd.merge(df1, df2, how='inner', on='ID',sort=True)
请注意,我得到A2的4个条目和A3的1个条目,而我只想要A2的行0和2以及A3的行4。
df_wanted = pd.DataFrame({'ID': ['A2', 'A2', 'A3'], 'DATE_x': [1, 2, 2], 'DATE_y': [3, 5, 5]})
2)完整的案例。每张登记入住和退房手续均应配对,每张退房手续均与之前最近的登记入住。因此,如果在第1天和第2天办理登机手续,并在第0天,第3天和第5天办理登机手续,则最后df中的唯一行应对应于后两个签到(第1,2天)和第二个退房(第3,5天)。
df1 = pd.DataFrame({'ID': ['A1', 'A2','A2', 'A3'], 'DATE': [1, 1, 2, 2]})
df2 = pd.DataFrame({'ID': ['A2', 'A3', 'A2', 'A4','A2'], 'DATE': [3, 5, 5, 7, 0]})
df = pd.merge(df1, df2, how='inner', on='ID',sort=True)
我在第0天向df2添加了退房。现在在df中,我获得了A2的6个条目(以及A3的一个条目),而我只想要A2的两个条目(以及A3的一个条目)。这应该与上面的df_wanted
相同。
注意:发布的答案将匹配从第1,2天到第0天结账的签到,而不是第3,5天的结账。因此,完整的解决方案需要确保结账日期> =办理登机手续的日期,或者在最早办理登机手续时启动柜台,或类似的东西。
我尝试了什么:
我尝试实现df.drop_duplicates()
的各种组合,但我最终得到了错误的组合。我也尝试通过循环遍历两个数据集(common=set(df1.ID.values) & set(df2.ID.values)
)共有的所有ID,按照它们的出现顺序对它们进行配对,然后逐个将它们添加到新的df中来手动执行此操作,但是这样做看起来效率很低。
这似乎是一个足够普遍的任务,可能有更“pythonic”的方式来处理它?</ p>
感谢您抽出宝贵时间,感谢您提出任何指导或建议。
答案 0 :(得分:2)
这是我的解决方案:
import pandas as pd
df1 = pd.DataFrame({'date':[1, 1, 2, 2], 'id':['A1', 'A2', 'A2', 'A3']})
df2 = pd.DataFrame({'date':[3, 5, 5, 7], 'id':['A2', 'A3', 'A2', 'A4']})
df1 = df1[df1.id.isin(df2.id)]
df2 = df2[df2.id.isin(df1.id)]
df1['ones'] = 1
df1['counter'] = df1.groupby('id')['ones'].cumsum()
del df1['ones']
df2['ones'] = 1
df2['counter'] = df2.groupby('id')['ones'].cumsum()
del df2['ones']
df3 = pd.merge(df1, df2, on=['id', 'counter'], suffixes = ['_checkin', '_checkout'])
del df3['counter']
print(df3)
date_checkin id date_checkout
0 1 A2 3
1 2 A2 5
2 2 A3 5
<强>步骤:强>
初始化数据帧:
import pandas as pd
df1 = pd.DataFrame({'date':[1, 1, 2, 2], 'id':['A1', 'A2', 'A2', 'A3']})
df2 = pd.DataFrame({'date':[3, 5, 5, 7], 'id':['A2', 'A3', 'A2', 'A4']})
仅按id
df1 = df1[df1.id.isin(df2.id)]
df2 = df2[df2.id.isin(df1.id)]
创建一个cumsum
计数器以匹配。这是我们将在#34;第一次检查&#34;用&#34;首先结帐&#34;
df1['ones'] = 1
df1['counter'] = df1.groupby('id')['ones'].cumsum()
del df1['ones']
df2['ones'] = 1
df2['counter'] = df2.groupby('id')['ones'].cumsum()
del df2['ones']
现在我们可以对inner join
和id
counter
df3 = pd.merge(df1, df2, on=['id', 'counter'], suffixes = ['_checkin', '_checkout'])
del df3['counter']
print(df3)
date_checkin id date_checkout
0 1 A2 3
1 2 A2 5
2 2 A3 5
我希望这有帮助!