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.
- Dimensione dei dati, numero di righe, colonne, tipi di colonne; stai aggiungendo righe o solo colonne?
- 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. ) - Dopo quell'elaborazione, cosa fai? Il passaggio 2 è ad hoc o ripetibile?
- 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?
- 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)?
- 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!