Проблемы с памятью при использовании bigmemory для загрузки большого набора данных в R

У меня есть большой текстовый файл (> 10 миллионов строк,> 1 ГБ), который я хочу обрабатывать по одной строке за раз, чтобы избежать загрузки всего в память. После обработки каждой строки я хочу сохранить некоторые переменные в объект big.matrix. Вот упрощенный пример:

library(bigmemory)
library(pryr)

con  <- file('x.csv', open = "r")
x <- big.matrix(nrow = 5, ncol = 1, type = 'integer')

for (i in 1:5){
   print(c(address(x), refs(x)))
   y <- readLines(con, n = 1, warn = FALSE)
   x[i] <- 2L*as.integer(y)
} 

close(con)

где x.csv содержит

4
18
2
14
16

Следуя советам здесь http://adv-r.had.co.nz/memory.html Я напечатал адрес памяти моего объекта big.matrix, и он меняется с каждой итерацией цикла:

[1] "0x101e854d8" "2"          
[1] "0x101d8f750" "2"          
[1] "0x102380d80" "2"          
[1] "0x105a8ff20" "2"          
[1] "0x105ae0d88" "2"   
  1. Можно ли изменить big.matrix объекты на месте?

  2. есть ли лучший способ загрузить, обработать и затем сохранить эти данные? Текущий метод медленный!


person slabofguinness    schedule 14.07.2015    source источник
comment
Я бы проверил пакет data.table. Он предназначен для больших данных (или, по крайней мере, больших данных).   -  person Richard Erickson    schedule 14.07.2015
comment
Общий комментарий: R полностью работает в памяти, поэтому, если ваш окончательный набор данных больше, чем память, выделенная для вашей консоли R, у вас могут возникнуть проблемы.   -  person Tim Biegeleisen    schedule 14.07.2015
comment
@ Тим, я попробую filebacked.big.matrix(), чтобы не превысить выделение ОЗУ.   -  person slabofguinness    schedule 14.07.2015
comment
@Richard Я не знаю о возможности в read.table обрабатывать ввод по одной строке за раз.   -  person slabofguinness    schedule 14.07.2015
comment
@SeamusO'Bairead, с data.table вы можете избежать чтения своих данных по одной строке за раз. Кроме того, использование data.table может обрабатывать память лучше, чем big.matrix. Наконец, насколько я помню, функция fread не работала, поэтому мне пришлось установить data.table из git hib. Если вы попробуете fread и это не сработает, это будет мой первый шаг по устранению неполадок.   -  person Richard Erickson    schedule 14.07.2015


Ответы (1)


  1. есть ли лучший способ загрузить, обработать и затем сохранить эти данные? Текущий метод медленный!

Самая медленная часть вашего метода, по-видимому, заключается в вызове чтения каждой строки по отдельности. Мы можем «разбивать» данные или читать по несколько строк за раз, чтобы не превысить лимит памяти и, возможно, ускорить процесс.

Вот план:

  1. Выяснить, сколько строк у нас есть в файле
  2. Прочитайте кусок этих строк
  3. Выполните некоторую операцию над этим фрагментом
  4. Вставьте этот фрагмент обратно в новый файл, чтобы сохранить его на потом.

    library(readr) 
    # Make a file
    x <- data.frame(matrix(rnorm(10000),100000,10))
    
    write_csv(x,"./test_set2.csv")
    
    # Create a function to read a variable in file and double it
    calcDouble <- function(calc.file,outputFile = "./outPut_File.csv",
    read.size=500000,variable="X1"){
      # Set up variables
      num.lines <- 0
      lines.per <- NULL
      var.top <- NULL
      i=0L
    
      # Gather column names and position of objective column
      connection.names <- file(calc.file,open="r+")
      data.names <- read.table(connection.names,sep=",",header=TRUE,nrows=1)
      close(connection.names)
      col.name <- which(colnames(data.names)==variable)
    
      #Find length of file by line
      connection.len <- file(calc.file,open="r+")
      while((linesread <- length(readLines(connection.len,read.size)))>0){
    
        lines.per[i] <- linesread
        num.lines <- num.lines + linesread
        i=i+1L 
      }
      close(connection.len)
    
      # Make connection for doubling function
      # Loop through file and double the set variables
      connection.double <- file(calc.file,open="r+")
      for (j in 1:length(lines.per)){
    
        # if stops read.table from breaking
        # Read in a chunk of the file
        if (j == 1) {
          data <- read.table(connection.double,sep=",",header=FALSE,skip=1,nrows=lines.per[j],comment.char="")
        } else {
          data <- read.table(connection.double,sep=",",header=FALSE,nrows=lines.per[j],comment.char="")
        }
          # Grab the columns we need and double them
          double <- data[,I(col.name)] * 2
        if (j != 1) {
          write_csv(data.frame(double),outputFile,append = TRUE)
        } else {
          write_csv(data.frame(double),outputFile)
        }
    
        message(paste0("Reading from Chunk: ",j, " of ",length(lines.per)))
      }
      close(connection.double)
    }
    
    calcDouble("./test_set2.csv",read.size = 50000, variable = "X1")
    

Таким образом, мы получаем файл .csv с измененными данными. Вы можете изменить double <- data[,I(col.name)] * 2 на любую вещь, которую вам нужно сделать с каждым фрагментом.

person Steve_Corrin    schedule 14.07.2015
comment
@Steve_Corring спасибо, я сделаю несколько тестов и вернусь к вам. Также write_csv должен быть write.csv. - person slabofguinness; 14.07.2015
comment
@Steve_Corring А, хорошо, понятно! - person slabofguinness; 14.07.2015
comment
@Steve_Corrin, вы правы в том, что чтение файла по строке является медленной частью, но ваше первое утверждение о модификации на месте неверно. big.matrix относится к блоку памяти, который был отображен. Когда вы изменяете элемент, вы изменяете его в этом блоке памяти, чтобы любые другие объекты, указывающие на эту память, отражали изменение. Адрес памяти, который возвращается во время каждой итерации, является копией указателя, а не самой матрицы. - person cdeterman; 15.07.2015
comment
@cdeterman Моя ошибка. Я предположил, что big.matrix использует какой-то подход с нехваткой памяти для обработки больших наборов данных. Просто просмотрел документы и удалил свой ответ на первый вопрос. Спасибо за проверку - person Steve_Corrin; 15.07.2015
comment
@Steve_Corrin, если используется опция filebacked.backed.matrix, то не хватает памяти. Однако он предназначен для изменения этой матрицы на месте (учитывая, что объект filebacked ссылается на отдельный временный файл). Идея состоит в том, чтобы создать как можно меньше накладных расходов, предотвратить копирование (если не указано явно) и разрешить объекты, размер которых превышает доступную оперативную память. - person cdeterman; 15.07.2015