DataFrame.merge()中copy = False的确切缺点是什么?

时间:2015-09-01 11:30:58

标签: python pandas

在一位同事问我之后,我对copy中的论点DataFrame.merge()感到有点困惑。

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

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

pandas documentation州:

  

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

文档字符串意味着复制数据不是必需的,并且几乎总是可以跳过。另一方面,该文件说,在许多情况下无法避免复制数据。

我的问题是:

  • 那是什么情况?
  • 有什么缺点?

1 个答案:

答案 0 :(得分:12)

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

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

调查从the source of merge in pandas/core/reshape/merge.pyformerly pandas/tools/merge.py)开始。忽略一些支持doc的装饰器:

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.pypasses 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的副本重新绑定到相同的名称,而实际上没有什么可以连接的。

现在,在这一点上,我缺乏大熊猫的知识开始表现出来,因为我不确定究竟是什么在调用堆栈的内部发生了什么。但是上面的hot-potato方案,copy关键字参数以连接函数的无操作分支结尾,与上面的“TODO”注释完全一致,documentation quoted in the question

  

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

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

  

IIRC我认为复制参数这里唯一重要的是它是一个简单的合并,你实际上确实希望它被复制(我喜欢带有相同索引的reindex)

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

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