PandasSeniorTechnical

В чём разница между view и copy в Pandas и почему это важно?

View разделяет память с исходным DataFrame — изменение одного меняет другой; copy — независимый объект в отдельной памяти. Pandas не всегда предсказуем, поэтому используйте явный .copy() везде, где нужна независимость.

View и Copy в Pandas

Понимание разницы между view и copy критично: неправильное предположение о том, какой из них вы держите, приводит либо к потере данных, либо к неожиданным мутациям исходного DataFrame.

Что такое view

View — объект, разделяющий память с исходным массивом. Изменение view отражается на оригинале и наоборот. Это работает на уровне NumPy.

import pandas as pd
import numpy as np

arr = np.array([1, 2, 3, 4, 5])
view = arr[1:4]   # numpy view
view[0] = 99
print(arr)  # [1, 99, 3, 4, 5] — оригинал изменён!

Что такое copy

Copy — независимый объект со своей памятью. Изменения не распространяются на родителя.

cp = arr[1:4].copy()
cp[0] = 0
print(arr)  # [1, 99, 3, 4, 5] — оригинал не изменён

Непредсказуемость в Pandas

В Pandas результат операции индексации — view или copy — зависит от внутренней структуры блоков данных. Pandas объединяет столбцы одного dtype в один NumPy массив; слайс по такому массиву — view. Слайс, затрагивающий разные dtype — copy.

df = pd.DataFrame({
    "a": [1, 2, 3],
    "b": [4, 5, 6],
    "c": [7, 8, 9],
})  # все int64 → один блок → слайс даст view

# Может быть view:
sub = df[["a", "b"]]
sub["a"] = 0  # SettingWithCopyWarning — неопределённое поведение
print(df)  # a может быть изменён или нет — зависит от версии Pandas

Как проверить: view или copy

# Способ 1: _is_copy (устарело в Pandas 2.x)
print(sub._is_copy)  # weakref или None

# Способ 2: проверить base numpy array
def is_view(df_or_series):
    arr = df_or_series.values
    return arr.base is not None

print(is_view(df["a"]))   # зависит от dtype и структуры

# Способ 3: id памяти
import ctypes
print(df["a"].values.ctypes.data == sub["a"].values.ctypes.data)

Явный .copy() — правильное решение

# ВСЕГДА используйте .copy() при работе с подмножеством
moscow = df[df["a"] > 1].copy()
moscow["b"] = 999
print(df)  # Оригинал не изменён — гарантировано

# При создании производного DataFrame
df2 = df.assign(d=df["a"] + df["b"])  # assign всегда возвращает копию

# Функции, не мутирующие вход — явный контракт
def process(df: pd.DataFrame) -> pd.DataFrame:
    result = df.copy()
    result["processed"] = result["a"] * 2
    return result

Copy-on-Write (CoW) в Pandas 2.0+ и 3.0

# Pandas 2.0: включить CoW
pd.options.mode.copy_on_write = True

# Pandas 3.0: CoW включён по умолчанию
# Любой slice теперь lazy copy:
# — пока не изменён, разделяет память (экономия RAM)
# — при первом изменении автоматически копируется

df = pd.DataFrame({"a": [1, 2, 3]})
sub = df[["a"]]      # lazy: пока view
sub["a"] = 99        # при записи: автоматически копируется
print(df)  # Оригинал не изменён
print(sub) # Изменён только sub

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

  • В Pandas до 3.0 нельзя предсказать view или copy без проверки dtype и структуры блоков — всегда используйте явный .copy() для безопасности.
  • Код, опирающийся на propagation через view (sub["a"] = 1 изменяет df), сломается с CoW в Pandas 3.0.
  • .loc[] и .iloc[] при выборке по строкам могут возвращать и view, и copy в зависимости от сложности маски.
  • .values возвращает numpy array — изменение df.values[0, 0] = 999 может мутировать DataFrame, обходя все защитные механизмы Pandas.
  • После pd.concat() результат — всегда новый объект (copy), но составляющие его блоки могут быть view на исходные массивы до CoW.
  • deep=True в .copy(deep=True) — дефолт, копирует и данные, и индекс. deep=False копирует только метаданные структуры, но не данные.
  • В многопоточном коде view без блокировок ведёт к гонке данных — в таких сценариях всегда передавайте .copy().

Common mistakes

  • Объяснять view vs copy только синтаксисом без shape, dtype, состояния или режима выполнения.
  • Игнорировать leakage, воспроизводимость, пустые входы и скрытые копии данных.
  • Не проверять production-симптомы: latency, память, ретраи, дрейф качества и несовпадение версий.

What the interviewer is testing

  • Может ли связать view vs copy с реальным контрактом входов и выходов.
  • Упоминает ли тесты, метрики, reproducibility и диагностику ошибок.
  • Видит ли различие между demo-кодом в ноутбуке и production-пайплайном.
  • Предлагает ли observability, rollback, ограничения стоимости и стратегию incident replay.

Sources

Related topics