如何在特定用例的两个 pandas 数据帧上执行左连接?

How to perform a left join on two pandas dataframes for a specific use case?

提问人:Aryman Deshwal 提问时间:11/17/2023 更新时间:11/17/2023 访问量:68

问:

示例数据: 以下是我的数据帧的简化表示:

我的第一个数据帧(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

我试图理解为什么生成的数据帧有额外的列以及如何实现所需的输出。非常感谢任何帮助或见解。

python pandas 数据帧 合并 左联接

评论


答:

0赞 Aryman Deshwal 11/17/2023 #1

我确信有更好的方法可以做到这一点,但目前我的策略是找到 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

如果有人有一个优雅的解决方案,请随时发布,任何形式的改进都会受到极大的赞赏。

4赞 Timeless 11/17/2023 #2

一个带有 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]
4赞 Nick 11/17/2023 #3

这是使用 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
2赞 sammywemmy 11/17/2023 #4

这源于@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