Domanda

Ho un sacco di set di dati CSV, di circa 10 GB ciascuno.Mi piacerebbe generare istogrammi dalle loro colonne.Ma sembra che l'unico modo per farlo in Numpy sia prima caricare l'intera colonna in un array Numpy e poi chiamare numpy.histogram su quella matrice.Ciò consuma una quantità non necessaria di memoria.

Numpy supporta il binning online?Spero in qualcosa che esegua il mio CSV riga per riga e raccogli i valori mentre li legge.In questo modo è presente in memoria al massimo una riga alla volta.

Non sarebbe difficile farla rotolare da sola, ma mi chiedevo se qualcuno avesse già inventato questa ruota.

È stato utile?

Soluzione

Come hai detto, non è così difficile realizzarne uno tuo.Dovrai impostare tu stesso i contenitori e riutilizzarli mentre esegui l'iterazione del file.Quanto segue dovrebbe essere un buon 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

Immagino che le prestazioni saranno un problema con file così grandi e il sovraccarico della chiamata dell'istogramma su ciascuna riga potrebbe essere troppo lento. Il suggerimento di @doug di un generatore sembra un buon modo per risolvere questo problema.

Altri suggerimenti

Ecco un modo per raggruppare direttamente i tuoi valori:

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 appartiene ciascun valore in column_of_values.

'bincount' ti darà (ovviamente) il conteggio dei contenitori:

NP.bincount(binned_values)

Data la dimensione del tuo set di dati, potrebbe essere utile utilizzare "loadtxt" di Numpy per creare un generatore:

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

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

sto postando una seconda risposta alla stessa domanda dal momento che questo approccio è molto diverso, e affronta tematiche differenti.

Che cosa succede se si dispone di un grande insieme di dati (miliardi di campioni), e non si sa in anticipo dove i vostri confini bin dovrebbero essere? Per esempio, forse si vuole bin cose per quartili o decili.

Per piccoli gruppi di dati, la risposta è semplice:. Caricare i dati in un array, quindi ordinare, poi leggere i valori in qualsiasi percentile saltando all'indice quella percentuale della strada attraverso l'array

Per i grandi insiemi di dati in cui la dimensione della memoria per contenere la matrice non è pratico (per non parlare del tempo per risolvere) ... poi considerare l'utilizzo di un albero di Fenwick, alias un "binario indicizzato Tree".

Credo che questi funzionano solo per i dati di interi positivi, quindi avrete almeno bisogno di conoscere abbastanza il set di dati di spostare (e possibilmente scala) i dati prima di catalogare nella struttura ad albero Fenwick.

Ho usato questo per trovare la mediana di un set di dati campione di 100 miliardi, 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:

Discretizza con generatori ( gamma di dati; bin a larghezza fissa; galleggiare dati )

Se si conosce la larghezza dei contenitori desiderati prima del tempo - anche se ci sono centinaia o migliaia di secchi - allora penso a rotazione la propria soluzione sarebbe quella veloce (sia di scrivere, e per l'esecuzione). 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 è supponendo che si utilizza un binwidth intero; potrebbe essere necessario modificare questo un po 'se si desidera utilizzare un binwidth di un certo 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 iter metodo () 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 essere esso stesso un generatore, e utilizzare 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
Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top