2010-03-17 20 views
13

Ho un sacco di set di dati csv, circa 10 GB di dimensione ciascuno. Mi piacerebbe generare istogrammi dalle loro colonne. Ma sembra che l'unico modo per fare questo in numpy è di caricare prima l'intera colonna in una matrice numpy e quindi chiamare numpy.histogram su quella matrice. Questo consuma una quantità inutile di memoria.Istogramma di Numpy di array di grandi dimensioni

Il numpy supporta il binning online? Sto sperando in qualcosa che itera sulla mia csv riga per riga e valori di bins mentre li legge. In questo modo al massimo una riga è in memoria in qualsiasi momento.

Non sarebbe difficile far rotolare il mio, ma mi chiedo se qualcuno abbia già inventato questa ruota.

risposta

9

Come hai detto, non è difficile da rotolare. Dovrai configurare tu stesso i contenitori e riutilizzarli mentre esegui l'iterazione sul file. Quanto segue dovrebbe essere un dignitoso punto di partenza:

import numpy as np 
datamin = -5 
datamax = 5 
numbins = 20 
mybins = np.linspace(datamin, datamax, numbins) 
myhist = np.zeros(numbins-1, dtype='int32') 
for i in range(100): 
    d = np.random.randn(1000,1) 
    htemp, jnk = np.histogram(d, mybins) 
    myhist += htemp 

sto cercando di indovinare le prestazioni sarà un problema con tali file di grandi dimensioni, e il sovraccarico di chiamare istogramma su ciascuna linea potrebbe essere troppo lento. @doug's suggestion di un generatore sembra un buon modo per risolvere questo problema.

+0

Buona soluzione. Se vuoi renderlo un po 'più veloce, puoi fare 'myhist + = htemp' (penso che sia più veloce perché aggiorna l'istogramma in posizione). – EOL

+0

Grazie a @EOL. Ho dimenticato alcune delle belle funzionalità di Python perché non sono completamente passato da Octave. E poi ci sono le funzionalità avanzate come i generatori che devo ancora imparare. – mtrw

6

Ecco un modo per bin i valori direttamente:

import numpy as NP 

column_of_values = NP.random.randint(10, 99, 10) 

# set the bin values: 
bins = NP.array([0.0, 20.0, 50.0, 75.0]) 

binned_values = NP.digitize(column_of_values, bins) 

'binned_values' è un array di indici, contenente l'indice del contenitore a cui ciascun valore column_of_values ​​frazione.

'bincount' vi darà (ovviamente) i conteggi dei bin:

NP.bincount(binned_values) 

Date le dimensioni del set di dati, utilizzando di Numpy 'loadtxt' di costruire un generatore, potrebbe essere utile:

data_array = NP.loadtxt(data_file.txt, delimiter=",") 
def fnx() : 
    for i in range(0, data_array.shape[1]) : 
    yield dx[:,i] 
+3

Ma non caricheresti per prima cosa l'intero file in memoria prima? Questo è esattamente il problema che voglio evitare. –

2

Binning con generatori (grande insieme di dati; bin a larghezza fissa; float dati)

Se si conosce la larghezza dei contenitori desiderati prima del tempo - anche se ci sono centinaia o migliaia di secchi - Quindi penso che il rollover della propria soluzione sarebbe veloce (sia per scrivere che per eseguire). Ecco alcuni Python che presuppone che un iteratore che ti dà il valore successivo dal file:

from math import floor 
binwidth = 20 
counts = dict() 
filename = "mydata.csv" 
for val in next_value_from_file(filename): 
    binname = int(floor(val/binwidth)*binwidth) 
    if binname not in counts: 
     counts[binname] = 0 
    counts[binname] += 1 
print counts 

I valori possono essere galleggianti, ma questo sta assumendo si utilizza un binwidth intero; potrebbe essere necessario modificarlo leggermente se si desidera utilizzare una larghezza di banda di un valore float.

Per quanto riguarda next_value_from_file(), come accennato in precedenza, probabilmente si vorrà scrivere un generatore personalizzato o un oggetto con un metodo iter() non farlo in modo efficiente.Il pseudocodice per un tale generatore sarebbe questo:

def next_value_from_file(filename): 
    f = open(filename) 
    for line in f: 
    # parse out from the line the value or values you need 
    val = parse_the_value_from_the_line(line) 
    yield val 

Se una data linea ha più valori, quindi fare parse_the_value_from_the_line() restituire un elenco o sé tramite un generatore, Utilizzando questo pseudocodice:

def next_value_from_file(filename): 
    f = open(filename) 
    for line in f: 
    for val in parse_the_values_from_the_line(line): 
     yield val 
3

Binning con un albero Fenwick(molto grande insieme di dati; confini percentile necessario)

sto postando un secondo un passare alla stessa domanda poiché questo approccio è molto diverso e affronta diversi problemi.

Cosa succede se si dispone di un set di dati MOLTO grande (miliardi di campioni) e non si sa in anticipo DOVE dovrebbero essere i limiti del contenitore? Ad esempio, forse vuoi mettere le cose in ordine in quartili o decili.

Per i set di dati di piccole dimensioni, la risposta è semplice: caricare i dati in un array, quindi ordinare, quindi leggere i valori in ogni dato percentile saltando all'indice tale percentuale del modo attraverso l'array.

Per dataset di grandi dimensioni in cui la dimensione della memoria per contenere l'array non è pratica (per non parlare del tempo di ordinare) ... quindi considerare l'utilizzo di un albero di Fenwick, noto anche come "Binary Indexed Tree".

Penso che funzionino solo per dati interi positivi, quindi avrai almeno bisogno di sapere abbastanza sul set di dati per spostare (e possibilmente scalare) i dati prima di tabularli nell'albero di Fenwick.

L'ho usato per trovare la mediana di un set di dati di 100 miliardi di campioni, in tempi ragionevoli e limiti di memoria molto confortevoli. (Considerare l'utilizzo di generatori per aprire e leggere i file, come per la mia altra risposta, che è ancora utile.)

Maggiori info su Fenwick Alberi:

+0

Il conteggio è indipendente dall'ordine e non richiede il caricamento simultaneo dei dati in un array né l'ordinamento. – rafaelvalle

Problemi correlati