Какие правила использует 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, но последняя - нет!