【问题标题】:What are the exact downsides of copy=False in DataFrame.merge()?DataFrame.merge() 中 copy=False 的确切缺点是什么?
【发布时间】:2015-11-26 14:58:27
【问题描述】:

在一位同事问我这个问题后,我对 DataFrame.merge() 中的 copy 争论有点困惑。

DataFrame.merge() 的文档字符串声明:

copy : boolean, default True
    If False, do not copy data unnecessarily

pandas documentation 声明:

copy:始终从传递的 DataFrame 对象中复制数据(默认为 True),即使不需要重新索引也是如此。在许多情况下无法避免,但可能会提高性能/内存使用率。可以避免复制的情况有些病态,但仍然提供了此选项。

docstring 类型暗示​​复制数据不是必需的,并且几乎总是可以跳过。另一方面,文档说,在许多情况下无法避免复制数据。

我的问题是:

  • 这些是什么情况?
  • 有什么缺点?

【问题讨论】:

    标签: python pandas


    【解决方案1】:

    免责声明:我对 pandas 不是很有经验,这是我第一次挖掘它的来源,所以我不能保证我在下面的评估中没有遗漏任何东西。

    最近重构了相关的代码。我将根据当前的稳定版本 0.20 来讨论这个主题,但我不怀疑与早期版本相比的功能变化。

    调查从the source of merge in pandas/core/reshape/merge.py (formerly pandas/tools/merge.py) 开始。忽略一些 doc-aware 装饰器:

    def merge(left, right, how='inner', on=None, left_on=None, right_on=None,
              left_index=False, right_index=False, sort=False,
              suffixes=('_x', '_y'), copy=True, indicator=False):
        op = _MergeOperation(left, right, how=how, on=on, left_on=left_on,
                             right_on=right_on, left_index=left_index,
                             right_index=right_index, sort=sort, suffixes=suffixes,
                             copy=copy, indicator=indicator)
        return op.get_result()
    

    调用merge会将copy参数传递给class _MergeOperation的构造函数,然后调用它的get_result()方法。前几行带有上下文:

    # TODO: transformations??
    # TODO: only copy DataFrames when modification necessary
    class _MergeOperation(object):
        [...]
    

    现在第二条评论非常可疑。继续前进,copy kwarg 是bound to an eponymous instance attribute,这似乎只是reappear once within the class

    result_data = concatenate_block_managers(
        [(ldata, lindexers), (rdata, rindexers)],
        axes=[llabels.append(rlabels), join_index],
        concat_axis=0, copy=self.copy)
    

    然后我们可以追踪the concatenate_block_managers function in pandas/core/internals.py 那个passes on the copy kwarg to concatenate_join_units

    我们到达了 concatenate_join_units 中原始 copy 关键字参数的最后安息地:

    if len(to_concat) == 1:
        # Only one block, nothing to concatenate.
        concat_values = to_concat[0]
        if copy and concat_values.base is not None:
            concat_values = concat_values.copy()
    else:
        concat_values = _concat._concat_compat(to_concat, axis=concat_axis)
    

    如您所见,copy 所做的唯一事情是将此处的 concat_values 的副本重新绑定到相同的名称,以在没有什么可连接的特殊情况下进行连接。

    现在,在这一点上,我开始表现出我对 pandas 知识的缺乏,因为我不确定调用堆栈深处到底发生了什么。但是上面带有copy关键字参数的热土豆方案以连接函数的类似no-op的分支结尾,与上面的“TODO”注释完全一致,documentation quoted in the question

    copy:始终从传递的 DataFrame 对象中复制数据(默认为 True),即使不需要重新索引也是如此。在许多情况下无法避免,但可能会提高性能/内存使用率。 可以避免复制的情况有些病态,但仍然提供了此选项。

    (强调我的)和related discussion on an old issue

    IIRC 我认为复制参数在这里很重要,因为它是一个微不足道的合并,你确实希望它被复制(我喜欢具有相同索引的重新索引)

    基于这些提示,我怀疑在绝大多数实际用例中复制是不可避免的,并且从不使用 copy 关键字参数。但是,由于对于少数例外情况,跳过复制步骤可能会提高性能(同时不会对大多数用例造成任何性能影响),因此选择已实施。

    我怀疑其基本原理是这样的:除非必要,否则不进行复制的好处(仅在非常特殊的少数情况下才有可能)是代码在这种情况下避免了一些内存分配和复制,但是 不 在非常特殊的少数情况下返回副本可能会导致意外的惊喜,如果人们不希望改变 merge 的返回值会以任何方式影响原始数据帧。所以copy 关键字参数的默认值是True,因此用户只有在明确自愿的情况下才不会从merge 获得副本(但即便如此,他们仍然可能最终得到副本)。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2010-10-26
      • 1970-01-01
      • 2015-05-25
      • 2011-12-10
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多