¿Por qué no funciona mi función 'aplicar' de Pandas que hace referencia a múltiples columnas? [cerrado]

239

Tengo algunos problemas con la función de aplicación Pandas, cuando uso varias columnas con el siguiente marco de datos

df = DataFrame ({'a' : np.random.randn(6),
                 'b' : ['foo', 'bar'] * 3,
                 'c' : np.random.randn(6)})

y la siguiente función

def my_test(a, b):
    return a % b

Cuando trato de aplicar esta función con:

df['Value'] = df.apply(lambda row: my_test(row[a], row[c]), axis=1)

Me sale el mensaje de error:

NameError: ("global name 'a' is not defined", u'occurred at index 0')

No entiendo este mensaje, definí el nombre correctamente.

Agradecería cualquier ayuda sobre este tema

Actualizar

Gracias por tu ayuda. Realmente cometí algunos errores de sintaxis con el código, el índice debería ser puesto ''. Sin embargo, sigo teniendo el mismo problema usando una función más compleja como:

def my_test(a):
    cum_diff = 0
    for ix in df.index():
        cum_diff = cum_diff + (a - df['a'][ix])
    return cum_diff 
Andy
fuente
1
Evite usar applytanto como sea posible. Si no está seguro de que necesita usarlo, probablemente no. Recomiendo echar un vistazo a ¿ Cuándo debería utilizar pandas apply () en mi código? .
cs95
Esto se trata solo de errores de sintaxis que hacen referencia a una columna de marco de datos, y por qué las funciones necesitan argumentos. En cuanto a su segunda pregunta, la función my_test(a)no sabe qué dfes, ya que no se pasó como argumento (a menos que dfse suponga que es global, lo que sería una práctica terrible). Debe pasar todos los valores que necesitará dentro de una función como argumentos (preferiblemente en orden), de lo contrario, ¿de qué otra manera la función sabría de dónde dfviene? Además, es una mala práctica programar en un espacio de nombres lleno de variables globales, no detectará errores como este.
smci

Respuestas:

379

Parece que olvidaste el ''de tu cuerda.

In [43]: df['Value'] = df.apply(lambda row: my_test(row['a'], row['c']), axis=1)

In [44]: df
Out[44]:
                    a    b         c     Value
          0 -1.674308  foo  0.343801  0.044698
          1 -2.163236  bar -2.046438 -0.116798
          2 -0.199115  foo -0.458050 -0.199115
          3  0.918646  bar -0.007185 -0.001006
          4  1.336830  foo  0.534292  0.268245
          5  0.976844  bar -0.773630 -0.570417

Por cierto, en mi opinión, la siguiente forma es más elegante:

In [53]: def my_test2(row):
....:     return row['a'] % row['c']
....:     

In [54]: df['Value'] = df.apply(my_test2, axis=1)
waitingkuo
fuente
Gracias, tienes razón, olvidé el ''. Sin embargo, todavía tengo el mismo problema con una función más compleja. Agradecería mucho su ayuda con eso. Gracias
Andy
55
@Andy siguiendo [53-54] le permite aplicar funciones más complejas.
Andy Hayden
@Andy puedes definir tu función compleja de la manera In [53].
waitingkuo
¿todas las estrategias aplicadas funcionan igual? Soy nuevo en los pandas y siempre he encontrado que aplicar es ligeramente enigmático, pero su estrategia en [53-54] es fácil de entender (y espero recordar) ... en una mesa grande es tan rápido como la otra forma de aplicar presentado?
whytheq
¿Por qué es que crear un método separado se considera más elegante, incluso para métodos pequeños? He estado haciendo proyectos importantes en Python durante 7 años, pero probablemente nunca se considerará pythonistadebido a algunas perspectivas, incluida esta.
javadba
33

Si solo desea calcular (columna a)% (columna b), no necesita apply, simplemente hágalo directamente:

In [7]: df['a'] % df['c']                                                                                                                                                        
Out[7]: 
0   -1.132022                                                                                                                                                                    
1   -0.939493                                                                                                                                                                    
2    0.201931                                                                                                                                                                    
3    0.511374                                                                                                                                                                    
4   -0.694647                                                                                                                                                                    
5   -0.023486                                                                                                                                                                    
Name: a
herrfz
fuente
16
Lo sé, es solo un ejemplo para mostrar mi problema al aplicar una función a varias columnas
Andy
18

Digamos que queremos aplicar una función add5 a las columnas 'a' y 'b' de DataFrame df

def add5(x):
    return x+5

df[['a', 'b']].apply(add5)
Mir_Murtaza
fuente
Recibo el siguiente error al probar el fragmento de código. TypeError: ('debe ser str, no int', 'ocurrió en el índice b') ¿puede investigar eso?
debaonline4u
La columna b de su marco de datos es una columna de tipo cadena o tipo de objeto, debe ser una columna entera que se agregará con un número.
Mir_Murtaza
¿No se aplicarían los cambios solo después de la asignación?
S.aad
11

Todas las sugerencias anteriores funcionan, pero si desea que sus cálculos sean más eficientes, debe aprovechar las operaciones de vectores numpy (como se señala aquí) .

import pandas as pd
import numpy as np


df = pd.DataFrame ({'a' : np.random.randn(6),
             'b' : ['foo', 'bar'] * 3,
             'c' : np.random.randn(6)})

Ejemplo 1: bucle con pandas.apply():

%%timeit
def my_test2(row):
    return row['a'] % row['c']

df['Value'] = df.apply(my_test2, axis=1)

La carrera más lenta tomó 7.49 veces más que la más rápida. Esto podría significar que se está almacenando en caché un resultado intermedio. 1000 bucles, lo mejor de 3: 481 µs por bucle

Ejemplo 2: vectorizar usando pandas.apply():

%%timeit
df['a'] % df['c']

La carrera más lenta tardó 458.85 veces más que la más rápida. Esto podría significar que se está almacenando en caché un resultado intermedio. 10000 bucles, lo mejor de 3: 70.9 µs por bucle

Ejemplo 3: vectorizar usando matrices numpy:

%%timeit
df['a'].values % df['c'].values

La carrera más lenta tomó 7.98 veces más que la más rápida. Esto podría significar que se está almacenando en caché un resultado intermedio. 100000 bucles, lo mejor de 3: 6,39 µs por bucle

Por lo tanto, la vectorización con matrices numpy mejoró la velocidad en casi dos órdenes de magnitud.

Blane
fuente
Los resultados cambian aún más dramáticamente para grandes números, por ejemplo, al reemplazar 6 con 10K, obtengo 248 ms, 332 µs, 263 µs respectivamente. Entonces, ambas soluciones vectorizadas están mucho más cerca una de la otra, pero la solución no vectorizada es 1000 veces más lenta. (probado en python-3.7)
stason
3

Esto es lo mismo que la solución anterior, pero he definido la función en df.

df['Value'] = df.apply(lambda row: row['a']%row['c'], axis=1)
shaurya airi
fuente
2

He dado la comparación de los tres discutidos anteriormente.

Usando valores

% timeit df ['valor'] = df ['a']. valores% df ['c']. valores

139 µs ± 1.91 µs por bucle (media ± desviación estándar de 7 corridas, 10000 bucles cada una)

Sin valores

% timeit df ['valor'] = df ['a']% df ['c'] 

216 µs ± 1.86 µs por ciclo (media ± estándar de desarrollo de 7 carreras, 1000 ciclos cada una)

Aplicar función

% timeit df ['Valor'] = df.apply (fila lambda: fila ['a']% fila ['c'], eje = 1)

474 µs ± 5.07 µs por ciclo (media ± estándar de desarrollo de 7 carreras, 1000 ciclos cada una)

Gursewak Singh
fuente