web-dev-qa-db-fra.com

Découper un énorme fichier csv (3,5 Go) à lire dans R

Donc, j'ai un fichier de données (séparé par des points-virgules) qui a beaucoup de détails et des lignes incomplètes (ce qui conduit Access et SQL à étrangler). C'est un ensemble de données au niveau du comté divisé en segments, sous-segments et sous-sous-segments (pour un total d'environ 200 facteurs) pendant 40 ans. En bref, c'est énorme, et ça ne va pas aller dans la mémoire si j'essaie simplement de le lire.

Donc ma question est la suivante, étant donné que je veux tous les comtés, mais seulement une année (et juste le plus haut niveau de segment… conduisant à environ 100 000 lignes au final), quelle serait la meilleure façon d’obtenir ce cumul dans R?

Actuellement, j'essaie de réduire les années non pertinentes avec Python, en contournant la limite de taille de fichier en lisant et en opérant sur une ligne à la fois, mais je préférerais une solution R-only (CRAN packages OK). Existe-t-il une manière similaire de lire dans les fichiers pièce par pièce dans R?

Toutes les idées seraient grandement appréciées.

Mettre à jour:

  • Contraintes
    • A besoin d'utiliser ma machine, donc pas d'instances EC2
    • Aussi R que possible. La vitesse et les ressources ne sont pas des préoccupations 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 travailler plus tard.
  • Les données
    • Les données sont 3,5 Go, avec environ 8,5 millions de lignes et 17 colonnes
    • Quelques milliers de lignes (~ 2k) sont malformées, avec une seule colonne au lieu de 17
      • Ceux-ci sont entièrement sans importance et peuvent être supprimés
    • J'ai seulement besoin d'environ 100 000 lignes sur 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 couper certaines colonnes et choisir deux des 40 années disponibles (2009-2010 de 1980 à 2020), afin que les données puissent s'intégrer à 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é 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 ici une version légèrement adaptée de l'implémentation de Marek pour ma réponse finale, en utilisant strsplit et cat pour ne conserver que les colonnes que je veux.

Il faut aussi noter que c’est BEAUCOUP moins efficace que Python ... comme dans, Python parcourt le fichier de 3,5 Go en 5 minutes tandis que R en prend environ 60 ... mais si vous n’avez que R alors c'est le billet.

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

Echecs par approche:

  • sqldf
    • C’est définitivement ce que je vais utiliser pour ce type de problème à l’avenir si les données sont bien formées. Cependant, si ce n'est pas le cas, SQLite étrangle.
  • MapReduce
    • Pour être honnête, les docteurs m'ont un peu intimidé sur ce coup-là, alors je n'ai pas essayé de l'essayer. Il semblait qu'il fallait également que l'objet soit en mémoire, ce qui irait à l'encontre du but recherché si tel était le cas.
  • grande mémoire
    • Cette approche est clairement liée aux données, mais elle ne peut gérer qu'un type à la fois. En conséquence, tous mes vecteurs de caractères ont été abandonnés lorsqu’ils ont été placés dans une table volante. Toutefois, si j’ai besoin de concevoir de grands ensembles de données pour l’avenir, je n’envisagerais d’utiliser que des nombres pour que cette option reste active.
  • balayage
    • Scan semblait avoir des problèmes de type similaires à ceux de mémoire importante, mais avec tous les mécanismes de readLines. En bref, cela ne correspondait pas à la facture cette fois-ci.
84
FTWynn

Mon essai avec readLines. Ce morceau de code crée csv avec les 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)
37
Marek

Existe-t-il une manière similaire de lire dans les fichiers pièce par pièce dans R?

Oui. La fonction readChar () lira un bloc de caractères sans supposer qu'ils sont terminés par un caractère nul. Si vous voulez lire des données ligne par ligne, vous pouvez utiliser 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. Cependant, si vous avez envie de lancer une grande instance de mémoire sur l'EC2 d'Amazon, vous pouvez obtenir jusqu'à 64 Go de RAM. Cela devrait contenir votre fichier et beaucoup d’espace 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. Cependant, si vous utilisez une grande instance de mémoire sur EC2, vous devriez examiner le package multicœur pour utiliser tous les cœurs d'une machine. 

Si vous avez envie de lire de nombreux concerts de données délimitées dans R, vous devez au moins rechercher le paquetage sqldf qui vous permet d'importer directement dans sqldf à partir de R, puis d'exploiter les données de R. J'ai trouvé que sqldf en était un. des moyens les plus rapides pour importer des concerts de données dans R, comme indiqué dans cette question précédente

10
JD Long

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

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

Sinon, R fournit plusieurs packages pour traiter des données volumineuses qui sortent de la mémoire (sur le disque). Vous pourriez probablement charger l'intégralité du jeu de données dans un objet bigmemory et effectuer la réduction complètement dans R. Voir http://www.bigmemory.org/ pour un ensemble d'outils permettant de gérer cela.

9
Shane

Le paquetage ff est un moyen transparent de traiter des fichiers volumineux.

Vous pouvez voir le paquet website et/ou une présentation à ce sujet.

J'espère que ça aide

6
Ali

Qu'en est-il d'utiliser readr et le read_*_chunked family?

Donc dans votre cas:

testfile.csv

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
lol
Ada County;NC;2013;1;FIRE;Financial;Banks;82.5

Code actuel

require(readr)
f <- function(x, pos) subset(x, Year %in% c(2009, 2010))
read_csv2_chunked("testfile.csv", DataFrameCallback$new(f), chunk_size = 1)

Ceci applique f à chaque morceau, en mémorisant les noms de colonnes et en combinant les résultats filtrés. Voir ?callback qui est la source de cet exemple.

Cela se traduit par:

# A tibble: 2 × 8
      County State  Year Quarter Segment `Sub-Segment` `Sub-Sub-Segment`   GDP
*      <chr> <chr> <int>   <int>   <chr>         <chr>             <chr> <dbl>
1 Ada County    NC  2009       4    FIRE     Financial             Banks   801
2 Ada County    NC  2010       1    FIRE     Financial             Banks   825

Vous pouvez même augmenter chunk_size mais dans cet exemple, il n'y a que 4 lignes.

5
Rentrop

Il existe un tout nouveau package appelé colbycol qui vous permet de lire uniquement les variables souhaitées à partir d'énormes fichiers texte:

http://colbycol.r-forge.r-project.org/

Elle transmet tous les arguments à read.table. La combinaison devrait donc vous permettre de créer des sous-ensembles assez serrés.

5
Ari B. Friedman

Vous pouvez importer des données dans la base de données SQLite , puis utiliser RSQLite pour sélectionner des sous-ensembles.

5
Marek

Avez-vous consiséed bigmemory ? Découvrez this et this

4
George Dontas

scan () a un argument nlines et un argument skip. Y a-t-il une raison pour laquelle vous pouvez simplement utiliser cela pour lire une ligne de lignes en une fois, en vérifiant la date pour voir si elle convient? Si le fichier d'entrée est classé par date, vous pouvez stocker un index qui vous indique ce que devraient être vos sauts et lignes, ce qui accélérerait le processus à l'avenir.

3
frankc

Peut-être pouvez-vous migrer vers MySQL ou PostgreSQL pour vous éviter les limitations de MS Access. 

Il est assez facile de connecter R à ces systèmes avec un connecteur de base de données basé sur DBI (disponible sur CRAN). 

3
FloE

De nos jours, 3,5 Go n’est tout simplement pas si gros que ça, je peux avoir accès à une machine avec 244 Go RAM (r3.8xlarge) sur le cloud Amazon pour 2,80 $/heure. Combien d’heures vous faudra-t-il pour déterminer comment résoudre le problème à l’aide de solutions de type Big Data? Combien vaut votre temps? Oui, cela vous prendra une heure ou deux pour comprendre comment utiliser AWS - mais vous pouvez apprendre les bases sur un niveau gratuit, télécharger les données et lire les 10 premières lignes dans R pour le vérifier, puis vous pouvez lancer une grande instance de mémoire comme r3.8xlarge et lisez le tout! Juste mon 2c.

1
Sean

Maintenant, 2017, je suggérerais d'aller pour l'étincelle et l'étincelle. 

  • la syntaxe peut être écrite d'une manière simple, plutôt similaire à celle de Dplyr

  • cela correspond assez bien à une petite mémoire (petite au sens de 2017)

Cependant, cela peut être une expérience intimidante de commencer ...

0
Ott Toomet