панди створюють новий стовпчик на основі значень з інших стовпців / застосовують функцію з декількох стовпців, по рядках


316

Я хочу , щоб застосувати свою призначену для користувача функцію (вона використовує якщо-інакше сходи) в цих шести колон ( ERI_Hispanic, ERI_AmerInd_AKNatv, ERI_Asian, ERI_Black_Afr.Amer, ERI_HI_PacIsl, ERI_White) в кожному рядку мого dataframe.

Я спробував різні методи, ніж інші питання, але все ще не можу знайти правильну відповідь на свою проблему. Критичне значення цього полягає в тому, що якщо людину зараховують до латиноамериканського, їх не можна вважати нічим іншим. Навіть якщо вони мають "1" в іншій колоні етнічної приналежності, вони все-таки вважаються іспаномовними не двома або більше расами. Аналогічно, якщо сума всіх стовпців ERI більша за 1, вони рахуються як дві або більше рас і не можуть бути зараховані як унікальний етнос (крім іспаномовних). Сподіваємось, це має сенс. Будь-яка допомога буде дуже вдячна.

Це майже як робити цикл для кожного рядка, і якщо кожен запис відповідає критерію, вони додаються до одного списку та виключаються з оригіналу.

Із фрейму даних нижче мені потрібно обчислити новий стовпець на основі наступних специфікацій у 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

Ваша особлива функція - це просто довга сходова клітка, де значення деяких змінних мають пріоритет над іншими. Це було б названо декодером пріоритету в машинобудуванні.
smci

Відповіді:


407

Добре, два кроки до цього - спочатку потрібно написати функцію, яка виконує потрібний вам переклад - я склав приклад разом на основі вашого псевдо-коду:

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 з позначкою "рядок".

Потім застосуйте функцію застосувати в пандах, щоб застосувати функцію - наприклад

df.apply (lambda row: label_race(row), axis=1)

Зверніть увагу на специфікатор ax = 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)
Пол H

1
Якби я хотів зробити щось подібне з іншим рядком, чи можу я використовувати ту саму функцію? Наприклад, з результатів, якщо ['race_label'] == "Білий" повертає "Білий" тощо. Але якщо ['race_label'] == 'Невідомий' повертає значення зі стовпця ['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", вам слід переглянути метод "призначити". Див: 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дає нам значно покращену продуктивність, а розбіжність буде збільшуватися лише у міру зростання даних.


8
Цей розчин настільки занижений. Я знав, що я можу зробити щось подібне з застосувати, але шукав альтернативу, оскільки мені потрібно зробити цю операцію для тисяч файлів. Тож радий, що знайшов твій пост.
mlx

У мене виникають проблеми зі створенням чогось подібного. Я отримую "значення правди у серії неоднозначне ..." повідомлення про помилку. Мій код - Kansas_City = ['ND', 'SD', 'NE', 'KS', 'MN', 'IA', 'MO'] умови = [df_merge ['state_alpha'] у Kansas_City] output = [' Канзас-Сіті '] df_merge [' Регіон '] = np.select (умови, результати,' Інше ') Чи можна допомогти?
Шон Шрейер

3
Це має бути прийнятою відповіддю. Інші - це добре, але коли ви працюєте з більшими даними, ця єдина, яка працює, і вона працює надзвичайно швидко.
TheProletariat

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 циклів у кожній)

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.