【问题标题】:Merging and updating multiple pandas dataframes with overlapping columns合并和更新具有重叠列的多个熊猫数据框
【发布时间】:2021-06-17 19:18:07
【问题描述】:

我现在很难合并和更新 Pandas 数据框。

我有一堆用 pandas 解析的 CSV 文件(这不是 问题)。在极少数情况下,我有多个文件包含一些列 存在于两个文件中。

例如,假设我有:

import pandas as pd

a = pd.DataFrame({"A": [0, 1, 2, 3], "B": [4, 5, 6, 7]}, index=[0,1,2,3])
b = pd.DataFrame({"A": [11, 12, 13, 14]}, index=[41,51,61,71])
c = pd.DataFrame({"A": [110, 111, 113]}, index=[0,1,3])

我想要的是这个数据框:

     A  B
0  110  4
1  111  5
2    2  6
3  113  7
41  11  NaN
51  12  NaN
61  13  NaN
71  14  NaN

Pandas 有这个不错的指南:Merge, join, concatenate and compare。但是我 无法找到我想要实现的解决方案。

例如a.join(b, how="outer") 引发ValueError: columns overlap but no suffix specified: Index(['A'], dtype='object')。路过rsuffix="R" 不是一个选项,因为最终结果是:

      A    B    AR
0   0.0  4.0   NaN
1   1.0  5.0   NaN
2   2.0  6.0   NaN
3   3.0  7.0   NaN
41  NaN  NaN  11.0
51  NaN  NaN  12.0
61  NaN  NaN  13.0
71  NaN  NaN  14.0

不是我想要的。

pd.merge(a, b, how="outer") 看起来很有希望,但也不完全正确, 因为索引被忽略了:

    A    B
0   0  4.0
1   1  5.0
2   2  6.0
3   3  7.0
4  11  NaN
5  12  NaN
6  13  NaN
7  14  NaN

传递 left_index=Trueright_index=True 会产生一个类似于 .join(..., rsuffix="_x", lsuffix="_y"),所以不是我想要的。

使用update 几乎是我想要的,a.merge(c) 会将a 修改为

       A  B
0  110.0  4
1  111.0  5
2    2.0  6
3  113.0  7

但是a.update(b) 什么都不做(我假设是因为ab 的索引是 分离)。

那么,我想用一行代码实现吗?

编辑

我想出了这个:

> lll = pd.concat([a,b, c]).sort_index()
> pd.concat([a,b,c]).sort_index().drop_duplicates().groupby(a.index).last()

      A    B
0   110  4.0
1   111  5.0
2     2  6.0
3   113  7.0
41   11  NaN
51   12  NaN
61   13  NaN
71   14  NaN

这就是我想要的,问题是:这是正确的还是只是巧合 这会产生与我想要的结果相同的结果吗?


您如何确定哪个“A”列具有优先权?

按照我阅读文件的顺序。这些文件由设备生成(其中 对我来说是一种“黑匣子”)并生成带有日期的文件。所以我 做:


tasks = [parse_csv_file(fn) for fn in sorted(glob.glob("*.csv"))]
results = await asyncio.gather(*tasks)

我想做(没有错误检查,因为这是一个例子):

results = iter(results)
merged_df = next(results)

for df in results:
    merged_df = the_magic_function_Im_looking_for(df)

【问题讨论】:

  • 您如何确定哪个“A”列具有优先权?
  • 按照我读取数据文件的顺序(文件名中有日期),所以我这样做:for fn in sorted(glob.glob("*.csv")): ...

标签: python pandas dataframe


【解决方案1】:

reducecombine_first:

from functools import reduce

to_merge = [c, b, a]

result = reduce(pd.DataFrame.combine_first, to_merge)

它依次将combine_first 应用于列表的条目以最终得到全部组合,即最后减少的数据帧。

(如果to_merge的顺序相反,我们可以把reversed(to_merge)放在reduce中),

得到

>>> result

        A    B
0   110.0  4.0
1   111.0  5.0
2     2.0  6.0
3   113.0  7.0
41   11.0  NaN
51   12.0  NaN
61   13.0  NaN
71   14.0  NaN

【讨论】:

  • 谢谢,我觉得这更容易理解,可惜我不能同时接受这两个答案。
【解决方案2】:

尝试在axis=1 上使用concat + groupby last 来合并数据框,然后获取每个列组的“最后一个”有效值:

df = pd.concat([a, b, c], axis=1).groupby(level=0, axis=1).last()

df:

        A    B
0   110.0  4.0
1   111.0  5.0
2     2.0  6.0
3   113.0  7.0
41   11.0  NaN
51   12.0  NaN
61   13.0  NaN
71   14.0  NaN

或者concating long 并通过@anky获得每行索引的最后一个有效行:

df = pd.concat([a, b, c]).groupby(level=0).last()

df:

      A    B
0   110  4.0
1   111  5.0
2     2  6.0
3   113  7.0
41   11  NaN
51   12  NaN
61   13  NaN
71   14  NaN

【讨论】:

  • 这与我想出的几乎相同(我编辑了我的原始帖子),但我没有使用 level=0, axis=1 参数。我的电话和你的电话有什么区别?
  • 我在 axis=1 上连接,它构建宽然后按列分组。或者正如 anky 指出的那样,连接 long 并获得每个索引的最后一个有效值。无论哪种方式,区别在于我没有对重复项进行排序或删除,而且我不知道lll 是什么,所以我无法提供有关它是如何构建的更多信息。 @巴勃罗
  • 啊,我在 ipython 里玩,然后lll = pd.concat([a,b, c]).sort_index(),所以这似乎是纯粹的运气
  • 不,@Pablo 产生你想要的东西并非巧合。 .sort_index().drop_duplicates() 没有朝着目标取得进展(在这种情况下),因为您的数据中没有重复项(drop_duplicates 不考虑索引)并且groupby(a.index)groupby(level=0) 相同,它相当于@987654339 @ 除了额外的排序和删除步骤。 (并创建两次连接数据)
  • 非常感谢,我明白了。很遗憾我不能同时接受这两个答案,但我发现combine_first 的解决方案更易于理解和可视化
猜你喜欢
  • 2021-09-13
  • 1970-01-01
  • 2022-11-17
  • 2023-04-07
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多