我想创建一个函数
new_merge
,它是一个 pandas 合并了 left_on
和 right_on
列的所有常用规则,但有一些额外的逻辑。为此,将有 2 个额外的列表参数(都指定一些列) - 让我们称它们为 left_partial_on
和 right_partial_on
。仅当 left_partial_on[i][j]
的每个元素都包含在 right_partial_on[i][k]
中时,2 个单独列 left_partial_on[i][j]
、right_partial_on[i][k]
的 2 个条目才被视为“匹配”,反之亦然(即 right_partial_on[i][j]
的每个元素都包含在 left_partial_on[i][k]
中)
)。
例如,假设我有这些数据框
df1 = pd.DataFrame({'Writer': [['John', 'Adam'], ['Gary', 'Peter'], ['Frida', 'Kate']], 'Cast': [['Carrie', 'Ryan'], ['Jamie'], ['Maggie', 'Shaun']], 'Title': ['The Boring Film', 'The Great Film', 'The Awesome Film']})
df2 = pd.DataFrame({'Writer': [['Adam', 'John'], ['Betty', 'Bob'], ['Frida']], 'Cast': [['Carrie', 'Rita'], ['Paula', 'Beatrice'], ['Maggie']], 'Title': ['The Boring Film', 'The Great Film', 'The Awesome Film']})
然后当我打电话时
new_merge(df1, df2, left_on = ['Title'], right_on = ['Title'], left_partial_on = ['Writer', 'Cast'], right_partial_on = ['Writer', 'Cast'])
我最终只得到最后一行:
Writer Cast Title
[Frida] [Maggie] The Awesome Film
解释:数据帧之间唯一可能的匹配是第一、第二和第三行,否则
Title
之间不相等。对于第一行,每个 [Adam, John]
都包含在 [John, Adam]
中
所以 Writer
是一个匹配项,但是 Ryan
不包含在 [Carrie, Rita]
中,所以 Cast
不是一个匹配项。同样,在第二行中,Jamie
不包含在 [Paula, Beatrice]
中,因此 Cast
也不是第二行中的匹配项。但是第三个 [Frida, Kate]
包含 [Frida]
中的每个元素,[Maggie, Shaun]
包含 [Maggie]
中的每个元素,并且正如我们已经确定的 Title
是相等的,因此我们有一个匹配。
当谈到将其打包为一个函数时,我所能想到的就是尝试基于 for 循环的东西,因此非常慢,因为我正在处理非常大的数据帧(不仅仅是这个例子)。我有点困惑什么是更/最有效的方法来做到这一点。我在这里寻找实现以及 pandas 中是否存在执行此操作的现有函数,但我没有找到任何内容。
这是一种可能的解决方案。首先,分解
left_partial_on
和 right_partial_on
中的所有列。然后计算每部电影的行数。现在将两个数据帧在“所有”列上合并在一起,并计算结果中每部电影的行数。如果该计数与任一数据帧中的计数匹配,则将胶片保留在结果中。最后,将“部分”字段重新聚合回列表中。
def new_merge(df1, df2, left_on, right_on, left_partial_on, right_partial_on):
def explode_partials(df, cols):
dfe = df.explode(cols[0])
for col in cols[1:]:
dfe = dfe.explode(col)
return dfe
df1e = explode_partials(df1, left_partial_on)
df2e = explode_partials(df2, left_partial_on)
df1e['rows'] = df1e.groupby(left_on).transform('size')
df2e['rows'] = df2e.groupby(right_on).transform('size')
merged = df1e.merge(df2e, left_on=left_on + left_partial_on, right_on=right_on + right_partial_on, how='inner')
group_cols = list(set(left_on + right_on))
mergedsizes = merged.groupby(group_cols).transform('size')
merged = merged[(merged['rows_x'] == mergedsizes) | (merged['rows_y'] == mergedsizes)]
merged = merged.drop(columns=['rows_x', 'rows_y'])
return merged.groupby(group_cols, sort=False).agg(lambda g:g.unique()).reset_index()
new_merge(df1, df2, ['Title'], ['Title'], ['Writer', 'Cast'], ['Writer', 'Cast'])
输出:
Title Writer Cast
0 The Awesome Film [Frida] [Maggie]
另一个例子:
df3 = df2.copy()
df3.loc[0, 'Cast'] = df1.loc[0, 'Cast']
new_merge(df3, df1, ['Title'], ['Title'], ['Writer', 'Cast'], ['Writer', 'Cast'])
输出:
Title Writer Cast
0 The Boring Film [Adam, John] [Carrie, Ryan]
1 The Awesome Film [Frida] [Maggie]