Lire en morceaux à la fois en utilisant fread dans les données du paquet.table

j'essaie d'entrer un grand fichier délimité par des tabulations (environ 2 Go) en utilisant le fread fonction dans le paquet data.table. Cependant, parce qu'il est si grand, il ne rentre pas complètement dans la mémoire. J'ai essayé de l'entrer en morceaux en utilisant le skip et nrow des arguments tels que:

chunk.size = 1e6
done = FALSE
chunk = 1
while(!done)
{
    temp = fread("myfile.txt",skip=(chunk-1)*chunk.size,nrow=chunk.size-1)
    #do something to temp
    chunk = chunk + 1
    if(nrow(temp)<2) done = TRUE
}

dans le cas ci-dessus, je lis dans 1 million de lignes à la fois, je fais un calcul sur elles, et puis j'obtiens le million suivant, etc. Le problème avec ce code, c'est qu'après chaque morceau est récupéré,fread doit commencer à numériser le fichier dès le début car après chaque itération de boucle, skip augmente d'un million de dollars. Par conséquent, après chaque morceau, fread prend de plus en plus de temps pour atteindre le prochain morceau, ce qui rend cela très inefficace.

Est-il un moyen de dire fread mettre en pause chaque disons 1 million de lignes, et puis continuer à lire à partir de ce point sans avoir à recommencer au début? Toute solution, ou s'il s'agit d'une nouvelle caractéristique la demande?

23
demandé sur SabDeM 2013-11-10 23:38:43

3 réponses

Vous devez utiliser le LaF paquet. Cela introduit une sorte de pointeur sur vos données, évitant ainsi le - pour de très grandes données - comportement ennuyeux de la lecture de l'ensemble du fichier. Aussi loin que je le reçois fread()data.table pckg besoin de connaître le nombre total de lignes, ce qui prend du temps pour les données GB. À l'aide du pointeur de LaF vous pouvez aller à chaque ligne que vous voulez; et lire dans des morceaux de données que vous pouvez appliquer votre fonction sur, puis passer à la prochaine partie de données. Sur mon petit PC j'ai couru à travers un 25 Go csv-fichier par étapes de 10e6 lignes et extrait le total ~5E6 observations nécessaires - chaque 10e6 morceau a pris 30 secondes.

mise à jour:

library('LaF')
huge_file <- 'C:/datasets/protein.links.v9.1.txt'

#First detect a data model for your file:
model <- detect_dm_csv(huge_file, sep=" ", header=TRUE)

puis créez une connexion à votre fichier en utilisant le model:

df.laf <- laf_open(model)

une Fois fait, vous pouvez faire toute sorte de choses sans avoir besoin de connaître la taille du fichier de données.table pckgs. Par exemple, placez le pointeur sur la ligne No 100e6 et lisez les lignes de données 1e6 à partir d'ici:

goto(df.laf, 100e6)
data <- next_block(df.laf,nrows=1e6)

data contient les lignes 1e6 de votre fichier CSV (à partir de la ligne 100e6).

vous pouvez lire des morceaux de données (taille en fonction de votre mémoire) et ne garder que ce dont vous avez besoin. par exemple, le huge_file dans mon exemple un fichier avec toutes les séquences de protéines connues et a une taille de >27 GB - sorte de gros pour mon PC. Pour obtenir seulement la séquence humaine j'ai filtré en utilisant l'Identificateur de l'organisme qui est 9606 pour l'humain, et cela devrait apparaître dans le début de la variable protein1. Sale est de le mettre dans une simple boucle for et juste aller lire les données d'un morceau à un moment:

library('dplyr')
library('stringr')

res <- df.laf[1,][0,]
for(i in 1:10){
  raw <-
    next_block(df.laf,nrows=100e6) %>% 
    filter(str_detect(protein1,"^9606\."))
  res <- rbind(res, raw)

    }

res contient les données humaines filtrées. Mais mieux-et pour les opérations plus complexes, par exemple le calcul sur les données à la volée-la fonction process_blocks() prend comme argument une fonction. Ainsi, dans la fonction que vous faites ce que vous voulez à chaque morceau de données. Lisez la documentation.

8
répondu user3375672 2017-04-17 03:46:28

Vous pouvez utiliser readr read_*_chunked lire dans les données et, par exemple, les filtrer dans le sens des morceaux. Voir ici et ici un exemple:

# Cars with 3 gears
f <- function(x, pos) subset(x, gear == 3)
read_csv_chunked(readr_example("mtcars.csv"), DataFrameCallback$new(f), chunk_size = 5)
5
répondu Rentrop 2017-05-02 09:53:57

une option apparentée est le bloc paquet. Voici un exemple avec un fichier texte de 3,5 Go:

library(chunked)
library(tidyverse)

# I want to look at the daily page views of Wikipedia articles
# before 2015... I can get zipped log files
# from here: hhttps://dumps.wikimedia.org/other/pagecounts-ez/merged/2012/2012-12/
# I get bz file, unzip to get this: 

my_file <- 'pagecounts-2012-12-14/pagecounts-2012-12-14'

# How big is my file?
print(paste(round(file.info(my_file)$size  / 2^30,3), 'gigabytes'))
# [1] "3.493 gigabytes" too big to open in Notepad++ !
# But can read with 010 Editor

# look at the top of the file 
readLines(my_file, n = 100)

# to find where the content starts, vary the skip value, 
read.table(my_file, nrows = 10, skip = 25)

C'est ici que nous commençons à travailler dans les morceaux du fichier, Nous pouvons utiliser la plupart des verbes dplyr de la manière habituelle:

# Let the chunked pkg work its magic! We only want the lines containing 
# "Gun_control". The main challenge here was identifying the column
# header
df <- 
read_chunkwise(my_file, 
               chunk_size=5000,
               skip = 30,
               format = "table",
               header = TRUE) %>% 
  filter(stringr::str_detect(De.mw.De.5.J3M1O1, "Gun_control"))

# this line does the evaluation, 
# and takes a few moments...
system.time(out <- collect(df))

Et ici, nous pouvons travailler sur la sortie comme à l'habitude, car il est beaucoup plus petit que le fichier d'entrée:

# clean up the output to separate into cols, 
# and get the number of page views as a numeric
out_df <- 
out %>% 
  separate(De.mw.De.5.J3M1O1, 
           into = str_glue("V{1:4}"),
           sep = " ") %>% 
  mutate(V3 = as.numeric(V3))

 head(out_df)
    V1                                                        V2   V3
1 en.z                                               Gun_control 7961
2 en.z Category:Gun_control_advocacy_groups_in_the_United_States 1396
3 en.z          Gun_control_policy_of_the_Clinton_Administration  223
4 en.z                            Category:Gun_control_advocates   80
5 en.z                         Gun_control_in_the_United_Kingdom   68
6 en.z                                    Gun_control_in_america   59
                                                                                 V4
1 A34B55C32D38E32F32G32H20I22J9K12L10M9N15O34P38Q37R83S197T1207U1643V1523W1528X1319
2                                     B1C5D2E1F3H3J1O1P3Q9R9S23T197U327V245W271X295
3                                     A3B2C4D2E3F3G1J3K1L1O3P2Q2R4S2T24U39V41W43X40
4                                                            D2H1M1S4T8U22V10W18X14
5                                                             B1C1S1T11U12V13W16X13
6                                                         B1H1M1N2P1S1T6U5V17W12X12

#--------------------
1
répondu Ben 2018-03-08 19:51:52