Script pour convertir colonne de table trois énormes dans la table
-
27-09-2019 - |
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")
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é.
- Importer vos données dans R. (Terminé)
- Bibliothèque (RSQLite)
- Déplacez votre cadre de données SQLite.
- Création d'index sur les colonnes A et B.
- Créer une vue qui construit votre table.
- 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.
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 defaultdict
s 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"))