R: управление памятью во время xmlEventParse огромных (>20 ГБ) файлов


Основываясь на этом предыдущем вопросе (смотрите здесь ), я пытаюсь читать во многих больших xml-файлах с помощью xmlEventParse, сохраняя изменяющиеся по узлам данные. Работа с этим примером xml: https://www.nlm.nih.gov/databases/dtd/medsamp2015.xml .

Приведенный ниже код использует xpathSapply для извлечения необходимых значений и ряд операторов if для объединения значений таким образом, чтобы уникальное значение (PMID) соответствовало каждому из неуникальных значений (фамилия) в записи - для которых не может быть никаких фамилий. Цель состоит в том, чтобы написать серию небольших csv по пути (здесь, после каждых 1000 фамилий), чтобы минимизировать объем используемой памяти.

При запуске на полноразмерном наборе данных, код успешно выводит файлы в пакетах, однако что-то все еще хранится в памяти, что в конечном итоге вызывает системную ошибку, как только вся оперативная память используется. Я наблюдал за диспетчером задач во время выполнения кода и могу видеть, как память R растет по мере выполнения программы. И если я остановлю ... программа в середине выполнения, а затем очистить рабочее пространство R, включая скрытые элементы, память по-прежнему, кажется, используется R. это не так, пока я выключаю R память освобождается снова.

Запустите это несколько раз самостоятельно, и вы увидите, что использование памяти R растет даже после очистки рабочей области.

Пожалуйста, помогите! Эта проблема, по-видимому, является общей для других, читающих в больших XML-файлах таким образом (см., например, комментарии в этом вопросе).

Мой код выглядит следующим образом: образом:

library(XML)

filename <- "~/Desktop/medsamp2015.xml"

tempdat <- data.frame(pmid=as.numeric(),
                      lname=character(), 
                      stringsAsFactors=FALSE) 
cnt <- 1
branchFunction <- function() {
  func <- function(x, ...) {
    v1 <- xpathSApply(x, path = "//PMID", xmlValue)
    v2 <- xpathSApply(x, path = "//Author/LastName", xmlValue)
    print(cbind(c(rep(v1,length(v2))), v2))

    #below is where I store/write the temp data along the way
    #but even without doing this, memory is used (even after clearing)

    tempdat <<- rbind(tempdat,cbind(c(rep(v1,length(v2))), v2))
    if (nrow(tempdat) > 1000){
      outname <- paste0("~/Desktop/outfiles",cnt,".csv")
      write.csv(tempdat, outname , row.names = F)
      tempdat <<- data.frame(pmid=as.numeric(),
                            lname=character(), 
                            stringsAsFactors=FALSE)
      cnt <<- cnt+1
    }
  }
  list(MedlineCitation = func)
}

myfunctions <- branchFunction()

#RUN
xmlEventParse(
  file = filename, 
  handlers = NULL, 
  branches = myfunctions
)
1 4

1 ответ:

Вот пример, у нас есть скрипт запуска invoke.sh, который вызывает скрипт R и передает url и имя файла в качестве параметров... В этом случае я предварительно скачал тестовый файл medsamp2015.xml и поместить в каталог ./data.

  • мой смысл состоял бы в том, чтобы создать цикл в скрипте invoke.sh и перебрать список имен целевых файлов. Для каждого файла вы вызываете экземпляр R, загружаете его, обрабатываете файл и переходите к следующему.

Предостережение: I не проверял и не изменял свою функцию по сравнению с другими загружаемыми файлами и форматами. Я бы выключил печать вывода, удалив обертку print() в строке 62.

print( cbind(c(rep(v1, length(v2))), v2))

Примечание: это производная от предыдущего ответа, предоставленного мной по этому вопросу.: R память, не освобожденная в Windows . Я надеюсь, что это поможет путем образец.

Запуск Скрипта

  1 #!/usr/local/bin/bash -x
  2
  3 R --no-save -q --slave < ./47162861.R --args "https://www.nlm.nih.gov/databases/dtd" "medsamp2015.xml"

R Файл - 47162861.R

# Set working directory

projectDir <- "~/dev/stackoverflow/47162861"
setwd(projectDir)

# -----------------------------------------------------------------------------
# Load required Packages...
requiredPackages <- c("XML")

ipak <- function(pkg) {
  new.pkg <- pkg[!(pkg %in% installed.packages()[, "Package"])]
  if (length(new.pkg))
    install.packages(new.pkg, dependencies = TRUE)
  sapply(pkg, require, character.only = TRUE)
}

ipak(requiredPackages)

# -----------------------------------------------------------------------------
# Load required Files
# trailingOnly=TRUE means that only your arguments are returned
args <- commandArgs(trailingOnly = TRUE)

if ( length(args) != 0 ) {
  dataDir <- file.path(projectDir,"data")
  fileUrl = args[1]
  fileName = args[2]
} else {
  dataDir <- file.path(projectDir,"data")
  fileUrl <- "https://www.nlm.nih.gov/databases/dtd"
  fileName <- "medsamp2015.xml"
}

# -----------------------------------------------------------------------------
# Download file

# Does the directory Exist? If it does'nt create it
if (!file.exists(dataDir)) {
  dir.create(dataDir)
}

# Now we check if we have downloaded the data already if not we download it

if (!file.exists(file.path(dataDir, fileName))) {
  download.file(fileUrl, file.path(dataDir, fileName), method = "wget")
}

# -----------------------------------------------------------------------------
# Now we extrat the data

tempdat <- data.frame(pmid = as.numeric(), lname = character(),
  stringsAsFactors = FALSE)
cnt <- 1

branchFunction <- function() {
  func <- function(x, ...) {
    v1 <- xpathSApply(x, path = "//PMID", xmlValue)
    v2 <- xpathSApply(x, path = "//Author/LastName", xmlValue)
    print(cbind(c(rep(v1, length(v2))), v2))

    # below is where I store/write the temp data along the way
    # but even without doing this, memory is used (even after
    # clearing)

    tempdat <<- rbind(tempdat, cbind(c(rep(v1, length(v2))),
      v2))
    if (nrow(tempdat) > 1000) {
      outname <- file.path(dataDir, paste0(cnt, ".csv")) # Create FileName
      write.csv(tempdat, outname, row.names = F) # Write File to created directory
      tempdat <<- data.frame(pmid = as.numeric(), lname = character(),
        stringsAsFactors = FALSE)
      cnt <<- cnt + 1
    }
  }
  list(MedlineCitation = func)
}

myfunctions <- branchFunction()

# -----------------------------------------------------------------------------
# RUN
xmlEventParse(file = file.path(dataDir, fileName),
              handlers = NULL,
              branches = myfunctions)

Тестовый файл и выходные данные

~/dev / stackoverflow / 47162861 / data / medsamp2015.xml

$ ll                                                            
total 2128
drwxr-xr-x@ 7 hidden  staff   238B Nov 10 11:05 .
drwxr-xr-x@ 9 hidden  staff   306B Nov 10 11:11 ..
-rw-r--r--@ 1 hidden  staff    32K Nov 10 11:12 1.csv
-rw-r--r--@ 1 hidden  staff    20K Nov 10 11:12 2.csv
-rw-r--r--@ 1 hidden  staff    23K Nov 10 11:12 3.csv
-rw-r--r--@ 1 hidden  staff    37K Nov 10 11:12 4.csv
-rw-r--r--@ 1 hidden  staff   942K Nov 10 11:05 medsamp2015.xml

Вывод Во Время Выполнения

> ./invoke.sh > runtime.txt
+ R --no-save -q --slave --args https://www.nlm.nih.gov/databases/dtd medsamp2015.xml
Loading required package: XML

Файл: среда выполнения.txt