Perché la funzione "Applica" dei miei Panda che fa riferimento a più colonne funziona? [chiuso]


239

Ho alcuni problemi con la funzione di applicazione di Panda, quando utilizzo più colonne con il seguente frame di dati

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

e la seguente funzione

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

Quando provo ad applicare questa funzione con:

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

Ricevo il messaggio di errore:

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

Non capisco questo messaggio, ho definito correttamente il nome.

Apprezzerei molto qualsiasi aiuto su questo problema

Aggiornare

Grazie per l'aiuto. Ho fatto davvero alcuni errori di sintassi con il codice, l'indice dovrebbe essere messo ''. Tuttavia ho ancora lo stesso problema usando una funzione più complessa come:

def my_test(a):
    cum_diff = 0
    for ix in df.index():
        cum_diff = cum_diff + (a - df['a'][ix])
    return cum_diff 

1
Evita di usare applyil più possibile. Se non sei sicuro di doverlo usare, probabilmente non lo fai. Consiglio di dare un'occhiata a Quando dovrei mai voler usare Panda Applica () nel mio codice? .
cs95,

Si tratta solo di errori di sintassi che fanno riferimento a una colonna di frame di dati e perché le funzioni hanno bisogno di argomenti. Per quanto riguarda la tua seconda domanda, la funzione my_test(a)non sa cosa dfsia dal momento che non è stata trasmessa come argomento (a meno che non dfsi supponga che sia un globale, il che sarebbe una pratica terribile). È necessario passare tutti i valori necessari all'interno di una funzione come argomenti (preferibilmente in ordine), altrimenti in quale altro modo la funzione saprebbe da dove dfviene? Inoltre, è una cattiva pratica programmare in uno spazio dei nomi disseminato di variabili globali, non si colpiranno errori come questo.
smci

Risposte:


379

Sembra che tu abbia dimenticato la ''stringa.

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

A proposito, secondo me, il modo seguente è più elegante:

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

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

Grazie, hai ragione, ho dimenticato il ''. Tuttavia ho ancora lo stesso problema con una funzione più complessa. Apprezzerei molto il tuo aiuto in tal senso. Grazie
Andy,

5
@Andy che segue [53-54] consente di applicare funzioni più complesse.
Andy Hayden,

@Andy puoi definire la tua complessa funzione come nel modo [53].
waitingkuo,

tutte le strategie applicate funzionano allo stesso modo? Sono nuovo ai panda e ho sempre trovato applicare un po 'enigmatico, ma la tua strategia in [53-54] è facile per me capire (e speriamo di ricordare) ... su un grande tavolo è veloce come l'altra forma di applicare presentato?
whytheq,

Perché la creazione di un metodo separato è considerata più elegante, anche per metodi minuscoli. Ho fatto progetti significativi in ​​Python per 7 anni, ma probabilmente non sarò mai considerato a pythonistacausa di alcune prospettive tra cui questa.
javadba,

33

Se vuoi solo calcolare (colonna a)% (colonna b), non è necessario apply, fallo direttamente:

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

16
Lo so, è solo un esempio per mostrare il mio problema nell'applicazione di una funzione a più colonne
Andy,

18

Diciamo che vogliamo applicare una funzione add5 alle colonne 'a' e 'b' di DataFrame df

def add5(x):
    return x+5

df[['a', 'b']].apply(add5)

Ricevo il seguente errore durante il tentativo dello snippet di codice. TypeError: ('must be str, not int', 'si è verificato all'indice b') puoi per favore esaminare questo.
debaonline4u,

La colonna b del tuo frame di dati è un tipo di stringa o colonna di tipo di oggetto, dovrebbe essere una colonna intera da aggiungere con un numero.
Mir_Murtaza,

Le modifiche non si applicherebbero solo dopo l'assegnazione?
S.aad,

11

Tutti i suggerimenti sopra riportati funzionano, ma se desideri che i tuoi calcoli siano più efficienti, dovresti trarre vantaggio dalle operazioni vettoriali intorpidite (come indicato qui) .

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)})

Esempio 1: looping con pandas.apply():

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

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

La corsa più lenta ha richiesto 7,49 volte di più rispetto alla più veloce. Ciò potrebbe significare che un risultato intermedio viene memorizzato nella cache. 1000 loop, meglio di 3: 481 µs per loop

Esempio 2: vettorializzare usando pandas.apply():

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

La corsa più lenta ha richiesto 458,85 volte di più rispetto alla più veloce. Ciò potrebbe significare che un risultato intermedio viene memorizzato nella cache. 10000 loop, meglio di 3: 70,9 µs per loop

Esempio 3: vettorializzare usando matrici intorpidite:

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

La corsa più lenta ha richiesto 7,98 volte di più rispetto alla più veloce. Ciò potrebbe significare che un risultato intermedio viene memorizzato nella cache. 100000 loop, meglio di 3: 6,39 µs per loop

Quindi la vettorializzazione usando array intorpiditi ha migliorato la velocità di quasi due ordini di grandezza.


I risultati cambiano ancora più drasticamente per numeri grandi, ad esempio sostituendo 6 con 10K, ottengo rispettivamente 248 ms, 332 µs, 263 µs. Quindi entrambe le soluzioni vettorializzate sono molto più vicine tra loro, ma la soluzione non vettorializzata è 1000 volte più lenta. (testato su python-3.7)
stason il

3

Questa è la stessa della soluzione precedente ma ho definito la funzione in df.apply stessa:

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

2

Ho dato il confronto di tutti e tre discussi sopra.

Utilizzando i valori

% timeit df ['value'] = df ['a']. valori% df ['c']. valori

139 µs ± 1,91 µs per ciclo (media ± deviazione standard di 7 cicli, 10000 loop ciascuno)

Senza valori

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

216 µs ± 1,86 µs per ciclo (media ± deviazione standard di 7 cicli, 1000 loop ciascuno)

Applica la funzione

% timeit df ['Value'] = df.apply (riga lambda: riga ['a']% riga ['c'], asse = 1)

474 µs ± 5,07 µs per loop (media ± deviazione standard di 7 cicli, 1000 loop ciascuno)

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.