在非空值上合并2个数据框

时间:2019-01-08 02:49:07

标签: python pandas dataframe

我必须合并两个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合并,然后删除重复项。即我会在这里

(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:根据df2中的真/假值将split_groups中的列组追加到数组group_mask

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中所有值都不为False的列创建dfs数组

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)

第5步:遍历数组,并根据现有列合并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中配置conf dfs

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

第7步:删除重复项

merged_df = merged_df.drop_duplicates()

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

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

4 个答案:

答案 0 :(得分:0)

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

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')

答案 1 :(得分:0)

尝试Combined_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

答案 2 :(得分:0)

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

答案 3 :(得分:-1)

没有足够的声誉来发表评论,但是为什么不使用

df.dropna()

然后尝试合并数据框?