MongoDB
 sql >> Database >  >> NoSQL >> MongoDB

Flussi di lavoro di dati di grandi dimensioni utilizzando i panda

Uso abitualmente decine di gigabyte di dati proprio in questo modo, ad es. Ho tabelle su disco che leggo tramite query, creo dati e aggiungo indietro.

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 possibili; 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. Per esempio. esegui una query sulle colonne per selezionare un gruppo di righe e colonne specifiche, quindi esegui un'operazione (in memoria), crea nuove colonne, salvale.
    (Fornire un esempio di giocattolo potrebbe consentirci di offrire consigli più specifici. )
  3. Dopo quell'elaborazione, cosa fai? Il passaggio 2 è ad hoc o ripetibile?
  4. Inserisci file flat:quante dimensioni totali approssimative in Gb. Come sono organizzati ad es. da record? Ognuno contiene campi diversi o hanno alcuni record per file con tutti i campi in ogni file?
  5. Selezioni mai sottoinsiemi di righe (record) in base a criteri (ad es. 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 colonne (in gruppi) o c'è una buona proporzione che puoi utilizzare solo per i rapporti (ad es. desideri mantenere i dati in giro, ma non è necessario inserirli colonna in modo esplicito fino all'ora dei risultati finali)?

Soluzione

Assicurati di avere i panda almeno 0.10.1 installato.

Leggi l'iterazione dei file pezzo per pezzo e le query su più tabelle.

Poiché pytables è ottimizzato per operare in base alle righe (che è ciò su cui esegui query), creeremo una tabella per ogni gruppo di campi. In questo modo è facile selezionare un piccolo gruppo di campi (che funzionerà con una grande tabella, ma è più efficiente farlo in questo modo... penso di poter correggere questa limitazione in futuro... questo è comunque più intuitivo):
(Quello che segue è uno 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 lo spazio di archiviazione (essenzialmente facendo ciò che append_to_multiple fa):

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 salvarle in file separati se lo desideri, probabilmente dovresti aggiungere il nome del file alla mappa_gruppo, ma probabilmente questo 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 la post_elaborazione:

# 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)

Per quanto riguarda data_columns, in realtà non è necessario definire ANY colonne_dati; ti consentono di sottoselezionare le righe in base alla colonna. Per esempio. qualcosa come:

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

Potrebbero 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 mappa_gruppi, quindi li seleziona e concatena i risultati in modo da ottenere il frame risultante (questo è essenzialmente ciò che fa select_as_multiple). In questo modo la struttura sarebbe abbastanza trasparente per te.
  • indicizza su determinate colonne di dati (rende molto più veloce il subset delle righe).
  • abilita la compressione.

Fammi sapere quando hai domande!