Pregunta

Tengo un conjunto de datos (archivos CSV) en el siguiente formato de 3 columnas:

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 

Y que necesito para obtener una tabla de contingencia de tres vías como: (lo siento, esto no se ve completamente bueno)

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

De manera similar a una "tabla dinámica" excel, piloto de datos OpenOffice, o R "mesa (x, y, z)"

El problema es que mi conjunto de datos es enorme (más de 500.000 filas en total, con cerca de 400 factores diferentes en A y B. (OOo, MSO y los límites R impiden la consecución de este)

Estoy seguro de una secuencia de comandos de Python se podría utilizar para crear una tabla de este tipo. tanto A como B son números (pero pueden ser tratados como cadenas).

Cualquier persona se ha ocupado de esto? (Pseudocódigo o código en C o Java también es bienvenida ... pero yo prefiero Python, ya que es más rápido de implementar:)

Editar Casi lo tiene, gracias a John Machin. La siguiente secuencia de comandos de Python casi proporciona lo que yo estoy buscando, sin embargo, al escribir el archivo de salida que puedo ver que los valores de las "cabeceras" Estoy escribiendo (tomado de la primera fila) no corresponden a las otras filas.

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")
¿Fue útil?

Solución

Toda la nueva historia merece una respuesta completamente nuevo.

No necesito defaultdict, no quiero ni defaultdict, porque el uso de descuidadamente se chupan memoria como rayo tractor de la Estrella de la Muerte.

Este código no se ha probado, puede que ni siquiera compilar; Pude haber intercambiado filas y columnas en alguna parte; correcciones / explicaciones tarde ... debe precipitarse ...

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")

Actualizar Aquí hay una versión fija y refactorizado, probado en la medida de muestra:

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"....")

Aquí está la salida:

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

Otros consejos

Cuando todo lo que tienes es un martillo. . . . .

Conceptualmente, lo que estamos tratando de hacer es simple pero debido al tamaño de los datos, es computacionalmente difícil. Yo tiendo a usar R para que la analítica y gráfica de la capacidad, no la de los datos de las habilidades reúnes. Cuando necesito mover alrededor de un montón de datos, por lo general sólo se adhieren todo en una base de datos.

Últimamente he tenido un poco de éxito con SQLite y R. La mejor parte es que en realidad se puede utilizar R para leer en los datos, lo que hace que sea fácil para importar grandes archivos de SPSS u otras fuentes de datos que pueden SQLite 't realmente manejar, pero R puede.

http://cran.r-project.org/web/ paquetes / RSQLite / index.html

Aquí está mi flujo de trabajo recomendada.

  1. importar sus datos en R. (Hecho)
  2. Biblioteca (RSQLite)
  3. Mueva su trama de datos de SQLite.
  4. crear índices en las columnas A y B.
  5. Crear una vista que se basa su mesa.
  6. Consulta su vista desde R y coaccionar a los rendimientos en una tabla.

R puedo hacer esto:

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)
pico

Y la memoria es menor que 800 MB.

Entonces, ¿qué limitaciones tiene?


Editar. Esta paz de la I-código:

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")

crear lo que quiera con menos de 300 MB de RAM.

En mis datos que es motivo de advertencia Hay no únicos combinaciones A-B, sino por ustedes deben estar bien.

Si usted podría utilizar table(x,y,z) en R, entonces ¿qué hay de probar el R de paquetes de memoria que manejan este tipo de conjuntos de datos enormes? Utilice la función read.big.matrix en el paquete bigmemory para leer en el conjunto de datos y el bigtable función en el paquete bigtabulate para crear la tabla.

viñetas .

Su ejemplo de salida deseado no se ve como una tabla de contingencia de 3 vías para mí. Eso sería un mapeo de (key1, clave2, key3) a una cuenta de ocurrencias. Sus ejemplo sería un mapeo de (key1, clave2) a un número. Usted no dicen lo que hay que hacer cuando (key1, clave2) se duplica:? Promedio, total, algo más

Si se asume que desea un total, aquí hay un enfoque de ahorro de memoria en Python, utilizando defaultdicts anidados:

>>> 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})})
>>>

y otro enfoque utilizando un único defaultdict con una clave compuesta:

>>> 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})
>>>

Podría ayudar si tuviera que decir lo que quiere hacer con estos datos después de que lo tienes agrupados ...

Si desea (por ejemplo) un promedio, tiene dos opciones: (1) las estructuras de dos datos, uno para el total, uno para el recuento, y luego hacer "Media = Total - recuento" (2) Ordenar los datos en una 2 primeras columnas, el usuario itertools.groupby para recoger sus duplicados juntos, hacer el cálculo, y se suman los resultados en su estructura de datos "promedio". ¿Cuál de estos enfoques se utilizan menos memoria es difícil de decir; Python Python es que podría tratar tanto con bastante rapidez.

Una pequeña subclasse de dict le puede proporcionar un objeto cómodo para trabajar con la tabla. 500.000 artículos no debería ser un problema en un PC de escritorio - si le sucede que tiene 500.000.000 objetos, una clase similar podría asignar a las teclas a posiciones en el propio archivo (que sería mucho más fresco para poner en práctica :-))

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"))
Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top