2016-02-03 14 views
5

Ho un grande dataframe df (~ 100 colonne e ~ 7 milioni di righe) e ho bisogno di creare ~ 50 nuove variabili/colonne che sono semplici trasformazioni delle variabili attuali. Un modo di procedere sarebbe stato con molte .apply affermazioni (sto usando solo transform* come segnaposto per le trasformazioni semplici come max o squadratura):La maggior parte del modo Pythonic per creare molte nuove colonne in panda

df['new_var1'] = df['old_var1'].apply(lambda x : transform1(x)) 
... 
df['new_var50'] = df['old_var50'].apply(lambda x : transform50(x)) 

Un altro modo sarebbe quello di creare prima un dizionario

transform_dict = { 
'new_var1' : lambda row : transform1(row), 
..., 
'new_var50' : lambda row : transform50(row) 
} 

e poi scrivere una .apply combinato con .concat:

df = pd.concat([df, 
    df.apply(lambda r: pd.Series({var : transform_dict[var](r) for var in transform_dict.keys()}), axis=1)], axis=1) 

I Un metodo preferito rispetto all'altro, sia per quanto "Pythonic" sia, per efficienza, scalabilità, flessibilità?

+0

ci si aspetterebbe la seconda forma di essere più efficiente come primo metodo è iterativamente incrementando il df che significa molte riassegnazioni di memoria e la copia di dati come df cresce – EdChum

+0

parte: 'applicare (riga lambda: transform1 (row)) 'è solo un modo più lento per scrivere' apply (transform1) '. – DSM

risposta

2

partire con:

df = pd.DataFrame(np.random.random((1000, 100))) 

Aggiunta di singole colonne:

def cols_via_apply(df): 
    for i in range(100, 150): 
     df[i] = df[i-100].apply(lambda x: x * i) 
    return df 

%timeit cols_via_apply(df) 

10 loops, best of 3: 29.6 ms per loop 

<class 'pandas.core.frame.DataFrame'> 
Int64Index: 1000 entries, 0 to 999 
Columns: 150 entries, 0 to 149 
dtypes: float64(150) 
memory usage: 1.2 MB 
None 

sembra un po 'più efficiente rispetto all'utilizzo di pd.concat - presumibilmente perché c'è un loop all'interno di rows del DataFrame coinvolti. Così questa differenza peggiorerà come il DataFrame ottiene più:

def cols_via_concat(df): 
    df = pd.concat([df, df.apply(lambda row: pd.Series({i : i * row[i-100] for i in range(100, 150)}), axis=1)]) 
    return df 


%timeit cols_via_concat(df) 

1 loops, best of 3: 450 ms per loop 

<class 'pandas.core.frame.DataFrame'> 
Int64Index: 1000 entries, 0 to 999 
Columns: 150 entries, 0 to 149 
dtypes: float64(150) 
memory usage: 1.2 MB 
None 
2

Continuando l'esperimento di @Stefan, ma con una dimensione di 100k x 100 e con un nuovo metodo che prima alloca un blocco di Nans e concatena al dataframe. Quindi utilizza iloc per eseguire calcoli su ciascuna colonna.

def cols_via_iloc(df): 
    df = pd.concat([df, pd.DataFrame(np.tile(np.nan, [len(df), 50]))], axis=1) 
    for i in range(100, 150): 
     df.iloc[:, i] = i * df.iloc[:, i - 100] 

def cols_via_apply(df): 
    for i in range(100, 150): 
     df[i] = df[i-100].apply(lambda x: x * i) 
    return df 

def cols_via_concat(df): 
    df = pd.concat([df, df.apply(lambda row: pd.Series({i : i * row[i - 100] 
                 for i in range(100, 150)}), axis=1)]) 
    return df 

>>> %%timeit df = pd.DataFrame(np.random.randn(100000, 100)) 
    cols_via_iloc(df) 
1 loops, best of 3: 540 ms per loop 

>>> %%timeit df = pd.DataFrame(np.random.randn(100000, 100)) 
    cols_via_apply(df) 
1 loops, best of 3: 2.91 s per loop 

>>> %%timeit df = pd.DataFrame(np.random.randn(100000, 100)) 
    cols_via_concat(df) 
1 loops, best of 3: 55.8 s per loop 
Problemi correlati