2016-02-03 9 views
5

Mam dużą ramkę danych df (~ 100 kolumn i ~ 7 milionów wierszy) i potrzebuję utworzyć ~ 50 nowych zmiennych/kolumn, które są prostymi przekształceniami bieżących zmiennych. Jednym ze sposobów, aby przejść byłoby z wielu .apply sprawozdania (jestem tylko przy użyciu transform* jako zastępczy dla prostych przekształceń, takich jak max lub płytowe):Najbardziej Pythoniczny sposób tworzenia wielu nowych kolumn w pandach

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

Innym sposobem byłoby najpierw utworzyć słownik

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

i napisać .apply połączeniu z .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 s jedna metoda preferowana w stosunku do drugiej, czy to "Python", czy efektywność, skalowalność, elastyczność?

+0

spodziewałbym druga forma jest bardziej efektywne jak pierwsza metoda jest iteracyjnie zwiększając DF która będzie oznaczać wiele realokacje pamięci i kopiowanie danych, jak DF rośnie – EdChum

+0

marginesie: 'Zastosuj (lambda wiersz: transform1 (row)) 'jest po prostu wolniejszym sposobem zapisu' apply (transform1) '. – DSM

Odpowiedz

2

Począwszy:

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

Dodawanie poszczególne kolumny:

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 

wydaje się dość nieco bardziej wydajny niż przy użyciu pd.concat - przypuszczalnie dlatego, że jest to pętla nad rows z DataFrame zaangażowanych. Więc ta różnica będzie jeszcze gorzej jak DataFrame wydłuża:

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

Kontynuując eksperyment @Stefan ale o rozmiarze 100k x 100 oraz z nową metodę, że najpierw przydziela blok Nans i skleja je do ramka danych. Następnie używa iloc do wykonywania obliczeń w każdej kolumnie.

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 
Powiązane problemy