Вопрос или проблема
Я пишу скрипт на Pandas для выполнения манипуляций с данными в Excel-файле. Сначала я загружаю два листа в DataFrame. Один – это оригинальные данные df
, второй – лист с деталями замен, которые необходимо сделать в оригинальных данных replace
.
Скрипт должен выполнять две задачи для каждой строки в df
.
-
Заменить каждое вхождение
'Name'
вdf
на'NameReplace'
(работает) -
Для тех же строк в
df
заменить срез столбцов (указанный в списке) значениями из того же среза столбцов вreplace
Воспроизводимый минимальный пример моей текущей реализации:
import pandas
df = pandas.DataFrame([["John", None, None],["Phil", None, None],["John", None, None],["Bob", None, None]], columns=["Name", "Age", "Height"])
replace = pandas.DataFrame([["John", "Dom", 25, 175],["Phil", "Kevin", 56, 145],["Bob", "Michael", 33, 180]], columns=["Name", "NameReplace", "Age", "Height"])
detailsList = ["Age", "Height"]
for i, row in replace.iterrows():
df.loc[df['Name'] == row['Name'], 'Name'] = row['NameReplace']
df.loc[df['Name'] == row['NameReplace'], detailsList] = row[detailsList]
print(df)
Шаг 1) работает с этой реализацией, но столбцы detailsList
в df
не заполняются.
Текущий вывод:
Name Age Height
0 Dom NaN NaN
1 Kevin NaN NaN
2 Dom NaN NaN
3 Michael NaN NaN
Желаемый вывод:
Name Age Height
0 Dom 25 175
1 Kevin 56 145
2 Dom 25 175
3 Michael 33 180
Я уже некоторое время пытаюсь решить эту проблему и не могу продвинуться. Я также не совсем понимаю, почему это не работает, поэтому любая информация об этом будет очень ценна!
Примечание: Использование detailsList
для указания среза столбцов обязательно, так как в реальном решении я работаю только с конкретным срезом полного DataFrame, в отличие от примера, который я привел.
Проблема заключается в том, как pandas пытается назначить серию всему DataFrame. В любом случае, вот простое решение, которое обеспечивает необходимое поведение, воспользовавшись тем, что pandas делает правильные действия при присвоении с помощью массива numpy, а не с помощью серии.
for i, row in replace.iterrows():
df.loc[df['Name'] == row['Name'], 'Name'] = row['NameReplace']
df.loc[df['Name'] == row['NameReplace'], detailsList] = row[detailsList].values
Другие оптимизации:
- Обратите внимание, что вы можете повторно использовать маску
df['Name'] == row['Name']
. В частности, вы сэкономите немного усилий с:
for i, row in replace.iterrows():
mask = df['Name'] == row['Name']
df.loc[mask, 'Name'] = row['NameReplace']
df.loc[mask, detailsList] = row[detailsList].values
- Вы можете избежать использования iterrows, если используете объединение
df = (df[['Name']].merge(replace, on = 'Name')
.drop(columns="Name")
.rename(columns={'NameReplace':'Name'}))
Однако при этом подходе строки могут оказаться переупорядоченными.
Ответ или решение
Чтобы заменить часть значений в столбцах одного DataFrame на значения из другого DataFrame по определённым условиям в Pandas, можно использовать несколько подходов. Ниже приведен подробный ответ на ваш запрос, включающий все необходимые шаги.
Шаги выполнения задачи
-
Импорт библиотеки и создание DataFrame.
Мы начнём с импорта нужной библиотеки и создания двух DataFrame: один содержит оригинальные данные (df
), другой — данные о замене (replace
). -
Замена имен и значений в других столбцах.
Мы будем выполнять замену значений вdf
на основе значений изreplace
. Для этого мы используем цикл, чтобы пройтись по каждому ряду вreplace
, и соответствующим образом заменить значения вdf
.
Пример кода
import pandas as pd
# Создание оригинального DataFrame
df = pd.DataFrame([
["John", None, None],
["Phil", None, None],
["John", None, None],
["Bob", None, None]
], columns=["Name", "Age", "Height"])
# Создание DataFrame с заменами
replace = pd.DataFrame([
["John", "Dom", 25, 175],
["Phil", "Kevin", 56, 145],
["Bob", "Michael", 33, 180]
], columns=["Name", "NameReplace", "Age", "Height"])
# Список столбцов для замены
detailsList = ["Age", "Height"]
# Проход по рядам в DataFrame с заменами
for i, row in replace.iterrows():
# Создание маски для текущего имени
mask = df['Name'] == row['Name']
# Замена имени
df.loc[mask, 'Name'] = row['NameReplace']
# Замена значений в указанных столбцах
df.loc[mask, detailsList] = row[detailsList].values
# Печать итогового DataFrame
print(df)
Результат выполнения кода
После выполнения данного кода, вывод будет следующим:
Name Age Height
0 Dom 25.0 175
1 Kevin 56.0 145
2 Dom 25.0 175
3 Michael 33.0 180
Объяснение
-
Создание маски. Мы создаем маску
mask
, которая определяет, какие строки вdf
соответствуют текущему имени изreplace
. -
Замена имени. С помощью
df.loc[mask, 'Name']
мы заменяем имя в тех строках, где оно совпадает с текущим именем изreplace
. -
Замена значений в столбцах. Для замены значений в других столбцах (в нашем случае это
Age
иHeight
), используемdf.loc[mask, detailsList] = row[detailsList].values
. Это позволяет избежать проблем с несовпадением индексов.
Оптимизация
Если необходимо, можно улучшить производительность за счёт использования метода merge
, что избавит вас от необходимости проходить по каждой строке. Однако стоит учитывать, что в этом случае порядок строк может измениться:
df = (df[['Name']]
.merge(replace, on='Name', how='left')
.drop(columns='Name')
.rename(columns={'NameReplace': 'Name'}))
Заключение
Таким образом, вы можете эффективно выполнять замену строк и значений в DataFrame с использованием методов Pandas. Важно помнить о механизмах работы с индексами и тем, как Pandas управляет преобразованиями данных для минимизации возможных ошибок.