【问题标题】:Merge 2 dataframes on notnull values在非空值上合并 2 个数据帧
【发布时间】:2019-06-02 17:19:35
【问题描述】:

我必须合并两个 df。一个是我的主df,另一个有很多NaN

df1 样本:

code        hotel_region   hotel_country        chain_name   brand_name
9737              EUROPE       ESTONIA        Bridgestreet        NaN
5397       LATIN AMERICA    COSTA RICA         Independent   No Brand
2392       LATIN AMERICA         ARUBA        DIVI RESORTS        NaN
9776       LATIN AMERICA        BRAZIL         Independent   W Hotels
4720       LATIN AMERICA     ARGENTINA         Independent   No Brand

df2 示例:

r_id  hotel_region    hotel_country                   chain_name     brand_name
78   LATIN AMERICA         HONDURAS     Barcelo Hotels and Resorts        NaN
92   LATIN AMERICA     SANDWICH ISL     Barcelo Hotels and Resorts        NaN
151            NaN              NaN                   Bridgestreet        NaN
117  NORTH AMERICA           CANADA                Magnuson Hotels        NaN
47   LATIN AMERICA           BRAZIL                            NaN   W Hotels 

我想要的结果大致是这样的:

code   hotel_region   hotel_country     chain_name   brand_name  r_id
9737         EUROPE       ESTONIA     Bridgestreet        NaN     151
9776  LATIN AMERICA        BRAZIL      Independent   W Hotels      47

合并应该只是“忽略” NaN 值,并且只在列值不是 NaN 的情况下合并。我尝试了不同的方法,但是 df2 中的数据有数十种可能出现 NaN 值。 df1 有 168k 行,df2 大约有 170 行,r_id 应该与任何匹配所有非 NaN 值的code 相关联。有没有人知道如何有效地做到这一点?

在对不同方法进行广泛研究后,似乎不存在忽略 NaN 的“神奇”方法。我考虑过在 df2 上应用掩码并分组,循环遍历它们,将每个组与 df1 合并,然后删除重复项。 IE。我会在这里

(True, True, True,  True, False),
(True, False, False, True, False),
(True, True, True, False, True)

但是我不确定这是否是最好的方法,坦率地说,我对如何实施它感到困惑。

编辑 - 我最终是如何解决这个问题的

我最终探索了上述方法 - 在 df2 上应用蒙版,根据蒙版拆分它,将其与 df1 合并。

第 1 步:创建掩码

masked = df2[['hotel_region', 'hotel_country', 'chain_name', 'brand_name']]

mask = pd.notnull(masked)

第 2 步:根据 NaN (= False) 值对 df 进行分组

    group_mask = mask.groupby(['hotel_region','hotel_country', 'chain_name','brand_name']).count().reset_index()

第 3 步:根据 group_mask 中的真/假值将 df2 中的列组附加到数组 split_groups

split_groups = []

for index, row in group_mask.iterrows():
    bool_groups = []
    # If the whole group is False, then cannot be taken in consideration, 
    # as it would result in a merge on the whole df1
    if not any(row.to_dict().values()):
        pass
    else:
        bool_groups.append(
                [key for key in row.to_dict().keys() if row.to_dict()[key] == False])
        bool_groups.append(
                [key for key in row.to_dict().keys() if row.to_dict()[key] == True])
        split_groups.append(bool_groups)

第 4 步:根据 df2 中的列创建 dfs 拆分数组,其中所有值都不为 False

mps = []
"""
First, we extract rows where i[0] is null. In the resulting df, we extract rows
where i[1] is not null. Then, we drop all columns with na values. In this way
we retain only columns good for the merge. 
"""
for i in split_groups:
    df = df2[(df2[i[0]].isnull()).all(1)]
    df = df[(df[i[1]].notnull()).all(1)]
    df = df.dropna(axis='columns', how='all')
    mps.append(df)

第五步:遍历数组,根据已有的列合并2个dfs

merged_dfs = []

for i in range(len(mps)):
    merged_dfs.append(df1.merge(mps[i], on=(split_groups[i][1]), how='left'))

第 6 步:在merged_dfs 中连接 dfs

merged_df = pd.concat(merged_dfs, sort=False)

第 7 步:删除重复项

merged_df = merged_df.drop_duplicates()

第 8 步调用 merged_df.columns.tolist() 并仅保留对最终结果有用的列。

我认为这种方法不是最佳的 - 如果有人对如何提高效率有任何想法,我将不胜感激。感谢 @qingshan 关于循环的建议,它给了我最终循环遍历不同 dfs 列表的提示。

【问题讨论】:

  • 必须要有r_id吗?
  • 不幸的是,这是我必须在 df1 中标记与 df2 相关的行的参数 :-)

标签: python pandas dataframe


【解决方案1】:

我猜你想合并具有相同列值的两行(忽略 NaN)。如果数据不大,可以用两个for循环来完成。

【讨论】:

    【解决方案2】:

    试试 combine_first 函数

    http://pandas.pydata.org/pandas-docs/stable/generated/pandas.DataFrame.combine_first.html#pandas.DataFrame.combine_first

    >>> df1 = pd.DataFrame([[1, np.nan]])
    >>> df2 = pd.DataFrame([[3, 4]])
    >>> df1.combine_first(df2)
    
       0    1
    0  1  4.0
    

    【讨论】:

    • 已经尝试过,但输出不一致/不正确。它合并了不应触及的行
    【解决方案3】:

    您可以合并过滤后的数据框以获得所需的内容。 使用它来过滤您的数据框,然后进行左合并以获取输出。

    out_df = df1[~df1.isnull().T.any().T].merge(df2[~df2.isnull().T.any().T], on=['hotel_region', 'hotel_country', 'chain_name', 'brand_name'], how='left')
    

    【讨论】:

    • 不幸的是,使用此解决方案我在合并后从 df2 的列中只得到 na 值
    • 这在您上面提供的数据集中有效。请提供更多数据以供处理
    猜你喜欢
    • 2020-03-27
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2022-01-07
    • 2018-05-06
    • 1970-01-01
    • 2016-01-31
    • 2015-05-24
    相关资源
    最近更新 更多