Differenza tra mappa, metodo di applicazione e metodi di applicazione in Panda


468

Puoi dirmi quando usare questi metodi di vettorializzazione con esempi di base?

Vedo che mapè un Seriesmetodo mentre il resto sono DataFramemetodi. Mi sono confuso applye applymapmetodi però. Perché abbiamo due metodi per applicare una funzione a un DataFrame? Ancora una volta, semplici esempi che illustrano l'uso sarebbero fantastici!


5
Correggimi se sbaglio, ma credo che quelle funzioni non vettorizzino i metodi in quanto implicano tutti un ciclo sugli elementi su cui sono applicati.
Tanguy,

Risposte:


534

Direttamente dal libro Python for Data Analysis di Wes McKinney , pag. 132 (consiglio vivamente questo libro):

Un'altra operazione frequente è l'applicazione di una funzione su array 1D a ciascuna colonna o riga. Il metodo apply di DataFrame fa esattamente questo:

In [116]: frame = DataFrame(np.random.randn(4, 3), columns=list('bde'), index=['Utah', 'Ohio', 'Texas', 'Oregon'])

In [117]: frame
Out[117]: 
               b         d         e
Utah   -0.029638  1.081563  1.280300
Ohio    0.647747  0.831136 -1.549481
Texas   0.513416 -0.884417  0.195343
Oregon -0.485454 -0.477388 -0.309548

In [118]: f = lambda x: x.max() - x.min()

In [119]: frame.apply(f)
Out[119]: 
b    1.133201
d    1.965980
e    2.829781
dtype: float64

Molte delle statistiche di array più comuni (come sum e mean) sono metodi DataFrame, quindi non è necessario l'utilizzo di apply.

Si possono usare anche funzioni Python basate sull'elemento. Supponiamo di voler calcolare una stringa formattata da ciascun valore in virgola mobile nel frame. Puoi farlo con applymap:

In [120]: format = lambda x: '%.2f' % x

In [121]: frame.applymap(format)
Out[121]: 
            b      d      e
Utah    -0.03   1.08   1.28
Ohio     0.65   0.83  -1.55
Texas    0.51  -0.88   0.20
Oregon  -0.49  -0.48  -0.31

Il motivo del nome applymap è che Series ha un metodo map per l'applicazione di una funzione basata sull'elemento:

In [122]: frame['e'].map(format)
Out[122]: 
Utah       1.28
Ohio      -1.55
Texas      0.20
Oregon    -0.31
Name: e, dtype: object

Riassumendo, applyfunziona su una riga / colonna di un DataFrame, applymapfunziona in termini di elementi su un DataFrame e mapfunziona in termini di elementi su una serie.


31
a rigor di termini, applyMap internamente è implementata tramite applicare con un po 'di parametro di funzione over passato wrap-up (rougly parlando sostituzione funca lambda x: [func(y) for y in x], e l'applicazione di colonna-saggio)
Alko

5
Grazie per la spiegazione. Dal momento che maped applymapentrambi funzionano dal punto di vista degli elementi, mi aspetterei un singolo metodo (o mapo applymap) che funzionerebbe sia per una serie che per un DataFrame. Probabilmente ci sono altre considerazioni sul design e Wes McKinney ha deciso di proporre due metodi diversi.
marillion

2
È a pagina 129 nella mia copia per qualche motivo. Non c'è etichetta per la seconda edizione o niente.
Jody,

1
C'è un modo di fare applymapinsieme alla groupbyfunzione nei panda?
everestial007,

Come applicare una funzione su dati raggruppati a colonne?
hhh,

84

Confrontando map, applymape : Matters Contestoapply

Prima grande differenza: DEFINIZIONE

  • map è definito SOLO in serie
  • applymap è definito SOLO su DataFrames
  • apply è definito in ENTRAMBI

Seconda differenza principale: INPUT ARGUMENT

  • mapaccetta dicts Series, o richiamabile
  • applymape applyaccetta solo callable

Terza differenza principale: COMPORTAMENTO

  • map è elementwise per la serie
  • applymap è elementwise per DataFrames
  • applyfunziona anche in modo elementare ma è adatto a operazioni e aggregazioni più complesse. Il comportamento e il valore restituito dipendono dalla funzione.

Quarta differenza principale (la più importante): USA IL CASO

  • mapè pensato per mappare i valori da un dominio all'altro, quindi è ottimizzato per le prestazioni (ad es. df['A'].map({1:'a', 2:'b', 3:'c'}))
  • applymapè buono per trasformazioni elementally su più righe / colonne (ad es. df[['A', 'B', 'C']].applymap(str.strip))
  • applyserve per applicare qualsiasi funzione che non può essere vettorializzata (ad es. df['sentences'].apply(nltk.sent_tokenize))

Riassumendo

inserisci qui la descrizione dell'immagine

Le note

  1. mapquando passato un dizionario / serie mapperà gli elementi in base alle chiavi di quel dizionario / serie. I valori mancanti verranno registrati come NaN nell'output.
  2. applymapnelle versioni più recenti è stato ottimizzato per alcune operazioni. Lo troverai applymapleggermente più veloce che applyin alcuni casi. Il mio suggerimento è di testarli entrambi e usare qualunque cosa funzioni meglio.

  3. mapè ottimizzato per mappature e trasformazioni elementwise. Le operazioni che coinvolgono dizionari o serie consentiranno ai panda di utilizzare percorsi di codice più veloci per prestazioni migliori.

  4. Series.applyrestituisce uno scalare per operazioni di aggregazione, altrimenti Serie. Allo stesso modo per DataFrame.apply. Notare che applyha anche fastpaths quando chiamato con alcune funzioni NumPy quali mean, sum, etc.

70

Ci sono ottime informazioni in queste risposte, ma sto aggiungendo le mie per riassumere chiaramente quali metodi funzionano in termini di array e di elementi. jeremiahbuddha per lo più ha fatto questo, ma non ha menzionato Series.apply. Non ho il rappresentante per commentare.

  • DataFrame.apply opera su intere righe o colonne alla volta.

  • DataFrame.applymap, Series.applye Series.mapoperare su un elemento alla volta.

C'è molta sovrapposizione tra le capacità di Series.applye Series.map, nel senso che uno dei due funzionerà nella maggior parte dei casi. Tuttavia presentano alcune lievi differenze, alcune delle quali sono state discusse nella risposta di osa.


38

Aggiungendo alle altre risposte, in un Seriesci sono anche mappa e applicare .

Applicare può creare un DataFrame da una serie ; tuttavia, map inserirà semplicemente una serie in ogni cella di un'altra serie, che probabilmente non è ciò che desideri.

In [40]: p=pd.Series([1,2,3])
In [41]: p
Out[31]:
0    1
1    2
2    3
dtype: int64

In [42]: p.apply(lambda x: pd.Series([x, x]))
Out[42]: 
   0  1
0  1  1
1  2  2
2  3  3

In [43]: p.map(lambda x: pd.Series([x, x]))
Out[43]: 
0    0    1
1    1
dtype: int64
1    0    2
1    2
dtype: int64
2    0    3
1    3
dtype: int64
dtype: object

Inoltre, se avessi una funzione con effetti collaterali, come "connettersi a un server Web", probabilmente userei applysolo per motivi di chiarezza.

series.apply(download_file_for_every_element) 

Mappuò usare non solo una funzione, ma anche un dizionario o un'altra serie. Diciamo che vuoi manipolare le permutazioni .

Prendere

1 2 3 4 5
2 1 4 5 3

Il quadrato di questa permutazione è

1 2 3 4 5
1 2 5 3 4

Puoi calcolarlo usando map. Non sono sicuro che l'auto-applicazione sia documentata, ma funziona 0.15.1.

In [39]: p=pd.Series([1,0,3,4,2])

In [40]: p.map(p)
Out[40]: 
0    0
1    1
2    4
3    2
4    3
dtype: int64

3
Inoltre, .apply () ti permette di passare a kwargs nella funzione mentre .map () no.
Neilxdims,

19

@jeremiahbuddha ha menzionato che apply funziona su righe / colonne, mentre applymap funziona in termini di elementi. Ma sembra che tu possa ancora usare applicare per il calcolo in base agli elementi ....

    frame.apply(np.sqrt)
    Out[102]: 
                   b         d         e
    Utah         NaN  1.435159       NaN
    Ohio    1.098164  0.510594  0.729748
    Texas        NaN  0.456436  0.697337
    Oregon  0.359079       NaN       NaN

    frame.applymap(np.sqrt)
    Out[103]: 
                   b         d         e
    Utah         NaN  1.435159       NaN
    Ohio    1.098164  0.510594  0.729748
    Texas        NaN  0.456436  0.697337
    Oregon  0.359079       NaN       NaN

29
Buona cattura con questo. Il motivo per cui funziona nel tuo esempio è perché np.sqrt è un ufunc, cioè se gli dai un array, trasmetterà la funzione sqrt su ogni elemento dell'array. Quindi, quando si applica push np.sqrt su ogni colonna, np.sqrt funziona da solo su ciascuno degli elementi delle colonne, quindi si ottiene sostanzialmente lo stesso risultato di applymap.
jeremiahbuddha,

11

Volevo solo sottolineare, mentre ho lottato con questo per un po '

def f(x):
    if x < 0:
        x = 0
    elif x > 100000:
        x = 100000
    return x

df.applymap(f)
df.describe()

questo non modifica il frame di dati stesso, deve essere riassegnato

df = df.applymap(f)
df.describe()

1
A volte ho difficoltà a capire se devi riassegnare o meno dopo aver fatto qualcosa con il df. È principalmente prova ed errore per me, ma scommetto che c'è una logica su come funziona (che mi sto perdendo).
marillion

2
in generale, un frame di dati Panda viene modificato solo mediante riassegnazione df = modified_dfo se si imposta inplace=Trueflag. Anche il frame di dati cambierà se si passa un frame di dati a una funzione per riferimento e la funzione modifica il frame di dati
muon

1
Questo non è del tutto vero, pensa .ixo .whereecc. Non sei sicuro di quale sia la spiegazione completa per quando devi riassegnare e quando no.
Thanos,

10

Spiegazione probabilmente più semplice della differenza tra apply e applymap:

applica accetta l'intera colonna come parametro e quindi assegna il risultato a questa colonna

applymap accetta il valore di cella separato come parametro e assegna il risultato a questa cella.

NB Se si applica restituisce il valore singolo si avrà questo valore invece della colonna dopo l'assegnazione e alla fine si avrà solo una riga anziché matrice.


3

La mia comprensione:

Dal punto di vista della funzione:

Se la funzione ha variabili che devono essere confrontate all'interno di una colonna / riga, utilizzare apply.

ad es lambda x: x.max()-x.mean(). : .

Se la funzione deve essere applicata a ciascun elemento:

1> Se si trova una colonna / riga, utilizzare apply

2> Se si applicano all'intero frame di dati, utilizzare applymap

majority = lambda x : x > 17
df2['legal_drinker'] = df2['age'].apply(majority)

def times10(x):
  if type(x) is int:
    x *= 10 
  return x
df2.applymap(times10)

Fornisci df2 anche per una migliore chiarezza in modo che possiamo testare il tuo codice.
Ashish Anand,

1

Basato sulla risposta di cs95

  • map è definito SOLO in serie
  • applymap è definito SOLO su DataFrames
  • apply è definito in ENTRAMBI

fai alcuni esempi

In [3]: frame = pd.DataFrame(np.random.randn(4, 3), columns=list('bde'), index=['Utah', 'Ohio', 'Texas', 'Oregon'])

In [4]: frame
Out[4]:
            b         d         e
Utah    0.129885 -0.475957 -0.207679
Ohio   -2.978331 -1.015918  0.784675
Texas  -0.256689 -0.226366  2.262588
Oregon  2.605526  1.139105 -0.927518

In [5]: myformat=lambda x: f'{x:.2f}'

In [6]: frame.d.map(myformat)
Out[6]:
Utah      -0.48
Ohio      -1.02
Texas     -0.23
Oregon     1.14
Name: d, dtype: object

In [7]: frame.d.apply(myformat)
Out[7]:
Utah      -0.48
Ohio      -1.02
Texas     -0.23
Oregon     1.14
Name: d, dtype: object

In [8]: frame.applymap(myformat)
Out[8]:
            b      d      e
Utah     0.13  -0.48  -0.21
Ohio    -2.98  -1.02   0.78
Texas   -0.26  -0.23   2.26
Oregon   2.61   1.14  -0.93

In [9]: frame.apply(lambda x: x.apply(myformat))
Out[9]:
            b      d      e
Utah     0.13  -0.48  -0.21
Ohio    -2.98  -1.02   0.78
Texas   -0.26  -0.23   2.26
Oregon   2.61   1.14  -0.93


In [10]: myfunc=lambda x: x**2

In [11]: frame.applymap(myfunc)
Out[11]:
            b         d         e
Utah    0.016870  0.226535  0.043131
Ohio    8.870453  1.032089  0.615714
Texas   0.065889  0.051242  5.119305
Oregon  6.788766  1.297560  0.860289

In [12]: frame.apply(myfunc)
Out[12]:
            b         d         e
Utah    0.016870  0.226535  0.043131
Ohio    8.870453  1.032089  0.615714
Texas   0.065889  0.051242  5.119305
Oregon  6.788766  1.297560  0.860289

0

FOMO:

L'esempio seguente mostra applye si applymapapplica a a DataFrame.

mapla funzione è qualcosa che si applica solo sulla serie. Non è possibile applicare map su DataFrame.

La cosa da ricordare è che applypuò fare qualsiasi cosa applymap possibile, ma applyha opzioni eXtra .

Le opzioni del fattore X sono: axise result_typedove result_typefunziona solo quando axis=1(per colonne).

df = DataFrame(1, columns=list('abc'),
                  index=list('1234'))
print(df)

f = lambda x: np.log(x)
print(df.applymap(f)) # apply to the whole dataframe
print(np.log(df)) # applied to the whole dataframe
print(df.applymap(np.sum)) # reducing can be applied for rows only

# apply can take different options (vs. applymap cannot)
print(df.apply(f)) # same as applymap
print(df.apply(sum, axis=1))  # reducing example
print(df.apply(np.log, axis=1)) # cannot reduce
print(df.apply(lambda x: [1, 2, 3], axis=1, result_type='expand')) # expand result

Come sidenote, la mapfunzione Series , non deve essere confusa con la mapfunzione Python .

Il primo viene applicato su Serie per mappare i valori e il secondo su ogni elemento di un iterabile.


Infine, non confondere il applymetodo dataframe con il applymetodo groupby .

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.