Script per convertire enorme tavolo colonna Tre nella tabella
-
27-09-2019 - |
Domanda
Ho una serie di dati (file CSV) nel seguente formato 3 colonne:
A, B, C
3277,4733,54.1
3278,4741,51.0
3278,4750,28.4
3278,4768,36.0
3278,4776,50.1
3278,4784,51.4
3279,4792,82.6
3279,4806,78.2
3279,4814,36.4
E ho bisogno di ottenere un tavolo di contingenza a tre vie come: (scusate, questo non sembra del tutto buono)
A /B 4733 4741 4750 4768 4776 4784 4792 4806 4814
3277 C 54.1
3278 51 28.4 36 50.1 51.4
3279 82.6 78.2 36.4
Analogamente a una "tabella pivot" cbm pilota dati OpenOffice, o R "tavolo (x, y, z)"
Il problema è che il mio gruppo di dati è enorme (più di 500.000 righe totali, con circa 400 fattori diversi in A e B. (OOo, MSO e limiti R impediscono il raggiungimento di questo)
Sono sicuro che uno script Python potrebbe essere utilizzato per creare un tavolo. sia A che B sono numeri (ma possono essere trattati come stringhe).
Chiunque ha affrontato questo? (Pseudocodice o il codice in C o Java è inoltre accolto con favore ... ma io preferisco Python in quanto è più veloce da implementare:)
Modifica Quasi avere, grazie a John Machin. Il seguente script Python quasi fornisce quello che sto cercando, tuttavia, quando si scrive il file di output posso vedere che i valori nelle "headers" Scrivo (tratto dalla prima fila) non corrispondono alle altre righe.
from collections import defaultdict as dd
d = dd(lambda: dd(float))
input = open("input.txt")
output = open("output.txt","w")
while 1:
line = input.readline()
if not line:
break
line = line.strip('\n').strip('\r')
splitLine = line.split(',')
if (len(splitLine) <3):
break
d[splitLine[0]][splitLine[1]] = splitLine[2]
output.write("\t")
for k,v in d.items()[0][1].items():
output.write(str(k)+"\t")
output.write("\n")
for k,v in d.items():
output.write(k+"\t")
for k2,v2 in v.items():
output.write(str(v2)+"\t")
output.write("\n")
Soluzione
Tutta la nuova storia merita tutta una risposta nuova.
Non è necessario defaultdict, non voglio neppure defaultdict, perché usando con noncuranza avrebbe fatto schifo memoria come raggio traente della Morte Nera.
Questo codice è testato, può anche non compilare; Forse ho scambiato righe e colonne da qualche parte; correzioni / spiegazioni più tardi ... deve correre ...
d = {}
col_label_set = set()
row_label_set = set()
input = open("input.txt")
output = open("output.txt","w")
for line in input:
line = line.strip()
splat = line.split(',')
if len(splat) != 3:
break # error message???
k1, k2, v = splat
try:
subdict = d[k1]
except KeyError:
subdict = {}
d[k1] = subdict
subdict[k2] = v
row_label_set.add(k1)
col_label_set.add(k2)
col_labels = sorted(col_label_set)
row_labels = sorted(row_label_set
output.write("\t")
for v in col_labels::
output.write(v + "\t")
output.write("\n")
for r in row_labels:
output.write(r + "\t")
for c in col_labels:
output.write(d[r].get(c, "") + "\t")
output.write("\n")
Aggiorna Ecco una versione fissa e riscritta, testato nella misura indicata:
class SparseTable(object):
def __init__(self, iterable):
d = {}
col_label_set = set()
for row_label, col_label, value in iterable:
try:
subdict = d[row_label]
except KeyError:
subdict = {}
d[row_label] = subdict
subdict[col_label] = value
col_label_set.add(col_label)
self.d = d
self.col_label_set = col_label_set
def tabulate(self, row_writer, corner_label=u"", missing=u""):
d = self.d
col_labels = sorted(self.col_label_set)
row_labels = sorted(d.iterkeys())
orow = [corner_label] + col_labels
row_writer(orow)
for row_label in row_labels:
orow = [row_label]
subdict = d[row_label]
for col_label in col_labels:
orow.append(subdict.get(col_label, missing))
row_writer(orow)
if __name__ == "__main__":
import sys
test_data = u"""
3277,4733,54.1
3278,4741,51.0
3278,4750,28.4
3278,4768,36.0
3278,4776,50.1
3278,4784,51.4
3279,4792,82.6
3279,4806,78.2
3279,4814,36.4
""".splitlines(True)
def my_writer(row):
sys.stdout.write(u"\t".join(row))
sys.stdout.write(u"\n")
def my_reader(iterable):
for line in iterable:
line = line.strip()
if not line: continue
splat = line.split(u",")
if len(splat) != 3:
raise ValueError(u"expected 3 fields, found %d" % len(splat))
yield splat
table = SparseTable(my_reader(test_data))
table.tabulate(my_writer, u"A/B", u"....")
Ecco l'output:
A/B 4733 4741 4750 4768 4776 4784 4792 4806 4814
3277 54.1 .... .... .... .... .... .... .... ....
3278 .... 51.0 28.4 36.0 50.1 51.4 .... .... ....
3279 .... .... .... .... .... .... 82.6 78.2 36.4
Altri suggerimenti
Quando tutto quello che hai è un martello. . . . .
Concettualmente, ciò che si sta cercando di fare è semplice, ma a causa delle dimensioni dei dati, è computazionalmente difficile. Io tendo ad usare R per la sua analitica e la grafica della capacità, non lo è di dati dispute competenze. Quando ho bisogno di muoversi un po 'di dati, di solito solo bastone tutto in un database.
Ultimamente ho avuto un bel po 'di successo con SQLite e R. La parte migliore è che si può effettivamente utilizzare R per leggere i dati, il che rende semplice l'importazione di grandi file SPSS o altre fonti di dati che può SQLite 't realmente gestire, ma R può.
http://cran.r-project.org/web/ pacchetti / RSQLite / index.html
Ecco il mio flusso di lavoro consigliata.
- Importa i dati in R. (Done)
- Library (RSQLite)
- Spostare il frame di dati per SQLite.
- Creare indici su colonne A e B.
- Creare una visualizzazione che costruisce la vostra tavola.
- Query vostra vista da R e costringere i rendimenti in una tabella.
In R posso fare questo:
N <- 1000000
x <- sample(1:400,N,TRUE)
y <- sample(1:400,N,TRUE)
z <- sample(1:400,N,TRUE)
w <- table(x,y,z)
E la memoria di picco è inferiore quindi 800MB.
Quindi, quali limitazioni hai?
EDIT. Questa pace di R-codice:
N <- 1000000
mydata <- data.frame(
A=sample(runif(400),N,TRUE),
B=sample(runif(400),N,TRUE),
C=runif(N)
)
require(reshape)
results <- cast(mydata, A~B, value="C")
write.table(as.matrix(results),na="",sep="\t",file="results.txt")
creare ciò che si vuole con meno di 300 MB di RAM.
Su miei dati dà causa di avviso ci sono combinazioni di A-B non univoci, ma per il vostro dovrebbe essere ok.
Se si potesse utilizzare table(x,y,z)
in R, allora come di provare la R di pacchetti di memoria che gestiscono tali insiemi di dati enormi? Utilizzare la funzione read.big.matrix
nel pacchetto bigmemory di leggere nel set di dati e la funzione bigtable
nel pacchetto bigtabulate per creare la tabella.
vignette .
Il tuo esempio di output desiderato non ha l'aspetto di una tabella di contingenza a 3 vie per me. Questo sarebbe una mappatura da (key1, key2, key3) per un conteggio di occorrenze. Il vostro esempio appare come una mappatura da (key1, key2) per qualche numero. non dici cosa fare quando viene duplicato (key1, key2): media, totale, qualcos'altro
Supponendo che si desidera un totale, ecco un approccio il risparmio di memoria in Python, utilizzando defaultdict
s nidificati:
>>> from collections import defaultdict as dd
>>> d = dd(lambda: dd(float))
>>> d[3277][4733] += 54.1
>>> d
defaultdict(<function <lambda> at 0x00D61DF0>, {3277: defaultdict(<type 'float'>, {4733: 54.1})})
>>> d[3278][4741] += 51.0
>>> d
defaultdict(<function <lambda> at 0x00D61DF0>, {3277: defaultdict(<type 'float'>, {4733: 54.1}), 3278: defaultdict(<type 'float'>, {4741: 51.0})})
>>>
e un altro approccio utilizzando un unico defaultdict
con una chiave composta:
>>> d2 = dd(float)
>>> d2[3277,4733] += 54.1
>>> d2
defaultdict(<type 'float'>, {(3277, 4733): 54.1})
>>> d2[3278,4741] += 51.0
>>> d2
defaultdict(<type 'float'>, {(3277, 4733): 54.1, (3278, 4741): 51.0})
>>>
potrebbe essere utile se si dovesse dire quello che vuoi a che fare con questi dati dopo ce l'hai raggruppato ...
Se si desidera (per esempio) una media, si hanno due opzioni: (1) strutture di due dati, uno per totale, uno per il conteggio, poi fare "= media totale - conta" (2) ordinare i dati sul prime 2 colonne, l'utente itertools.groupby per raccogliere i duplicati insieme, fanno il calcolo, e aggiungere i risultati nella vostra struttura dati "media". Quale di questi approcci userebbero meno memoria è difficile da dire; Python essendo Python si potrebbe provare entrambi piuttosto rapidamente.
Una piccola subclasse di dict in grado di fornire un oggetto comodo per lavorare con la tabella. 500.000 articoli non dovrebbero essere un problema su un PC desktop - se vi capita di avere 500.000.000 elementi, una classe simile potrebbe mappare dai tasti a posizioni nel file stesso (che sarebbe modo più cool per implementare :-))
import csv
class ContingencyTable(dict):
def __init__(self):
self.a_keys=set()
self.b_keys=set()
dict.__init__(self)
def __setitem__(self, key,value):
self.a_keys.add(key[0])
self.b_keys.add(key[1])
dict.__setitem__(self, key, value)
def feed(self, file):
reader = csv.reader(file)
reader.next()
for a, b, c in reader:
self[int(a),int(b)] = float(c)
table = ContingencyTable()
table.feed(open("yourfile.csv"))