88 votes

Découpage d'un énorme fichier csv (3,5 Go) pour le lire dans R

J'ai donc un fichier de données (séparé par des points-virgules) qui contient beaucoup de détails et de lignes incomplètes (ce qui fait que Access et SQL s'étranglent). Il s'agit d'un ensemble de données au niveau du comté, réparties en segments, sous-segments et sous-sous-segments (pour un total d'environ 200 facteurs) sur 40 ans. En bref, c'est énorme, et ça ne va pas tenir dans la mémoire si j'essaie de le lire simplement.

Ma question est donc la suivante : étant donné que je veux tous les comtés, mais seulement une seule année (et seulement le plus haut niveau de segment... ce qui donne environ 100 000 lignes à la fin), quelle serait la meilleure façon d'obtenir ce rollup dans R ?

Actuellement, j'essaie d'éliminer les années non pertinentes avec Python, en contournant la limite de taille des fichiers en lisant et en opérant sur une ligne à la fois, mais je préférerais une solution uniquement R (paquets CRAN OK). Existe-t-il un moyen similaire de lire dans les fichiers un morceau à la fois dans R ?

Toute idée serait grandement appréciée.

Mise à jour :

  • Contraintes

  • Besoin d'utiliser mon machine, donc pas d'instances EC2

  • Aussi R-only que possible. La vitesse et les ressources ne sont pas un souci dans ce cas... à condition que ma machine n'explose pas...

  • Comme vous pouvez le voir ci-dessous, les données contiennent des types mixtes, sur lesquels je dois opérer plus tard

  • Données

  • Les données représentent 3,5 Go, avec environ 8,5 millions de lignes et 17 colonnes.

  • Quelques milliers de lignes (~2k) sont mal formées, avec une seule colonne au lieu de 17.

  • Ils sont tout à fait insignifiants et peuvent être abandonnés.

  • Je n'ai besoin que d'environ 100 000 lignes de ce fichier (voir ci-dessous).

Exemple de données :

County; State; Year; Quarter; Segment; Sub-Segment; Sub-Sub-Segment; GDP; ...
Ada County;NC;2009;4;FIRE;Financial;Banks;80.1; ...
Ada County;NC;2010;1;FIRE;Financial;Banks;82.5; ...
NC  [Malformed row]
[8.5 Mill rows]

Je veux supprimer certaines colonnes et choisir deux années parmi les 40 disponibles (2009-2010 de 1980-2020), afin que les données puissent être intégrées dans R :

County; State; Year; Quarter; Segment; GDP; ...
Ada County;NC;2009;4;FIRE;80.1; ...
Ada County;NC;2010;1;FIRE;82.5; ...
[~200,000 rows]

Résultats :

Après avoir bricolé avec toutes les suggestions faites, j'ai décidé que readLines, suggéré par JD et Marek, fonctionnerait mieux. J'ai donné le chèque à Marek parce qu'il a donné un exemple d'implémentation.

J'ai reproduit une version légèrement adaptée de l'implémentation de Marek pour ma réponse finale ici, en utilisant strsplit et cat pour ne garder que les colonnes que je veux.

Il convient également de noter qu'il s'agit BEAUCOUP moins efficace que Python... en effet, Python déchiquette le fichier de 3,5 Go en 5 minutes alors que R prend environ 60 minutes... mais si vous n'avez que R, c'est la solution.

## Open a connection separately to hold the cursor position
file.in <- file('bad_data.txt', 'rt')
file.out <- file('chopped_data.txt', 'wt')
line <- readLines(file.in, n=1)
line.split <- strsplit(line, ';')
# Stitching together only the columns we want
cat(line.split[[1]][1:5], line.split[[1]][8], sep = ';', file = file.out, fill = TRUE)
## Use a loop to read in the rest of the lines
line <- readLines(file.in, n=1)
while (length(line)) {
  line.split <- strsplit(line, ';')
  if (length(line.split[[1]]) > 1) {
    if (line.split[[1]][3] == '2009') {
        cat(line.split[[1]][1:5], line.split[[1]][8], sep = ';', file = file.out, fill = TRUE)
    }
  }
  line<- readLines(file.in, n=1)
}
close(file.in)
close(file.out)

Défaillances par approche :

  • sqldf

  • C'est définitivement ce que j'utiliserai pour ce type de problème à l'avenir si les données sont bien formées. Cependant, si elles ne le sont pas, alors SQLite s'étrangle.

  • MapReduce

  • Pour être honnête, les docteurs m'ont un peu intimidé sur ce point, alors je n'ai pas eu le temps de l'essayer. Il semble que l'objet doive également être en mémoire, ce qui irait à l'encontre du but recherché si c'était le cas.

  • bigmemory

  • Cette approche permet d'établir un lien propre avec les données, mais elle ne peut traiter qu'un seul type à la fois. En conséquence, tous mes vecteurs de caractères sont tombés lorsqu'ils ont été placés dans un big.table. Si je devais concevoir de grands ensembles de données à l'avenir, j'envisagerais de n'utiliser que des nombres, juste pour conserver cette option.

  • balayage

  • Scan semble avoir le même type de problèmes que big memory, mais avec toute la mécanique de readLines. En bref, il n'a tout simplement pas fait l'affaire cette fois-ci.

39voto

Marek Points 18000

Mon essai avec readLines . Ce morceau de code crée csv avec des années sélectionnées.

file_in <- file("in.csv","r")
file_out <- file("out.csv","a")
x <- readLines(file_in, n=1)
writeLines(x, file_out) # copy headers

B <- 300000 # depends how large is one pack
while(length(x)) {
    ind <- grep("^[^;]*;[^;]*; 20(09|10)", x)
    if (length(ind)) writeLines(x[ind], file_out)
    x <- readLines(file_in, n=B)
}
close(file_in)
close(file_out)

10voto

JD Long Points 20477

Existe-t-il un moyen similaire de lire les fichiers un par un dans R ?

Sí. Le site readChar() lira un bloc de caractères sans supposer qu'ils sont terminés par un caractère nul. Si vous souhaitez lire les données ligne par ligne, vous pouvez utiliser la fonction readLines() . Si vous lisez un bloc ou une ligne, effectuez une opération, puis écrivez les données, vous pouvez éviter le problème de mémoire. Si vous avez envie de lancer une instance à grande mémoire sur EC2 d'Amazon, vous pouvez obtenir jusqu'à 64 Go de RAM. Cela devrait suffire à contenir votre fichier et vous laisser suffisamment de place pour manipuler les données.

Si vous avez besoin de plus de vitesse, la recommandation de Shane d'utiliser Map Reduce est très bonne. Toutefois, si vous choisissez d'utiliser une instance à grande mémoire sur EC2, vous devriez vous tourner vers le package multicore pour utiliser tous les cœurs d'une machine.

Si vous souhaitez lire de nombreux gigaoctets de données délimitées dans R, vous devriez au moins faire des recherches sur le paquet sqldf qui vous permet d'importer directement dans sqldf à partir de R et d'opérer ensuite sur les données à partir de R. J'ai trouvé que sqldf était l'un des moyens les plus rapides d'importer des gigaoctets de données dans R, comme mentionné dans cette question précédente .

10voto

Shane Points 40885

Je ne suis pas un expert en la matière, mais vous pourriez envisager d'essayer MapReduce ce qui reviendrait à adopter une approche "diviser pour mieux régner". R a plusieurs options pour cela, notamment :

  1. mapReduce (R pur)
  2. RHIPE (qui utilise Hadoop ) ; voir l'exemple 6.2.2 dans la documentation pour un exemple de sous-ensemble de fichiers

Par ailleurs, R fournit plusieurs paquets pour traiter les données volumineuses qui sortent de la mémoire (sur le disque). Vous pourriez probablement charger l'ensemble des données dans un fichier de type bigmemory et faire la réduction complètement dans R. Voir http://www.bigmemory.org/ pour un ensemble d'outils permettant de gérer cette situation.

6voto

Ali Points 2693

El ff est un moyen transparent de traiter des fichiers volumineux.

Vous pouvez voir le paquet site web et/ou un présentation à ce sujet.

J'espère que cela vous aidera

5voto

Marek Points 18000

Vous pourriez importer des données dans une base de données SQLite et ensuite utiliser RSQLite pour sélectionner des sous-ensembles.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X