Каковы точные недостатки copy = False в DataFrame.merge()?

Я немного смущен аргументом copy в DataFrame.merge() после того, как сотрудник спросил меня об этом.

В столбце DataFrame.merge() указано:

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

pandas документация гласит:

copy: всегда копировать данные (по умолчанию True) из переданных объектов DataFrame, даже если переиндексация не требуется. Во многих случаях избежать можно избежать, но может улучшить производительность/использование памяти. Случаи, когда копирование можно избежать, несколько патологичны, но этот вариант предоставляется тем не менее.

Тип docstring подразумевает, что копирование данных не является необходимым и может быть пропущено почти всегда. В документе, с другой стороны, говорится, что во многих случаях невозможно избежать копирования данных.

Мои вопросы:

  • Каковы эти случаи?
  • Каковы недостатки?

Ответ 1

Отказ от ответственности: я не очень опытен с pandas, и это первый раз, когда я врывался в его источник, поэтому я не могу гарантировать, что я не пропущу что-то в моей оценке ниже.

Соответствующие биты кода были недавно реорганизованы. Я обсужу тему с точки зрения текущей стабильной версии 0.20, но я не подозреваю функциональных изменений по сравнению с более ранними версиями.

Исследование начинается с источника merge в pandas/core/reshape/merge.py (ранее 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 привязан к атрибуту одноименного экземпляра, который только кажется снова появится в классе:

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

Затем мы можем отследить функцию concatenate_block_managers в pandas/core/internals.py, которая просто переходит на copy kwarg до concatenate_join_units.

Мы достигли конечного места покоя исходного аргумента ключевого слова copy в concatenate_join_units:

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, заканчивающимся в том, что не-op-подобная ветвь функции конкатенации полностью согласуется с комментарием "TODO" выше, документация, указанная в вопросе:

copy: всегда копируйте данные (по умолчанию True) из переданных объектов DataFrame, даже если переиндексация не требуется. Во многих случаях избежать можно избежать, но может улучшить производительность/использование памяти. Случаи, когда копирование можно избежать, несколько патологичны, но этот параметр предоставляется тем не менее.

(акцент мой) и обсуждение по старой проблеме:

IIRC Я думаю, что параметр копирования имеет значение только в том, что это тривиальное слияние, и вы действительно хотите его скопировать (вроде мне нравится переиндекс с тем же индексом)

Основываясь на этих подсказках, я подозреваю, что в очень подавляющем большинстве случаев использование копий неизбежно, а аргумент ключевого слова copy никогда не используется. Однако, поскольку для небольшого количества исключений, пропущенных на этапе копирования, можно повысить производительность (без какого-либо повышения производительности для большинства случаев использования в среднем) выбор был реализован.

Я подозреваю, что логическое обоснование - это что-то вроде этого: недопустимость копирования, если это необходимо (что возможно только в очень особых случаях), заключается в том, что код избегает некоторых распределений памяти и копий в этом случае, но не возврат копии в очень особых случаях может привести к неожиданным неожиданностям, если вы не ожидаете, что изменение значения возвращаемого значения merge может каким-либо образом повлиять на исходный фрейм данных. Таким образом, значение по умолчанию для аргумента copy ключевого слова True, поэтому пользователь не получает копию из merge, если они явно добровольно участвуют в этом (но даже тогда они все же, вероятно, получат копию).