2016-12-22 14 views
7

Rozważ ten słownik serii pand. Indeks we wszystkich seriach jest liczbami całkowitymi i ma pewne potencjalne pokrywanie się, ale z pewnością nie pokrywa się. Zrobiłem spostrzeżenie, że pd.concat wydaje się powolne, gdy łączę rzeczy wzdłuż axis=1, gdy mam duże indeksy, wiele nie nakładających się i wiele elementów do łączenia. To skłoniło mnie do opuszczenia axis=0, a następnie unstack(). Kończę z tym samym dokładnym wynikiem. Ale rozpakowywanie jest szybsze.Dlaczego pd.concat ({}, axis = 1) jest wolniejsze niż pd.concat ({}, axis = 0) .unstack (0)?

Czy ktoś ma pojęcie, dlaczego tak się dzieje?

Dostaję, że łącząca się seria jedna na drugiej powinna być szybka, ale zgadłbym, że procesy rozpakowywania byłyby prawie identyczne, jak w przypadku pd.concat(axis=1).

dict_of_series = { 
    's%s' % i: pd.Series(
     1, np.unique(np.random.randint(1000, 10000, size=1000)) 
    ) for i in range(100) 
} 

%%timeit 
pd.concat(dict_of_series, axis=0).unstack(0) 

10 pętle, najlepsze 3: 29,6 ms na pętli

%%timeit 
pd.concat(dict_of_series, axis=1) 

10 pętle, najlepiej od 3: 43,1 ms na pętli

Odpowiedz

2

Szybkie profilowanie pokazuje, że dużo czasu w rozmowie pd.concat(dict_of_series, axis=1) jest zużywane przez pd.Index.union. Jest wywoływany na każdym indeksie jeden po drugim wewnątrz wewnętrznej funkcji _union_indexes - podobnie jak w mojej funkcji union poniżej.

Z drugiej strony, pd.concat(dict_of_series, axis=0).unstack(0) korzysta z jakiejś sprytnej optymalizacji, której nie próbowałem analizować. W każdym razie nie ma tam połączenia z _union_indexes lub nawet pd.Index.union.

Czy "_union_indexes" może być winowajcą? Bardzo możliwe. Wziąłem tylko indeksy:

idxs = [d.index for d in dict_of_series.values()] 

i porównał dwa następujące funkcje:

def union(idxs): 
    ans = pd.Index([]) 
    for idx in idxs: 
     ans = ans.union(idx) 
    return ans 

def union_multi(idxs): 
    arr0 = np.concatenate(idxs) 
    arr1 = np.zeros(arr0.size) 
    ans = pd.MultiIndex.from_arrays([arr0, arr1]) 
    return ans.levels[0] 

union stanowi równowartość _union_indexes, natomiast union_multi ma pewien narzut multi-index, ale unika wezwanie do pd.Index.union.

Moi szorstkie czasy:

>>> %timeit -n1 -r1 pd.concat(dict_of_series, axis=1) 
1 loop, best of 1: 82.9 ms per loop 
>>> %timeit -n1 -r1 pd.concat(dict_of_series, axis=0).unstack(0) 
1 loop, best of 1: 57.9 ms per loop 

>>> %timeit -n1 -r1 union(idxs) 
1 loop, best of 1: 32.8 ms per loop 
>>> %timeit -n1 -r1 union_multi(idxs) 
1 loop, best of 1: 12.5 ms per loop 

Prosty pd.Index(np.unique(np.concatenate(idxs))) jest nieco szybciej, ale to nie działa w przypadku mieszanych typów.

(BTW, pd.concat działa tylko wtedy, gdy wszystkie indeksy są unikatowe.)