Какой смысл в пандах, если не определено, возвращает ли операция индексирования вид или копию?

Я переключился с R на панд. Я обычно получаю SettingWithCopyWarnings, когда я делаю что-то вроде

df_a = pd.DataFrame({'col1': [1,2,3,4]})    

# Filtering step, which may or may not return a view
df_b = df_a[df_a['col1'] > 1]

# Add a new column to df_b
df_b['new_col'] = 2 * df_b['col1']

# SettingWithCopyWarning!!

Мне кажется, я понимаю проблему, хотя я с радостью узнаю, в чем я ошибся. В данном примере не определено, df_b это взгляд на df_a или нет. Таким образом, эффект присвоения df_b неясно: влияет ли это df_a? Проблема может быть решена путем явного создания копии при фильтрации:

df_a = pd.DataFrame({'col1': [1,2,3,4]})    

# Filtering step, definitely a copy now
df_b = df_a[df_a['col1'] > 1].copy()

# Add a new column to df_b
df_b['new_col'] = 2 * df_b['col1']

# No Warning now

Я думаю, что мне чего-то не хватает: если мы никогда не сможем быть уверенными, создаем ли мы представление или нет, для чего нужны представления? Из документации панд ( http://pandas-docs.github.io/pandas-docs-travis/indexing.html?highlight=view)

За исключением простых случаев, очень трудно предсказать, вернет ли он [ getitem ] представление или копию (это зависит от структуры памяти массива, о которой pandas не дает никаких гарантий)

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

Я считаю очень громоздким и ошибочным распространять вызовы.copy() по всему коду. Я использую неправильный стиль для манипулирования моими DataFrames? Или прирост производительности настолько высок, что оправдывает кажущуюся неловкость?

2 ответа

Решение

Отличный вопрос!

Короткий ответ: это недостаток в пандах, который исправляется.

Вы можете найти более подробное обсуждение природы проблемы здесь, но основной вывод заключается в том, что мы сейчас переходим к поведению "копирование при записи", при котором при каждом разрезании вы получаете новую копию, и вам никогда не придется думать о взглядах. Исправление скоро придет через этот проект рефакторинга. Я на самом деле пытался исправить это напрямую ( см. Здесь), но это просто не осуществимо в текущей архитектуре.

По правде говоря, мы будем держать представления в фоновом режиме - они делают панду СУПЕР-памятью эффективной и быстрой, когда они могут быть предоставлены - но мы в конечном итоге будем скрывать их от пользователей, поэтому, с точки зрения пользователя, если вы разрежете, индексируйте или вырезать DataFrame, то, что вы получите обратно, будет новой копией.

(Это достигается созданием представлений, когда пользователь только читает данные, но всякий раз, когда используется операция назначения, представление будет преобразовано в копию до того, как произойдет назначение.)

Лучше всего предположить, что исправление будет в течение года - в то же время, я боюсь, что некоторые .copy() может понадобиться, извините!

Я согласен, это немного смешно. Моя текущая практика - искать "функциональный" метод для всего, что я хочу сделать (по моему опыту, они почти всегда существуют, за исключением переименования столбцов и рядов). Иногда это делает код более элегантным, иногда - хуже (мне не нравится assign с lambda), но, по крайней мере, мне не нужно беспокоиться об изменчивости.

Таким образом, для индексации, вместо использования обозначения среза, вы можете использовать query который вернет копию по умолчанию:

In [5]: df_a.query('col1 > 1')
Out[5]:
   col1
1     2
2     3
3     4

Я немного расскажу об этом в этом посте.

Изменить: как поднят в комментариях, похоже, что я ошибаюсь query вернуть копию по умолчанию, однако, если вы используете assign style, затем assign создаст копию перед возвратом вашего результата, и у вас все хорошо:

df_b = (df_a.query('col1 > 1')
            .assign(newcol = 2*df_a['col1']))
Другие вопросы по тегам