2017-10-10 23 views
13

Đây là câu hỏi tiếp theo cho this one, trong đó jezrael sử dụng pandas.DataFrame.groupby để tăng thêm một yếu tố hàng trăm tốc độ tạo danh sách. Cụ thể, chúng ta hãy df là một dataframe lớn, sau đóTại sao nhóm lại quá nhanh?

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)] 

tạo ra kết quả tương tự, với thứ hai là nhanh hơn so với trước đây hơn 200 lần, thậm chí bỏ qua bước tạo danh sách. Tại sao?

Tôi sẽ rất vui nếu có ai đó có thể cho tôi hiểu lý do tại sao có sự khác biệt lớn về hiệu suất. Cảm ơn trước!

Chỉnh sửa: theo đề xuất của Alex Riley trong nhận xét của mình, tôi xác nhận rằng các thử nghiệm đã được chạy trên một khung dữ liệu với chỉ mục không độc nhất và không đơn điệu.

+0

Tôi tin rằng thế nào là thú vị hơn lý do tại sao. –

+2

Dường như bạn có chỉ mục không phải duy nhất, có thể cũng không phải là đơn điệu. Trong những trường hợp thoái hóa như vậy, với mỗi cuộc gọi đến 'loc', tôi tin rằng gấu trúc phải lặp qua chỉ mục * toàn bộ * để xây dựng một mảng mới (có cùng độ dài với chỉ mục) để sử dụng cho việc lập chỉ mục boolean. OTOH, 'groupby' chỉ quét chỉ mục một lần và theo dõi các vị trí số nguyên cho mỗi nhãn. Tôi phải kiểm tra kỹ mọi thứ trong nguồn để chắc chắn. –

Trả lời

5

Vì khung dữ liệu của bạn không được sắp xếp trên chỉ mục, điều này có nghĩa là tất cả việc đặt trước phải được thực hiện bằng quét vector chậm và thuật toán nhanh như tìm kiếm nhị phân không thể áp dụng được; Trong khi groupby luôn sắp xếp các khung dữ liệu bằng cách nhóm bởi biến đầu tiên, bạn có thể bắt chước hành vi này bằng cách viết một thuật toán đơn giản mà sắp xếp các chỉ số và sau đó tập hợp con để xác nhận điều này:

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 

Một số thời gian:

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) 

Hãy so sánh kết quả:

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

Bạn thấy một tốc độ đáng kể lên nếu bạn sắp xếp các chỉ số trước Subsetting cũng như:

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) 
Các vấn đề liên quan