Какие правила использует Pandas для создания представления против копии?

Я запутался в правилах, которые использует Pandas, когда решает, что выборка из фрейма данных является копией оригинального фрейма данных или представлением оригинала.

Если у меня есть, например,

df = pd.DataFrame(np.random.randn(8,8), columns=list('ABCDEFGH'), index=range(1,9))

Я понимаю что query возвращает копию, чтобы что-то вроде

foo = df.query('2 < index <= 5')
foo.loc[:,'E'] = 40

не повлияет на исходный фрейм данных, df, Я также понимаю, что скалярные или именованные срезы возвращают представление, так что присваивания им, такие как

df.iloc[3] = 70

или же

df.ix[1,'B':'E'] = 222

изменится df, Но я заблудился, когда дело доходит до более сложных дел. Например,

df[df.C <= df.B]  = 7654321

изменения df, но

df[df.C <= df.B].ix[:,'B':'E']

не.

Есть ли простое правило, которое использует Панд, которое я просто скучаю? Что происходит в этих конкретных случаях; и, в частности, как мне изменить все значения (или подмножество значений) в кадре данных, которые удовлетворяют конкретному запросу (как я пытаюсь сделать в последнем примере выше)?


Примечание: это не то же самое, что этот вопрос; и я прочитал документацию, но не просвещен ею. Я также прочитал "Связанные" вопросы по этой теме, но мне все еще не хватает простого правила, которое использует Pandas, и того, как бы я применил его, например, для изменения значений (или подмножества значений). в кадре данных, которые удовлетворяют конкретному запросу.

3 ответа

Решение

Вот правила, последующее переопределение:

  • Все операции генерируют копию

  • Если inplace=True предоставляется, он будет изменяться на месте; только некоторые операции поддерживают это

  • Индексатор, который устанавливает, например, .loc/.ix/.iloc/.iat/.at установит на месте.

  • Индексатор, который получает объект с однотипным типом, почти всегда является представлением (в зависимости от структуры памяти это может быть не так, поэтому это ненадежно). Это в основном для эффективности. (пример сверху для .query; это всегда будет возвращать копию, как ее оценивает numexpr)

  • Индексатор, который получает объект с несколькими типами шрифтов, всегда является копией.

Ваш пример chained indexing

df[df.C <= df.B].ix[:,'B':'E']

не гарантированно работать (и, таким образом, вы никогда не будете делать это).

Вместо этого сделайте:

df.ix[df.C <= df.B, 'B':'E']

как это быстрее и всегда будет работать

Цепная индексация - это 2 отдельные операции на Python, и поэтому панды не могут быть надежно перехвачены (вы часто получаете SettingWithCopyWarning, но это также не обнаруживается на 100%). Документы разработчика, которые вы указали, предлагают гораздо более полное объяснение.

Начиная с версии pandas 1.5.0, в pandas есть режим копирования при записи (CoW) , который заставляет любой кадр данных/серию, полученный из другого, вести себя как копия в представлениях. Когда он включен, копия создается только в том случае, если данные используются совместно с другим фреймом данных/серией. При отключенном CoW такие операции, как нарезка, создают представление (и неожиданно изменяют оригинал, если изменяется новый фрейм данных), но при использовании CoW создается копия.

      pd.options.mode.copy_on_write = False   # disable CoW (this is the default as of pandas 2.0)
df = pd.DataFrame({'A': range(4), 'B': list('abcd')})

df1 = df.iloc[:4]                       # view
df1.iloc[0] = 100
df.equals(df1)                          # True <--- df changes together with df1



pd.options.mode.copy_on_write = True    # enable CoW (this is planned to be the default by pandas 3.0)
df = pd.DataFrame({'A': range(4), 'B': list('abcd')})

df1 = df.iloc[:4]                       # copy because data is shared
df1.iloc[0] = 100
df.equals(df1)                          # False <--- df doesn't change when df1 changes

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

      df = pd.DataFrame({'A': range(1_000_000), 'B': range(1_000_000)})

%%timeit
with pd.option_context('mode.copy_on_write', False):  # disable CoW in a context manager
    df1 = df.add_prefix('col ').set_index('col A').rename_axis('index col').reset_index()
# 30.5 ms ± 561 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)


%%timeit
with pd.option_context('mode.copy_on_write', True):   # enable CoW in a context manager
    df2 = df.add_prefix('col ').set_index('col A').rename_axis('index col').reset_index()
# 18 ms ± 513 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)

Вот что-то забавное:

      u = df
v = df.loc[:, :]
w = df.iloc[:,:]
z = df.iloc[0:, ]

Первые три кажутся отсылками к df, но последняя - нет!

Другие вопросы по тегам