2014-05-10 15 views
20

Ho un DataFrame panda, df_test. Contiene una "dimensione" della colonna che rappresenta le dimensioni in byte. Ho calcolato KB, MB e GB utilizzando il seguente codice:Restituisce più colonne da applicare i panda

df_test = pd.DataFrame([ 
    {'dir': '/Users/uname1', 'size': 994933}, 
    {'dir': '/Users/uname2', 'size': 109338711}, 
]) 

df_test['size_kb'] = df_test['size'].astype(int).apply(lambda x: locale.format("%.1f", x/1024.0, grouping=True) + ' KB') 
df_test['size_mb'] = df_test['size'].astype(int).apply(lambda x: locale.format("%.1f", x/1024.0 ** 2, grouping=True) + ' MB') 
df_test['size_gb'] = df_test['size'].astype(int).apply(lambda x: locale.format("%.1f", x/1024.0 ** 3, grouping=True) + ' GB') 

df_test 


      dir  size  size_kb size_mb size_gb 
0 /Users/uname1  994933  971.6 KB 0.9 MB 0.0 GB 
1 /Users/uname2 109338711 106,776.1 KB 104.3 MB 0.1 GB 

[2 rows x 5 columns] 

Ho eseguito questo oltre 120.000 file e il tempo necessario circa 2,97 secondi per colonna * 3 = ~ 9 secondi in base alla% timeit.

C'è comunque posso farlo più veloce? Per esempio, posso invece restituire una colonna alla volta da applicare ed eseguirla 3 volte, posso restituire tutte e tre le colonne in un unico passaggio per inserirle di nuovo nel dataframe originale?

Le altre domande che ho trovato tutte vogliono prendere più valori e restituire un singolo valore. Voglio prendere un singolo valore e restituire più colonne.

risposta

27

Questa è una vecchia domanda, ma per completezza, puoi restituire una Serie dalla funzione applicata che contiene i nuovi dati, impedendo la necessità di ripetere tre volte. Passando alla funzione apply axis=1 si applica la funzione sizes a ciascuna riga del dataframe, restituendo una serie da aggiungere a un nuovo dataframe. Questa serie, s, contiene i nuovi valori, così come i dati originali.

def sizes(s): 
    s['size_kb'] = locale.format("%.1f", s['size']/1024.0, grouping=True) + ' KB' 
    s['size_mb'] = locale.format("%.1f", s['size']/1024.0 ** 2, grouping=True) + ' MB' 
    s['size_gb'] = locale.format("%.1f", s['size']/1024.0 ** 3, grouping=True) + ' GB' 
    return s 

df_test = df_test.append(rows_list) 
df_test = df_test.apply(sizes, axis=1) 
+2

Sono sorpreso che abbia trascorso quasi 2 anni senza la risposta giusta. Stavo cercando qualcos'altro e sono inciampato su questo. Spero che non sia troppo tardi per essere utile! – Nelz11

0

Generalmente, per tornare più valori, questo è quello che faccio

def gimmeMultiple(group): 
    x1 = 1 
    x2 = 2 
    return array([[1, 2]]) 
def gimmeMultipleDf(group): 
    x1 = 1 
    x2 = 2 
    return pd.DataFrame(array([[1,2]]), columns=['x1', 'x2']) 
df['size'].astype(int).apply(gimmeMultiple) 
df['size'].astype(int).apply(gimmeMultipleDf) 

Tornando un dataframe definitivamente ha i suoi vantaggi, ma a volte non richiesto. Puoi vedere cosa restituisce e giocare un po 'con le funzioni;)

+0

Grazie per questo campione. Tuttavia, questo non produce un singolo dataframe per tutti i risultati. Quando provo ad aggiungerlo al dataframe originale, ottengo "ValueError: array non è broadcastable per correggere la forma". – PaulMest

+0

È possibile fornire il codice per produrre un campione di dati di piccole dimensioni? – FooBar

+0

Sicuro. Ho appena aggiornato il codice nel mio post originale per includere dati di esempio e output. – PaulMest

0

Utilizzare apply e zip saranno 3 volte più veloci rispetto al modo Serie.

def sizes(s):  
    return locale.format("%.1f", s/1024.0, grouping=True) + ' KB', \ 
     locale.format("%.1f", s/1024.0 ** 2, grouping=True) + ' MB', \ 
     locale.format("%.1f", s/1024.0 ** 3, grouping=True) + ' GB' 
df_test['size_kb'], df_test['size_mb'], df_test['size_gb'] = zip(*df_test['size'].apply(sizes)) 

Risultato del test sono:

Separate df.apply(): 

    100 loops, best of 3: 1.43 ms per loop 

Return Series: 

    100 loops, best of 3: 2.61 ms per loop 

Return tuple: 

    1000 loops, best of 3: 819 µs per loop 
0

Alcune delle risposte attuali funzionano bene, ma voglio offrire un altro, forse più "pandifyed" opzione. Questo funziona per me con gli attuali panda 0,22 (non sono sicuro se funzionerà nelle versioni precedenti):

import pandas as pd 

df_test = pd.DataFrame([ 
    {'dir': '/Users/uname1', 'size': 994933}, 
    {'dir': '/Users/uname2', 'size': 109338711}, 
]) 

def sizes(s): 
    a = locale.format("%.1f", s['size']/1024.0, grouping=True) + ' KB' 
    b = locale.format("%.1f", s['size']/1024.0 ** 2, grouping=True) + ' MB' 
    c = locale.format("%.1f", s['size']/1024.0 ** 3, grouping=True) + ' GB' 
    return a, b, c 

df_test[['size_kb', 'size_mb', 'size_gb']] = df_test.apply(sizes, axis=1, result_type="expand") 

Si noti che il trucco è sul parametro result_type di apply, che si espanderà il suo risultato in un DataFrame che può essere assegnato direttamente a colonne nuove/vecchie.

Problemi correlati