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

490

Какие приемы люди используют для управления доступной памятью интерактивного сеанса 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)
}
Дирк Эддельбюттель
источник
Обратите внимание, я не сомневаюсь в этом, но какая польза от этого? Я довольно плохо знаком с проблемами с памятью в R, но в последнее время у меня возникают некоторые проблемы (вот почему я искал этот пост :) - поэтому я только начинаю со всего этого. Как это помогает моей повседневной работе?
Мэтт Баннер
4
если вы хотите видеть объекты внутри функции, вы должны использовать: lsos (pos = environment ()), иначе он будет показывать только глобальные переменные. Для записи в стандартную ошибку: write.table (lsos (pos = environment ()), stderr (), quote = FALSE, sep = '\ t')
Майкл Кун,
Почему 64-битная Linux, а не 64-битная Windows? Имеет ли выбор ОС нетривиальную разницу, когда у меня есть 32 ГБ оперативной памяти?
Джейс
3
@pepsimax: это было упаковано в multilevelPSAпакет . Пакет предназначен для чего-то другого, но вы можете использовать функцию оттуда, не загружая пакет, говоря requireNamespace(multilevelPSA); multilevelPSA::lsos(...). Или в Dmiscупаковке (не на CRAN).
krlmlr
1
Если набор данных имеет управляемый размер, я обычно выбираю R studio> Environment> Grid View. Здесь вы можете увидеть и отсортировать все элементы в вашей текущей среде в зависимости от размера.
kRazzy R

Ответы:

197

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

Hadley
источник
58
Моя стратегия состоит в том, чтобы разбить мои сценарии по направлениям load.R и do.R, где load.R может потребоваться довольно много времени для загрузки данных из файлов или базы данных, и выполняет минимальную предварительную обработку / объединение эти данные. Последняя строка load.R - это что-то для сохранения состояния рабочего пространства. Тогда do.R - мой блокнот, с помощью которого я строю свои аналитические функции. Я часто перезагружаю do.R (с или без перезагрузки состояния рабочего пространства из load.R по мере необходимости).
Джош Рейх
32
Это хорошая техника. Когда файлы выполняются в определенном порядке , как это, я часто их префикс с номером: 1-load.r, 2-explore.r, 3-model.r- таким образом , это очевидно для других , что есть какой - то порядок присутствует.
Хэдли
4
Я не могу поддержать эту идею достаточно. Я учил R нескольким людям, и это одна из первых вещей, которые я говорю. Это также относится к любому языку, в котором разработка включает в себя REPL и редактируемый файл (например, Python). rm (ls = list ()) и source () тоже работают, но повторное открытие лучше (пакеты тоже очищены).
Винс
53
Тот факт, что ответ с наибольшим количеством голосов включает перезапуск R, является наихудшей критикой R.
sds
7
@ MartínBel, который удаляет только объекты, созданные в глобальной среде. Он не выгружает пакеты, объекты S4 и многое другое.
Хэдли
160

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

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

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

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

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

Мэтт Доул
источник
109

Видел это в твиттере и думаю, что это потрясающая функция от Дирка! Исходя из ответа 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") })
Тони Бреял
источник
Можно ли добавить эту функцию в dplyr или какой-либо другой пакет ключей.
userJT
1
Стоит отметить, что (по крайней мере с base-3.3.2) capture.outputбольше не нужно, и obj.prettysize <- napply(names, function(x) {format(utils::object.size(x), units = "auto") })выдает чистый результат. Фактически, не удаляя его, вы получите нежелательные кавычки в выводе, то есть [1] "792.5 Mb"вместо 792.5 Mb.
Nutle
@Nutle Отлично, я обновил код соответствующим образом :)
Тони Бреял
Я бы также изменил obj.class <- napply(names, function(x) as.character(class(x))[1])на, obj.class <- napply(names, function(x) class(x)[1]) так classкак теперь всегда возвращаю вектор символов (base-3.5.0).
DeltaIV
49

Я активно использую 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представлял собой вектор символов, который суммировал предварительное и подтверждающее тестирование на ВИЧ ,

IRTFM
источник
48

Мне нравится сценарий Дирка .ls.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.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
}
JD Long
источник
34

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

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

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

Шейн
источник
25
Разве это не применимо только к Windows?
Кристофер Дюбуа
4
> Memory.limit () [1] сообщение Inf Предупреждение: 'memory.limit ()' является Windows , специфические
LJT
Помогает ли использование tibble вместо data.frame еще лучше сэкономить память?
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
Майкл Халлквист
источник
30

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

kpierce8
источник
30

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

Деннис Джаэруддин
источник
4
да, я испытал то же самое. Использование памяти падает даже до 30% в моем случае. Используется 1,5 ГБ памяти, сохранено в .RData (~ 30 МБ). Новый сеанс после загрузки .RData использует менее 500 МБ памяти.
f3lix
Я попытался с 2 наборами данных (100 МБ и 2,7 ГБ), загруженных в data.table с помощью fread, а затем сохранены в .RData. Файлы RData действительно были примерно на 70% меньше, но после перезагрузки используемая память была точно такой же. Хотелось надеяться, что этот трюк уменьшит объем памяти ... я что-то упустил?
NoviceProg
@NoviceProg Я не думаю, что вы что-то упустили, но это трюк, я думаю, это не будет работать для всех ситуаций. В моем случае память после повторной загрузки была фактически уменьшена, как описано.
Деннис Джаэруддин
6
@NoviceProg Пара вещей. Во-первых, fread, следуя кредо data.table, вероятно, более эффективно использует память при загрузке файлов, чем read.csv. Во-вторых, экономия памяти, которую люди здесь отмечают, в первую очередь связана с размером памяти процесса R (который расширяется для хранения объектов и убирается, когда происходит сборка мусора). Однако сборка мусора не всегда освобождает всю оперативную память обратно в ОС. Остановка сеанса R и загрузка элемента из того места, где он был сохранен, высвободит столько оперативной памяти, сколько возможно ... но если издержки были небольшими, для начала ... нет выгоды.
russellpierce
27

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

 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) и запускает примеры функции spMatrix. Поскольку r всегда запускается «заново», этот метод также является хорошим тестом при разработке пакета.

И последнее, но не менее важное: r также прекрасно работает для автоматического пакетного режима в сценариях, использующих заголовок shebang '#! / Usr / bin / r'. Rscript - это альтернатива, где littler недоступен (например, в Windows).

Дирк Эддельбюттель
источник
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пакет, tables()кажется, является довольно хорошей заменой .ls.objects()пользовательской функции Дирка (подробно описано в предыдущих ответах), хотя только для data.frames / таблиц, а не, например, для матриц, массивов, списков.

geotheory
источник
это не перечисляет какие-либо data.frames, так что это не так здорово
userJT
16
  1. Мне повезло, и мои большие наборы данных сохраняются прибором в виде «кусков» (подмножеств) размером примерно 100 МБ (32-разрядный двоичный код). Таким образом, я могу последовательно выполнить этапы предварительной обработки (удаление неинформативных частей, уменьшение частоты дискретизации) перед объединением набора данных.

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

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

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

cbeleites недоволен SX
источник
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 сделали использование [сред] существенно менее важным, потому что изменение списка больше не делает глубокую копию».
petrelharp
8

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

gdata::ll(unit='MB')
user1436187
источник
Не в моей системе: версия R 3.1.1 (2014-07-10), x86_64-pc-linux-gnu (64-разрядная версия), gdata_2.13.3, gtools_3.4.1.
krlmlr
Вы правы, я протестировал его, как только он был заказан случайно!
user1436187
1
пожалуйста, измените функцию для использования Gb, Mb
userJT
7

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

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

Александр Радев
источник
7

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

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

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

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

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

'fread' и 'fwrite' из data.table могут быть очень быстрыми по сравнению с основанием R для чтения и записи.

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

Начиная с 2017 года, я думаю, что самые быстрые твердотельные накопители работают со скоростью несколько ГБ в секунду через порт M2. У меня действительно базовый SSD-накопитель Kingston V300 (550 МБ / с) емкостью 50 ГБ, который я использую в качестве основного (на нем установлены Windows и R). Я храню всю основную информацию на дешевом 500-гигабайтном диске WD. Я перемещаю наборы данных в SSD, когда начинаю над ними работать. Это, в сочетании с «fread» и «fwrite» все работало замечательно. Я пытался использовать 'FF', но предпочитаю первое. Скорость чтения / записи 4K может создать проблемы с этим; Резервное копирование четверти миллиона файлов размером 1 КБ (стоимостью 250 МБ) с SSD на диск может занять несколько часов. Насколько я знаю, еще нет доступных пакетов R, которые могли бы автоматически оптимизировать процесс «чанкификации»; например, посмотрите, сколько у пользователя оперативной памяти, проверьте скорость чтения / записи ОЗУ / всех подключенных дисков, а затем предложите оптимальный протокол «чанкификации». Это может привести к значительным улучшениям рабочего процесса / оптимизации ресурсов; например, разделить его на ... МБ для оперативной памяти -> разделить на ... МБ для SSD -> разделить на ... МБ на блюде -> разделить на ... МБ на ленте. Он мог бы забрать наборы данных заранее, чтобы дать ему более реалистичный измерительный стержень для работы.

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

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

Надеюсь, что-то из этого может помочь другим.

bg49ag
источник
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]))),])
JamesF
источник
5

Это более новый ответ на этот отличный старый вопрос. От продвинутого 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 )

Крис Били
источник
3

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

gdkrmr
источник
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))
   }
}

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

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

user1265067
источник
2

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

my.matrix <- Matrix(my.matrix)

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

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

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

Д Гринвуд
источник
2

Совет по работе с объектами, требующими сложных промежуточных вычислений: при использовании объектов, для создания которых требуется много тяжелых вычислений и промежуточных шагов, я часто нахожу полезным написать фрагмент кода с функцией для создания объекта, а затем отдельный фрагмент кода, который дает мне возможность либо сгенерировать и сохранить объект в виде 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обходит использование моей сложной функции и избегает всех тяжелых вычислений в ней. Этот метод все еще требует достаточно памяти для хранения интересующего вас объекта, но он избавляет вас от необходимости вычислять его каждый раз, когда вы запускаете свой код. Для объектов, которые требуют большого количества сложных вычислений промежуточных шагов (например, для вычислений, включающих циклы над большими массивами), это может сэкономить значительное количество времени и вычислений.

Бен - Восстановить Монику
источник
1

Бег

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

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

Марсело Вентура
источник
Что forцикл делает здесь? Там нет iв gcвызове.
Umaomamaomao
@qqq это просто чтобы избежать копирования-вставки gc(reset = T)девять раз
Марсело Вентура
14
Но почему вы запускаете его 9 раз? (любопытно, не критично)
Umaomamaomao
1

Вы также можете получить некоторую выгоду, используя knitr и помещая свой скрипт в 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)
```

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

Матиас Тэйер
источник
1

Основываясь на ответах @ Дирка и @ Тони, я сделал небольшое обновление. Результат выводился [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

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

dataframe-> step1-> step2-> step3->result

raster-> multipliedRast-> meanRastF-> sqrtRast->resultRast

Я работаю с временными объектами, которые я называю temp.

dataframe-> temp-> temp-> temp->result

Что оставляет мне меньше промежуточных файлов и больше обзора.

raster  <- raster('file.tif')
temp <- raster * 10
temp <- mean(temp)
resultRast <- sqrt(temp)

Чтобы сохранить больше памяти, я могу просто удалить, tempкогда больше не нужен.

rm(temp)

Если мне нужно несколько промежуточных файлов, я использую temp1, temp2, temp3.

Для тестирования я использую test, test2...

булава
источник