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")
È stato utile?

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.

  1. Importa i dati in R. (Done)
  2. Library (RSQLite)
  3. Spostare il frame di dati per SQLite.
  4. Creare indici su colonne A e B.
  5. Creare una visualizzazione che costruisce la vostra tavola.
  6. 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 defaultdicts 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"))
Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top