Панды создают новый столбец на основе значений из других столбцов / применяют функцию из нескольких столбцов, построчно

316

Я хочу , чтобы применить свою пользовательскую функцию (она использует если-иначе лестница) в этих шести колонн ( 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 флага испанского языка, не являющегося латиноамериканским, вернуть «Два или более»

====================== 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
Дейв
источник
Ваша конкретная функция - это просто длинная лестница if-else, где значения некоторых переменных имеют приоритет над другими. Это будет называться приоритетным декодером на языке аппаратного обеспечения.
SMCI

Ответы:

408

Хорошо, два шага к этому - сначала написать функцию, которая выполняет перевод, который вы хотите - я собрал пример на основе вашего псевдокода:

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
Томас Кимбер
источник
69
просто примечание: если вы только вводите ряд в свою функцию, вы можете просто сделать:df.apply(label_race, axis=1)
Пол Х
1
Если бы я хотел сделать что-то подобное с другой строкой, мог бы я использовать ту же функцию? Например, из результатов, если ['race_label'] == "White" вернуть 'White' и так далее. Но если ['race_label'] == 'Unknown' вернет значения из столбца ['rno_defined']. Я предполагаю, что та же функция будет работать, но я не могу понять, как получить значения из другого столбца.
Дейв
2
Вы можете написать новую функцию, которая просматривает поле 'race_label', и отправить результаты в новое поле, или - и я думаю, что в этом случае может быть лучше, отредактировать исходную функцию, изменив последнюю return 'Other'строку, на return row['rno_defined']которую следует подставьте значение из этого столбца в тех случаях, когда набор операторов if / then не находит соответствия (то есть, где в настоящий момент вы видите «Other»).
Томас Кимбер
9
Вы можете упростить: df.apply(lambda row: label_race (row),axis=1)доdf.apply(label_race, axis=1)
user48956
5
В более новых версиях, если вы получаете «SettingWithCopyWarning», вы должны посмотреть на метод «assign». См .: stackoverflow.com/a/12555510/3015186
np8
218

Так как это первый результат 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'

Источник: https://stackoverflow.com/a/12555510/243392

И если имя вашего столбца содержит пробелы, вы можете использовать синтаксис, подобный следующему:

df = df.assign(**{'some column name': col.values})

А вот документацию для подачи заявки и назначения .

Брайан Бернс
источник
1
Короткий ответ, перегоненный до самого необходимого!
Фроде Аксельсен
1
Я понимаю, SettingWithCopyWarningкогда я это делаю. df['c'] = df.apply(lambda row: row.a + row.b, axis=1) Это реальная проблема здесь, или я не должен беспокоиться об этом?
конец
2
@Nate Я никогда не получал это предупреждение - возможно, это зависит от данных в кадре данных? Но я исправил ответ, основанный на другом ответе от 2017 года.
Брайан Бернс
57

Приведенные выше ответы совершенно верны, но существует векторизованное решение в форме 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дает нам значительно улучшенную производительность, и расхождение будет только увеличиваться по мере роста данных.

user3483203
источник
8
Это решение так недооценено. Я знал, что могу сделать что-то подобное с apply, но искал альтернативу, так как я должен выполнить эту операцию для тысяч файлов. Так рада, что нашла твой пост.
mlx
У меня проблемы с созданием чего-то подобного. Я получаю сообщение об ошибке «Истина серии неоднозначна ...». Мой код: Kansas_City = ['ND', 'SD', 'NE', 'KS', 'MN', 'IA', 'MO'] условий = [df_merge ['state_alpha'] в Канзас_City] output = [' Канзас-Сити '] df_merge [' Region '] = np.select (условия, выходы,' Other ') Может помочь?
Шон Шрайер
3
Это должен быть принятый ответ. С остальными все в порядке, но как только вы работаете с большими данными, это единственный, который работает, и работает удивительно быстро.
Пролетариат
29

.apply()принимает функцию в качестве первого параметра; передать в label_raceфункцию так:

df['race_label'] = df.apply(label_race, axis=1)

Вам не нужно делать лямбда-функцию для передачи функции.

Габриель Симард-Мур
источник
12

попробуй это,

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

Показатели эффективности:

Принятый ответ:

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 с ± 46,5 мс на цикл (среднее ± стандартное отклонение из 7 циклов, по 1 циклу каждый)

Мой предложенный ответ:

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 мс ± 1,7 мс на цикл (среднее ± стандартное отклонение из 7 циклов, по 10 циклов в каждом)

Мохамед Тасин ах
источник