Có cách nào đơn giản để chạy pandas.DataFrame.isin song song không?


25

Tôi có một chương trình lập mô hình và tính điểm sử dụng nhiều DataFrame.isinchức năng của gấu trúc, tìm kiếm thông qua danh sách các bản ghi "thích" của từng người dùng cho mỗi một vài nghìn trang cụ thể. Đây là phần tốn nhiều thời gian nhất của chương trình, hơn cả phần mô hình hóa hay tính điểm, đơn giản vì nó chỉ chạy trên một lõi trong khi phần còn lại chạy trên vài chục đồng thời.

Mặc dù tôi biết rằng tôi có thể tự chia nhỏ khung dữ liệu thành các khối và chạy hoạt động song song, có cách nào đơn giản để thực hiện điều đó một cách tự động không? Nói cách khác, có loại gói nào ngoài đó sẽ nhận ra tôi đang chạy một hoạt động được ủy quyền dễ dàng và tự động phân phối nó không? Có lẽ đó là yêu cầu quá nhiều, nhưng tôi đã rất ngạc nhiên trong quá khứ bởi những gì đã có sẵn trong Python, vì vậy tôi cho rằng nó đáng để hỏi.

Bất kỳ đề xuất nào khác về cách thực hiện điều này (ngay cả khi không phải bằng một gói kỳ lân ma thuật nào đó!) Cũng sẽ được đánh giá cao. Chủ yếu, chỉ cần cố gắng tìm cách để tắt đi 15-20 phút mỗi lần chạy mà không mất một lượng thời gian tương đương để mã hóa giải pháp.


Làm thế nào lớn là danh sách các giá trị của bạn? Bạn đã cố gắng vượt qua nó như một bộ? Đối với song song, bạn có thể quan tâm đến Joblib. Nó rất dễ sử dụng và có thể tăng tốc tính toán. Sử dụng nó với khối dữ liệu lớn.
oao

Một lựa chọn khác là điều chỉnh lại vấn đề của bạn khi tham gia. Tham gia nhanh hơn nhiều trong Pandas stackoverflow.com/questions/23945493/
Brian Spiering

Tuy nhiên, một tùy chọn khác là sử dụng np.in1d, đây cũng là stackoverflow.com/questions/21738882/fast-pandas-filtering
Brian Spiering

Câu trả lời:


8

Thật không may, song song chưa được thực hiện trong gấu trúc. Bạn có thể tham gia vấn đề github này nếu bạn muốn tham gia phát triển tính năng này.

Tôi không biết bất kỳ "gói kỳ lân ma thuật" nào cho mục đích này, vì vậy điều tốt nhất sẽ là viết giải pháp của riêng bạn. Nhưng nếu bạn vẫn không muốn dành thời gian cho điều đó và muốn tìm hiểu một cái gì đó mới - bạn có thể thử hai phương thức được tích hợp trong MongoDB (khung giảm bản đồ và khung agg). Xem mongodb_agg_framework .




0

Có một phiên bản phổ biến hơn của câu hỏi này liên quan đến việc song song hóa chức năng áp dụng gấu trúc - vì vậy đây là một câu hỏi mới mẻ :)

Đầu tiên , tôi muốn đề cập đến swifter vì bạn đã yêu cầu giải pháp "đóng gói" và nó xuất hiện trên hầu hết các câu hỏi SO liên quan đến song song gấu trúc.

Nhưng .. Tôi vẫn muốn chia sẻ mã chính của mình cho nó, vì sau vài năm làm việc với DataFrame, tôi chưa bao giờ tìm thấy giải pháp song song 100% (chủ yếu cho chức năng áp dụng) và tôi luôn phải quay lại với " hướng dẫn sử dụng "mã.

Nhờ có bạn, tôi đã làm cho nó chung chung hơn để hỗ trợ bất kỳ phương thức DataFrame nào (về mặt lý thuyết) theo tên của nó (vì vậy bạn sẽ không phải giữ các phiên bản cho isin, áp dụng, v.v.).

Tôi đã thử nghiệm nó trên các hàm "isin", "áp dụng" và "isna" bằng cả hai python 2.7 và 3.6. Đó là dưới 20 dòng, và tôi đã theo quy ước đặt tên gấu trúc như "tập hợp con" và "njobs".

Tôi cũng đã thêm một so sánh thời gian với mã tương đương dask cho "isin" và có vẻ như ~ X2 lần chậm hơn ý chính này.

Nó bao gồm 2 chức năng:

df_multi_core - đây là người bạn gọi. Nó chấp nhận:

  1. Đối tượng df của bạn
  2. Tên hàm bạn muốn gọi
  3. Tập hợp con của cột có thể được thực hiện theo (giúp giảm thời gian / bộ nhớ)
  4. Số lượng công việc chạy song song (-1 hoặc bỏ qua cho tất cả các lõi)
  5. Bất kỳ kwarg nào khác chức năng của df đều chấp nhận (như "trục")

_df_split - đây là chức năng của trình trợ giúp nội bộ phải được định vị toàn cầu cho mô-đun đang chạy (Pool.map là "phụ thuộc vị trí"), nếu không tôi sẽ xác định vị trí bên trong ..

đây là mã từ ý chính của tôi (Tôi sẽ thêm nhiều bài kiểm tra chức năng gấu trúc ở đó):

import pandas as pd
import numpy as np
import multiprocessing
from functools import partial

def _df_split(tup_arg, **kwargs):
    split_ind, df_split, df_f_name = tup_arg
    return (split_ind, getattr(df_split, df_f_name)(**kwargs))

def df_multi_core(df, df_f_name, subset=None, njobs=-1, **kwargs):
    if njobs == -1:
        njobs = multiprocessing.cpu_count()
    pool = multiprocessing.Pool(processes=njobs)

    try:
        splits = np.array_split(df[subset], njobs)
    except ValueError:
        splits = np.array_split(df, njobs)

    pool_data = [(split_ind, df_split, df_f_name) for split_ind, df_split in enumerate(splits)]
    results = pool.map(partial(_df_split, **kwargs), pool_data)
    pool.close()
    pool.join()
    results = sorted(results, key=lambda x:x[0])
    results = pd.concat([split[1] for split in results])
    return results

Bellow là một mã kiểm tra cho một isin song song , so sánh hiệu suất chính, đa lõi và hiệu suất dask. Trên máy I7 với 8 lõi vật lý, tôi đã tăng tốc khoảng X4 lần. Tôi rất muốn nghe những gì bạn nhận được trên dữ liệu thực của bạn!

from time import time

if __name__ == '__main__': 
    sep = '-' * 50

    # isin test
    N = 10000000
    df = pd.DataFrame({'c1': np.random.randint(low=1, high=N, size=N), 'c2': np.arange(N)})
    lookfor = np.random.randint(low=1, high=N, size=1000000)

    print('{}\ntesting pandas isin on {}\n{}'.format(sep, df.shape, sep))
    t1 = time()
    print('result\n{}'.format(df.isin(lookfor).sum()))
    t2 = time()
    print('time for native implementation {}\n{}'.format(round(t2 - t1, 2), sep))

    t3 = time()
    res = df_multi_core(df=df, df_f_name='isin', subset=['c1'], njobs=-1, values=lookfor)
    print('result\n{}'.format(res.sum()))
    t4 = time()
    print('time for multi core implementation {}\n{}'.format(round(t4 - t3, 2), sep))


    t5 = time()
    ddata = dd.from_pandas(df, npartitions=njobs)
    res = ddata.map_partitions(lambda df: df.apply(apply_f, axis=1)).compute(scheduler='processes')
    t6 = time()
    print('result random sample\n{}'.format(res.sample(n=3, random_state=0)))
    print('time for dask implementation {}\n{}'.format(round(t6 - t5, 2), sep))

--------------------------------------------------
testing pandas isin on (10000000, 2)
--------------------------------------------------
result
c1    953213
c2    951942
dtype: int64
time for native implementation 3.87
--------------------------------------------------
result
c1    953213
dtype: int64
time for multi core implementation 1.16
--------------------------------------------------
result
c1    953213
c2    951942
dtype: int64
time for dask implementation 2.88

@Therriault Tôi đã thêm một so sánh dask với isin- có vẻ như đoạn mã có hiệu quả nhất với 'isin' - ~ X1,75 lần sau đó nhanh hơn (so với applychức năng chỉ nhanh hơn 5% sau đó)
mork
Licensed under cc by-sa 3.0 with attribution required.