Коротко
- В 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
qty = stock["qty"]
qty[0] = 999 # Series из столбца — тоже как копия
stockВывод
item qty 0 латте 12 1 чай 40 2 раф 7
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).
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 удалён: код, который его ловит или отключает, падает.
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.
pd.errors.SettingWithCopyWarningОшибка
AttributeError: module 'pandas.errors' has no attribute 'SettingWithCopyWarning'