8

Tôi có một chức năng xử lý một DataFrame, phần lớn để xử lý dữ liệu thành các nhóm tạo một ma trận nhị phân các tính năng trong một cột cụ thể bằng cách sử dụng pd.get_dummies(df[col]).Tại sao ghép các DataFrames lại chậm hơn theo cấp số nhân?

Để tránh xử lý tất cả các dữ liệu của tôi sử dụng chức năng này cùng một lúc (mà đi ra khỏi bộ nhớ và gây ipython sụp đổ), tôi đã phá vỡ DataFrame lớn thành nhiều phần bằng:

chunks = (len(df)/10000) + 1 
df_list = np.array_split(df, chunks) 

pd.get_dummies(df) sẽ tự động tạo các cột mới dựa trên nội dung của df[col] và các cột này có thể khác nhau cho mỗi df trong df_list.

Sau khi xử lý, tôi concatenating DataFrames trở lại với nhau sử dụng:

for i, df_chunk in enumerate(df_list): 
    print "chunk", i 
    [x, y] = preprocess_data(df_chunk) 
    super_x = pd.concat([super_x, x], axis=0) 
    super_y = pd.concat([super_y, y], axis=0) 
    print datetime.datetime.utcnow() 

Thời gian xử lý các đoạn đầu tiên là hoàn toàn chấp nhận được, tuy nhiên, nó phát triển mỗi đoạn! Điều này không liên quan đến số preprocess_data(df_chunk) vì không có lý do gì để nó tăng lên. Sự gia tăng này có xảy ra do cuộc gọi đến số pd.concat() không?

Hãy xem đăng nhập dưới đây:

chunks 6 
chunk 0 
2016-04-08 00:22:17.728849 
chunk 1 
2016-04-08 00:22:42.387693 
chunk 2 
2016-04-08 00:23:43.124381 
chunk 3 
2016-04-08 00:25:30.249369 
chunk 4 
2016-04-08 00:28:11.922305 
chunk 5 
2016-04-08 00:32:00.357365 

Có một cách giải quyết để tăng tốc độ này lên? Tôi có 2900 khối để xử lý vì vậy bất kỳ trợ giúp được đánh giá cao!

Mở với bất kỳ đề xuất nào khác bằng Python!

Trả lời

8

Không bao giờ gọi DataFrame.append hoặc pd.concat bên trong một vòng lặp for. Nó dẫn đến sao chép bậc hai.

pd.concat trả về một DataFrame mới. Không gian phải được cấp cho DataFrame mới và dữ liệu từ các Khung dữ liệu cũ phải được sao chép vào DataFrame mới. Xem xét số lượng sao chép theo yêu cầu của dòng này bên trong for-loop (giả sử mỗi x có kích thước 1):

super_x = pd.concat([super_x, x], axis=0) 

| iteration | size of old super_x | size of x | copying required | 
|   0 |     0 |   1 |    1 | 
|   1 |     1 |   1 |    2 | 
|   2 |     2 |   1 |    3 | 
|  ... |      |   |     | 
|  N-1 |     N-1 |   1 |    N | 

1 + 2 + 3 + ... + N = N(N-1)/2. Vì vậy, cần có O(N**2) bản sao để hoàn thành vòng lặp.

Bây giờ xem xét

super_x = [] 
for i, df_chunk in enumerate(df_list): 
    [x, y] = preprocess_data(df_chunk) 
    super_x.append(x) 
super_x = pd.concat(super_x, axis=0) 

Phụ thêm vào một danh sách là một hoạt động O(1) và không cần sao chép. Bây giờ có một cuộc gọi đến pd.concat sau khi vòng lặp hoàn tất. Cuộc gọi này đến pd.concat yêu cầu phải có N bản sao, kể từ super_x chứa N DataFrames kích thước 1. Vì vậy, khi được xây dựng theo cách này, super_x yêu cầu O(N) bản sao.

+0

Hi @unutbu, cảm ơn lời giải thích chi tiết, điều này thực sự giải thích lý thuyết chi tiết! – jfive

+0

Có khả thi nối được 2900 khối hình dạng này theo cách này (43717, 3261) không? Bước xử lý hiện chỉ mất 10 giây. – jfive

4

Mỗi lần bạn nối, bạn sẽ trả lại bản sao dữ liệu.

Bạn muốn giữ một danh sách các đoạn của bạn, và sau đó nối tất cả mọi thứ như là bước cuối cùng.

df_x = [] 
df_y = [] 
for i, df_chunk in enumerate(df_list): 
    print "chunk", i 
    [x, y] = preprocess_data(df_chunk) 
    df_x.append(x) 
    df_y.append(y) 

super_x = pd.concat(df_x, axis=0) 
del df_x # Free-up memory. 
super_y = pd.concat(df_y, axis=0) 
del df_y # Free-up memory. 
+0

Cảm ơn rất nhiều, điều này đã khắc phục được sự cố! – jfive

Các vấn đề liên quan