Как я могу изменить элемент dataframe из серии, определенной через df.loc[row]?

У меня есть код, в котором функция/метод принимает серию (строку из 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 в приведенных выше примерах.

Другие сопутствующие вопросы:


person anon01    schedule 07.03.2020    source источник


Ответы (2)


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

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

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

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

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

person Lilith Schneider    schedule 07.03.2020
comment
это хорошее начало, но я передаю серию (record) функции, которая должна изменить df на месте. Есть ли способ заставить record быть представлением исходной строки df? - person anon01; 07.03.2020
comment
Есть ли способ передать ROW_NAME, использованный для создания серии, в функцию, чтобы вы могли изменить исходный фрейм данных? Я не думаю, что панды предоставляют представления. - person Lilith Schneider; 07.03.2020
comment
Глядя на другие ссылки, я думаю, что мелкая копия имеет решающее значение, как вы указали. По крайней мере, еще одна ссылка предполагает, что принудительно вернуть представление невозможно. Похоже, мой лучший вариант - изменить сигнатуру функции, чтобы она принимала df и row_name. - person anon01; 07.03.2020

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

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

person anon01    schedule 07.03.2020