Создание нового столбца на основе значений других столбцов / Применение функции к нескольким столбцам построчно в Pandas
Описание проблемы:
Я хочу применить свою пользовательскую функцию (которая использует конструкции if-else) к шести столбцам (ERI_Hispanic
, ERI_AmerInd_AKNatv
, ERI_Asian
, ERI_Black_Afr.Amer
, ERI_HI_PacIsl
, ERI_White
) в каждой строке моего DataFrame.
Я пробовал разные методы из других вопросов, но все еще не могу найти правильное решение для своей задачи. Ключевой момент заключается в том, что если человек считается Hispanic, его нельзя относить к другой этнической группе. Даже если в другом столбце этнической принадлежности стоит "1", он все равно считается Hispanic, а не как лицо, относящееся к двум или более расам. Аналогично, если сумма всех столбцов ERI больше 1, его следует считать как относящегося к двум или более расам, и он не может быть отнесён к уникальной этнической группе (кроме Hispanic).
Это почти как пройтись в цикле по каждой строке и если каждая запись соответствует критерию, ее добавляют в один список и удаляют из исходного.
Из приведенного ниже DataFrame мне нужно вычислить новый столбец на основе следующих критериев в SQL:
КРИТЕРИИ
IF [ERI_Hispanic] = 1 THEN RETURN “Hispanic”
ELSE IF SUM([ERI_AmerInd_AKNatv] + [ERI_Asian] + [ERI_Black_Afr.Amer] + [ERI_HI_PacIsl] + [ERI_White]) > 1 THEN RETURN “Two or More”
ELSE IF [ERI_AmerInd_AKNatv] = 1 THEN RETURN “A/I AK Native”
ELSE IF [ERI_Asian] = 1 THEN RETURN “Asian”
ELSE IF [ERI_Black_Afr.Amer] = 1 THEN RETURN “Black/AA”
ELSE IF [ERI_HI_PacIsl] = 1 THEN RETURN “Haw/Pac Isl.”
ELSE IF [ERI_White] = 1 THEN RETURN “White”
Комментарий: Если флаг ERI для Hispanic равен True (1), сотрудник классифицируется как "Hispanic".
Комментарий: Если более 1 флага ERI для не-Hispanic истинно, вернуть “Two or More”.
DATAFRAME
lname fname rno_cd eri_afr_amer eri_asian eri_hawaiian eri_hispanic eri_nat_amer eri_white rno_defined
0 MOST JEFF E 0 0 0 0 0 1 White
1 CRUISE TOM E 0 0 0 1 0 0 White
2 DEPP JOHNNY 0 0 0 0 0 1 Unknown
3 DICAP LEO 0 0 0 0 0 1 Unknown
4 BRANDO MARLON E 0 0 0 0 0 0 White
5 HANKS TOM 0 0 0 0 0 1 Unknown
6 DENIRO ROBERT E 0 1 0 0 0 1 White
7 PACINO AL E 0 0 0 0 0 1 White
8 WILLIAMS ROBIN E 0 0 1 0 0 0 White
9 EASTWOOD CLINT E 0 0 0 0 0 1 White
Как я могу реализовать эту логику на Python с использованием pandas?
3 ответ(ов)
Для того чтобы реализовать необходимый перевод, выполните два шага. Сначала вам нужно написать функцию, которая будет выполнять нужную вам логику. Вот пример, основанный на вашем псевдокоде:
def label_race(row):
if row['eri_hispanic'] == 1:
return 'Hispanic'
if row['eri_afr_amer'] + row['eri_asian'] + row['eri_hawaiian'] + row['eri_nat_amer'] + row['eri_white'] > 1:
return 'Two Or More'
if row['eri_nat_amer'] == 1:
return 'A/I AK Native'
if row['eri_asian'] == 1:
return 'Asian'
if row['eri_afr_amer'] == 1:
return 'Black/AA'
if row['eri_hawaiian'] == 1:
return 'Haw/Pac Isl.'
if row['eri_white'] == 1:
return 'White'
return 'Other'
Обратите внимание на то, что параметром функции является объект Series с меткой "row".
После этого используйте функцию apply
из pandas, чтобы применить созданную функцию к вашему DataFrame:
df.apply(label_race, axis=1)
Не забудьте указать параметр axis=1
, который определяет, что обработка будет производиться по строкам, а не по столбцам. Результаты будут выглядеть примерно так:
0 White
1 Hispanic
2 White
3 White
4 Other
5 White
6 Two Or More
7 White
8 Haw/Pac Isl.
9 White
Если вас устраивают полученные результаты, то вы можете сохранить их в новом столбце вашего исходного DataFrame:
df['race_label'] = df.apply(label_race, axis=1)
В результате полученный DataFrame будет выглядеть так (прокрутите вправо, чтобы увидеть новый столбец):
lname fname rno_cd eri_afr_amer eri_asian eri_hawaiian eri_hispanic eri_nat_amer eri_white rno_defined race_label
0 MOST JEFF E 0 0 0 0 0 1 White White
1 CRUISE TOM E 0 0 0 1 0 0 White Hispanic
2 DEPP JOHNNY NaN 0 0 0 0 0 1 Unknown White
3 DICAP LEO NaN 0 0 0 0 0 1 Unknown White
4 BRANDO MARLON E 0 0 0 0 0 0 White Other
5 HANKS TOM NaN 0 0 0 0 0 1 Unknown White
6 DENIRO ROBERT E 0 1 0 0 0 1 White Two Or More
7 PACINO AL E 0 0 0 0 0 1 White White
8 WILLIAMS ROBIN E 0 0 1 0 0 0 White Haw/Pac Isl.
9 EASTWOOD CLINT E 0 0 0 0 0 1 White White
Если у вас есть вопросы или вам нужно что-то уточнить, не стесняйтесь задавать!
Ответы выше вполне валидны, но существует векторизированное решение с использованием numpy.select
. Это позволяет вам определить условия и соответствующие выходные значения для этих условий гораздо более эффективно, чем с помощью метода apply
.
Сначала определим условия:
conditions = [
df['eri_hispanic'] == 1,
df[['eri_afr_amer', 'eri_asian', 'eri_hawaiian', 'eri_nat_amer', 'eri_white']].sum(1).gt(1),
df['eri_nat_amer'] == 1,
df['eri_asian'] == 1,
df['eri_afr_amer'] == 1,
df['eri_hawaiian'] == 1,
df['eri_white'] == 1,
]
Теперь определим соответствующие выходные значения:
outputs = [
'Hispanic', 'Two Or More', 'A/I AK Native', 'Asian', 'Black/AA', 'Haw/Pac Isl.', 'White'
]
И, наконец, используя numpy.select
:
res = np.select(conditions, outputs, 'Other')
pd.Series(res)
Результат будет таким:
0 White
1 Hispanic
2 White
3 White
4 Other
5 White
6 Two Or More
7 White
8 Haw/Pac Isl.
9 White
dtype: object
Почему следует использовать numpy.select
вместо apply
? Вот некоторые тесты производительности:
df = pd.concat([df]*1000)
In [42]: %timeit df.apply(lambda row: label_race(row), axis=1)
1.07 s ± 4.16 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)
In [44]: %%timeit
...: conditions = [
...: df['eri_hispanic'] == 1,
...: df[['eri_afr_amer', 'eri_asian', 'eri_hawaiian', 'eri_nat_amer', 'eri_white']].sum(1).gt(1),
...: df['eri_nat_amer'] == 1,
...: df['eri_asian'] == 1,
...: df['eri_afr_amer'] == 1,
...: df['eri_hawaiian'] == 1,
...: df['eri_white'] == 1,
...: ]
...:
...: outputs = [
...: 'Hispanic', 'Two Or More', 'A/I AK Native', 'Asian', 'Black/AA', 'Haw/Pac Isl.', 'White'
...: ]
...:
...: np.select(conditions, outputs, 'Other')
...:
...:
3.09 ms ± 17 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)
Использование numpy.select
обеспечивает значительное улучшение производительности, и это различие будет только увеличиваться с ростом объема данных.
.apply()
принимает функцию в качестве первого параметра. Вы можете передать функцию label_race
следующим образом:
df['race_label'] = df.apply(label_race, axis=1)
Не нужно создавать lambda-функцию для передачи функции.
Как выбрать строки из DataFrame на основе значений столбцов?
Создание DataFrame в Pandas путём последовательного добавления строк
Преобразование списка словарей в DataFrame pandas
Объединение двух столбцов текста в DataFrame pandas
Получение списка из заголовков столбцов DataFrame в Pandas