Панды создают новый столбец на основе значений из других столбцов/применяют функцию из нескольких столбцов, построчно
Я хочу применить свою пользовательскую функцию (она использует лестницу if-else) к этим шести столбцам (ERI_Hispanic
, ERI_AmerInd_AKNatv
, ERI_Asian
, ERI_Black_Afr.Amer
, ERI_HI_PacIsl
, ERI_White
) в каждой строке моего dataframe.
Я пробовал разные методы из других вопросов, но все еще не могу найти правильный ответ для моей проблемы. Критическим моментом является то, что если человек считается латиноамериканцем, его нельзя считать ничем другим. Даже если у них есть "1" в другой колонке этнической принадлежности, они все равно считаются латиноамериканцами, а не двумя или более расами. Аналогичным образом, если сумма всех столбцов ERI больше 1, они считаются двумя или более расами и не могут считаться уникальной этнической принадлежностью (кроме испаноязычных). Надеюсь, это имеет смысл. Любая помощь будет оценена.
Это почти как цикл for для каждой строки, и если каждая запись соответствует критерию, они добавляются в один список и удаляются из оригинала.
Из приведенного ниже кадра данных мне нужно вычислить новый столбец на основе следующей спецификации в 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 для испаноязычных имеет значение "Истина" (1), сотрудник классифицируется как "испаноязычный"
Комментарий: если установлено более 1 флага неиспаноязычного ERI, вернуть "Два или более"
====================== 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
Ответы
Ответ 1
Хорошо, два шага к этому - сначала написать функцию, которая выполняет перевод, который вы хотите - я собрал пример на основе вашего псевдокода:
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, помеченным как "строка".
Затем используйте функцию apply в пандах, чтобы применить функцию - например,
df.apply (lambda row: label_race(row), 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
Если вы довольны этими результатами, запустите их снова, сохранив результаты в новом столбце в исходном кадре данных.
df['race_label'] = df.apply (lambda row: label_race(row), axis=1)
Результирующий кадр данных выглядит следующим образом (прокрутите вправо, чтобы увидеть новый столбец):
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
Ответ 2
Так как это первый результат Google для "нового столбца панд от других", вот простой пример:
import pandas as pd
# make a simple dataframe
df = pd.DataFrame({'a':[1,2], 'b':[3,4]})
df
# a b
# 0 1 3
# 1 2 4
# create an unattached column with an index
df.apply(lambda row: row.a + row.b, axis=1)
# 0 4
# 1 6
# do same but attach it to the dataframe
df['c'] = df.apply(lambda row: row.a + row.b, axis=1)
df
# a b c
# 0 1 3 4
# 1 2 4 6
Если вы получили SettingWithCopyWarning
вы также можете сделать это следующим образом:
fn = lambda row: row.a + row.b # define a function for the new column
col = df.apply(fn, axis=1) # get column data with an index
df = df.assign(c=col.values) # assign values to column 'c'
Источник: fooobar.com/questions/18059/...
И если имя вашего столбца содержит пробелы, вы можете использовать такой синтаксис:
df = df.assign(**{'some column name': col.values})
А вот документацию для подачи заявки и назначения.
Ответ 3
Приведенные выше ответы совершенно верны, но существует векторизованное решение в виде 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
следует использовать 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
дает нам значительно улучшенную производительность, и расхождение будет только увеличиваться по мере роста данных.
Ответ 4
.apply()
принимает функцию в качестве первого параметра; передать функцию label_race
следующим образом:
df['race_label'] = df.apply(label_race, axis=1)
Вам не нужно делать лямбда-функцию для передачи функции.
Ответ 5
попробуй это,
df.loc[df['eri_white']==1,'race_label'] = 'White'
df.loc[df['eri_hawaiian']==1,'race_label'] = 'Haw/Pac Isl.'
df.loc[df['eri_afr_amer']==1,'race_label'] = 'Black/AA'
df.loc[df['eri_asian']==1,'race_label'] = 'Asian'
df.loc[df['eri_nat_amer']==1,'race_label'] = 'A/I AK Native'
df.loc[(df['eri_afr_amer'] + df['eri_asian'] + df['eri_hawaiian'] + df['eri_nat_amer'] + df['eri_white']) > 1,'race_label'] = 'Two Or More'
df.loc[df['eri_hispanic']==1,'race_label'] = 'Hispanic'
df['race_label'].fillna('Other', inplace=True)
O/P:
lname fname rno_cd eri_afr_amer eri_asian eri_hawaiian \
0 MOST JEFF E 0 0 0
1 CRUISE TOM E 0 0 0
2 DEPP JOHNNY NaN 0 0 0
3 DICAP LEO NaN 0 0 0
4 BRANDO MARLON E 0 0 0
5 HANKS TOM NaN 0 0 0
6 DENIRO ROBERT E 0 1 0
7 PACINO AL E 0 0 0
8 WILLIAMS ROBIN E 0 0 1
9 EASTWOOD CLINT E 0 0 0
eri_hispanic eri_nat_amer eri_white rno_defined race_label
0 0 0 1 White White
1 1 0 0 White Hispanic
2 0 0 1 Unknown White
3 0 0 1 Unknown White
4 0 0 0 White Other
5 0 0 1 Unknown White
6 0 0 1 White Two Or More
7 0 0 1 White White
8 0 0 0 White Haw/Pac Isl.
9 0 0 1 White White
используйте .loc
вместо apply
.
это улучшает векторизацию.
.loc
работает простым способом, маскирует строки на основе условия, применяет значения к стоп-кадрам.
для получения более подробной информации посетите .loc docs
Показатели эффективности:
Принятый ответ:
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'
df=pd.read_csv('dataser.csv')
df = pd.concat([df]*1000)
%timeit df.apply(lambda row: label_race(row), axis=1)
1.15 s ± 46.5 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)
Мой предложенный ответ:
def label_race(df):
df.loc[df['eri_white']==1,'race_label'] = 'White'
df.loc[df['eri_hawaiian']==1,'race_label'] = 'Haw/Pac Isl.'
df.loc[df['eri_afr_amer']==1,'race_label'] = 'Black/AA'
df.loc[df['eri_asian']==1,'race_label'] = 'Asian'
df.loc[df['eri_nat_amer']==1,'race_label'] = 'A/I AK Native'
df.loc[(df['eri_afr_amer'] + df['eri_asian'] + df['eri_hawaiian'] + df['eri_nat_amer'] + df['eri_white']) > 1,'race_label'] = 'Two Or More'
df.loc[df['eri_hispanic']==1,'race_label'] = 'Hispanic'
df['race_label'].fillna('Other', inplace=True)
df=pd.read_csv('s22.csv')
df = pd.concat([df]*1000)
%timeit label_race(df)
24.7 ms ± 1.7 ms per loop (mean ± std. dev. of 7 runs, 10 loops each)