提问人:Aryman Deshwal 提问时间:11/17/2023 更新时间:11/17/2023 访问量:68
如何在特定用例的两个 pandas 数据帧上执行左连接?
How to perform a left join on two pandas dataframes for a specific use case?
问:
示例数据: 以下是我的数据帧的简化表示:
我的第一个数据帧(df1)是这样的:
col1 | col2 | col3 | col4 (英语) | col5 |
---|---|---|---|---|
1 | 2 | 一个 | 3 | 4 |
11 | 22 | 机 管 局 | 33 | 44 |
111 | 222 | AAA级 |
我的第二个数据帧(df2)是这样的:
col3 | col4 (英语) | col5 |
---|---|---|
一个 | 3 | 4 |
机 管 局 | 332 | 442 |
AAA级 | 333 | 444 |
我希望我的合并数据帧 (result_df) 看起来像这样:
col1 | col2 | col3 | col4 (英语) | col5 |
---|---|---|---|---|
1 | 2 | 一个 | 3 | 4 |
11 | 22 | 机 管 局 | 33 | 44 |
11 | 22 | 机 管 局 | 332 | 442 |
111 | 222 | AAA级 | 333 | 444 |
我尝试将 pd.merge 函数与左连接一起使用:
result_df = pd.merge(df1, df2, on='col3', how='left')
result_df如下所示:
col1 | col2 | col3 | col4_x | col5_x | col4_y | col5_y |
---|---|---|---|---|---|---|
1 | 2 | 一个 | 3 | 4 | 3 | 4 |
11 | 22 | 机 管 局 | 33 | 44 | 332 | 442 |
111 | 222 | AAA级 | 333 | 444 |
我试图理解为什么生成的数据帧有额外的列以及如何实现所需的输出。非常感谢任何帮助或见解。
答:
我确信有更好的方法可以做到这一点,但目前我的策略是找到 df1 中存在但不在 df2 中存在的所有行,然后将这些行连接到 df2,然后执行 df1 和 df2 的左连接
我的第一个数据帧 (DF1) 是:
data1 = {'col1': [1, 11, 111],
'col2': [2, 22, 222],
'col3': ['a', 'aa', 'aaa'],
'col4': [3, 33, pd.NA],
'col5': [4, 44, pd.NA]}
df1 = pd.DataFrame(data1)
这给了我:
col1 | col2 | col3 | col4 (英语) | col5 |
---|---|---|---|---|
1 | 2 | 一个 | 3 | 4 |
11 | 22 | 机 管 局 | 33 | 44 |
111 | 222 | AAA级 |
现在我将删除 col1 和 col2
df1_last3cols = df1.drop(['col1',"col2"], axis=1)
所以现在我的df1_last3cols看起来像:
col3 | col4 (英语) | col5 |
---|---|---|
一个 | 3 | 4 |
机 管 局 | 33 | 44 |
AAA级 |
我的 df2 看起来像:
data2 = {'col3': ['a', 'aa', 'aaa'],
'col4': [3, 332, 333],
'col5': [4, 442, 444]}
df2 = pd.DataFrame(data2)
col3 | col4 (英语) | col5 |
---|---|---|
一个 | 3 | 4 |
机 管 局 | 332 | 442 |
AAA级 | 333 | 444 |
然后我提取来自左侧 dataframe(df1_last3cols) 的行并使用 df2 连接。
merged_df = pd.merge(df1_last3cols, df2, on=['col3', 'col4', 'col5'], how='left', indicator=True)
# Filter rows where the _merge column indicates differences
diff_df = merged_df[merged_df['_merge'] == 'left_only']
# Drop the _merge column from the result
diff_df = diff_df.drop(columns=['_merge'])
#adding diff_df to df2
result_df = pd.concat([diff_df, df2], ignore_index=True)
所以现在我的result_df看起来像:
col3 | col4 (英语) | col5 |
---|---|---|
机 管 局 | 33 | 44 |
AAA级 | ||
一个 | 3 | 4 |
机 管 局 | 332 | 442 |
AAA级 | 333 | 444 |
然后我再次实现左连接,但这次是使用 df1 和 result_df。
merged_df = pd.merge(df1, result_df, on="col3", how = "left")
merged_df.drop(["col4_x","col5_x"], inplace=True, axis= 1)
merged_df.rename(columns={'col4_y': 'col4', 'col5_y': 'col5'})
merged_df.dropna(inplace=True)
merged_df
经过一些清洁和更改 col 名称。瞧!
col1 | col2 | col3 | col4 (英语) | col5 |
---|---|---|---|---|
1 | 2 | 一个 | 3 | 4 |
11 | 22 | 机 管 局 | 33 | 44 |
11 | 22 | 机 管 局 | 332 | 442 |
111 | 222 | AAA级 | 333 | 333 |
如果有人有一个优雅的解决方案,请随时发布,任何形式的改进都会受到极大的赞赏。
一个带有 merge
/lreshape
的命题:
mg = pd.merge(df1, df2, on="col3", how="left")
grps = {c: [f"{c}_{s}" for s in ["x", "y"]]
for c in df1.columns.intersection(df2.columns).drop("col3")}
out = pd.lreshape(mg, grps).drop_duplicates().convert_dtypes()
注意:循环在这里确实是可选的,可以替换为两个 DataFrame 之间公共列的硬编码映射(要连接的列除外,即 col3
):
grps = {'col4': ['col4_x', 'col4_y'], 'col5': ['col5_x', 'col5_y']}
输出:
print(out)
col1 col2 col3 col4 col5
0 1 2 a 3 4
1 11 22 aa 33 44
3 11 22 aa 332 442
4 111 222 aaa 333 444
[4 rows x 5 columns]
这是使用 concat
的另一种选择,填充 NaN,然后删除重复项:
out = pd.concat([df1,df2]).sort_values(['col3'])
out[['col1', 'col2']] = out[['col1', 'col2']].ffill()
out[['col4', 'col5']] = out[['col4', 'col5']].bfill()
out = (out
.drop_duplicates(subset=['col3', 'col4', 'col5'])
.convert_dtypes()
.reset_index(drop=True)
)
输出:
col1 col2 col3 col4 col5
0 1 2 a 3 4
1 11 22 aa 33 44
2 11 22 aa 332 442
3 111 222 aaa 333 444
这源于@Timeless合并逻辑,然后是重塑:
# pip install pyjanitor
import pandas as pd
import janitor
(df1
.merge(df2, on = 'col3')
# this is from janitor
# this keeps only the part of the column
# that matches .value as header
.pivot_longer(
index='col[123]',
names_to = '.value',
names_pattern=r"(.+)_",
dropna=True)
.drop_duplicates()
.convert_dtypes()
)
col1 col2 col3 col4 col5
0 1 2 a 3 4
1 11 22 aa 33 44
3 11 22 aa 332 442
4 111 222 aaa 333 444
评论