免责声明:我对 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 获得副本(但即便如此,他们仍然可能最终得到副本)。