2012-09-22 6 views
5

Il mio obiettivo principale è calcolare la mediana (per colonne) da una matrice ENORME di galleggianti. Esempio:Python - ottieni un iteratore di colonna da un file (senza leggere l'intero file)

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 matrice è troppo grande per entrare nella memoria di Python (~ 5 terabyte), così ho tenerlo in un file CSV. Quindi voglio correre su ogni colonna e calcolare la mediana.

C'è un modo per ottenere l'iteratore di colonna senza leggere l'intero file?

Qualsiasi altra idea sul calcolo della mediana per la matrice sarebbe buona. Grazie!

+2

Vedi anche: http://stackoverflow.com/questions/1053928/python-numpy-very-large-matrices –

risposta

1

Lo farei inizializzando N file vuoti, uno per ogni colonna. Quindi leggi la matrice una riga alla volta e invia ogni voce di colonna al file corretto. Una volta elaborata l'intera matrice, tornare indietro e calcolare la mediana di ciascun file in sequenza.

In pratica utilizza il filesystem per eseguire una trasposizione di matrici. Una volta trasposto, calcolare la mediana di ogni riga è facile.

+1

grazie per la vostra risposta!la mia dimensione della matrice è ~ 5 terabyte, temo di non avere spazio sufficiente per fare ciò :( – dbaron

3

Se è possibile adattare ogni colonna in memoria (che ti sembra implicare potete), allora questo dovrebbe funzionare:

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] 

Questo funziona da scoprire quante colonne che abbiamo, allora il ciclo sopra il file , prendendo l'elemento della colonna corrente da ogni riga. Questo significa, al massimo, stiamo usando la dimensione di una colonna più la dimensione di una riga di memoria in una volta. È un generatore piuttosto semplice. Nota che dobbiamo continuare a riaprire il file, mentre esteniamo l'iteratore quando lo attraversiamo.

+0

Se riaprire il file è un problema, basta spostare 'with' all'esterno del ciclo for e fare' file.seek (0) 'inside. –

+0

@MuMind Questa è una buona alternativa alla riapertura più e più volte (e significherebbe anche che potresti passare un oggetto file nel caso in cui non avessi un nome file per qualsiasi motivo) –

0

È possibile utilizzare bucketsort per ordinare ciascuna delle colonne sul disco senza leggerle tutte in memoria. Quindi puoi semplicemente scegliere il valore medio.

Oppure è possibile utilizzare i comandi UNIX awk e sort per suddividere e quindi ordinare le colonne prima di selezionare la mediana.

1

Probabilmente non c'è un modo diretto per fare quello che stai chiedendo con un file CSV (a meno che non ti abbia frainteso). Il problema è che non c'è alcun senso significativo in cui ogni file abbia "colonne" a meno che il file non sia appositamente progettato per avere file a larghezza fissa. I file CSV non sono generalmente progettati in questo modo. Sul disco, sono nient'altro che una stringa di 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).. 

Come si può vedere, i campi di colonna non si allineano prevedibile; la seconda colonna inizia dall'indice 2, ma poi nella riga successiva, la larghezza delle colonne aumenta di uno, eliminando l'allineamento. Ciò è ancora peggiore quando le lunghezze di input variano. Il risultato è che il lettore di csv dovrà leggere l'intero file, buttando fuori i dati che non usi. (Se non ti dispiace, allora questa è la risposta - leggi l'intero file riga per riga, buttando fuori i dati che non utilizzerai.)

Se non ti dispiace sprecare spazio e sapere che nessuno dei tuoi dati sarà più lungo di una larghezza fissa, potresti creare un file con campi a larghezza fissa, e quindi potrai cercarlo attraverso gli offset. Ma poi, una volta fatto, potresti anche iniziare a usare un vero database. PyTables sembra essere la scelta preferita di molti per la memorizzazione di array numpy.

+1

+1 Se stai andando per fare questo più di una volta, CSV è una scelta scadente di formato per tenerlo dentro. –

+0

@senderle DB è il mio obiettivo.Sapete se numpy.loadtxt (file_path, usecols = [1,2,3]) farà il trucco per ora? – dbaron

+0

@dbaron, dipende solo da cosa intendi per "fare il trucco". Sono abbastanza sicuro che 'usecols = [1, 2, 3]' eviterà di caricare l'intera matrice in memoria in una sola volta , quindi in questo senso, sì. Sono anche abbastanza sicuro che sarà _read_ l'intero file, riga per riga, buttando fuori i dati inutilizzati, quindi in quella s ense, no – senderle