【问题标题】:Combinatorial explosion while merging dataframes in pandas合并熊猫中的数据框时的组合爆炸
【发布时间】:2012-12-21 08:56:05
【问题描述】:

我正在尝试在 pandas 中合并一系列数据框。我有一个 dfs 列表,dfs 及其对应标签 labels 的列表,我想将所有 dfs 合并到 1 个 df 中,以便 df 中的常见标签从 @987654324 中的标签中获取后缀@ 列表。即:

def mymerge(dfs, labels):
  labels_dict = dict([(d, l) for d, l in zip(dfs, labels)])
  merged_df = reduce(lambda x, y:
                     pandas.merge(x, y, 
                                  suffixes=[labels_dict[x], labels_dict[y]]),
                     dfs)
  return merged_df

当我尝试这个时,我得到了错误:

pandas.tools.merge.MergeError: Combinatorial explosion! (boom)

我正在尝试进行一系列合并,每次合并时最多增长 N 列,其中 N 是列表中“下一个”df 中的列数。最终的 DF 应该有与所有 df 列加在一起一样多的列,所以它是累加增长的,而不是组合的。

我正在寻找的行为是:在指定的列名上加入 dfs(例如,由 on= 指定)或 dfs 被索引。联合不常见的列名(如在外部连接中)。如果一列出现在多个 dfs 中,可选择覆盖它。更多地查看文档,听起来update 可能是最好的方法。虽然当我尝试join='outer' 时,它会引发一个异常,表明它没有实现。

编辑

这是我对此的实现尝试,它不处理后缀,但说明了我正在寻找的合并类型:

def my_merge(dfs_list, on):
    """ list of dfs, columns to merge on. """
    my_df = dfs_list[0]
    for right_df in dfs_list[1:]:
        # Only put the columns from the right df
        # that are not in the existing combined df (i.e. new)
        # or which are part of the columns to join on
        new_noncommon_cols = [c for c in right_df \
                              if (c not in my_df.columns) or \
                                 (c in on)]
        my_df = pandas.merge(my_df,
                             right_df[new_noncommon_cols],
                             left_index=True,
                             right_index=True,
                             how="outer",
                             on=on)
    return my_df

这假设合并发生在每个 dfs 的索引上。新列以外部连接样式添加,但常见的列(而不是索引的一部分)通过 on= 关键字在连接中使用。

例子:

df1 = pandas.DataFrame([{"employee": "bob",
                         "gender": "male",
                         "bob_id1": "a"},
                        {"employee": "john",
                         "gender": "male",
                         "john_id1": "x"}])
df1 = df1.set_index("employee")
df2 = pandas.DataFrame([{"employee": "mary",
                         "gender": "female",
                         "mary_id1": "c"},
                        {"employee": "bob",
                         "gender": "male",
                         "bob_id2": "b"}])
df2 = df2.set_index("employee")
df3 = pandas.DataFrame([{"employee": "mary",
                         "gender": "female",
                         "mary_id2": "d"}])
df3 = df3.set_index("employee")
merged = my_merge([df1, df2, df3], on=["gender"])
print "MERGED: "
print merged

对此的扭曲是,您可以根据一组常见的列标签任意标记每个 df 的后缀,但这并不重要。上述合并操作是否可以在 pandas 中更优雅地完成,或者已经作为内置函数存在?

【问题讨论】:

  • 多么有趣的例外!你认为你可以举一个你想要的小例子吗(例如几个小数据框和他们想要的 mymerge 输出)。
  • @hayden:看我的编辑——我举了一个例子
  • suffixes 在您的 merge1 示例中似乎没有任何作用(是否需要?)
  • 可能想尝试传递right_index=Trueleft_index=True。你是按索引合并的吗?
  • 目前,joining a list of dataframes 不支持后缀,这周晚些时候会添加它。

标签: python numpy scipy pandas


【解决方案1】:

你的方法的输出:

In [29]: merged
Out[29]: 
         bob_id1  gender john_id1 bob_id2 mary_id1 mary_id2
employee                                                   
bob            a    male      NaN       b      NaN      NaN
john         NaN    male        x     NaN      NaN      NaN
mary         NaN  female      NaN     NaN        c        d

一个内置pandas的解决方案df.combine_first

In [28]: reduce(lambda x,y: x.combine_first(y), [df1, df2, df3])
Out[28]: 
         bob_id1 bob_id2  gender john_id1 mary_id1 mary_id2
employee                                                   
bob            a       b    male      NaN      NaN      NaN
john         NaN     NaN    male        x      NaN      NaN
mary         NaN     NaN  female      NaN        c        d

要为每一帧的列添加后缀,我建议在调用 combine_first 之前重命名列。

另一方面,您可能想要研究像pd.concat([df1, df2, df3], keys=['d1', 'd2', 'd3'], axis=1) 这样的操作,它会生成一个带有 MultiIndex 列的数据框。在这种情况下,可能需要考虑将性别作为索引的一部分或忍受它的重复。

【讨论】:

  • 谢谢,reduce/combine_first 解决方案是最好的,我同意在合并之前重命名 dfs 是一个优雅的解决方案。您能解释一下为什么合并不会导致组合问题吗?从文档来看,这似乎是对索引进行合并的外连接,但它们的行为非常不同......
  • 好问题——这似乎是当前合并实现的一个限制。打开 github issue github.com/pydata/pandas/issues/2690 跟进 Wes 的上述问题。
【解决方案2】:

来自源代码:

max_groups = 1L
for x in group_sizes:
    max_groups *= long(x)

if max_groups > 2**63:  # pragma: no cover
    raise Exception('Combinatorial explosion! (boom)')

而且,在same file

# max groups = largest possible number of distinct groups
left_key, right_key, max_groups = self._get_group_keys()

max_groups *= long(x) 行表示它不是附加的,因此很关键。

【讨论】:

    猜你喜欢
    • 2021-04-06
    • 2019-01-15
    • 2022-01-27
    • 2021-05-27
    • 2023-01-19
    • 2023-02-02
    • 2014-07-02
    • 2013-09-26
    相关资源
    最近更新 更多