Für alle, die tqdm auf ihren benutzerdefinierten parallelen Pandas-Apply-Code anwenden möchten.
(Ich habe im Laufe der Jahre einige der Bibliotheken für die Parallelisierung ausprobiert, aber ich habe nie eine 100% ige Parallelisierungslösung gefunden, hauptsächlich für die Apply-Funktion, und ich musste immer zurückkommen, um meinen "manuellen" Code zu erhalten.)
df_multi_core - das ist das, was du anrufst . Es akzeptiert:
- Dein df Objekt
- Der Funktionsname, den Sie aufrufen möchten
- Die Teilmenge der Spalten, für die die Funktion ausgeführt werden kann (hilft, Zeit / Speicher zu reduzieren)
- Die Anzahl der Jobs, die parallel ausgeführt werden sollen (-1 oder für alle Kerne weglassen)
- Alle anderen kwargs, die die Funktion des df akzeptiert (wie "Achse")
_df_split - Dies ist eine interne Hilfsfunktion , die global zum laufenden Modul positioniert werden muss (Pool.map ist "platzierungsabhängig"), andernfalls würde ich sie intern lokalisieren.
Hier ist der Code aus meinem Kern (ich werde dort weitere Pandas-Funktionstests hinzufügen):
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
Unten ist ein Testcode für eine parallelisierte Anwendung mit 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))
In der Ausgabe sehen Sie 1 Fortschrittsbalken für die Ausführung ohne Parallelisierung und Fortschrittsbalken pro Kern für die Ausführung mit Parallelisierung. Es gibt ein leichtes Hickup und manchmal erscheinen die restlichen Kerne sofort, aber selbst dann denke ich, dass es nützlich ist, da Sie die Fortschrittsstatistiken pro Kern erhalten (z. B. it / sec und Gesamtaufzeichnungen).
Vielen Dank an @abcdaa für diese großartige Bibliothek!