Как я могу изменить элемент dataframe из Серии, определенной через df.loc [row]? - PullRequest
0 голосов
/ 07 марта 2020

У меня есть код, в котором функция / метод принимает Серию (строку из df) и, как предполагается, изменяет ее на месте, так что изменения отражаются в исходном df. Однако я не могу заставить модификацию как представление, а не как копию. Информация из документации и вопроса о переполнении стека не решает проблему, как показано в следующем примере:

import pandas as pd
pd.__version__ # 0.24.2

ROW_NAME = "r1"
COL_NAME = "B"
NEW_VAL = 100.0

# df I would like to modify in-place
df = pd.DataFrame({"A":[[1], [2], [3,4]], "B": [1.0, 2.0, 3.0]}, index=["r1", "r2", "r3"])

# a row (Series reference) is the input param to a function that should modify df in-place
record = df.loc[ROW_NAME]
record.loc[COL_NAME] = NEW_VAL
assert df.loc[ROW_NAME, COL_NAME] == NEW_VAL #False

Строка, начинающаяся с record.loc приводит к знакомому предупреждению: SettingWithCopyWarning: A value is trying to be set on a copy of a slice from a DataFrame, что может иметь смысл, за исключением того, что record представляется для ссылки df и может быть изменено на месте при некоторых обстоятельствах. Пример этого:

record = df.loc[ROW_NAME]
record.loc["A"].append(NEW_VALUE)
assert NEW_VALUE in df.loc["r1", "A"] # True

Мой вопрос: как я могу принудительно изменить значение с плавающей точкой на df.loc[ROW_NAME, COL_NAME] вместо серии record? Бонусные баллы за разъяснение, почему можно изменить столбец A на месте, но не столбец B в приведенных выше примерах.

Другие связанные вопросы:

Ответы [ 3 ]

1 голос
/ 07 марта 2020

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

Как @Lilith Schneider указывает на то, что первоначальная путаница по этому поводу связана с тем фактом, что record = df.loc["r1"] возвращает мелкую копию - некую гибридную копию и представление, которые могут вызвать путаницу и привести к неожиданному поведению.

1 голос
/ 07 марта 2020

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

Если вы ссылаетесь на этот стековый пост , это звучит как .loc[] обычно ожидается, что он вернет копию, а не представление, и это назначение не будет работать, если .loc были связаны.

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

df.loc[ROW_NAME, COL_NAME] = NEW_VAL
assert(df.loc[ROW_NAME, COL_NAME] == NEW_VAL) # True

А что касается .append, все еще работающего, вот почему я упомянул "мелкое" поведение копирования. Ваша новая копия записи по-прежнему содержит ссылку на исходный список в столбце A. См. этот пост , чтобы узнать о разнице между привязкой к новому объекту и изменением существующего объекта.

0 голосов
/ 07 марта 2020

@ anon01 @sergiomahi правильно, что вы должны использовать .copy ().

Я переписал ваш код так, как я бы решил:

import pandas as pd
pd.__version__ #0.24.2

ROW_NAME = "r1"
COL_NAME = "B"
NEW_VAL = 100.0

# df I would like to modify in-place
df = pd.DataFrame({"A":[[1], [2], [3,4]],
                   "B": [1.0, 2.0, 3.0]},
                   index=["r1", "r2", "r3"])
df.loc[df.index == ROW_NAME, COL_NAME] = NEW_VAL
df

Я узнал о методе .copy () пару недель go, так как мне, наконец, надоело видеть предупреждение каждый раз, когда я неосознанно делал цепную индексацию, которая связана с топикой c с использованием .copy (). Если вас интересует цепная индексация, а также если вы хотите увидеть пример .copy (), то вот хорошая статья, если вы просто управляете + F для .copy (): https://www.dataquest.io/blog/settingwithcopywarning/

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...