Нашли ошибку?
продвинутыйpandas 3.0.6

Copy-on-Write

Почему изменение выборки больше не меняет исходную таблицу, куда делся SettingWithCopyWarning и как правильно изменять данные.

Нужно знать:loc, iloc, at, iatNumPy: Представления и копии

Содержание статьи

Коротко

  • В pandas 3 Copy-on-Write включён всегда: любая выборка, столбец или результат метода ведёт себя как отдельная копия.
  • Изменение выборки (low = df[mask]; low["x"] = 0) не меняет исходную таблицу, и предупреждений об этом больше нет.
  • Цепочечное присваивание df["col"][0] = 1 никогда не меняет df — выдаётся ChainedAssignmentError. Изменять нужно одной операцией: df.loc[0, "col"] = 1.
  • Копирование ленивое: данные копируются только в момент изменения, поэтому reset_index, rename и подобные методы стали дешёвыми.
  • .copy() «на всякий случай» после фильтра больше не нужен.

Было и стало

Код pandas 2.x pandas 3
sub = df[mask]; sub["x"] = 0 SettingWithCopyWarning, результат неочевиден меняется только sub
s = df["a"]; s[0] = 9 иногда менял df меняется только s
df["a"][0] = 9 иногда работал не работает, ChainedAssignmentError
df.loc[0, "a"] = 9 работает работает
df["a"].to_numpy() изменяемый массив массив только для чтения

Синтаксис

df.loc[row_mask, "col"] = value     # изменить исходную таблицу
sub = df[mask]                     # независимая выборка, можно менять
df = df.assign(col=...)            # новый объект вместо изменения на месте

Примеры

Данные для примеровиспользуются во всех примерах ниже
stock = pd.DataFrame({"item": ["латте", "чай", "раф"], "qty": [12, 40, 7]})
Изменение выборки
low = stock[stock["qty"] < 20]
low["qty"] = 0                           # меняем выборку…
stock                                    # …исходная таблица не изменилась
Вывод
    item  qty
0  латте   12
1    чай   40
2    раф    7
Изменение Series из столбца
qty = stock["qty"]
qty[0] = 999                             # Series из столбца — тоже как копия
stock
Вывод
    item  qty
0  латте   12
1    чай   40
2    раф    7
Правильно: одна операция loc
stock.loc[0, "qty"] = 100                # правильно: одна операция loc
stock.loc[stock["item"] == "чай", "qty"] += 5
stock
Вывод
    item  qty
0  латте  100
1    чай   45
2    раф    7
Копия появляется только при изменении
same = stock.reset_index(drop=True)
print(np.shares_memory(stock["qty"].to_numpy(), same["qty"].to_numpy()))   # пока данные общие
same.loc[0, "qty"] = -1                                                    # копия — при изменении
np.shares_memory(stock["qty"].to_numpy(), same["qty"].to_numpy())
Вывод
True
False

Подводные камни

Цепочечное присваивание молча ничего не делает

Две пары скобок
stock["qty"][0] = 100                    # цепочечное присваивание: две пары скобок
stock                                    # ничего не изменилось
Вывод
ChainedAssignmentError: A value is being set on a copy of a DataFrame or Series through chained assignment.
Such chained assignment never works to update the original DataFrame or Series, because the intermediate object on which we are setting values always behaves as a copy (due to Copy-on-Write).

Try using '.loc[row_indexer, col_indexer] = value' instead, to perform the assignment in a single step.

See the documentation for a more detailed explanation: https://pandas.pydata.org/pandas-docs/stable/user_guide/copy_on_write.html#chained-assignment
    item  qty
0  латте   12
1    чай   40
2    раф    7

Массив из to_numpy только для чтения

Массив может смотреть в данные таблицы, поэтому запись в него запрещена. Нужен изменяемый массив — to_numpy(copy=True).

Запись в массив столбцаошибка: ValueError
arr = stock["qty"].to_numpy()
arr[0] = 5                               # массив смотрит в данные таблицы — только чтение
Ошибка
ValueError: assignment destination is read-only

Настройка mode.copy_on_write

В pandas 2.x Copy-on-Write включали настройкой. В pandas 3 отключить его нельзя, а сама настройка устарела. Класс SettingWithCopyWarning удалён: код, который его ловит или отключает, падает.

Попытка отключитьустаревший API
pd.set_option("mode.copy_on_write", False)   # отключить Copy-on-Write нельзя
Вывод
Pandas4Warning: The 'mode.copy_on_write' option is deprecated. Copy-on-Write can no longer be disabled (it is always enabled with pandas >= 3.0), and setting the option has no impact. This option will be removed in pandas 4.0.
SettingWithCopyWarningошибка: AttributeError
pd.errors.SettingWithCopyWarning
Ошибка
AttributeError: module 'pandas.errors' has no attribute 'SettingWithCopyWarning'