6

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

4

Описание проблемы:

Я хочу применить свою пользовательскую функцию (которая использует конструкции 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 ответ(ов)

7

Для того чтобы реализовать необходимый перевод, выполните два шага. Сначала вам нужно написать функцию, которая будет выполнять нужную вам логику. Вот пример, основанный на вашем псевдокоде:

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

Если у вас есть вопросы или вам нужно что-то уточнить, не стесняйтесь задавать!

1

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

0

.apply() принимает функцию в качестве первого параметра. Вы можете передать функцию label_race следующим образом:

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

Не нужно создавать lambda-функцию для передачи функции.

Чтобы ответить на вопрос, пожалуйста, войдите или зарегистрируйтесь