Question

J'ai un ensemble de données (fichiers CSV) dans le format 3 colonne suivante:

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 

Et je dois obtenir un à trois voies tableau de contingence comme: (désolé, cela ne semble pas tout à fait bien)

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 la même façon à une excel "table de pivot", pilote de données OpenOffice, ou R "tableau (x, y, z)"

Le problème est que mon jeu de données est énorme (plus de 500.000 lignes au total, avec environ 400 facteurs différents A et B. (OOo, limites MSO et R empêchent de parvenir)

Je suis sûr un script Python pourrait être utilisé pour créer une telle table. A et B sont des nombres (mais peuvent être traités comme des chaînes).

Toute personne a traité ce sujet? (Ou un code pseudo-code en C ou Java est également bien accueillie ... mais je préfère python comme il est plus rapide à mettre en œuvre:)

Edit: Presque avoir, grâce à John Machin. Le script Python suivant presque fournit ce que je cherche, mais, lors de l'écriture du fichier de sortie, je peux voir que les valeurs dans les « têtes » Je suis en train d'écrire (extrait de la première ligne) ne correspondent pas pour les autres rangées.

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")
Était-ce utile?

La solution

Toute nouvelle histoire mérite une nouvelle réponse entière.

Ne pas besoin defaultdict, ne veulent même pas defaultdict, parce que son utilisation serait négligemment sucer la mémoire comme le rayon tracteur de l'étoile de la mort.

Ce code est non testé, ne peut même compiler; J'ai échangé des lignes et des colonnes quelque part; corrections / explications plus tard ... doit se précipiter ...

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

Mise à jour Voici une version fixe et refondus, testé dans la mesure indiquée:

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

Voici la sortie:

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

Autres conseils

Quand tout vous est un marteau. . . . .

Conceptuellement, ce que vous essayez de faire est simple, mais en raison de la taille de vos données, il est difficile informatiquement. J'ai tendance à utiliser R pour elle sa capacité d'analyse et de graphiques, et non ses données compétences quereller. Quand j'ai besoin de se déplacer autour d'un tas de données, je habituellement juste bâton tout dans une base de données.

Dernièrement, j'ai eu un peu de succès avec SQLite et R. La meilleure partie est que vous pouvez réellement utiliser R pour lire dans vos données, ce qui le rend facile d'importer de gros fichiers SPSS ou d'autres sources de données SQLite peut « t vraiment gérer, mais R peut.

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

Voici mon flux de travail recommandé.

  1. Importer vos données dans R. (Terminé)
  2. Bibliothèque (RSQLite)
  3. Déplacez votre cadre de données SQLite.
  4. Création d'index sur les colonnes A et B.
  5. Créer une vue qui construit votre table.
  6. Requête votre point de vue de la R et les contraindre dans une table des retours.

R Je peux faire ceci:

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)

et le pic de la mémoire est plus faible puis 800MB.

Alors quelles sont les limites que vous avez?


EDIT. Cette paix de R-code:

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

créer ce que vous voulez avec moins de 300 Mo de RAM.

Sur mes données, il donne cause de l'avertissement il y a des combinaisons A-B non-unique, mais pour le vôtre devrait être ok.

Si vous pouvez utiliser table(x,y,z) dans R, alors que diriez-vous d'essayer le R de paquets de mémoire qui gèrent ces énormes ensembles de données? Utiliser la fonction de read.big.matrix dans le paquet BigMemory à lire dans l'ensemble de données et le fonction bigtable dans le paquet bigtabulate pour créer la table.

Voir vignettes.

Votre exemple de sortie désirée ne ressemble pas à un tableau de contingence 3 voies pour moi. Ce serait une application de (key1, key2, key3) à un compte de occurences. Votre exemple ressemble à une application de (key1, key2) à un certain nombre. Vous ne dites pas quoi faire quand (key1, key2) est dupliquée: moyenne, total, autre chose

En supposant que vous voulez un total, voici une approche économiser de la mémoire en Python, en utilisant defaultdicts imbriqués:

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

et une autre approche à l'aide d'une seule defaultdict avec une clé composite:

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

Il peut être utile si vous deviez dire ce que vous voulez faire avec ces données une fois que vous l'avez regroupés ...

Si vous voulez (par exemple) en moyenne, vous avez deux options: (1) deux structures de données, un pour le total, un pour le nombre, alors ne « = moyen - nombre » (2) trier vos données sur le 2 premières colonnes, l'utilisateur itertools.groupby pour recueillir vos doublons ensemble, faites vos calculs et ajouter les résultats dans votre structure de données « moyenne ». Laquelle de ces approches utilisent moins de mémoire est difficile de dire; Python étant Python vous pouvez essayer à la fois assez rapidement.

Une petite subclasse de dict peut vous offrir un objet confortable de travailler avec la table. 500.000 articles ne devraient pas être un problème sur un PC de bureau - si vous arrive d'avoir 500.000.000 articles, une classe similaire pourrait mapper des clés à des postes dans le fichier lui-même (ce serait de façon plus refroidir à mettre en œuvre :-))

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"))
Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top