2010-03-17 18 views
13

Tengo un montón de conjuntos de datos csv, de aproximadamente 10 Gb de tamaño cada uno. Me gustaría generar histogramas a partir de sus columnas. Pero parece que la única forma de hacer esto en numpy es primero cargar toda la columna en una matriz numpy y luego llamar al numpy.histogram en esa matriz. Esto consume una cantidad innecesaria de memoria.Histograma nude de matrices grandes

¿Numpy admite binning en línea? Estoy esperando algo que itere sobre mi csv línea por línea y valores de los depósitos mientras los lee. De esta manera, como máximo, una línea está en la memoria en cualquier momento.

No sería difícil cambiar el mío, pero me pregunto si alguien ya inventó esta rueda.

Respuesta

9

Como dijiste, no es tan difícil hacer las tuyas. Tendrá que configurar los contenedores usted mismo y volver a utilizarlos mientras itera sobre el archivo. Lo siguiente debe ser un punto de partida decente:

rendimiento
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 

supongo será un problema con este tipo de archivos de gran tamaño, y la sobrecarga de llamar histograma en cada línea podría ser demasiado lento. @doug's suggestion de un generador parece una buena manera de abordar ese problema.

+0

Buena solución. Si quieres hacerlo un poco más rápido, puedes hacer 'myhist + = htemp' (supongo que es más rápido porque actualiza el histograma en su lugar). – EOL

+0

Gracias @EOL. Olvidé algunas de las bonitas funciones de Python porque no cambié completamente de Octave. Y luego están las características avanzadas como generadores que aún tengo que aprender. – mtrw

6

Aquí está una manera a bin sus valores directamente:

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' es una matriz de índice, que contiene el índice de la papelera a la que pertenece cada valor en column_of_values.

'bincount' le dará (obviamente) los recuentos de intervalos:

NP.bincount(binned_values) 

Dado el tamaño del conjunto de datos, usando 'loadtxt' de Numpy para construir un generador, podría ser útil:

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

¿Pero no cargaría loadtxt todo el archivo en la memoria primero? Ese es exactamente el problema que quiero evitar. –

2

Agrupación con los generadores de (gran conjunto de datos; contenedores de ancho fijo; flotar datos)

Si conoce el ancho de los contenedores deseados antes de tiempo - incluso si hay cientos o miles de cubos - Entonces creo que rodar tu propia solución sería rápido (tanto para escribir como para ejecutar). Aquí hay algo de Python que se supone que tiene un iterador que le da el siguiente valor del archivo:

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 

Los valores pueden ser flotadores, pero esto es suponiendo que se utiliza un número entero binwidth; Es posible que necesite modificar esto un poco si desea utilizar un ancho de banda de algún valor flotante.

En cuanto a next_value_from_file(), como se mencionó anteriormente, es probable que desea escribir un generador personalizado o un objeto con un método iter() no hacer esto de manera eficiente.El pseudocódigo para un generador de este tipo sería la siguiente:

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 

Si una línea dada tiene múltiples valores, a continuación, hacer parse_the_value_from_the_line() o bien devolver una lista o ser en sí mismo un generador, y utilizar este pseudocódigo:

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

de intervalos con un árbol Fenwick(muy grande conjunto de datos; los límites necesarios percentil)

les dejo un segundo una Haga clic en la misma pregunta, ya que este enfoque es muy diferente y aborda diferentes problemas.

¿Qué sucede si tiene un conjunto de datos MUY grande (miles de millones de muestras) y no sabe de antemano DONDE deberían ser los límites de su contenedor? Por ejemplo, tal vez quiera agrupar las cosas en cuartiles o deciles.

Para pequeños conjuntos de datos, la respuesta es fácil: cargue los datos en una matriz, luego ordene, luego lea los valores en cualquier percentil dado saltando al índice ese porcentaje del camino a través de la matriz.

Para grandes conjuntos de datos donde el tamaño de la memoria para mantener la matriz no es práctico (sin mencionar el tiempo para ordenar) ... entonces considere usar un Árbol Fenwick, también conocido como "Árbol binario indexado".

Creo que estos solo funcionan para datos enteros positivos, por lo que al menos necesitará saber lo suficiente acerca de su conjunto de datos para cambiar (y posiblemente escalar) sus datos antes de tabularlo en Fenwick Tree.

He usado esto para encontrar la mediana de un conjunto de datos de muestra de 100 mil millones, en un tiempo razonable y límites de memoria muy cómodos. (Considere el uso de generadores para abrir y leer los archivos, como por mi otra respuesta, eso es todavía útil.)

Más sobre Fenwick árboles:

+0

El recuento es independiente del orden y no requiere cargar todos los datos a la vez en una matriz ni clasificarlos. – rafaelvalle

Cuestiones relacionadas