Подтвердить что ты не робот

Почему так быстро?

Это следующий вопрос этого, где jezrael использовал pandas.DataFrame.groupby для увеличения в сотне скорости списка создание. В частности, пусть df - большой фрейм данных, тогда

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

и

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

дает тот же результат, причем последний более чем в 200 раз быстрее первого, даже игнорируя шаг создания списка. Почему?

Я был бы очень рад, если бы кто-то позволил мне понять, почему существует такая огромная разница в производительности. Спасибо заранее!

Изменить:, как предложил Алекс Райли в своем комментарии, я подтверждаю, что тесты выполняются на фреймворке с неидеальным и немонотонным индексом.

4b9b3361

Ответ 1

Поскольку ваш кадр данных не сортируется по индексу, это означает, что все подмножество должно выполняться с помощью медленного векторного сканирования, а быстрый алгоритм, такой как бинарный поиск, не может быть применен; Пока groupby всегда сначала сортирует фрейм данных по группе по переменной, вы можете имитировать это поведение, написав простой алгоритм, который сортирует индекс, а затем подмножество, чтобы проверить это:

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

Некоторое время:

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)

Сравните результат:

all(list_df_3[i].eq(list_df_2[i]).all().iat[0] for i in range(len(list_df_2)))
# True

Вы видите значительную скорость, если сортируете индекс до подмножества:

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)