Cree una nueva columna basada en valores de otras columnas / aplique una función de múltiples columnas, por filas en Pandas

Resuelto Dave asked hace 10 años • 8 respuestas

Quiero aplicar mi función personalizada (usa una escalera if-else) a estas seis columnas ( ,,,,, ) en cada fila de mi marco de datos ERI_Hispanic.ERI_AmerInd_AKNatvERI_AsianERI_Black_Afr.AmerERI_HI_PacIslERI_White

Probé diferentes métodos de otras preguntas, pero todavía no puedo encontrar la respuesta correcta para mi problema. La parte crítica de esto es que si la persona se cuenta como hispana no se puede contar como ninguna otra cosa. Incluso si tienen un "1" en otra columna de origen étnico, todavía se les cuenta como hispanos, no como dos o más razas. De manera similar, si la suma de todas las columnas ERI es mayor que 1, se cuentan como dos o más razas y no se pueden contar como una etnia única (excepto los hispanos).

Es casi como hacer un bucle for en cada fila y si cada registro cumple con un criterio, se agrega a una lista y se elimina del original.

Desde el marco de datos a continuación necesito calcular una nueva columna basada en la siguiente especificación en SQL:

CRITERIOS

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”

Comentario: Si el indicador ERI para hispano es Verdadero (1), el empleado se clasifica como “hispano”

Comentario: Si más de un indicador ERI no hispano es verdadero, devuelva “Dos o más”

MARCO DE DATOS

     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
Dave avatar Nov 12 '14 19:11 Dave
Aceptado

Bien, dos pasos para esto: el primero es escribir una función que haga la traducción que deseas. He creado un ejemplo basado en tu pseudocódigo:

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'

Es posible que desee repasar esto, pero parece funcionar: observe que el parámetro que ingresa a la función se considera un objeto Serie etiquetado como "fila".

A continuación, utilice la función de aplicación en pandas para aplicar la función, por ejemplo

df.apply(label_race, axis=1)

Tenga en cuenta el axis=1especificador, eso significa que la aplicación se realiza a nivel de fila, en lugar de a nivel de columna. Los resultados están aquí:

0           White
1        Hispanic
2           White
3           White
4           Other
5           White
6     Two Or More
7           White
8    Haw/Pac Isl.
9           White

Si está satisfecho con esos resultados, ejecútelo nuevamente y guarde los resultados en una nueva columna en su marco de datos original.

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

El marco de datos resultante se ve así (desplácese hacia la derecha para ver la nueva columna):

      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
Thomas Kimber avatar Nov 12 '2014 13:11 Thomas Kimber

Dado que este es el primer resultado de Google para 'pandas nueva columna de otros', aquí hay un ejemplo simple:

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

Si consigues el SettingWithCopyWarningpuedes hacerlo de esta manera también:

col = df.apply(lambda row: row.a + row.b, axis=1)
df = df.assign(c=col.values) # assign values to column 'c'

Fuente: https://stackoverflow.com/a/12555510/243392

Y si el nombre de tu columna incluye espacios, puedes usar una sintaxis como esta:

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

Y aquí está la documentación para aplicar y asignar .

Brian Burns avatar Oct 04 '2017 17:10 Brian Burns

Las respuestas anteriores son perfectamente válidas, pero existe una solución vectorizada, en forma de numpy.select. Esto le permite definir condiciones y luego definir resultados para esas condiciones, de manera mucho más eficiente que usar apply:


Primero, defina las condiciones:

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,
]

Ahora, defina las salidas correspondientes:

outputs = [
    'Hispanic', 'Two Or More', 'A/I AK Native', 'Asian', 'Black/AA', 'Haw/Pac Isl.', 'White'
]

Finalmente, usando 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

¿ Por qué debería numpy.selectusarse más apply? A continuación se muestran algunas comprobaciones de rendimiento:

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)

El uso numpy.selectnos brinda un rendimiento enormemente mejorado y la discrepancia solo aumentará a medida que crezcan los datos.

user3483203 avatar Nov 27 '2018 17:11 user3483203

.apply()toma una función como primer parámetro; pase la label_racefunción así:

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

No es necesario crear una función lambda para pasar una función.

Gabrielle Simard-Moore avatar Sep 18 '2017 02:09 Gabrielle Simard-Moore