1
votes

Façonnez une énorme table de données (1000000 × 4000 alias 8 Go) de large à longue

Il y a ce fichier CSV de 8 Go sur mon disque. Il a une "correspondance" par ligne.

Une "correspondance" se compose de certaines données telles que id , date et gagnant . Mais il contient également 10 joueurs avec toutes leurs données. Ceux-ci sont stockés dans participants.0.stats.visionScore , participants.1.stats.visionScore , ..., participants.0.stats.assists code>, ..., participants.9.stats.assists , ... je pense que vous obtenez le modèle. Il ne s'agit que de participants. {Number} .stats. {Variable_name} . Chaque participant a littéralement des centaines de statistiques; c'est pourquoi j'ai environ 4 000 colonnes au total.

J'ai lu les données comme ceci:

> [magic]
> head(d)
   participant             stats.totalDamageDealt
1:           1                             118504
2:           2                             190143
3:           3                              46700
4:           4                              60787
5:           5                              78108
6:           6                             124761
                  stats.totalDamageTaken                stats.totalPlayerScore
1:                                 18218                                     0
2:                                 15794                                     0
3:                                 34578                                     0
4:                                 78771                                     0
5:                                 16749                                     0
6:                                 11540                                     0
...

Bien sûr, je veux maintenant une représentation des données , où une ligne correspond à un participant. J'imagine un résultat comme celui-ci:

> d <- fread("Matches.csv")
> head(d)
   participants.1.stats.totalDamageDealt
1:                                118504
2:                                 20934
3:                                 76639
4:                                123932
5:                                160561
6:                                237046
   participants.8.stats.totalDamageTaken participants.9.stats.totalPlayerScore
1:                                 18218                                     0
2:                                 12378                                     0
3:                                 46182                                     0
4:                                 19340                                     0
5:                                 30808                                     0
6:                                 36194                                     0
... [there are thousands of lines I omit here] ...

Mais toutes les méthodes qui existent, comme meld , cast , et reshape aura besoin de moi pour nommer toutes les colonnes à la main. Même avec des modèles pour meld , je finis par devoir nommer toutes mes centaines de colonnes par participant. N'y a-t-il aucun moyen de donner une forme longue à cette chose en R?


6 commentaires

Quelque chose comme la bibliothèque (data.table); setDT (d); melt (d, measure = patterns ("^ participants"), value.name = "participant") . Ensuite, vous pouvez extraire le numéro de participant et les statistiques dans leurs propres colonnes avec Regex. Ensuite, dcast en utilisant stat et value comme paire clé-valeur, pour obtenir un résultat d'une ligne par ID.


Si vous fournissez un échantillon de vos données avec dput (head (d [ 1: 10], 10)) , il serait beaucoup plus facile de créer un exemple fonctionnel.


J'aborderais cela en rassemblant d'abord tous les en-têtes avec tidyr :: collecte (d, stat, valeur). Ensuite, analysez stat` en deux colonnes, une pour le participant et une pour le titre générique de la statistique. Puis tidyr :: spread en fonction du titre de la statistique.


@ Mako212 Voici la sortie exacte lorsque j'exécute votre commande: pastebin.com/PkMeTaAX


Voici les 2 premières lignes complètes de mes données au format CSV: pastebin.com/0rsuATXP


@JonSpring la sortie pour tidyr :: collecte (d, stat, valeur) est Fehler: kann Vektor der Größe 21,1 Go nicht allozieren qui est l'allemand pour Erreur: impossible d'allouer un vecteur de taille 21,1 Go .


3 Réponses :


0
votes

Je ne suis pas sûr à 100% de comprendre comment les données sont présentées, mais je pense l'avoir. D'après les données d'exemple, il semble que le participant 1 dispose de plusieurs lignes de données pour totalDamageDealt à partir des données brutes et que le résultat ne nécessite pas d'agrégation. Si ce n'est pas le cas, différentes étapes peuvent être nécessaires. J'ai dû créer mes propres exemples de données pour essayer de l'exécuter. Si vous souhaitez publier un ensemble minimal de données couvrant toutes les possibilités, ce serait utile.

Sinon, voici quelques méthodes pour rendre les données complètement longues pour extraire les informations des participants, puis à nouveau larges pour les rendre au format souhaité. Si vous avez besoin d'une agrégation lors de l'élargissement des données, cela se produira probablement à l'étape dcast .

library(data.table)
library(stringr)

# Create example data
dt <- data.table(participant.1.stats.visionScore = c(1,1.1,1.2,1.3,1.4,1.5),
           participant.1.stats.totalDamageDealt = c(7.1,8.1,9.1,10.1,11.1,12.1),
           participant.2.stats.visionScore = c(2,2.1,2.2,2.3,2.4,2.5),
           participant.2.stats.totalDamageDealt = c(7.2,8.2,9.2,10.2,11.2,12.2))

# Make data totally long (not wide at all)
dt <- melt(dt,measure.vars = names(dt))

# Separate participant and stat details into columns
dt[,participant := variable %>% str_extract("(?<=^participant\\.)\\d+")]
dt[,stat := variable %>% str_extract("(?<=.stats.).+")]

# Remove variable for cleanup
dt[,variable := NULL]

# Create an index to create a unique key in order to be able to dcast without aggregating
dt[,index := 1:.N, by = list(participant,stat)]

# dcast to make the data wide again
dt <- dcast(dt,index + participant ~ stat, value.var = "value")

# Sort to make it easier for a human to view the table
dt <- dt[order(participant)]

#     index participant totalDamageDealt visionScore
# 1:      1           1              7.1         1.0
# 2:      2           1              8.1         1.1
# 3:      3           1              9.1         1.2
# 4:      4           1             10.1         1.3
# 5:      5           1             11.1         1.4
# 6:      6           1             12.1         1.5
# 7:      1           2              7.2         2.0
# 8:      2           2              8.2         2.1
# 9:      3           2              9.2         2.2
# 10:     4           2             10.2         2.3
# 11:     5           2             11.2         2.4
# 12:     6           2             12.2         2.5


0 commentaires

0
votes

D'accord, en utilisant l'échantillon de données que vous avez fourni:

  ID participants.4.timeline.xpPerMinDeltas.20-30 stats.goldEarned stats.perk3Var1
1  1                                            0                0               0
2  4                                           NA                0            3475
3  7                                            0                0               0
4  8                                            0                0               0
5  9                                            0           105872               0

Modifier: réécrit ceci comme une solution data.table uniquement pour la vitesse

Notez que vous avez des colonnes supplémentaires dans vos données sources comme participantIdentities.6.player.accountId que vous ne répondez pas, donc je les ai simplement exclues. S'ils doivent être inclus, vous les ajouterez soit aux modèles , soit à id.vars dans melt .

Une remarque: toutes les valeurs que vous lancez doivent être numériques, sinon dcast échouera. Je pense que ce sera un problème avec l'ensemble de vos données. Cela signifie que vous devez soit identifier correctement les colonnes telles que participants.1.highestAchievedSeasonTier comme id.vars dans melt , soit les exclure de dcast .

Résultat (je ne fais que coller les 4 premières colonnes de plusieurs)

library(data.table)

setDT(d) 

d <- melt(d, measure = patterns("^participants"), value.name = "value")

d <- d[,  `:=` (ID = gsub(".*?\\.(\\d+)\\..*","\\1", variable),
                stats = gsub(".*?(stats\\..*)$","\\1", variable))
  ][, .(variable, value, ID, stats)]
d <- dcast(d, ID ~ stats, value.var= "value", fun.aggregate = sum)


6 commentaires

Sur papier, votre solution a l'air cool, mais même avec seulement 50k de mes 1 000k lignes, la ligne d <- melt (d, measure = patterns ("^ participants"), value.name = "value") a besoin de plus de 30 Go de RAM! R s'arrête juste. C'est peut-être parce qu'il essaie de convertir toutes les colonnes en type «caractère» (ce qui est nécessaire, car certaines d'entre elles sont vraiment de ce type).


Hmm, essayez de supprimer les colonnes non numériques, juste pour voir si cela résout le problème de mémoire: l1 <- unlist (lapply (d, is.numeric)); d <- d [ l1]


Oui, bonne idée, mais il continue d'allouer autant de RAM. Bien sûr, il ne dit plus qu'il se convertit en type «caractère», donc je suppose que ce n'était pas le problème en premier lieu.


Il existe de bonnes réponses sur le traitement de grands ensembles de données, en utilisant soit data.table , soit d'autres bibliothèques. Je recommanderais de jeter un coup d'œil à ceux-ci, étant donné qu'il semble que le principal problème soit la mémoire, plutôt que le processus de manipulation des données. Vous pouvez également envisager de faire tourner une instance AWS EC2 pour exécuter une solution data.table sur une machine avec beaucoup de RAM pour allouer le vecteur


@Matmarbon également, vérifiez que memory.limit () est proche de votre RAM totale installée, si elle est faible pour une raison quelconque, cela pourrait être à l'origine du problème. Il doit renvoyer une valeur en Mo


Vous avez raison ... avec (en comparaison) extrêmement peu de données et peu de colonnes, cette solution fonctionne - et c'est tout ce à quoi je peux m'attendre pour le moment. L'autre partie du problème peut être résolue par certaines recherches Google. Alors merci d'avoir résolu cette partie :)



0
votes

J'ai trouvé une réponse qui fonctionne de manière assez efficace même avec cette énorme quantité de données. En fait, je suppose que c'est tout simplement aussi efficace pour ce scénario que dans R:

cn <- names(d)
pc <- cn[which(grepl("participants.", cn))]
ppcn <- substring(pc[0:(length(pc)/10)], 16)
d_long <- reshape(d, direction='long', varying=pc, timevar='participant', times=c('participants.0', 'participants.1', 'participants.2', 'participants.3', 'participants.4', 'participants.5', 'participants.6', 'participants.7', 'participants.8', 'participants.9'), v.names=ppcn)

L'idée derrière cela est de créer les arguments de la fonction reshape avec quelques lignes de code supplémentaires pour que R puisse savoir de quelles colonnes je parle vraiment.

Avec cette solution, mon long d (sans jeu de mots) est créé (1) en une seule étape sans avoir besoin de tables temporaires potentiellement volumineuses et (2) sans conversion de type , y compris des colonnes de tous types.


0 commentaires