สำหรับทุกคนที่ต้องการใช้ tqdm กับรหัสการใช้แพนด้าแบบขนานที่กำหนดเอง
(ฉันลองห้องสมุดหลายแห่งเพื่อทำการขนานกันในช่วงหลายปีที่ผ่านมา แต่ฉันไม่เคยพบโซลูชันการทำให้เท่าเทียมกัน 100% ส่วนใหญ่สำหรับฟังก์ชั่นการใช้งานและฉันต้องกลับมาหารหัส "คู่มือ" ของฉันเสมอ)
df_multi_core - นี่คือสิ่งที่คุณเรียก มันยอมรับ:
- วัตถุ df ของคุณ
- ชื่อฟังก์ชันที่คุณต้องการโทร
- ชุดย่อยของคอลัมน์ที่สามารถใช้งานได้ (ช่วยลดเวลา / หน่วยความจำ)
- จำนวนของงานที่ต้องรันแบบขนาน (-1 หรือละเว้นสำหรับทุกคอร์)
- kwargs อื่น ๆ ที่ฟังก์ชั่นของ df ยอมรับ (เช่น "แกน")
_df_split - นี่คือฟังก์ชั่นตัวช่วยภายในที่จะต้องวางตำแหน่งทั่วโลกในโมดูลที่กำลังทำงาน (Pool.map คือ "การจัดวาง") มิฉะนั้นฉันจะหามันภายใน ..
นี่คือรหัสจากส่วนสำคัญของฉัน(ฉันจะเพิ่มการทดสอบฟังก์ชั่นแพนด้าเพิ่มเติมที่นั่น):
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
ร้องเป็นรหัสทดสอบสำหรับการใช้แบบขนานกับ tqdm "progress_apply"
from time import time
from tqdm import tqdm
tqdm.pandas()
if __name__ == '__main__':
sep = '-' * 50
# tqdm progress_apply test
def apply_f(row):
return row['c1'] + 0.1
N = 1000000
np.random.seed(0)
df = pd.DataFrame({'c1': np.arange(N), 'c2': np.arange(N)})
print('testing pandas apply on {}\n{}'.format(df.shape, sep))
t1 = time()
res = df.progress_apply(apply_f, axis=1)
t2 = time()
print('result random sample\n{}'.format(res.sample(n=3, random_state=0)))
print('time for native implementation {}\n{}'.format(round(t2 - t1, 2), sep))
t3 = time()
# res = df_multi_core(df=df, df_f_name='apply', subset=['c1'], njobs=-1, func=apply_f, axis=1)
res = df_multi_core(df=df, df_f_name='progress_apply', subset=['c1'], njobs=-1, func=apply_f, axis=1)
t4 = time()
print('result random sample\n{}'.format(res.sample(n=3, random_state=0)))
print('time for multi core implementation {}\n{}'.format(round(t4 - t3, 2), sep))
ในผลลัพธ์คุณจะเห็นแถบความคืบหน้า 1 แถบสำหรับการทำงานโดยไม่มีการขนานและแถบความคืบหน้าต่อคอร์เมื่อทำงานด้วยการขนาน มี hickup เล็กน้อยและบางครั้งส่วนที่เหลือของคอร์ปรากฏขึ้นพร้อมกัน แต่ถึงอย่างนั้นฉันก็คิดว่ามันมีประโยชน์เพราะคุณได้รับสถิติความคืบหน้าต่อคอร์ (เช่น / วินาทีและบันทึกทั้งหมดสำหรับอดีต)
ขอบคุณ @abcdaa สำหรับห้องสมุดที่ยอดเยี่ยมนี้!