C'è un modo semplice per eseguire panda.DataFrame.isin in parallelo?


25

Ho un programma di modellazione e punteggio che fa un uso DataFrame.isinintensivo della funzione dei panda, cercando negli elenchi di Facebook "come" i record dei singoli utenti per ciascuna delle poche migliaia di pagine specifiche. Questa è la parte del programma che richiede più tempo, più che la modellazione o il punteggio dei pezzi, semplicemente perché funziona solo su un core mentre il resto viene eseguito su alcune dozzine contemporaneamente.

Anche se so di poter suddividere manualmente il frame di dati in blocchi ed eseguire l'operazione in parallelo, esiste un modo semplice per farlo automaticamente? In altre parole, c'è qualche tipo di pacchetto là fuori che riconoscerà che sto eseguendo un'operazione facilmente delegabile e la distribuirà automaticamente? Forse è chiedere troppo, ma in passato sono stato abbastanza sorpreso da ciò che è già disponibile in Python, quindi immagino che valga la pena chiedere.

Sarebbe anche apprezzato qualsiasi altro suggerimento su come questo potrebbe essere realizzato (anche se non con un pacchetto di unicorno magico!). Principalmente, sto solo cercando di trovare un modo per radere 15-20 minuti per corsa senza spendere un uguale tempo per codificare la soluzione.


Quanto è grande il tuo elenco di valori? Hai provato a passarlo come set? Per parallelismo, potresti essere interessato a Joblib. È facile da usare e può velocizzare i calcoli. Usalo con grandi blocchi di dati.
oo

Un'altra opzione è quella di riformulare il problema come join. I join sono molto più veloci in Panda stackoverflow.com/questions/23945493/…
Brian Spiering

Un'altra opzione è usare np.in1d, che è anche più veloce stackoverflow.com/questions/21738882/fast-pandas-filtering
Brian Spiering

Risposte:


8

Sfortunatamente, la parallelizzazione non è ancora implementata nei panda. Puoi partecipare a questo problema github se vuoi partecipare allo sviluppo di questa funzione.

Non conosco nessun "pacchetto di unicorno magico" per questo scopo, quindi la cosa migliore sarà scrivere la tua soluzione. Ma se non vuoi ancora passare del tempo su questo e vuoi imparare qualcosa di nuovo, puoi provare i due metodi integrati in MongoDB (riduzione della mappa e framework agg). Vedi mongodb_agg_framework .




0

Esiste una versione più comune di questa domanda per quanto riguarda la parallelizzazione sulla funzione di applicazione dei panda - quindi questa è una domanda rinfrescante :)

In primo luogo , voglio menzionare più rapidamente poiché hai chiesto una soluzione "pacchettizzata", e appare sulla maggior parte delle domande SO relative alla parallelizzazione dei panda.

Ma .. mi piacerebbe ancora condividere il mio codice personale per questo, dal momento che dopo diversi anni di lavoro con DataFrame non ho mai trovato una soluzione di parallelizzazione al 100% (principalmente per la funzione apply) e dovevo sempre tornare per il mio " manuale "codice.

Grazie a te ho reso più generico supportare qualsiasi metodo DataFrame (teoricamente) con il suo nome (quindi non dovrai conservare le versioni per isin, apply, ecc.).

L'ho provato sulle funzioni "isin", "apply" e "isna" usando sia Python 2.7 che 3.6. Ha meno di 20 righe e ho seguito la convenzione di denominazione dei panda come "subset" e "njobs".

Ho anche aggiunto un confronto temporale con il codice equivalente di Dask per "isin" e sembra ~ X2 volte più lento di questo senso.

Include 2 funzioni:

df_multi_core - questo è quello che chiami. Accetta:

  1. Il tuo oggetto df
  2. Il nome della funzione che desideri chiamare
  3. Il sottoinsieme di colonne su cui può essere eseguita la funzione (aiuta a ridurre il tempo / la memoria)
  4. Il numero di lavori da eseguire in parallelo (-1 o ometti per tutti i core)
  5. 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 test per un'isina parallelizzata , che confronta le prestazioni native, multi-core e dask. Su una macchina I7 con 8 core fisici, ho ottenuto circa 4 volte la velocità. Mi piacerebbe sapere cosa ottieni sui tuoi dati reali!

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 Ho aggiunto un confronto Dask con isin- sembra che lo snippet di codice sia più efficace con 'isin' - ~ X1,75 volte più veloce di Dask (rispetto alla applyfunzione che ha ottenuto solo il 5% più veloce di Dask)
Mork
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.