Per chiunque stia cercando di applicare tqdm sul proprio codice di panda parallelo personalizzato.
(Ho provato alcune delle librerie per la parallelizzazione nel corso degli anni, ma non ho mai trovato una soluzione di parallelizzazione al 100%, principalmente per la funzione di applicazione, e ho sempre dovuto tornare per il mio codice "manuale".)
df_multi_core - questo è quello che chiami. Accetta:
- Il tuo oggetto df
- Il nome della funzione che desideri chiamare
- Il sottoinsieme di colonne su cui può essere eseguita la funzione (aiuta a ridurre il tempo / la memoria)
- Il numero di lavori da eseguire in parallelo (-1 o ometti per tutti i core)
- Qualsiasi altro kwarg accettato dalla funzione del df (come "axis")
_df_split - questa è una funzione di supporto interna che deve essere posizionata globalmente sul modulo in esecuzione (Pool.map è "dipendente dal posizionamento"), altrimenti lo localizzerei internamente.
ecco il codice dal mio succo (aggiungerò altri test di funzione panda lì):
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 è un codice di prova per un parallelized applicare con 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))
Nell'output è possibile visualizzare 1 barra di avanzamento per l'esecuzione senza parallelizzazione e barre di avanzamento per core durante l'esecuzione con parallelizzazione. C'è un leggero rallentamento e a volte il resto dei core appare contemporaneamente, ma anche allora penso che sia utile dal momento che ottieni le statistiche sui progressi per core (it / sec e record totali, per es.)
Grazie @abcdaa per questa fantastica biblioteca!