Flussi di lavoro di "grandi quantità di dati" utilizzando i panda


982

Ho cercato di trovare una risposta a questa domanda per molti mesi mentre imparavo i panda. Uso SAS per il mio lavoro quotidiano ed è ottimo per il suo supporto out-core. Tuttavia, SAS è orribile come un software per numerose altre ragioni.

Un giorno spero di sostituire il mio uso di SAS con python e panda, ma al momento mi manca un flusso di lavoro fuori core per grandi set di dati. Non sto parlando di "big data" che richiedono una rete distribuita, ma piuttosto file troppo grandi per adattarsi alla memoria ma abbastanza piccoli da adattarsi a un disco rigido.

Il mio primo pensiero è quello di utilizzare HDFStoreper contenere set di dati di grandi dimensioni su disco e estrarre solo i pezzi necessari nei frame di dati per l'analisi. Altri hanno citato MongoDB come alternativa più facile da usare. La mia domanda è questa:

Quali sono alcuni flussi di lavoro delle migliori pratiche per realizzare quanto segue:

  1. Caricamento di file flat in una struttura di database permanente su disco
  2. Esecuzione di query su quel database per recuperare i dati da inserire in una struttura di dati panda
  3. Aggiornamento del database dopo aver manipolato i pezzi nei panda

Gli esempi del mondo reale sarebbero molto apprezzati, soprattutto da chiunque utilizzi i panda su "dati di grandi dimensioni".

Modifica - un esempio di come vorrei che funzionasse:

  1. Iterativamente importa un file flat di grandi dimensioni e memorizzalo in una struttura di database permanente su disco. Questi file sono in genere troppo grandi per adattarsi alla memoria.
  2. Per poter utilizzare Panda, vorrei leggere sottoinsiemi di questi dati (di solito solo poche colonne alla volta) che possono adattarsi alla memoria.
  3. Vorrei creare nuove colonne eseguendo varie operazioni sulle colonne selezionate.
  4. Dovrei quindi aggiungere queste nuove colonne nella struttura del database.

Sto cercando di trovare un modo ottimale per eseguire questi passaggi. Leggendo i collegamenti su panda e pytables sembra che aggiungere una nuova colonna potrebbe essere un problema.

Modifica: rispondendo alle domande di Jeff in particolare:

  1. Sto costruendo modelli di rischio di credito al consumo. I tipi di dati includono telefono, SSN e caratteristiche dell'indirizzo; valori di proprietà; informazioni dispregiative come casellari giudiziari, fallimenti, ecc ... I set di dati che utilizzo ogni giorno hanno in media da 1.000 a 2.000 campi in media di tipi di dati misti: variabili continue, nominali e ordinali di dati numerici e di caratteri. Raramente aggiungo righe, ma eseguo molte operazioni che creano nuove colonne.
  2. Le operazioni tipiche prevedono la combinazione di più colonne utilizzando la logica condizionale in una nuova colonna composta. Ad esempio if var1 > 2 then newvar = 'A' elif var2 = 4 then newvar = 'B',. Il risultato di queste operazioni è una nuova colonna per ogni record nel mio set di dati.
  3. Infine, vorrei aggiungere queste nuove colonne nella struttura dei dati su disco. Ripeterei il passaggio 2, esplorando i dati con campi incrociati e statistiche descrittive cercando di trovare relazioni interessanti e intuitive da modellare.
  4. Un file di progetto tipico è in genere di circa 1 GB. I file sono organizzati in modo tale che una riga sia costituita da una registrazione di dati del consumatore. Ogni riga ha lo stesso numero di colonne per ogni record. Questo sarà sempre il caso.
  5. È abbastanza raro che io crei un sottoinsieme per righe quando creo una nuova colonna. Tuttavia, è abbastanza comune per me creare un sottoinsieme nelle righe durante la creazione di report o la generazione di statistiche descrittive. Ad esempio, potrei voler creare una frequenza semplice per una specifica linea di business, ad esempio le carte di credito al dettaglio. Per fare ciò, selezionerei solo quei record in cui la linea di business = vendita al dettaglio oltre a qualsiasi colonna su cui voglio riferire. Quando creo nuove colonne, tuttavia, estraggerei tutte le righe di dati e solo le colonne di cui ho bisogno per le operazioni.
  6. Il processo di modellazione richiede che analizzi ogni colonna, cerchi relazioni interessanti con alcune variabili di risultato e crei nuove colonne composte che descrivano tali relazioni. Le colonne che esploro di solito sono fatte in piccoli gruppi. Ad esempio, mi concentrerò su una serie di dire 20 colonne che trattano solo di valori di proprietà e osserverò come si relazionano al default su un prestito. Una volta esplorate quelle e create nuove colonne, passo poi a un altro gruppo di colonne, dico istruzione universitaria e ripeto il processo. Quello che sto facendo è creare variabili candidate che spieghino la relazione tra i miei dati e alcuni risultati. Alla fine di questo processo, applico alcune tecniche di apprendimento che creano un'equazione da quelle colonne composte.

È raro che aggiungerei mai righe al set di dati. Quasi sempre creerò nuove colonne (variabili o caratteristiche nella statistica / linguaggio di apprendimento automatico).


1
Il rapporto dimensioni core / dimensioni intere è 1%, 10%? Importa - se potessi comprimere cols in int8 o filtrare le file rumorose, ciò cambierebbe il tuo ciclo di calcolo-pensiero da dire ore a minuti? (Aggiungi anche tag large-data.)
denis

1
Memorizzare float32 invece di float64 e int8 dove possibile, dovrebbe essere banale (non so quali strumenti / funzioni facciano internamente float64)
denis

puoi dividere il tuo compito in blocchi di lavoro?
Andrew Scott Evans,

1
una bella soluzione del 2019 per fare panda come operazioni su dati "medi" che non rientrano nella memoria è dask
lunguini

Esistono alternative a python + panda che potresti prendere in considerazione dal momento in cui hai appena iniziato. Considera il fatto che Python è un linguaggio di programmazione per scopi generici (non un DSL per il munging e l'analisi dei dati) e che Panda è una libreria apposta su questo. Vorrei considerare guardando R o kdb.
Henry Henrinson,

Risposte:


622

Uso sistematicamente decine di gigabyte di dati proprio in questo modo, ad es. Ho tabelle su disco che leggo tramite query, creo dati e accoda di nuovo.

Vale la pena leggere i documenti e alla fine di questo thread per diversi suggerimenti su come archiviare i tuoi dati.

Dettagli che influenzeranno il modo in cui memorizzi i tuoi dati, come:
Fornisci quanti più dettagli possibile; e posso aiutarti a sviluppare una struttura.

  1. Dimensione dei dati, numero di righe, colonne, tipi di colonne; stai aggiungendo righe o solo colonne?
  2. Come saranno le operazioni tipiche. Ad esempio, eseguire una query su colonne per selezionare un gruppo di righe e colonne specifiche, quindi eseguire un'operazione (in memoria), creare nuove colonne, salvarle.
    (Dare un esempio di giocattolo potrebbe permetterci di offrire raccomandazioni più specifiche.)
  3. Dopo quell'elaborazione, allora cosa fai? Il passaggio 2 è ad hoc o ripetibile?
  4. Inserisci file flat: quante dimensioni totali approssimative in GB. Come sono organizzati, ad esempio, per record? Ognuno contiene campi diversi o hanno alcuni record per file con tutti i campi in ciascun file?
  5. Hai mai selezionato sottoinsiemi di righe (record) in base a criteri (ad esempio, seleziona le righe con campo A> 5)? e poi fai qualcosa, o selezioni semplicemente i campi A, B, C con tutti i record (e poi fai qualcosa)?
  6. "Lavori" su tutte le tue colonne (in gruppi) o ci sono buone proporzioni che puoi utilizzare solo per i rapporti (ad esempio, vuoi mantenere i dati in giro, ma non devi estrarre esplicitamente quella colonna fino a quando tempo dei risultati finali)?

Soluzione

Assicurati di avere almeno i panda0.10.1 installati.

Leggi i file iteranti chunk-by-chunk e più query di tabella .

Dal momento che pytables è ottimizzato per operare su una riga (che è ciò su cui si esegue una query), creeremo una tabella per ciascun gruppo di campi. In questo modo è facile selezionare un piccolo gruppo di campi (che funzionerà con un grande tavolo, ma è più efficiente farlo in questo modo ... Penso che potrei essere in grado di correggere questa limitazione in futuro ... questo è comunque più intuitivo):
(Quanto segue è pseudocodice.)

import numpy as np
import pandas as pd

# create a store
store = pd.HDFStore('mystore.h5')

# this is the key to your storage:
#    this maps your fields to a specific group, and defines 
#    what you want to have as data_columns.
#    you might want to create a nice class wrapping this
#    (as you will want to have this map and its inversion)  
group_map = dict(
    A = dict(fields = ['field_1','field_2',.....], dc = ['field_1',....,'field_5']),
    B = dict(fields = ['field_10',......        ], dc = ['field_10']),
    .....
    REPORTING_ONLY = dict(fields = ['field_1000','field_1001',...], dc = []),

)

group_map_inverted = dict()
for g, v in group_map.items():
    group_map_inverted.update(dict([ (f,g) for f in v['fields'] ]))

Leggere i file e creare l'archiviazione (essenzialmente facendo ciò che append_to_multiplefa):

for f in files:
   # read in the file, additional options may be necessary here
   # the chunksize is not strictly necessary, you may be able to slurp each 
   # file into memory in which case just eliminate this part of the loop 
   # (you can also change chunksize if necessary)
   for chunk in pd.read_table(f, chunksize=50000):
       # we are going to append to each table by group
       # we are not going to create indexes at this time
       # but we *ARE* going to create (some) data_columns

       # figure out the field groupings
       for g, v in group_map.items():
             # create the frame for this group
             frame = chunk.reindex(columns = v['fields'], copy = False)    

             # append it
             store.append(g, frame, index=False, data_columns = v['dc'])

Ora hai tutte le tabelle nel file (in realtà potresti memorizzarle in file separati se lo desideri, probabilmente dovresti aggiungere il nome del file a group_map, ma probabilmente non è necessario).

Ecco come ottenere colonne e crearne di nuove:

frame = store.select(group_that_I_want)
# you can optionally specify:
# columns = a list of the columns IN THAT GROUP (if you wanted to
#     select only say 3 out of the 20 columns in this sub-table)
# and a where clause if you want a subset of the rows

# do calculations on this frame
new_frame = cool_function_on_frame(frame)

# to 'add columns', create a new group (you probably want to
# limit the columns in this new_group to be only NEW ones
# (e.g. so you don't overlap from the other tables)
# add this info to the group_map
store.append(new_group, new_frame.reindex(columns = new_columns_created, copy = False), data_columns = new_columns_created)

Quando sei pronto per post_processing:

# This may be a bit tricky; and depends what you are actually doing.
# I may need to modify this function to be a bit more general:
report_data = store.select_as_multiple([groups_1,groups_2,.....], where =['field_1>0', 'field_1000=foo'], selector = group_1)

Riguardo a data_columns, in realtà non è necessario definire QUALSIASI data_columns; ti permettono di selezionare le righe in base alla colonna. Ad esempio qualcosa del tipo:

store.select(group, where = ['field_1000=foo', 'field_1001>0'])

Possono essere molto interessanti per te nella fase di generazione del rapporto finale (essenzialmente una colonna di dati è separata dalle altre colonne, il che potrebbe influire in qualche modo sull'efficienza se definisci molto).

Potresti anche voler:

  • crea una funzione che prende un elenco di campi, cerca i gruppi nella group_map, quindi li seleziona e concatena i risultati in modo da ottenere il frame risultante (questo è essenzialmente ciò che select_as_multiple fa). In questo modo la struttura sarebbe abbastanza trasparente per te.
  • indici su determinate colonne di dati (rende molto più veloce il sottoinsieme di righe).
  • Abilita la compressione.

Fammi sapere quando hai domande!


5
Grazie per i collegamenti. Il secondo link mi rende un po 'preoccupato di non poter aggiungere nuove colonne alle tabelle in HDFStore? È corretto? Inoltre, ho aggiunto un esempio di come utilizzerei questa configurazione.
Zelazny7,

4
La struttura attuale in hdf dipende da te. Pytables è orientato alle righe, con colonne fisse al momento della creazione. Non è possibile aggiungere colonne una volta creata una tabella. Tuttavia, è possibile creare una nuova tabella indicizzata come la tabella esistente. (vedi gli esempi select_as_multiple nei documenti). In questo modo è possibile creare oggetti di dimensioni arbitrarie pur avendo query piuttosto efficienti. Il modo in cui usi i dati è la chiave per come devono essere organizzati su disco. Inviami una e-mail fuori elenco con pseudo codice di un esempio più specifico.
Jeff

1
Ho aggiornato la mia domanda per rispondere ai tuoi punti dettagliati. Lavorerò su un esempio per inviarti fuori dalla lista. Grazie!
Zelazny7,

12
@Jeff, con Panda in 0.17.x ora i problemi descritti sopra sono stati risolti in Panda?
ctrl-alt-delete

5
@Jeff ti piacerebbe aggiungere un rapido aggiornamento alla tua risposta per promuovere Dask?
Boud,

137

Penso che alle risposte sopra manchi un approccio semplice che ho trovato molto utile.

Quando ho un file che è troppo grande per essere caricato in memoria, lo divido in più file più piccoli (per riga o cols)

Esempio: in caso di dati di trading di dimensioni di ~ 30 GB per 30 giorni, li divido in un file al giorno di dimensioni di ~ 1 GB. Successivamente elaboro ogni file separatamente e aggrego i risultati alla fine

Uno dei maggiori vantaggi è che consente l'elaborazione parallela dei file (più thread o processi)

L'altro vantaggio è che la manipolazione dei file (come l'aggiunta / rimozione di date nell'esempio) può essere ottenuta tramite normali comandi di shell, che non è possibile in formati di file più avanzati / complicati

Questo approccio non copre tutti gli scenari, ma è molto utile in molti di essi


39
Concordato. Con tutto l'hype, è facile dimenticare che gli strumenti da riga di comando possono essere 235 volte più veloci di un cluster Hadoop
zelusp,


66

Se i tuoi set di dati sono compresi tra 1 e 20 GB, dovresti ottenere una workstation con 48 GB di RAM. Quindi i panda possono contenere l'intero set di dati nella RAM. So che non è la risposta che stai cercando qui, ma fare calcoli scientifici su un notebook con 4 GB di RAM non è ragionevole.


7
"Fare calcoli scientifici su un notebook con 4 GB di RAM non è ragionevole" Definire ragionevole. Penso che l'UNIVAC avrebbe una visione diversa. arstechnica.com/tech-policy/2011/09/…
grisaitis,

2
Concordato! prova a continuare a lavorare in memoria anche se costa $$ in anticipo. Se il tuo lavoro porta a un ritorno finanziario, nel tempo recupererai le spese attraverso la tua maggiore efficienza.
Ansonw,

2
Fare calcoli scientifici su una workstation con 48 GB di RAM non è ragionevole.
Yaroslav Nikitenko,

4
@YaroslavNikitenko Una r4.2xlarge con 61 GB / RAM è di $ 0,532 / ora. Che tipo di elaborazione scientifica stai facendo che non è così prezioso? Sembra insolito, se non irragionevole.
rjurney,

4
@rjurney scusa, forse avrei dovuto cancellare il mio commento. Il tuo giudizio sul computer scientifico "irragionevole" sembra molto soggettivo. Faccio i miei calcoli scientifici per anni sui laptop, e questo mi sembra abbastanza, perché il più delle volte scrivo codice. I miei algoritmi sono molto più difficili dal punto di vista della programmazione che da quello computazionale. Inoltre sono abbastanza sicuro che per scrivere algoritmi scalabili non si dovrebbe fare affidamento sulle attuali limitazioni hardware. Il tuo commento sull'informatica di altre persone può sembrare un po 'offensivo (a parte la soggettività), ti dispiacerebbe eliminare queste poche parole?
Yaroslav Nikitenko

59

So che questo è un vecchio thread, ma penso che valga la pena dare un'occhiata alla libreria Blaze . È costruito per questo tipo di situazioni.

Dai documenti:

Blaze estende l'usabilità di NumPy e Panda all'elaborazione distribuita e out-of-core. Blaze fornisce un'interfaccia simile a quella dell'Array ND NumPy o Pandas DataFrame ma mappa queste interfacce familiari su una varietà di altri motori di calcolo come Postgres o Spark.

Modifica: A proposito, è supportato da ContinuumIO e Travis Oliphant, autore di NumPy.


Un'altra libreria che potrebbe essere utile guardare è GraphLab Create: ha un'efficiente struttura simile a DataFrame che non è limitata dalla capacità di memoria. blog.dato.com/…
impermeabile il

52

Questo è il caso di Pymongo. Ho anche prototipato usando sql server, sqlite, HDF, ORM (SQLAlchemy) in Python. Innanzitutto pymongo è un DB basato su documento, quindi ogni persona sarebbe un documento ( dictdi attributi). Molte persone formano una collezione e puoi avere molte collezioni (persone, borsa, reddito).

pd.dateframe -> pymongo Nota: utilizzo chunksizein read_csvper mantenerlo da 5 a 10k record (pymongo lascia cadere il socket se più grande)

aCollection.insert((a[1].to_dict() for a in df.iterrows()))

interrogazione: gt = maggiore di ...

pd.DataFrame(list(mongoCollection.find({'anAttribute':{'$gt':2887000, '$lt':2889000}})))

.find()restituisce un iteratore, quindi comunemente lo uso ichunkedper tagliare in iteratori più piccoli.

Che ne dici di un join dal momento che normalmente ottengo 10 origini dati da incollare insieme:

aJoinDF = pandas.DataFrame(list(mongoCollection.find({'anAttribute':{'$in':Att_Keys}})))

quindi (nel mio caso a volte devo aJoinDFprima agg "prima" della sua "fusione".)

df = pandas.merge(df, aJoinDF, on=aKey, how='left')

E puoi quindi scrivere le nuove informazioni nella tua raccolta principale tramite il metodo di aggiornamento di seguito. (raccolta logica vs origini dati fisiche).

collection.update({primarykey:foo},{key:change})

Nelle ricerche più piccole, denormalizza. Ad esempio, hai del codice nel documento e aggiungi semplicemente il testo del codice di campo e fai una dictricerca mentre crei i documenti.

Ora hai un bel set di dati basato su una persona, puoi liberare la tua logica su ogni caso e creare più attributi. Finalmente puoi leggere in panda i tuoi 3 indicatori chiave di memoria massima ed eseguire esplorazioni pivot / agg / data. Questo funziona per me per 3 milioni di record con numeri / big text / categorie / codici / float / ...

Puoi anche usare i due metodi integrati in MongoDB (MapReduce e aggregate framework). Vedi qui per maggiori informazioni sul framework aggregato , in quanto sembra essere più semplice di MapReduce e sembra utile per un lavoro aggregato veloce. Nota che non ho bisogno di definire i miei campi o relazioni e posso aggiungere elementi a un documento. Allo stato attuale del set di strumenti numpy, panda, python in rapida evoluzione, MongoDB mi aiuta a mettermi al lavoro :)


Ciao, sto giocando con il vostro esempio come bene e mi imbatto in questo errore quando si cerca di inserire in un database: In [96]: test.insert((a[1].to_dict() for a in df.iterrows())) --------------- InvalidDocument: Cannot encode object: 0. Qualche idea di cosa potrebbe essere sbagliato? Il mio frame di dati è composto da tutti i tipi di int64 ed è molto semplice.
Zelazny7,

2
Sì, ho fatto lo stesso per un semplice intervallo DF e l'int64 di numpy sembra infastidire Pymongo. Tutti i dati che ho giocato con i convertiti da CSV (vs artificialmente tramite range ()) e ha tipi lunghi e quindi nessun problema. In numpy puoi convertirti ma lo vedo come dannoso. Devo ammettere che gli elementi 10.1 per HDF sembrano eccitanti.
brian_the_bungler,

43

L'ho notato un po 'tardi, ma lavoro con un problema simile (modelli di pagamento anticipato del mutuo). La mia soluzione è stata quella di saltare il layer panda HDFStore e usare pytables diritti. Salvo ogni colonna come un singolo array HDF5 nel mio file finale.

Il mio flusso di lavoro di base è innanzitutto ottenere un file CSV dal database. Lo decomprimo, quindi non è così grande. Quindi lo converto in un file HDF5 orientato alla riga, ripetendo su di esso in Python, convertendo ogni riga in un tipo di dati reale e scrivendolo in un file HDF5. Ciò richiede alcune decine di minuti, ma non utilizza alcuna memoria, poiché funziona solo riga per riga. Quindi "traspongo" il file HDF5 orientato alla riga in un file HDF5 orientato alla colonna.

La tabella di trasposizione è simile a:

def transpose_table(h_in, table_path, h_out, group_name="data", group_path="/"):
    # Get a reference to the input data.
    tb = h_in.getNode(table_path)
    # Create the output group to hold the columns.
    grp = h_out.createGroup(group_path, group_name, filters=tables.Filters(complevel=1))
    for col_name in tb.colnames:
        logger.debug("Processing %s", col_name)
        # Get the data.
        col_data = tb.col(col_name)
        # Create the output array.
        arr = h_out.createCArray(grp,
                                 col_name,
                                 tables.Atom.from_dtype(col_data.dtype),
                                 col_data.shape)
        # Store the data.
        arr[:] = col_data
    h_out.flush()

Rileggendolo, si presenta come:

def read_hdf5(hdf5_path, group_path="/data", columns=None):
    """Read a transposed data set from a HDF5 file."""
    if isinstance(hdf5_path, tables.file.File):
        hf = hdf5_path
    else:
        hf = tables.openFile(hdf5_path)

    grp = hf.getNode(group_path)
    if columns is None:
        data = [(child.name, child[:]) for child in grp]
    else:
        data = [(child.name, child[:]) for child in grp if child.name in columns]

    # Convert any float32 columns to float64 for processing.
    for i in range(len(data)):
        name, vec = data[i]
        if vec.dtype == np.float32:
            data[i] = (name, vec.astype(np.float64))

    if not isinstance(hdf5_path, tables.file.File):
        hf.close()
    return pd.DataFrame.from_items(data)

Ora, generalmente lo eseguo su una macchina con una tonnellata di memoria, quindi potrei non essere abbastanza attento con il mio utilizzo della memoria. Ad esempio, per impostazione predefinita l'operazione di caricamento legge l'intero set di dati.

Questo in genere funziona per me, ma è un po 'goffo e non riesco a usare la magica fantasia dei pytables.

Modifica: il vero vantaggio di questo approccio, rispetto ai pytables di array di record predefiniti, è che posso quindi caricare i dati in R usando h5r, che non è in grado di gestire le tabelle. O, almeno, non sono stato in grado di farlo caricare tabelle eterogenee.


ti dispiacerebbe condividere con me parte del tuo codice? Sono interessato a come si caricano i dati da un formato di testo semplice senza conoscere i tipi di dati prima di passare ai pytables. Inoltre, sembra che lavori solo con dati di un tipo. È corretto?
Zelazny7,

1
Prima di tutto, presumo di conoscere i tipi di colonne prima del caricamento, piuttosto che provare a indovinare dai dati. Salvo un file "spec. Dati" JSON con i nomi e i tipi di colonna e lo utilizzo durante l'elaborazione dei dati. (Il file è in genere un output BCP terribile senza etichette.) I tipi di dati che uso sono stringhe, float, numeri interi o date mensili. Trasformo le stringhe in ints salvando una tabella di enumerazione e converto le date in ints (mesi passati 2000), quindi sono rimasto con ints e float nei miei dati, oltre all'enumerazione. Adesso salvo i float come float64, ma ho sperimentato float32.
Johann Hibschman,

1
se hai tempo, ti preghiamo di provarlo per la compatibilità esterna con R: pandas.pydata.org/pandas-docs/dev/… , e se hai difficoltà, forse possiamo modificarlo
Jeff

Ci proverò, se posso. rhdf5 è un dolore, dal momento che è un pacchetto bioconduttore, piuttosto che essere semplicemente su CRAN come h5r. Sono in balia del nostro team di architettura tecnica e l'ultima volta che l'ho chiesto c'è stato un problema con rhdf5. In ogni caso, sembra solo un errore orientarsi verso le righe anziché verso le colonne con un negozio OLAP, ma ora sto sconclusionando.
Johann Hibschman,

39

Un trucco che ho trovato utile per casi di utilizzo di dati di grandi dimensioni è quello di ridurre il volume dei dati riducendo la precisione float a 32 bit. Non è applicabile in tutti i casi, ma in molte applicazioni la precisione a 64 bit è eccessiva e vale la pena risparmiare 2 volte la memoria. Per rendere ancora più ovvio un punto ovvio:

>>> df = pd.DataFrame(np.random.randn(int(1e8), 5))
>>> df.info()
<class 'pandas.core.frame.DataFrame'>
RangeIndex: 100000000 entries, 0 to 99999999
Data columns (total 5 columns):
...
dtypes: float64(5)
memory usage: 3.7 GB

>>> df.astype(np.float32).info()
<class 'pandas.core.frame.DataFrame'>
RangeIndex: 100000000 entries, 0 to 99999999
Data columns (total 5 columns):
...
dtypes: float32(5)
memory usage: 1.9 GB

27

Come notato da altri, dopo alcuni anni è emerso un equivalente dei panda "fuori dal centro": il Dask . Sebbene dask non sia un sostituto drop-in dei panda e di tutte le sue funzionalità, si distingue per diversi motivi:

Dask è una libreria di calcolo parallelo flessibile per il calcolo analitico che è ottimizzata per la pianificazione dinamica delle attività per carichi di lavoro computazionali interattivi di raccolte di "Big Data" come matrici parallele, frame di dati ed elenchi che estendono interfacce comuni come NumPy, Pandas o iteratori Python a più grandi ambienti di memoria o distribuiti e scale da laptop a cluster.

Dask sottolinea le seguenti virtù:

  • Familiarità: fornisce array NumPy parallelizzati e oggetti Pandas DataFrame
  • Flessibile: fornisce un'interfaccia di pianificazione delle attività per maggiori carichi di lavoro personalizzati e integrazione con altri progetti.
  • Nativo: abilita l'elaborazione distribuita in Pure Python con accesso allo stack PyData.
  • Veloce: funziona con sovraccarico basso, bassa latenza e serializzazione minima necessaria per algoritmi numerici veloci
  • Ridimensiona: viene eseguito in modo resiliente su cluster con migliaia di core Ridimensiona: Trivial per impostare ed eseguire su un laptop in un unico processo
  • Reattivo: progettato pensando al calcolo interattivo, fornisce feedback e diagnostica rapidi per aiutare le persone

e per aggiungere un semplice esempio di codice:

import dask.dataframe as dd
df = dd.read_csv('2015-*-*.csv')
df.groupby(df.user_id).value.mean().compute()

sostituisce alcuni codici panda in questo modo:

import pandas as pd
df = pd.read_csv('2015-01-01.csv')
df.groupby(df.user_id).value.mean()

e, soprattutto degno di nota, fornisce attraverso l' concurrent.futuresinterfaccia un'infrastruttura generale per la presentazione di attività personalizzate:

from dask.distributed import Client
client = Client('scheduler:port')

futures = []
for fn in filenames:
    future = client.submit(load, fn)
    futures.append(future)

summary = client.submit(summarize, futures)
summary.result()

Ho aggiunto questa risposta poiché il post di @Private appare regolarmente nell'elenco di eliminazione suggerito per contenuto e lunghezza.
wp78de,

17

Vale la pena menzionare qui anche Ray ,
è un framework di calcolo distribuito, che ha una propria implementazione per i panda in modo distribuito.

Sostituisci semplicemente l'importazione dei panda e il codice dovrebbe funzionare così com'è:

# import pandas as pd
import ray.dataframe as pd

#use pd as usual

puoi leggere maggiori dettagli qui:

https://rise.cs.berkeley.edu/blog/pandas-on-ray/


16

Un'altra variazione

Molte delle operazioni eseguite in Panda possono anche essere eseguite come query db (sql, mongo)

L'uso di un RDBMS o mongodb consente di eseguire alcune delle aggregazioni nella query DB (che è ottimizzata per dati di grandi dimensioni e utilizza in modo efficiente cache e indici)

Successivamente, puoi eseguire l'elaborazione post usando i panda.

Il vantaggio di questo metodo è quello di ottenere le ottimizzazioni del DB per lavorare con dati di grandi dimensioni, pur definendo la logica in una sintassi dichiarativa di alto livello - e non dover affrontare i dettagli di decidere cosa fare in memoria e cosa fare di core.

E sebbene il linguaggio di query e i panda siano diversi, di solito non è complicato tradurre parte della logica dall'uno all'altro.


11

Prendi in considerazione Ruffus se segui il semplice percorso di creazione di una pipeline di dati suddivisa in più file più piccoli.


9

Di recente ho riscontrato un problema simile. Ho scoperto semplicemente di leggere i dati in blocchi e di aggiungerli mentre li scrivo in blocchi sullo stesso CSV funziona bene. Il mio problema era l'aggiunta di una colonna data basata sulle informazioni in un'altra tabella, usando il valore di determinate colonne come segue. Questo può aiutare quelli confusi da dask e hdf5 ma più familiari con i panda come me.

def addDateColumn():
"""Adds time to the daily rainfall data. Reads the csv as chunks of 100k 
   rows at a time and outputs them, appending as needed, to a single csv. 
   Uses the column of the raster names to get the date.
"""
    df = pd.read_csv(pathlist[1]+"CHIRPS_tanz.csv", iterator=True, 
                     chunksize=100000) #read csv file as 100k chunks

    '''Do some stuff'''

    count = 1 #for indexing item in time list 
    for chunk in df: #for each 100k rows
        newtime = [] #empty list to append repeating times for different rows
        toiterate = chunk[chunk.columns[2]] #ID of raster nums to base time
        while count <= toiterate.max():
            for i in toiterate: 
                if i ==count:
                    newtime.append(newyears[count])
            count+=1
        print "Finished", str(chunknum), "chunks"
        chunk["time"] = newtime #create new column in dataframe based on time
        outname = "CHIRPS_tanz_time2.csv"
        #append each output to same csv, using no header
        chunk.to_csv(pathlist[2]+outname, mode='a', header=None, index=None)

9

Vorrei sottolineare il pacchetto Vaex.

Vaex è una libreria Python per DataFrame Out-of-Core pigri (simili ai Pandas), per visualizzare ed esplorare grandi set di dati tabulari. Può calcolare statistiche come media, somma, conteggio, deviazione standard ecc., Su una griglia N-dimensionale fino a un miliardo (10 9 ) oggetti / righe al secondo. La visualizzazione viene eseguita utilizzando istogrammi, grafici di densità e rendering di volumi 3D, consentendo l'esplorazione interattiva di big data. Vaex utilizza la mappatura della memoria, la politica di copia della memoria zero e calcoli pigri per prestazioni ottimali (nessuna perdita di memoria).

Dai un'occhiata alla documentazione: https://vaex.readthedocs.io/en/latest/ L'API è molto vicina all'API di Panda.


0

Perché i panda? Hai provato Standard Python ?

L'uso della libreria standard python. Panda è soggetto a frequenti aggiornamenti, anche con la recente versione della versione stabile.

Usando la libreria standard di Python il tuo codice verrà sempre eseguito.

Un modo per farlo è quello di avere un'idea del modo in cui vuoi che i tuoi dati vengano archiviati e quali domande vuoi risolvere riguardo ai dati. Quindi traccia uno schema di come puoi organizzare i tuoi dati (pensa alle tabelle) che ti aiuterà a interrogare i dati, non necessariamente alla normalizzazione.

Puoi fare buon uso di:

  • elenco di dizionari per memorizzare i dati in memoria, un dict è una riga,
  • generatori per elaborare la riga di dati dopo riga per non overflow della RAM,
  • lista comprensione per interrogare i tuoi dati,
  • fare uso di Counter, DefaultDict, ...
  • archivia i tuoi dati sul tuo disco rigido usando qualunque soluzione di archiviazione tu abbia scelto, JSON potrebbe essere uno di questi.

Ram e HDD stanno diventando sempre più economici con il tempo e lo standard Python 3 è ampiamente disponibile e stabile.


-1

Al momento sto lavorando "come" te, solo su una scala inferiore, motivo per cui non ho un PoC per il mio suggerimento.

Tuttavia, mi sembra di avere successo nell'uso di pickle come sistema di memorizzazione nella cache e nell'esecuzione in outsourcing di varie funzioni in file - eseguendo questi file dal mio comando / file principale; Ad esempio, utilizzo un prepar_use.py per convertire i tipi di oggetto, dividere un set di dati in test, convalidare e prevedere un set di dati.

Come funziona la memorizzazione nella cache con il sottaceto? Uso le stringhe per accedere ai file pickle creati dinamicamente, a seconda di quali parametri e set di dati sono stati passati (con quello provo a catturare e determinare se il programma era già in esecuzione, usando .shape per set di dati, dict for passato parametri). Rispettando queste misure, ottengo una stringa per cercare di trovare e leggere un file .pickle e, se trovata, posso saltare il tempo di elaborazione per saltare all'esecuzione su cui sto lavorando in questo momento.

Usando i database ho riscontrato problemi simili, motivo per cui ho trovato gioia nell'usare questa soluzione, tuttavia ci sono molti vincoli, ad esempio la memorizzazione di enormi set di sottaceti a causa della ridondanza. L'aggiornamento di una tabella da prima a dopo una trasformazione può essere eseguito con un'indicizzazione corretta: la convalida delle informazioni apre un altro libro (ho provato a consolidare i dati dell'affitto scansionato e in pratica ho smesso di utilizzare un database dopo 2 ore), come mi sarebbe piaciuto tornare indietro dopo ogni processo di trasformazione)

Spero che i miei 2 centesimi ti aiutino in qualche modo.

Saluti.

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.