Прийоми управління доступною пам'яттю в R-сеансі


490

Які трюки використовують люди для управління наявною пам'яттю інтерактивного R-сеансу? Я використовую наведені нижче функції [на основі публікацій Петра Пікаля та Девіда Хіндса до списку r-довідки у 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 = оточення ()), stderr (), цитата = FALSE, sep = '\ t')
Майкл Кун

Чому 64-бітний Linux, а не 64-бітний Windows? Чи має вибір ОС нетривіальну різницю, коли у мене є 32 ГБ оперативної пам’яті?
Jase

3
@pepsimax: Це було вкладено в multilevelPSAпакет . Пакет розроблений для чогось іншого, але ви можете використовувати функцію звідти, не завантажуючи пакет, сказавши requireNamespace(multilevelPSA); multilevelPSA::lsos(...). Або в Dmiscупаковці (не на CRAN).
krlmlr

1
Якщо набір даних має керований розмір, я зазвичай заходжу в R studio> Середовище> Grid View. Тут ви можете бачити та сортувати всі предмети у вашому поточному середовищі залежно від розміру.
kRazzy R

Відповіді:


197

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


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

Побачив це у публікації на Twitter і подумайте, що це дивовижна функція від Дірка! Виходячи з відповіді Дж.Д. Лонга, я зробив би це для зручного читання:

# 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 або якогось іншого пакета ключів.
користувачJT

1
Варто зауважити, що (принаймні, з базовою 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завжди повертають вектор символів зараз (база-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був символьним вектором, який підсумовував попереднє та підтверджуюче тестування на ВІЛ .


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
}

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. Я використовую сценарії імпорту та сценарії даних та вивожу будь-які особливо великі об'єкти даних, які я не хочу часто відтворювати у файли. Таким чином я завжди починаю зі свіжої робочої області і не потрібно очищати великі об'єкти. Це дуже приємна функція, хоча.


30

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


4
так, я пережив те саме. Використання пам'яті падає навіть до 30% у моєму випадку. Використано 1,5 Гб пам'яті, збережено у .RData (~ 30 МБ). Новий сеанс після завантаження .RData використовує менше 500 МБ пам'яті.
f3lix

Я спробував з двома наборами даних (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 також чудово працює в автоматизованому пакетному режимі в сценаріях, використовуючи шебанг-заголовку '#! / 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 / таблиць, а не, наприклад, матриць, масивів, списків.


це не містить жодних фреймів data.frames, тому це не так вже й чудово
userJT

16
  1. Мені пощастило, і мої великі набори даних зберігаються інструментом в "шматках" (підмножинах) приблизно 100 Мб (32-бітний двійковий). Таким чином, я можу виконувати етапи попередньої обробки (видалення неінформативних частин, пониження тиску) послідовно перед тим, як з'єднати набір даних.

  2. Виклик gc ()"від руки" може допомогти, якщо розмір даних наблизиться до доступної пам'яті.

  3. Іноді для іншого алгоритму потрібно значно менше пам’яті.
    Іноді існує торгівля між векторизацією та використанням пам'яті.
    порівняти: split& lapplyvs. forцикл.

  4. Для швидкого та простого аналізу даних я часто працюю спочатку з невеликим випадковим підмножиною ( sample ()) даних. Після того, як скрипт аналізу даних / .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 3.1.0 зробила це використання [середовищ] суттєво менш важливим, оскільки зміна списку більше не робить глибоку копію".
petrelharp

8

llФункція в gDataпакеті може показати використання пам'яті кожного об'єкта , а також.

gdata::ll(unit='MB')

Не в моїй системі: R версія 3.1.1 (2014-07-10), x86_64-pc-linux-gnu (64-bit), gdata_2.13.3, gtools_3.4.1.
krlmlr

Ви маєте рацію, я тестую його, як тільки це було замовлено випадково!
користувач1436187

1
будь ласка, змініть функцію, щоб використовувати Gb, Mb
userJT

7

Якщо ви дійсно хочете уникнути витоків, вам слід уникати створення великих об'єктів у глобальному середовищі.

Що я зазвичай роблю, це мати функцію, яка виконує цю роботу і повертає NULL- всі дані читаються та обробляються в цій функції або інших, які вона викликає.


7

Маючи лише 4 Гб оперативної пам’яті (працює під управлінням Windows 10, тож зробіть це приблизно 2 або більше реально 1 ГБ), мені довелося бути дуже обережним з розподілом.

Я використовую data.table майже виключно.

Функція 'fread' дозволяє підмножувати інформацію за іменами полів при імпорті; імпортуйте лише поля, які фактично потрібні для початку. Якщо ви використовуєте базове R read, скасуйте помилкові стовпці відразу після імпорту.

Як підказує 42- , коли це можливо, я буду підмножити в стовпцях одразу після імпорту інформації.

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

'fread' і 'fwrite' з data.table може бути дуже швидким порівняно з базовим R читанням і записом .

Як kpierce8 підказує , я майже завжди переписую все з навколишнього середовища і повертаю його назад, навіть маючи тисячі / сотні тисяч крихітних файлів. Це не тільки підтримує «чисте» середовище та забезпечує низьке розподілення пам’яті, але, можливо, через сильну відсутність оперативної пам’яті, R має схильність до часто збоїв на моєму комп’ютері; дуже часто. Якщо резервна копія інформації на самому накопичувачі, коли код проходить через різні етапи, означає, що мені не потрібно починати з самого початку, якщо вона виходить з ладу.

Станом на 2017 рік, я думаю, що найшвидші SSD працюють через кілька ГБ в секунду через порт M2. У мене дійсно базовий накопичувач Kingston V300 (550 МБ / с) на 50 Гб, який я використовую як свій основний диск (на ньому є Windows та R). Я зберігаю всю основну інформацію на дешевому WD-диску на 500 ГБ. Коли я починаю працювати над ними, я переміщую набори даних на SSD. Це, у поєднанні з 'fread'ing' та 'fwrite' все, вийшло чудово. Я намагався використовувати "ff", але віддаю перевагу першому. 4K швидкості читання / запису можуть створювати проблеми з цим, хоча; резервне копіювання чверті мільйона 1 К файлів (250 МБ) з SSD на блюдо може зайняти години. Наскільки мені відомо, ще не існує жодного пакету R, який би міг автоматично оптимізувати процес «з’єднання»; наприклад, подивіться, скільки оперативної пам’яті має користувач, протестуйте швидкість читання / запису оперативної пам’яті / всіх підключених накопичувачів, а потім запропонуйте оптимальний протокол «chunkification». Це може призвести до значних покращень робочого процесу / оптимізації ресурсів; наприклад, розділіть його на ... MB для оперативної пам'яті -> розділіть її на ... MB для SSD -> розділіть її на ... MB на блюді -> розділіть її на ... MB на стрічці. Він міг заздалегідь відібрати набір даних, щоб надати йому більш реалістичну точку вимірювання.

Багато проблем, над якими я працював, стосуються формування комбінованих та перестановочних пар, трійки тощо, що лише робить обмежену оперативну пам'ять більшою мірою обмеженням, оскільки вони часто принаймні експоненціально розширяться в певний момент. Це змусило мене зосередити велику увагу на якості, а не на кількості інформації, що надходить у них для початку, а не на спроби їх очищення після цього, а також на послідовність операцій з підготовки інформації для початку (починаючи з найпростіша операція і підвищення складності); наприклад, підмножина, потім злиття / об'єднання, а потім формування комбінацій / перестановок тощо.

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

Сподіваємось, щось із цього може допомогти іншим.


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 та @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 знову відкривається, а пам'ять звільняється.

Сподіваюся, це допомагає.


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)дев'ять разів
Marcelo Ventura

14
Але чому б ти запустив це 9 разів? (цікаво, не критично)
Umaomamaomao

1

Ви також можете отримати певну вигоду, використовуючи плеєр та розмістивши свій скрипт у шматках Rmd.

Зазвичай я ділю код на різні фрагменти і вибираю, який з них збереже контрольну точку для кешування або файлу 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...

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.