Трюки для управления доступной памятью в сеансе R

459

Какие трюки люди используют для управления доступной памятью интерактивной сессии R? Я использую функции ниже [на основе сообщений Петра Пикаля и Дэвида Хинда в списке r-help в 2004 году], чтобы перечислять (и/или сортировать) самые большие объекты и иногда rm() некоторые из них. Но самым эффективным решением было... запустить под 64-разрядную Linux с достаточной памятью.

Любые другие интересные трюки, которые люди хотят поделиться? Один за сообщение, пожалуйста.

# improved list of objects
.ls.objects <- function (pos = 1, pattern, order.by,
                        decreasing=FALSE, head=FALSE, n=5) {
    napply <- function(names, fn) sapply(names, function(x)
                                         fn(get(x, pos = pos)))
    names <- ls(pos = pos, pattern = pattern)
    obj.class <- napply(names, function(x) as.character(class(x))[1])
    obj.mode <- napply(names, mode)
    obj.type <- ifelse(is.na(obj.class), obj.mode, obj.class)
    obj.size <- napply(names, object.size)
    obj.dim <- t(napply(names, function(x)
                        as.numeric(dim(x))[1:2]))
    vec <- is.na(obj.dim)[, 1] & (obj.type != "function")
    obj.dim[vec, 1] <- napply(names, length)[vec]
    out <- data.frame(obj.type, obj.size, obj.dim)
    names(out) <- c("Type", "Size", "Rows", "Columns")
    if (!missing(order.by))
        out <- out[order(out[[order.by]], decreasing=decreasing), ]
    if (head)
        out <- head(out, n)
    out
}
# shorthand
lsos <- function(..., n=10) {
    .ls.objects(..., order.by="Size", decreasing=TRUE, head=TRUE, n=n)
}
  • 0
    Обратите внимание, я не сомневаюсь в этом, но какая польза от этого? Я довольно плохо знаком с проблемами с памятью в R, но в последнее время у меня возникают некоторые проблемы (вот почему я искал этот пост :) - поэтому я только начинаю со всего этого. Как это помогает моей повседневной работе?
  • 4
    если вы хотите видеть объекты внутри функции, вы должны использовать: lsos (pos = environment ()), иначе он будет показывать только глобальные переменные. Для записи в стандартную ошибку: write.table (lsos (pos = environment ()), stderr (), quote = FALSE, sep = '\ t')
Показать ещё 9 комментариев
Теги:
memory-management

26 ответов

24
Лучший ответ

Для дополнительной иллюстрации общей стратегии частых перезапусков мы можем использовать littler, что позволяет нам запускать простые выражения непосредственно из командной строки, линия. Вот пример, который я иногда использую во время различных BLAS для простого crossprod.

 r -e'N<-3*10^3; M<-matrix(rnorm(N*N),ncol=N); print(system.time(crossprod(M)))'

Аналогично,

 r -lMatrix -e'example(spMatrix)'

загружает пакет Matrix (через -packages | -l switch) и запускает примеры функции spMatrix. Поскольку r всегда начинает "свежий", этот метод также является хорошим тестом при разработке пакета.

И последнее, но не менее важное значение также отлично подходит для автоматического пакетного режима в скриптах с использованием заголовка "#!/usr/bin/r" shebang. Rscript является альтернативой, где littler недоступен (например, в Windows).

180

Убедитесь, что вы записываете свою работу в воспроизводимом script. Время от времени заново откройте R, затем source() ваш script. Вы очистите все, что больше не используете, и в качестве дополнительного преимущества будет проверен ваш код.

  • 55
    Моя стратегия состоит в том, чтобы разбить мои сценарии по направлениям load.R и do.R, где load.R может потребоваться довольно много времени для загрузки данных из файлов или базы данных, и выполняется минимальная предварительная обработка / объединение эти данные. Последняя строка load.R - это что-то для сохранения состояния рабочего пространства. Тогда do.R - мой блокнот, с помощью которого я строю свои аналитические функции. Я часто перезагружаю do.R (с или без перезагрузки состояния рабочего пространства из load.R по мере необходимости).
  • 30
    Это хорошая техника. Когда файлы запускаются в определенном порядке, подобном этому, я часто 1-load.r ним префикс с номером: 1-load.r , 2-explore.r , 3-model.r - таким образом, для других очевидно, что существует некоторый порядок.
Показать ещё 11 комментариев
154

Я использую data.table. С помощью оператора := вы можете:

  • Добавить столбцы по ссылке
  • Изменить подмножества существующих столбцов по ссылке и по группам по ссылке
  • Удалить столбцы по ссылке

Ни одна из этих операций не копирует (потенциально большой) data.table вообще, даже не один раз.

  • Агрегация также особенно быстро, потому что data.table использует гораздо меньшую рабочую память.

Ссылки по теме:

96

Видел это на твиттере и думал, что это потрясающая функция Дирка! Следуя от JD Long, я бы сделал это для удобного чтения:

# improved list of objects
.ls.objects <- function (pos = 1, pattern, order.by,
                        decreasing=FALSE, head=FALSE, n=5) {
    napply <- function(names, fn) sapply(names, function(x)
                                         fn(get(x, pos = pos)))
    names <- ls(pos = pos, pattern = pattern)
    obj.class <- napply(names, function(x) as.character(class(x))[1])
    obj.mode <- napply(names, mode)
    obj.type <- ifelse(is.na(obj.class), obj.mode, obj.class)
    obj.prettysize <- napply(names, function(x) {
                           format(utils::object.size(x), units = "auto") })
    obj.size <- napply(names, object.size)
    obj.dim <- t(napply(names, function(x)
                        as.numeric(dim(x))[1:2]))
    vec <- is.na(obj.dim)[, 1] & (obj.type != "function")
    obj.dim[vec, 1] <- napply(names, length)[vec]
    out <- data.frame(obj.type, obj.size, obj.prettysize, obj.dim)
    names(out) <- c("Type", "Size", "PrettySize", "Length/Rows", "Columns")
    if (!missing(order.by))
        out <- out[order(out[[order.by]], decreasing=decreasing), ]
    if (head)
        out <- head(out, n)
    out
}

# shorthand
lsos <- function(..., n=10) {
    .ls.objects(..., order.by="Size", decreasing=TRUE, head=TRUE, n=n)
}

lsos()

Это приводит к следующему:

                      Type   Size PrettySize Length/Rows Columns
pca.res                 PCA 790128   771.6 Kb          7      NA
DF               data.frame 271040   264.7 Kb        669      50
factor.AgeGender   factanal  12888    12.6 Kb         12      NA
dates            data.frame   9016     8.8 Kb        669       2
sd.                 numeric   3808     3.7 Kb         51      NA
napply             function   2256     2.2 Kb         NA      NA
lsos               function   1944     1.9 Kb         NA      NA
load               loadings   1768     1.7 Kb         12       2
ind.sup             integer    448  448 bytes        102      NA
x                 character     96   96 bytes          1      NA

ПРИМЕЧАНИЕ. Основная часть, которую я добавил, была (опять же, адаптирована из ответа JD):

obj.prettysize <- napply(names, function(x) {
                           print(object.size(x), units = "auto") })
  • 0
    Можно ли добавить эту функцию в dplyr или какой-либо другой пакет ключей.
  • 1
    Стоит отметить, что (по крайней мере с base-3.3.2) capture.output больше не capture.output , и obj.prettysize <- napply(names, function(x) {format(utils::object.size(x), units = "auto") }) производит чистый вывод. Фактически, не удаляя его, вы получите нежелательные кавычки в выводе, то есть [1] "792.5 Mb" вместо 792.5 Mb .
Показать ещё 2 комментария
46

Мне нравится Dirk.ls.objects() script, но я продолжал щуриться, чтобы подсчитывать символы в столбце размера. Поэтому я сделал некоторые уродливые взломы, чтобы сделать его присутствующим с довольно форматированием для размера:

.ls.objects <- function (pos = 1, pattern, order.by,
                        decreasing=FALSE, head=FALSE, n=5) {
    napply <- function(names, fn) sapply(names, function(x)
                                         fn(get(x, pos = pos)))
    names <- ls(pos = pos, pattern = pattern)
    obj.class <- napply(names, function(x) as.character(class(x))[1])
    obj.mode <- napply(names, mode)
    obj.type <- ifelse(is.na(obj.class), obj.mode, obj.class)
    obj.size <- napply(names, object.size)
    obj.prettysize <- sapply(obj.size, function(r) prettyNum(r, big.mark = ",") )
    obj.dim <- t(napply(names, function(x)
                        as.numeric(dim(x))[1:2]))
    vec <- is.na(obj.dim)[, 1] & (obj.type != "function")
    obj.dim[vec, 1] <- napply(names, length)[vec]
    out <- data.frame(obj.type, obj.size,obj.prettysize, obj.dim)
    names(out) <- c("Type", "Size", "PrettySize", "Rows", "Columns")
    if (!missing(order.by))
        out <- out[order(out[[order.by]], decreasing=decreasing), ]
        out <- out[c("Type", "PrettySize", "Rows", "Columns")]
        names(out) <- c("Type", "Size", "Rows", "Columns")
    if (head)
        out <- head(out, n)
    out
}
45

Я активно использую параметр subset с выбором только необходимых переменных при передаче данных в аргумент data= функций регрессии. Это приводит к некоторым ошибкам, если я забываю добавлять переменные как к формуле, так и к вектору select=, но это все еще экономит много времени из-за уменьшения копирования объектов и значительно уменьшает объем памяти. Скажем, у меня 4 миллиона записей с 110 переменными (и я это делаю.) Пример:

# library(rms); library(Hmisc) for the cph,and rcs functions
Mayo.PrCr.rbc.mdl <- 
cph(formula = Surv(surv.yr, death) ~ age + Sex + nsmkr + rcs(Mayo, 4) + 
                                     rcs(PrCr.rat, 3) +  rbc.cat * Sex, 
     data = subset(set1HLI,  gdlab2 & HIVfinal == "Negative", 
                           select = c("surv.yr", "death", "PrCr.rat", "Mayo", 
                                      "age", "Sex", "nsmkr", "rbc.cat")
   )            )

В контексте настройки контекста и стратегии: переменная gdlab2 является логическим вектором, который был создан для объектов в наборе данных, который имел все нормальные или почти нормальные значения для группы лабораторных тестов, а HIVfinal был символом вектор, который обобщил предварительное и подтверждающее тестирование на ВИЧ.

33

Это хороший трюк.

Еще одно предложение состоит в том, чтобы использовать, по возможности, эффективные объекты памяти: например, вместо матрицы data.frame использовать матрицу.

Это не касается управления памятью, но одна важная функция, которая не получила широкого распространения, - memory.limit(). Вы можете увеличить значение по умолчанию с помощью этой команды memory.limit(size = 2500), где размер находится в MB. Как отметил Дирк, вам нужно использовать 64-битные, чтобы в полной мере воспользоваться этим.

  • 23
    Разве это не применимо только к Windows?
  • 2
    > memory.limit () [1] Inf Предупреждающее сообщение: memory.limit () 'зависит от Windows
32

Мне очень нравится улучшенная функция объектов, разработанная Дирком. Тем не менее, большую часть времени для меня достаточно базового вывода с именем и размером объекта. Здесь более простая функция с аналогичной целью. Использование памяти можно заказать в алфавитном порядке или по размеру, можно ограничить определенным количеством объектов и заказывать по возрастанию или по убыванию. Кроме того, я часто работаю с данными размером 1 ГБ +, поэтому функция соответственно меняет единицы.

showMemoryUse <- function(sort="size", decreasing=FALSE, limit) {

  objectList <- ls(parent.frame())

  oneKB <- 1024
  oneMB <- 1048576
  oneGB <- 1073741824

  memoryUse <- sapply(objectList, function(x) as.numeric(object.size(eval(parse(text=x)))))

  memListing <- sapply(memoryUse, function(size) {
        if (size >= oneGB) return(paste(round(size/oneGB,2), "GB"))
        else if (size >= oneMB) return(paste(round(size/oneMB,2), "MB"))
        else if (size >= oneKB) return(paste(round(size/oneKB,2), "kB"))
        else return(paste(size, "bytes"))
      })

  memListing <- data.frame(objectName=names(memListing),memorySize=memListing,row.names=NULL)

  if (sort=="alphabetical") memListing <- memListing[order(memListing$objectName,decreasing=decreasing),] 
  else memListing <- memListing[order(memoryUse,decreasing=decreasing),] #will run if sort not specified or "size"

  if(!missing(limit)) memListing <- memListing[1:limit,]

  print(memListing, row.names=FALSE)
  return(invisible(memListing))
}

И вот пример вывода:

> showMemoryUse(decreasing=TRUE, limit=5)
      objectName memorySize
       coherData  713.75 MB
 spec.pgram_mine  149.63 kB
       stoch.reg  145.88 kB
      describeBy    82.5 kB
      lmBandpass   68.41 kB
31

К сожалению, у меня не было времени тщательно его протестировать, но вот наконечник памяти, который я раньше не видел. Для меня требуемая память была уменьшена более чем на 50%. Когда вы читаете материал в R, например, read.csv, они требуют определенного объема памяти. После этого вы можете сохранить их с помощью save("Destinationfile",list=ls()) В следующий раз, когда вы откроете R, вы можете использовать load("Destinationfile") Теперь использование памяти могло бы уменьшиться. Было бы хорошо, если бы кто-нибудь мог подтвердить, дает ли это аналогичные результаты с другим набором данных.

  • 4
    да, я испытал то же самое. В моем случае использование памяти падает даже до 30%. Используется 1,5 ГБ памяти, сохранено в .RData (~ 30 МБ). Новый сеанс после загрузки .RData использует менее 500 МБ памяти.
  • 0
    Я попытался с 2 наборами данных (100 МБ и 2,7 ГБ), загруженных в data.table с использованием fread , а затем сохранены в .RData. Файлы RData действительно были примерно на 70% меньше, но после повторной загрузки используемая память была точно такой же. Хотелось надеяться, что этот трюк уменьшит объем памяти ... я что-то упустил?
Показать ещё 2 комментария
28

Я никогда не сохраняю рабочее пространство R. Я использую сценарии импорта и сценарии данных и выводят любые особенно большие объекты данных, которые я не хочу часто воссоздавать в файлы. Таким образом, я всегда начинаю со свежей рабочей области и не нуждаюсь в очистке больших объектов. Это очень приятная функция.

23

Как для скорости, так и для памяти, при построении большого кадра данных с помощью нескольких сложных шагов я периодически очищаю его (встроенный набор данных) на диск, добавляя к чему-либо, что было раньше, и затем перезапустите его. Таким образом, промежуточные этапы работают только на небольших кадрах данных (что хорошо, например, rbind значительно замедляет работу с более крупными объектами). Весь набор данных можно прочитать в конце процесса, когда все промежуточные объекты были удалены.

dfinal <- NULL
first <- TRUE
tempfile <- "dfinal_temp.csv"
for( i in bigloop ) {
    if( !i %% 10000 ) { 
        print( i, "; flushing to disk..." )
        write.table( dfinal, file=tempfile, append=!first, col.names=first )
        first <- FALSE
        dfinal <- NULL   # nuke it
    }

    # ... complex operations here that add data to 'dfinal' data frame  
}
print( "Loop done; flushing to disk and re-reading entire data set..." )
write.table( dfinal, file=tempfile, append=TRUE, col.names=FALSE )
dfinal <- read.table( tempfile )
17

Просто отметим, что data.table package tables() кажется довольно хорошей заменой настраиваемой функции Dirk .ls.objects() (подробно описанной в более ранних ответах), хотя и для data.frames/tables, а не, например. матрицы, массивы, списки.

  • 0
    это не перечисляет какие-либо data.frames, так что это не так здорово
14
  • Мне повезло, и мои большие наборы данных сохраняются инструментом в "кусках" (подмножествах) примерно 100 МБ (32-битный двоичный код). Таким образом, я могу выполнить шаги предварительной обработки (удаление неинформативных частей, понижающую дискретизацию) последовательно перед слиянием набора данных.

  • Вызов gc () "вручную" может помочь, если размер данных приблизится к доступной памяти.

  • Иногда для другого алгоритма требуется гораздо меньше памяти.
    Иногда существует компромисс между векторизации и использованием памяти.
    сравните: split и lapply против цикла for.

  • Для быстрого и легкого анализа данных я часто работаю сначала с небольшим случайным подмножеством (sample ()) данных. После анализа данных script/. Rnw закончен код анализа данных, и полные данные переходят на сервер расчета за ночь/за выходные /... расчет.

11

Использование окружений вместо списков для обработки коллекций объектов, которые занимают значительное количество рабочей памяти.

Причина: каждый раз, когда элемент структуры list изменяется, весь список временно дублируется. Это становится проблемой, если требование к хранилищу в списке составляет примерно половину доступной рабочей памяти, потому что тогда данные должны быть заменены на медленный жесткий диск. С другой стороны, среды не подвержены этому поведению, и к ним можно относиться подобно спискам.

Вот пример:

get.data <- function(x)
{
  # get some data based on x
  return(paste("data from",x))
}

collect.data <- function(i,x,env)
{
  # get some data
  data <- get.data(x[[i]])
  # store data into environment
  element.name <- paste("V",i,sep="")
  env[[element.name]] <- data
  return(NULL)  
}

better.list <- new.env()
filenames <- c("file1","file2","file3")
lapply(seq_along(filenames),collect.data,x=filenames,env=better.list)

# read/write access
print(better.list[["V1"]])
better.list[["V2"]] <- "testdata"
# number of list elements
length(ls(better.list))

В сочетании со структурами, такими как big.matrix или data.table, которые позволяют изменять их содержимое на месте, может быть достигнуто очень эффективное использование памяти.

  • 6
    Это уже не так: от продвинутого R Хэдли: «Изменения в R 3.1.0 сделали это использование [сред] существенно менее важным, потому что изменение списка больше не делает глубокую копию».
7

Функция ll в пакете gData также может отображать использование памяти для каждого объекта.

gdata::ll(unit='MB')
  • 1
    Если бы только по порядку размер был по умолчанию ...
  • 0
    Не в моей системе: версия R 3.1.1 (2014-07-10), x86_64-pc-linux-gnu (64-разрядная версия), gdata_2.13.3, gtools_3.4.1.
Показать ещё 3 комментария
6

Только с 4 ГБ оперативной памяти (под управлением Windows 10, так что это примерно 2 или более реалистично 1 ГБ), я должен быть очень осторожным с распределением.

Я использую data.table почти исключительно.

Функция "fread" позволяет вам подмножать информацию по именам полей при импорте; импортируйте поля, которые действительно необходимы для начала. Если вы используете чтение базы R, запустите ложные столбцы сразу после импорта.

Как показано в 42-м, когда это возможно, я потом подмножусь внутри столбцов сразу после импорта информации.

Я часто rm() объекты из среды, как только они больше не нужны, например. на следующей строке после их использования для подмножества чего-то другого и вызовите gc().

'fread' и 'fwrite' из data.table может быть очень быстрым по сравнению с базой R, читает и записывает.

Как говорит kpierce8, я почти всегда вывожу все из окружающей среды и исправляю его, даже с тысячами/сотнями тысяч крошечных файлов, чтобы пройти. Это не только защищает окружающую среду и сохраняет распределение памяти на низком уровне, но, возможно, из-за серьезного отсутствия ОЗУ, R имеет склонность к частому сбою на моем компьютере; очень часто. Имея информацию, резервную копию на самом диске, поскольку код проходит через различные этапы, означает, что мне не нужно начинать с самого начала, если он сработает.

По состоянию на 2017 год, я думаю, что самые быстрые SSD-устройства работают примерно на несколько ГБ в секунду через порт M2. У меня есть действительно базовый 50-гигабайтный SSD-накопитель Kingston V300 (550 МБ/с), который я использую в качестве основного диска (на нем есть Windows и R). Я сохраняю всю объемную информацию на дешевом 500-граммовом диске WD. Я перемещаю наборы данных на SSD, когда я начинаю работать над ними. Это, в сочетании с "fread'ing" и "fwrite", все отлично работает. Я пробовал использовать 'ff', но предпочитаю первый. Скорость чтения/записи 4K может создавать проблемы с этим; резервное копирование четверти миллиона файлов 1 тыс. (250 МБ) с SSD на планшет может занять несколько часов. Насколько мне известно, пока еще нет пакета R, который может автоматически оптимизировать процесс "chunkification"; например посмотрите, сколько оперативной памяти у пользователя есть, проверьте скорость чтения/записи RAM/всех подключенных дисков и затем предложите оптимальный протокол "chunkification". Это может привести к значительным улучшениям рабочего процесса/оптимизации ресурсов; например разделите его на... MB для ram → разделите его на... MB для SSD → разделите его на... MB на блюде → разделите его на... MB на ленту. Он мог бы заранее набирать данные, чтобы дать ему более реалистичную ручку.

Многие проблемы, с которыми я работал в R, связаны с формированием комбинаций и пар перестановок, тройками и т.д., что только ограничивает ограниченность ОЗУ ограничением, поскольку они часто будут по крайней мере экспоненциально расширяться в какой-то момент. Это заставило меня сосредоточить внимание на качестве, а не на количестве информации, вступающей в них для начала, вместо того, чтобы пытаться очистить его потом, и о последовательности операций по подготовке информации для начала (начиная с простейшая операция и увеличение сложности); например подмножество, затем слияние/объединение, затем форматирование комбинаций/перестановок и т.д.

В некоторых случаях, похоже, есть некоторые преимущества для использования чтения и записи базы R. Например, обнаружение ошибок внутри "fread" настолько хорошо, что может быть трудно попытаться получить действительно грязную информацию в R, чтобы начать ее очищать. База R также кажется намного проще, если вы используете Linux. Base R, похоже, отлично работает в Linux, Windows 10 использует ~ 20 ГБ дискового пространства, тогда как Ubuntu требуется всего несколько ГБ, оперативная память, необходимая для Ubuntu, немного ниже. Но я заметил большое количество предупреждений и ошибок при установке сторонних пакетов в (L) Ubuntu. Я бы не рекомендовал дрейфовать слишком далеко от (L) Ubuntu или других дистрибутивов с Linux, поскольку вы можете потерять такую ​​общую совместимость, что делает процесс почти бессмысленным (я думаю, что "единство" должно быть отменено в Ubuntu с 2017 года). Я понимаю, что это не будет хорошо согласуется с некоторыми пользователями Linux, но некоторые из пользовательских дистрибутивов являются границей бессмысленной за пределами новизны (я провел годы, используя только Linux).

Надеюсь, некоторые из них могут помочь другим.

6

Если вы действительно хотите избежать утечек, вам следует избегать создания каких-либо больших объектов в глобальной среде.

Обычно я использую функцию, которая выполняет задание и возвращает NULL - все данные считываются и обрабатываются в этой функции или других, которые она вызывает.

5

Это ничего не добавляет к вышеупомянутому, но написано в простом и сильно прокомментированном стиле, который мне нравится. Он дает таблицу с объектами, упорядоченными по размеру, но без некоторых деталей, приведенных в приведенных выше примерах:

#Find the objects       
MemoryObjects = ls()    
#Create an array
MemoryAssessmentTable=array(NA,dim=c(length(MemoryObjects),2))
#Name the columns
colnames(MemoryAssessmentTable)=c("object","bytes")
#Define the first column as the objects
MemoryAssessmentTable[,1]=MemoryObjects
#Define a function to determine size        
MemoryAssessmentFunction=function(x){object.size(get(x))}
#Apply the function to the objects
MemoryAssessmentTable[,2]=t(t(sapply(MemoryAssessmentTable[,1],MemoryAssessmentFunction)))
#Produce a table with the largest objects first
noquote(MemoryAssessmentTable[rev(order(as.numeric(MemoryAssessmentTable[,2]))),])
3

Если вы работаете с Linux и хотите использовать несколько процессов и должны выполнять операции читать на одном или более большие объекты используйте makeForkCluster вместо makePSOCKcluster. Это также экономит время отправки большого объекта другим процессам.

2

Это более новый ответ на этот отличный старый вопрос. От Hadley Advanced R:

install.packages("pryr")

library(pryr)

object_size(1:10)
## 88 B

object_size(mean)
## 832 B

object_size(mtcars)
## 6.74 kB

(http://adv-r.had.co.nz/memory.html)

2

Как и более общие методы управления памятью, приведенные в ответах выше, я всегда стараюсь как можно меньше уменьшить размер моих объектов. Например, я работаю с очень большими, но очень разреженными матрицами, другими словами, матрицами, где большинство значений равно нулю. Используя пакет "Матрица" (важная капитализация), я смог уменьшить средние размеры объекта от ~ 2 до 200 МБ так же, как:

my.matrix <- Matrix(my.matrix)

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

Кроме того, необработанные файлы, которые я получаю, находятся в "длинном" формате, где каждая точка данных имеет переменные x, y, z, i. Гораздо эффективнее преобразовать данные в массив измерения x * y * z с переменной i.

Знайте свои данные и используйте немного здравого смысла.

2

Я действительно ценю некоторые из приведенных выше ответов, следуя @hadley и @Dirk, которые предлагают закрыть R и выдавать source и использовать командную строку. Я придумал решение, которое очень хорошо работало для меня. Мне приходилось иметь дело с сотнями масс-спектров, каждый из которых занимает около 20 Мб памяти, поэтому я использовал два сценария R, как показано ниже:

Сначала обертка:

#!/usr/bin/Rscript --vanilla --default-packages=utils

for(l in 1:length(fdir)) {

   for(k in 1:length(fds)) {
     system(paste("Rscript runConsensus.r", l, k))
   }
}

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

Надеюсь, это поможет.

1

На основе ответов @Dirk и @Tony я сделал небольшое обновление. Результат выдавал [1] до значений довольно большого размера, поэтому я вынул capture.output, который решил проблему:

.ls.objects <- function (pos = 1, pattern, order.by,
                     decreasing=FALSE, head=FALSE, n=5) {
napply <- function(names, fn) sapply(names, function(x)
    fn(get(x, pos = pos)))
names <- ls(pos = pos, pattern = pattern)
obj.class <- napply(names, function(x) as.character(class(x))[1])
obj.mode <- napply(names, mode)
obj.type <- ifelse(is.na(obj.class), obj.mode, obj.class)
obj.prettysize <- napply(names, function(x) {
    format(utils::object.size(x),  units = "auto") })
obj.size <- napply(names, utils::object.size)

obj.dim <- t(napply(names, function(x)
    as.numeric(dim(x))[1:2]))
vec <- is.na(obj.dim)[, 1] & (obj.type != "function")
obj.dim[vec, 1] <- napply(names, length)[vec]
out <- data.frame(obj.type, obj.size, obj.prettysize, obj.dim)
names(out) <- c("Type", "Size", "PrettySize", "Rows", "Columns")
if (!missing(order.by))
    out <- out[order(out[[order.by]], decreasing=decreasing), ]
if (head)
    out <- head(out, n)

return(out)
}

# shorthand
lsos <- function(..., n=10) {
    .ls.objects(..., order.by="Size", decreasing=TRUE, head=TRUE, n=n)
}

lsos()
1

Вы также можете получить некоторую выгоду, используя knitr, и поместите свой script в Rmd chuncks.

Я обычно делю код в разных кусках и выбираю, какой из них будет сохранять контрольную точку в кеше или в файл RDS, а

Там вы можете установить кусок, который будет сохранен в "кеш", или вы можете решить запустить или не использовать определенный фрагмент. Таким образом, в первом запуске вы можете обрабатывать только "часть 1", другое исполнение вы можете выбрать только "часть 2" и т.д.

Пример:

part1
```{r corpus, warning=FALSE, cache=TRUE, message=FALSE, eval=TRUE}
corpusTw <- corpus(twitter)  # build the corpus
```
part2
```{r trigrams, warning=FALSE, cache=TRUE, message=FALSE, eval=FALSE}
dfmTw <- dfm(corpusTw, verbose=TRUE, removeTwitter=TRUE, ngrams=3)
```

Как побочный эффект, это также может сэкономить вам некоторые головные боли с точки зрения воспроизводимости:)

0

Совет по работе с объектами, требующими сложных промежуточных вычислений: при использовании объектов, для создания которых требуется много тяжелых вычислений и промежуточных шагов, я часто нахожу полезным написать фрагмент кода с функцией для создания объекта, а затем отдельный фрагмент кода, который дает мне возможность либо сгенерировать и сохранить объект в виде файла rmd, либо загрузить его извне из файла rmd который я уже ранее сохранил. Это особенно легко сделать в R Markdown используя следующую структуру фрагмента кода.

'''{r Create OBJECT}

COMPLICATED.FUNCTION <- function(...) { Do heavy calculations needing lots of memory;
                                        Output OBJECT; }

'''
'''{r Generate or load OBJECT}

LOAD <- TRUE;
#NOTE: Set LOAD to TRUE if you want to load saved file
#NOTE: Set LOAD to FALSE if you want to generate and save

if(LOAD == TRUE) { OBJECT <- readRDS(file = 'MySavedObject.rds'); } else
                 { OBJECT <- COMPLICATED.FUNCTION(x, y, z);
                             saveRDS(file = 'MySavedObject.rds', object = OBJECT); }

'''

С этой структурой кода все, что мне нужно сделать, это изменить LOAD зависимости от того, хочу ли я создать и сохранить объект, или загрузить его непосредственно из существующего сохраненного файла. (Конечно, я должен сгенерировать его и сохранить в первый раз, но после этого у меня есть возможность загрузить его.) Установка LOAD = TRUE обойти использование моей сложной функции и избежать всех тяжелых вычислений в ней. Этот метод все еще требует достаточно памяти для хранения интересующего вас объекта, но он избавляет вас от необходимости вычислять его каждый раз, когда вы запускаете свой код. Для объектов, которые требуют большого количества сложных вычислений промежуточных шагов (например, для вычислений, включающих циклы над большими массивами), это может сэкономить значительное количество времени и вычислений.

0

Запуск

for (i in 1:10) 
    gc(reset = T)

время от времени также помогает R освобождать неиспользуемую, но еще не освобожденную память.

  • 0
    Что здесь делает цикл for ? Там нет i в вызове gc .
  • 0
    @qqq это просто чтобы избежать копирования-вставки gc(reset = T) девять раз
Показать ещё 1 комментарий

Ещё вопросы

Сообщество Overcoder
Наверх
Меню