2012-09-22 29 views
5

Mi objetivo principal es calcular la mediana (por columnas) de una ENORME matriz de flotantes. Ejemplo:Python: obtenga el iterador de columna de un archivo (sin leer todo el archivo)

a = numpy.array(([1,1,3,2,7],[4,5,8,2,3],[1,6,9,3,2])) 

numpy.median(a, axis=0) 

Out[38]: array([ 1., 5., 8., 2., 3.]) 

La matriz es demasiado grande para caber en la memoria de Python (~ 5 terabytes), por lo que mantener en un archivo csv. Así que quiero correr sobre cada columna y calcular la mediana.

¿Hay alguna forma de que obtenga el iterador de columna sin leer todo el archivo?

Cualquier otra idea sobre el cálculo de la mediana de la matriz sería buena también. ¡Gracias!

+2

Vea también: http://stackoverflow.com/questions/1053928/python-numpy-very-large-matrices –

Respuesta

1

Lo haría al inicializar N archivos vacíos, uno para cada columna. Luego lea la matriz una fila a la vez y envíe cada entrada de columna al archivo correcto. Una vez que haya procesado la matriz completa, vuelva atrás y calcule la mediana de cada archivo secuencialmente.

Esto básicamente utiliza el sistema de archivos para hacer una transposición de matriz. Una vez transpuesto, calcular la mediana de cada fila es fácil.

+1

¡gracias por su respuesta!el tamaño de mi matriz es ~ 5 terabytes, me temo que no tengo suficiente almacenamiento para hacer esto :( – dbaron

3

Si usted puede caber cada columna en la memoria (que parece dar a entender que puede), entonces esto debería funcionar:

import itertools 
import csv 

def columns(file_name): 
    with open(file_name) as file: 
     data = csv.reader(file) 
     columns = len(next(data)) 
    for column in range(columns): 
     with open(file_name) as file: 
      data = csv.reader(file) 
      yield [row[column] for row in data] 

Esto funciona mediante la búsqueda de cuántas columnas que tenemos, a continuación, un bucle sobre el archivo , sacando el elemento de la columna actual de cada fila. Esto significa, como máximo, que estamos usando el tamaño de una columna más el tamaño de una fila de memoria a la vez. Es un generador bastante simple. Tenga en cuenta que tenemos que seguir volviendo a abrir el archivo, ya que agotamos el iterador cuando lo recorremos.

+0

Si volver a abrir el archivo es un problema, simplemente mueva el 'con' fuera del ciclo for y haga' file.seek (0) 'inside. –

+0

@MuMind Esa es una buena alternativa para reabrir una y otra vez (y también podría significar que podría pasar un objeto de archivo en caso de que no tenga un nombre de archivo por el motivo que sea). –

0

Puede usar bucketsort para ordenar cada una de las columnas en el disco sin leerlas todas en la memoria. Entonces puedes simplemente elegir el valor medio.

O puede utilizar los comandos UNIX awk y sort para dividir y ordenar sus columnas antes de seleccionar la mediana.

1

Probablemente no haya una forma directa de hacer lo que está pidiendo con un archivo csv (a menos que no lo haya entendido bien). El problema es que no hay un sentido significativo en el que un archivo tenga "columnas", a menos que el archivo esté especialmente diseñado para tener filas de ancho fijo. Los archivos CSV generalmente no se diseñan de esa manera. En el disco, que son nada más que una cadena gigante:

>>> import csv 
>>> with open('foo.csv', 'wb') as f: 
...  writer = csv.writer(f) 
...  for i in range(0, 100, 10): 
...   writer.writerow(range(i, i + 10)) 
... 
>>> with open('foo.csv', 'r') as f: 
...  f.read() 
... 
'0,1,2,3,4,5,6,7,8,9\r\n10,11,12,13,14,15,16,17,18,19\r\n20..(output truncated).. 

Como se puede ver, los campos de columna no lo alinea predecible; la segunda columna comienza en el índice 2, pero luego en la siguiente fila, el ancho de las columnas aumenta en uno, arrojando la alineación. Esto es aún peor cuando las longitudes de entrada varían. El resultado es que el lector csv tendrá que leer todo el archivo, descartando los datos que no usa. (Si no le importa eso, entonces esa es la respuesta: lea todo el archivo línea por línea, tirando los datos que no usará).

Si no le importa perder un poco de espacio y saber que ninguno de sus datos será más largo que un ancho fijo, podría crear un archivo con campos de ancho fijo, y luego podría buscar a través de él utilizando compensaciones. Pero luego, una vez que estás haciendo eso, también podrías comenzar a usar una base de datos real. PyTables parece ser la opción favorita de muchos para almacenar matrices numpy.

+1

+1 Si va para hacer esto más de una vez, CSV es una opción de formato pobre para mantenerlo. –

+0

@senderle DB es mi objetivo. ¿Sabes si numpy.loadtxt (file_path, usecols = [1,2,3]) hará el truco por ahora? – dbaron

+0

@dbaron, solo depende de lo que quieres decir con "haz el truco". Estoy bastante seguro de que 'usecols = [1, 2, 3]' evitará cargar toda la matriz en la memoria a la vez , así que en ese sentido, sí. También estoy bastante seguro de que leerá todo el archivo, línea por línea, arrojando datos no utilizados, por lo que en ese s ense, no. – senderle

Cuestiones relacionadas