Truques para gerenciar a memória disponível em uma sessão R


490

Quais truques as pessoas usam para gerenciar a memória disponível de uma sessão R interativa? Uso as funções abaixo [baseadas nas postagens de Petr Pikal e David Hinds na lista de ajuda em 2004] para listar (e / ou classificar) os maiores objetos e ocasionalmenterm() alguns deles. Mas, de longe, a solução mais eficaz era ... rodar no Linux de 64 bits com ampla memória.

Quaisquer outros truques legais que as pessoas querem compartilhar? Um por post, por favor.

# 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)
}

Note, eu não duvido, mas qual é a utilidade disso? Eu sou muito novo em problemas de memória no R, mas estou tendo alguns problemas recentemente (é por isso que eu estava procurando por este post :) - então estou apenas começando com tudo isso. Como isso ajuda meu trabalho diário?
Matt Bannert

4
se você quiser ver os objetos dentro de uma função, precisará usar: lsos (pos = environment ()); caso contrário, ele mostrará apenas variáveis ​​globais. Para escrever para o erro padrão: write.table (LSOs (pos = ambiente ()), stderr (), cotação = FALSE, Set = '\ t')
Michael Kuhn

Por que Linux de 64 bits e não Windows de 64 bits? A escolha do SO faz uma diferença não trivial quando tenho 32 GB de RAM para usar?
Jase

3
@pepsimax: Isso foi empacotado no multilevelPSApacote . O pacote foi projetado para outra coisa, mas você pode usar a função de lá sem carregar o pacote dizendo requireNamespace(multilevelPSA); multilevelPSA::lsos(...). Ou no Dmiscpacote (não no CRAN).
krlmlr

1
Se o conjunto de dados tiver um tamanho gerenciável, normalmente vou para o R studio> Ambiente> Exibição em Grade. Aqui você pode ver e classificar todos os itens em seu ambiente atual com base no tamanho.
Krazzy R

Respostas:


197

Certifique-se de registrar seu trabalho em um script reproduzível. De tempos em tempos, reabra R e, em seguida, source()seu script. Você limpará tudo o que não estiver mais usando e, como benefício adicional, testará seu código.


58
Minha estratégia é dividir meus scripts de acordo com as linhas load.R e do.R, em que load.R pode levar algum tempo para carregar dados de arquivos ou banco de dados e faz qualquer pré-processamento / mesclagem mínima esses dados. A última linha de load.R é algo para salvar o estado da área de trabalho. Então do.R é o meu bloco de anotações, pelo qual construo minhas funções de análise. Freqüentemente recarrego do.R (com ou sem recarregar o estado da área de trabalho de load.R, conforme necessário).
21139 Josh Reich

32
Essa é uma boa técnica. Quando os arquivos são executados em uma determinada ordem como essa, muitas vezes eu prefixar-los com um número: 1-load.r, 2-explore.r, 3-model.r- de que maneira é óbvio para os outros que há alguma ordem presente.
394 hadley

4
Eu não posso apoiar essa idéia o suficiente. Eu ensinei R a algumas pessoas e essa é uma das primeiras coisas que digo. Isso também se aplica a qualquer linguagem em que o desenvolvimento incorpore um REPL e um arquivo sendo editado (por exemplo, Python). O rm (ls = list ()) e o source () também funcionam, mas a reabertura é melhor (os pacotes também foram limpos).
Vince

53
O fato de a resposta mais votada envolver o reinício de R é a pior crítica possível a R.
Sds

7
@ MartínBel que remove apenas objetos criados no ambiente global. Ele não descarrega pacotes ou objetos S4 ou muitas outras coisas.
21413 hadley

160

Eu uso o pacote data.table . Com seu :=operador, você pode:

  • Adicionar colunas por referência
  • Modifique subconjuntos de colunas existentes por referência e por grupo por referência
  • Excluir colunas por referência

Nenhuma dessas operações copia o (potencialmente grande) data.table, nem mesmo uma vez.

  • A agregação também é particularmente rápida porque data.tableusa muito menos memória de trabalho.

Links Relacionados :


109

Vi isso em um post no twitter e acho que é uma função incrível de Dirk! Seguindo a resposta de JD Long, eu faria isso para uma leitura amigável:

# 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()

O que resulta em algo como o seguinte:

                      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

NOTA: A parte principal que adicionei foi (novamente, adaptada da resposta de JD):

obj.prettysize <- napply(names, function(x) {
                           print(object.size(x), units = "auto") })

Essa função pode ser adicionada ao dplyr ou a algum outro pacote de chaves.
userJT

1
Vale notar que (pelo menos com a base 3.3.2) capture.outputnão é mais necessário e obj.prettysize <- napply(names, function(x) {format(utils::object.size(x), units = "auto") })produz uma saída limpa. De fato, não removê-lo produz aspas indesejadas na saída, ou seja, em [1] "792.5 Mb"vez de 792.5 Mb.
Nutle

Excelente @Nutle, eu tenho atualizado o código de acordo :)
Tony Breyal

Eu também mudaria obj.class <- napply(names, function(x) as.character(class(x))[1])para, obj.class <- napply(names, function(x) class(x)[1]) desde classsempre, retornar um vetor de caracteres agora (base-3.5.0).
DeltaIV

49

Uso agressivamente o subsetparâmetro com seleção apenas das variáveis ​​necessárias ao passar quadros de dados para o data=argumento das funções de regressão. Isso resulta em alguns erros se eu esquecer de adicionar variáveis ​​à fórmula e ao select=vetor, mas ainda poupa muito tempo devido à diminuição da cópia de objetos e reduz significativamente a pegada de memória. Digamos que tenho 4 milhões de registros com 110 variáveis ​​(e tenho). Exemplo:

# 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")
   )            )

Por meio da definição do contexto e da estratégia: a gdlab2variável é um vetor lógico que foi construído para indivíduos em um conjunto de dados que tinha todos os valores normais ou quase normais para vários testes de laboratório e HIVfinalera um vetor de caracteres que resumia os testes preliminares e confirmatórios para o HIV .


48

Eu amo o script .ls.objects () de Dirk, mas continuei apertando os olhos para contar caracteres na coluna de tamanho. Então eu fiz alguns hacks feios para torná-lo presente com formatação bonita para o tamanho:

.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

Esse é um bom truque.

Uma outra sugestão é usar objetos com eficiência de memória sempre que possível: por exemplo, use uma matriz em vez de um data.frame.

Isso realmente não trata do gerenciamento de memória, mas uma função importante que não é amplamente conhecida é memory.limit (). Você pode aumentar o padrão usando este comando, memory.limit (tamanho = 2500), em que o tamanho está em MB. Como Dirk mencionou, você precisa usar 64 bits para tirar proveito real disso.


25
Isso não é aplicável apenas ao Windows?
Christopher DuBois

4
> memory.limit () [1] Inf Mensagem de aviso: 'memory.limit ()' é específico do Windows
LJT

Usar o tibble em vez do data.frame nos ajuda a economizar ainda mais memória?

32

Eu gosto bastante da função de objetos aprimorados desenvolvida por Dirk. Na maioria das vezes, porém, uma saída mais básica com o nome e o tamanho do objeto é suficiente para mim. Aqui está uma função mais simples com um objetivo semelhante. O uso da memória pode ser ordenado alfabeticamente ou por tamanho, pode ser limitado a um certo número de objetos e pode ser ordenado ascendente ou descendente. Além disso, costumo trabalhar com dados com mais de 1 GB, portanto a função altera as unidades de acordo.

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))
}

E aqui está um exemplo de saída:

> 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

Eu nunca salvei um espaço de trabalho R. Uso scripts de importação e scripts de dados e produzo objetos de dados especialmente grandes que não quero recriar frequentemente para arquivos. Dessa forma, sempre começo com um novo espaço de trabalho e não preciso limpar objetos grandes. Essa é uma função muito boa embora.


30

Infelizmente, não tive tempo de testá-lo extensivamente, mas aqui está uma dica de memória que eu não tinha visto antes. Para mim, a memória necessária foi reduzida em mais de 50%. Quando você lê coisas no R com, por exemplo, read.csv, elas exigem uma certa quantidade de memória. Depois disso, você pode salvá-los com. save("Destinationfile",list=ls()) Na próxima vez que você abrir R, poderá usar load("Destinationfile") Agora o uso da memória pode ter diminuído. Seria bom se alguém pudesse confirmar se isso produz resultados semelhantes com um conjunto de dados diferente.


4
sim, eu experimentei o mesmo. O uso de memória cai até 30% no meu caso. 1,5 GB de memória usada, salva em .RData (~ 30 MB). Nova sessão após o carregamento .RData usa menos de 500 MB de memória.
F3lix

Tentei com 2 conjuntos de dados (100 MB e 2,7 GB) carregados no data.table usando freade salvos em .RData. Os arquivos RData eram de fato cerca de 70% menores, mas após o recarregamento, a memória usada era exatamente a mesma. Esperava que esse truque reduzisse a pegada de memória ... estou perdendo alguma coisa?
NoviceProg

@NoviceProg Não acho que você esteja perdendo alguma coisa, mas é um truque, acho que não funcionará para todas as situações. No meu caso, a memória após o recarregamento foi realmente reduzida conforme descrito.
Dennis Jaheruddin 19/03/2015

6
@NoviceProg Algumas coisas. Primeiro, lembre-se de que seguir o credo do data.table provavelmente é mais eficiente em termos de memória no carregamento de arquivos do que o read.csv. Segundo, a economia de memória que as pessoas observam aqui tem a ver principalmente com o tamanho da memória do processo R (que se expande para reter objetos e se retrai quando a coleta de lixo ocorre). No entanto, a coleta de lixo nem sempre libera toda a RAM de volta ao sistema operacional. Parar a sessão R e carregar o item de onde foi armazenado liberará o máximo de RAM possível ... mas se a sobrecarga for pequena, para começar ... não haverá ganho.
russellpierce

27

Para ilustrar ainda mais a estratégia comum de reinicializações frequentes, podemos usar o littler, que permite executar expressões simples diretamente da linha de comando. Aqui está um exemplo que às vezes uso para cronometrar BLAS diferentes para um simples produto cruzado.

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

Da mesma forma,

 r -lMatrix -e'example(spMatrix)'

carrega o pacote Matrix (via opção --packages | -l) e executa os exemplos da função spMatrix. Como o r sempre inicia 'novo', esse método também é um bom teste durante o desenvolvimento do pacote.

Por último, mas não menos importante, r também funciona muito bem no modo de lote automatizado em scripts usando o cabeçalho shebang '#! / Usr / bin / r'. O Rscript é uma alternativa onde o littler não está disponível (por exemplo, no Windows).


23

Para propósitos de velocidade e memória, ao criar um quadro de dados grande por meio de uma série complexa de etapas, eu o lavarei periodicamente (o conjunto de dados em andamento sendo construído) no disco, anexando tudo o que veio antes e depois o reiniciando . Dessa forma, as etapas intermediárias estão funcionando apenas em quadros de dados pequenos (o que é bom porque, por exemplo, rbind diminui consideravelmente com objetos maiores). O conjunto de dados inteiro pode ser lido novamente no final do processo, quando todos os objetos intermediários tiverem sido removidos.

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

Apenas para observar que os data.tablepacotes tables()parecem ser um bom substituto para a .ls.objects()função personalizada do Dirk (detalhada nas respostas anteriores), embora apenas para data.frames / tables e não por exemplo matrizes, matrizes, listas.


este não lista nenhum data.frames por isso não é tão grande
userJT

16
  1. Tenho sorte e meus grandes conjuntos de dados são salvos pelo instrumento em "pedaços" (subconjuntos) de aproximadamente 100 MB (binário de 32 bits). Assim, eu posso executar as etapas de pré-processamento (excluir peças não informativas, reduzir a amostragem) sequencialmente antes de fundir o conjunto de dados.

  2. Chamar gc ()"manualmente" pode ajudar se o tamanho dos dados se aproximar da memória disponível.

  3. Às vezes, um algoritmo diferente precisa de muito menos memória.
    Às vezes, há uma troca entre vetorização e uso de memória.
    compare: split& lapplyvs. um forloop.

  4. Para uma análise rápida e fácil dos dados, geralmente trabalho primeiro com um pequeno subconjunto aleatório ( sample ()) dos dados. Depois que o script de análise de dados / .Rnw terminar o código de análise de dados e os dados completos forem para o servidor de cálculo para cálculo durante a noite / no final de semana / ....


11

O uso de ambientes em vez de listas para manipular coleções de objetos que ocupam uma quantidade significativa de memória de trabalho.

O motivo: sempre que um elemento de uma listestrutura é modificado, a lista inteira é temporariamente duplicada. Isso se torna um problema se o requisito de armazenamento da lista for cerca da metade da memória de trabalho disponível, pois os dados deverão ser trocados para o disco rígido lento. Os ambientes, por outro lado, não estão sujeitos a esse comportamento e podem ser tratados de maneira semelhante às listas.

Aqui está um exemplo:

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))

Em conjunto com estruturas como big.matrixou data.tableque permitem alterar o conteúdo no local, é possível obter um uso muito eficiente da memória.


6
Isso não é mais verdade: no avançado R de Hadley , "Alterações no R 3.1.0 tornaram esse uso [de ambientes] substancialmente menos importante porque a modificação de uma lista não faz mais uma cópia profunda".
Petrelharp

8

A llfunção no gDatapacote também pode mostrar o uso da memória de cada objeto.

gdata::ll(unit='MB')

Não está no meu sistema: R versão 3.1.1 (10-07-2014), x86_64-pc-linux-gnu (64 bits), gdata_2.13.3, gtools_3.4.1.
Krlmlr

Você está certo, eu testei uma vez que foi ordenado por acaso!
user1436187

1
modifique a função para usar Gb, Mb
userJT

7

Se você realmente deseja evitar vazamentos, evite criar grandes objetos no ambiente global.

O que costumo fazer é ter uma função que faça o trabalho e retorne NULL- todos os dados são lidos e manipulados nessa função ou em outras que ela chama.


7

Com apenas 4 GB de RAM (executando o Windows 10, então faça isso com 2 ou mais 1 GB de forma realista), tive que ter muito cuidado com a alocação.

Eu uso data.table quase exclusivamente.

A função 'fread' permite agrupar informações por nomes de campos na importação; importe apenas os campos realmente necessários para começar. Se você estiver usando a leitura base R, anule as colunas falsas imediatamente após a importação.

Como 42- sugere, sempre que possível, subconfigurarei as colunas imediatamente após importar as informações.

Eu frequentemente rm () objetos do ambiente assim que eles não são mais necessários, por exemplo, na próxima linha depois de usá-los para definir um subconjunto de outra coisa, e chamo gc ().

'fread' e 'fwrite' de data.table podem ser muito rápidos em comparação com as leituras e gravações básicas R.

Como o kpierce8 sugere, quase sempre escrevo tudo fora do ambiente e o luto de volta, mesmo com milhares / centenas de milhares de pequenos arquivos para passar. Isso não apenas mantém o ambiente 'limpo' e mantém a alocação de memória baixa, mas, possivelmente devido à grave falta de RAM disponível, o R tem uma propensão a travar frequentemente no meu computador; com muita frequência. Ter o backup das informações na própria unidade à medida que o código progride em vários estágios significa que não preciso iniciar desde o início, se houver uma falha.

A partir de 2017, acho que os SSDs mais rápidos estão rodando em torno de alguns GB por segundo através da porta M2. Eu tenho um SSD Kingston V300 (550MB / s) realmente básico de 50 GB que eu uso como disco principal (possui Windows e R). Eu mantenho todas as informações em massa em um prato WD barato de 500 GB. Movo os conjuntos de dados para o SSD quando começo a trabalhar neles. Isso, combinado com 'fread'ing e' fwrite'ing, tudo tem funcionado muito bem. Eu tentei usar 'ff', mas prefiro o primeiro. As velocidades de leitura / gravação em 4K podem criar problemas com isso; fazer o backup de um quarto de milhão de arquivos de 1k (no valor de 250 MB) do SSD para o prato pode levar horas. Tanto quanto sei, ainda não existe nenhum pacote R que possa otimizar automaticamente o processo de 'chunkification'; por exemplo, observe quanta RAM um usuário possui, teste as velocidades de leitura / gravação da RAM / todas as unidades conectadas e, em seguida, sugira um protocolo ideal de 'chunkification'. Isso poderia produzir algumas melhorias significativas no fluxo de trabalho / otimizações de recursos; por exemplo, divida-o em ... MB para a ram -> divida-o em ... MB no SSD -> divida-o em ... MB no prato -> divida-o em ... MB na fita. Ele poderia coletar amostras de conjuntos de dados de antemão para fornecer um indicador mais realista para trabalhar.

Muitos dos problemas nos quais trabalhei em R envolvem a formação de pares de combinação e permutação, triplos etc., o que torna a limitação de RAM limitada mais uma limitação, pois elas geralmente se expandem pelo menos exponencialmente em algum momento. Isso me fez focar muita atenção na qualidade, em vez da quantidade de informações inseridas neles, em vez de tentar limpá-las posteriormente, e na sequência de operações na preparação das informações (começando com a operação mais simples e aumentando a complexidade); por exemplo, subconjunto, em seguida, mesclar / unir, formar combinações / permutações etc.

Parece haver alguns benefícios em usar a base R de leitura e gravação em alguns casos. Por exemplo, a detecção de erros no 'fread' é tão boa que pode ser difícil tentar obter informações realmente confusas no R para começar a limpá-las. A base R também parece ser muito mais fácil se você estiver usando Linux. A base R parece funcionar bem no Linux, o Windows 10 usa ~ 20 GB de espaço em disco, enquanto o Ubuntu precisa apenas de alguns GB, a RAM necessária com o Ubuntu é um pouco menor. Mas notei grandes quantidades de avisos e erros ao instalar pacotes de terceiros no (L) Ubuntu. Eu não recomendaria me afastar muito do (L) Ubuntu ou de outras distribuições de estoque com o Linux, pois você pode perder tanta compatibilidade geral que torna o processo quase inútil (acho que a 'unidade' deve ser cancelada no Ubuntu a partir de 2017 )

Espero que parte disso possa ajudar outras pessoas.


5

Isso não acrescenta nada ao exposto, mas está escrito no estilo simples e muito comentado que eu gosto. Ele gera uma tabela com os objetos ordenados em tamanho, mas sem alguns dos detalhes fornecidos nos exemplos acima:

#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]))),])

5

Esta é uma resposta mais recente para esta excelente e antiga pergunta. Do Hadley's 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 )


3

Se você está trabalhando no Linux e deseja usar vários processos e precisa executar operações de leitura em um ou mais objetos grandes, use em makeForkClustervez de a makePSOCKcluster. Isso também poupa o tempo de envio do objeto grande para os outros processos.


2

Eu realmente aprecio algumas das respostas acima, seguindo @hadley e @Dirk que sugerem fechar R e emitir source e usar a linha de comando, eu venho com uma solução que funcionou muito bem para mim. Eu tive que lidar com centenas de espectros de massa, cada um ocupando cerca de 20 Mb de memória, então usei dois scripts R, da seguinte maneira:

Primeiro um invólucro:

#!/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))
   }
}

com esse script, basicamente controlo o que meu script principal faz runConsensus.re escrevo a resposta dos dados para a saída. Com isso, cada vez que o wrapper chama o script, parece que o R é reaberto e a memória é liberada.

Espero que ajude.


2

Além das técnicas mais gerais de gerenciamento de memória fornecidas nas respostas acima, sempre tento reduzir o tamanho dos meus objetos o máximo possível. Por exemplo, trabalho com matrizes muito grandes, mas muito esparsas, ou seja, matrizes em que a maioria dos valores é zero. Usando o pacote 'Matrix' (capitalização importante), pude reduzir meus tamanhos médios de objetos de ~ 2 GB para ~ 200 MB, simplesmente como:

my.matrix <- Matrix(my.matrix)

O pacote Matrix inclui formatos de dados que podem ser usados ​​exatamente como uma matriz comum (sem necessidade de alterar seu outro código), mas são capazes de armazenar dados esparsos com muito mais eficiência, sejam carregados na memória ou salvos no disco.

Além disso, os arquivos brutos que eu recebo estão no formato 'longo', onde cada ponto de dados tem variáveis x, y, z, i. Muito mais eficiente para transformar os dados em uma x * y * zmatriz de dimensão com apenas variável i.

Conheça seus dados e use um pouco de bom senso.


2

Dica para lidar com objetos que requerem cálculo intermediário pesado: ao usar objetos que exigem muito cálculo pesado e etapas intermediárias para criar, geralmente acho útil escrever um pedaço de código com a função para criar o objeto e, em seguida, um pedaço separado de código que me dá a opção de gerar e salvar o objeto como um rmdarquivo ou carregá-lo externamente de um rmdarquivo que eu já salvei anteriormente. Isso é especialmente fácil de R Markdownusar , usando a seguinte estrutura de blocos de código.

```{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); }

```

Com essa estrutura de código, tudo o que preciso fazer é mudar, LOADdependendo se desejo gerar e salvar o objeto ou carregá-lo diretamente de um arquivo salvo existente. (Obviamente, tenho que gerá-lo e salvá-lo na primeira vez, mas depois disso tenho a opção de carregá-lo.) A configuração LOAD = TRUEignora o uso da minha função complicada e evita toda a computação pesada nela. Esse método ainda requer memória suficiente para armazenar o objeto de interesse, mas evita que você precise calculá-lo sempre que executar seu código. Para objetos que requerem muito cálculo pesado de etapas intermediárias (por exemplo, para cálculos envolvendo loops em grandes matrizes), isso pode economizar uma quantidade substancial de tempo e computação.


1

Corrida

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

de tempos em tempos também ajuda o R a liberar memória não utilizada, mas ainda não liberada.


O que o forloop faz aqui? Não há ina gcligação.
Umaomamaomao

@qqq ele está lá apenas para evitar copiar e colar gc(reset = T)nove vezes
Marcelo Ventura

14
Mas por que você o executaria 9 vezes? (curioso, não crítico)
Umaomamaomao 14/09/17

1

Você também pode obter algum benefício usando o knitr e colocando seu script em chmks Rmd.

Normalmente, divido o código em diferentes partes e seleciono qual salvará um ponto de verificação para armazenar em cache ou em um arquivo RDS, e

Lá, você pode definir um pedaço para ser salvo no "cache" ou pode optar por executar ou não um pedaço específico. Dessa forma, em uma primeira execução, você pode processar apenas a "parte 1", outra execução pode selecionar apenas a "parte 2", etc.

Exemplo:

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)
```

Como efeito colateral, isso também pode poupar algumas dores de cabeça em termos de reprodutibilidade :)


1

Com base nas respostas de @ Dirk e @ Tony, fiz uma pequena atualização. O resultado foi gerado [1]antes dos valores de tamanho bonito, então eu retirei o capture.outputque resolveu o problema:

.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

Eu tento manter a quantidade de objetos pequena ao trabalhar em um projeto maior com várias etapas intermediárias. Então, em vez de criar muitos objetos exclusivos chamados

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

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

Eu trabalho com objetos temporários que eu chamo temp.

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

O que me deixa com menos arquivos intermediários e mais visão geral.

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

Para economizar mais memória, posso simplesmente remover tempquando não for mais necessário.

rm(temp)

Se eu precisar de vários arquivos intermediários, eu uso temp1, temp2, temp3.

Para testar eu uso test, test2...

Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.