2017-10-10 1 views
13

Dies ist eine Folgefrage an this one, wo jezrael pandas.DataFrame.groupby verwendet, um die Geschwindigkeit einer Listenerstellung um einen Faktor von einigen hundert zu erhöhen. Insbesondere lassen df eine große Datenrahmen ist, dannWarum ist groupby so schnell?

index = list(set(df.index)) 
list_df = [df.loc(x) for x in index] 

und

list_df = [x for i,x in df.groupby(level=0, sort=False)] 

das gleiche Ergebnis produzieren, wobei letztere mehr als 200-mal schneller als die ersteren, auch die Listenerstellung Schritt ignorieren. Warum?

Ich wäre sehr froh, wenn mich jemand verstehen könnte, warum es so einen gewaltigen Leistungsunterschied gibt. Danke im Voraus!

Bearbeiten: wie von Alex Riley in seinem Kommentar vorgeschlagen, bestätige ich, dass die Tests auf einem Datenrahmen mit nicht eindeutigen und nicht-monotonen Index ausgeführt wurden.

+0

ich das glauben, wie viel interessanter als das ist der Grund. –

+2

Es sieht so aus, als hätten Sie einen nicht eindeutigen Index, möglicherweise auch nicht monoton. In solchen degenerierten Fällen, glaube ich, muss Pandas bei jedem Aufruf von "loc" über den * gesamten * Index iterieren, um ein neues Array (mit der gleichen Länge wie der Index) für boolesche Indexierung zu erstellen. OTOH, 'groupby' durchsucht den Index nur einmal und verfolgt die ganzzahligen Positionen für jedes Etikett. Ich müsste alles in der Quelle überprüfen, um sicher zu gehen. –

Antwort

5

Da Ihre Datenrahmen nicht auf dem Index sortiert ist, was bedeutet, alle subsetting mit langsamem Vektor-Scan und schnellen Algorithmus wie binäre Suche durchgeführt werden muss nicht angewandt werden können; Während groupby immer den Datenrahmen von der Gruppe durch variable sortieren zuerst, können Sie dieses Verhalten nachahmen durch einen einfachen Algorithmus zu schreiben, die den Index sortieren und dann diese zu validieren Teilmenge:

def sort_subset(df): 
    # sort index and find out the positions that separate groups 
    df = df.sort_index() 
    split_indices = np.flatnonzero(np.ediff1d(df.index, to_begin=1, to_end=1)) 
    list_df = [] 
    for i in range(len(split_indices)-1): 
     start_index = split_indices[i] 
     end_index = split_indices[i+1] 
     list_df.append(df.iloc[start_index:end_index]) 
    return list_df 

Einige Timing:

import pandas as pd 
import numpy as np 
​ 
nrow = 1000000 
df = pd.DataFrame(np.random.randn(nrow), columns=['x'], index=np.random.randint(100, size=nrow)) 

index = list(set(df.index)) 
print('no of groups: ', len(index)) 
​ 
%timeit list_df_1 = [df.loc[x] for x in index] 
#no of groups: 100 
#13.6 s ± 228 ms per loop (mean ± std. dev. of 7 runs, 1 loop each) 

%timeit list_df_2 = [x for i, x in df.groupby(level=0, sort=False)] 
#54.8 ms ± 1.36 ms per loop (mean ± std. dev. of 7 runs, 10 loops each) 

# Not as fast because my algorithm is not optimized at all but the same order of magnitude 
%timeit list_df_3 = sort_subset(df) 
#102 ms ± 3.53 ms per loop (mean ± std. dev. of 7 runs, 10 loops each) 

list_df_1 = [df.loc[x] for x in index] 
list_df_2 = [x for i, x in df.groupby(level=0, sort=False)] 
list_df_3 = sort_subset(df) 

Vergleichen Sie das Ergebnis:

Sie sehen eine signifikante Geschwindigkeit, wenn Sie den Index sortieren, bevor auch subsetting:

def sort_subset_with_loc(df): 
    df = df.sort_index() 
    list_df_1 = [df.loc[x] for x in index] 
    return list_df_1 

%timeit sort_subset_with_loc(df) 
# 25.4 ms ± 897 µs per loop (mean ± std. dev. of 7 runs, 10 loops each) 
Verwandte Themen