Astuces pour gérer la mémoire disponible dans une session R


490

Quelles astuces les gens utilisent-ils pour gérer la mémoire disponible d'une session R interactive? J'utilise les fonctions ci-dessous [basées sur les publications de Petr Pikal et David Hinds sur la liste r-help en 2004] pour répertorier (et / ou trier) les plus gros objets et occasionnellement rm()certains d'entre eux. Mais de loin la solution la plus efficace était ... de fonctionner sous Linux 64 bits avec une mémoire suffisante.

D'autres bons trucs que les gens veulent partager? Un par poste, s'il vous plaît.

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

Remarque, je n'en doute pas, mais à quoi ça sert? Je suis assez nouveau pour les problèmes de mémoire dans R, mais j'en éprouve récemment (c'est pourquoi je cherchais ce post :) - alors je commence juste avec tout cela. Comment cela aide-t-il mon travail quotidien?
Matt Bannert

4
si vous voulez voir les objets dans une fonction, vous devez utiliser: lsos (pos = environment ()), sinon il n'affichera que les variables globales. Pour écrire sur l'erreur standard: write.table (lsos (pos = environment ()), stderr (), quote = FALSE, sep = '\ t')
Michael Kuhn

Pourquoi Linux 64 bits et non Windows 64 bits? Le choix du système d'exploitation fait-il une différence non triviale lorsque j'ai 32 Go de RAM à utiliser?
Jase

3
@pepsimax: Cela a été emballé dans le multilevelPSApaquet . Le package est conçu pour autre chose, mais vous pouvez utiliser la fonction à partir de là sans charger le package en disant requireNamespace(multilevelPSA); multilevelPSA::lsos(...). Ou dans l' Dmiscemballage (pas sur CRAN).
krlmlr

1
Si l'ensemble de données est d'une taille gérable, je vais généralement dans R studio> Environnement> Grid View. Ici, vous pouvez voir et trier tous les éléments de votre environnement actuel en fonction de la taille.
kRazzy R

Réponses:


197

Assurez-vous d'enregistrer votre travail dans un script reproductible. De temps en temps, rouvrez R, puis source()votre script. Vous nettoierez tout ce que vous n'utilisez plus et, comme avantage supplémentaire, vous aurez testé votre code.


58
Ma stratégie consiste à décomposer mes scripts selon les lignes de load.R et do.R, où load.R peut prendre un certain temps pour charger des données à partir de fichiers ou d'une base de données, et fait tout pré-traitement / fusion minimal de ces données. La dernière ligne de load.R est quelque chose pour enregistrer l'état de l'espace de travail. Ensuite, do.R est mon bloc-notes grâce auquel je développe mes fonctions d'analyse. Je recharge fréquemment do.R (avec ou sans rechargement de l'état de l'espace de travail à partir de load.R au besoin).
Josh Reich

32
Voilà une bonne technique. Lorsque les fichiers sont exécutés dans un certain ordre comme ça, je les préfixer souvent avec un numéro: 1-load.r, 2-explore.r, 3-model.r- de cette façon , il est évident aux autres qu'il ya un certain présent ordre.
hadley

4
Je ne peux pas assez étayer cette idée. J'ai enseigné le R à quelques personnes et c'est l'une des premières choses que je dis. Cela s'applique également à tout langage dans lequel le développement intègre un REPL et un fichier en cours d'édition (c'est-à-dire Python). rm (ls = list ()) et source () fonctionnent aussi, mais la réouverture est meilleure (les paquets sont également effacés).
Vince

53
Le fait que la réponse la plus votée implique le redémarrage de R est la pire critique de R possible.
sds

7
@ MartínBel qui supprime uniquement les objets créés dans l'environnement global. Il ne décharge pas les packages ou les objets S4 ou bien d'autres choses.
hadley

160

J'utilise le package data.table . Avec son :=opérateur, vous pouvez:

  • Ajouter des colonnes par référence
  • Modifier des sous-ensembles de colonnes existantes par référence et par groupe par référence
  • Supprimer des colonnes par référence

Aucune de ces opérations ne copie le (potentiellement grand) data.table, pas même une seule fois.

  • L'agrégation est également particulièrement rapide car elle data.tableutilise beaucoup moins de mémoire de travail.

Liens connexes :


109

J'ai vu ça sur un post Twitter et je pense que c'est une fonction géniale de Dirk! Dans le prolongement de la réponse de JD Long, je le ferais pour une lecture conviviale:

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

Ce qui se traduit par quelque chose comme ceci:

                      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

REMARQUE: La partie principale que j'ai ajoutée était (encore une fois, adaptée de la réponse de JD):

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

cette fonction peut-elle être ajoutée à dplyr ou à un autre package clé.
userJT

1
Il convient de noter que (au moins avec la base-3.3.2) capture.outputn'est plus nécessaire et obj.prettysize <- napply(names, function(x) {format(utils::object.size(x), units = "auto") })produit une sortie propre. En fait, ne pas le supprimer produit des guillemets indésirables dans la sortie, c'est-à-dire [1] "792.5 Mb"au lieu de 792.5 Mb.
Nutle

@Nutle Excellent, j'ai mis à jour le code en conséquence :)
Tony Breyal

Je voudrais aussi changer obj.class <- napply(names, function(x) as.character(class(x))[1])à obj.class <- napply(names, function(x) class(x)[1]) depuis classtoujours revenir un vecteur de caractères maintenant (base-3.5.0).
DeltaIV

49

Je fais un usage agressif du subsetparamètre en sélectionnant uniquement les variables requises lors du passage de trames de données à l' data=argument des fonctions de régression. Cela entraîne des erreurs si j'oublie d'ajouter des variables à la fois à la formule et au select=vecteur, mais cela économise encore beaucoup de temps en raison d'une diminution de la copie d'objets et réduit considérablement l'empreinte mémoire. Disons que j'ai 4 millions d'enregistrements avec 110 variables (et je le fais.) Exemple:

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

En définissant le contexte et la stratégie: la gdlab2variable est un vecteur logique qui a été construit pour des sujets dans un ensemble de données qui avait toutes les valeurs normales ou presque normales pour un tas de tests de laboratoire et HIVfinalétait un vecteur de caractères qui résumait les tests préliminaires et confirmatifs pour le VIH .


48

J'adore le script .ls.objects () de Dirk mais j'ai continué à plisser les yeux pour compter les caractères dans la colonne de taille. J'ai donc fait de vilains hacks pour le rendre présent avec un joli formatage pour la taille:

.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

Voilà une bonne astuce.

Une autre suggestion est d'utiliser autant que possible des objets économes en mémoire: par exemple, utilisez une matrice au lieu d'un data.frame.

Cela ne concerne pas vraiment la gestion de la mémoire, mais une fonction importante qui n'est pas largement connue est memory.limit (). Vous pouvez augmenter la valeur par défaut à l'aide de cette commande, memory.limit (taille = 2500), où la taille est en Mo. Comme Dirk l'a mentionné, vous devez utiliser 64 bits pour en tirer un réel avantage.


25
N'est-ce pas uniquement applicable à Windows?
Christopher DuBois

4
> memory.limit () [1] Message d'avertissement Inf: 'memory.limit ()' est spécifique à Windows
LJT

L'utilisation de tibble au lieu de data.frame nous aide-t-elle encore mieux à économiser de la mémoire?

32

J'aime bien la fonction d'objets améliorés développée par Dirk. La plupart du temps cependant, une sortie plus basique avec le nom et la taille de l'objet me suffit. Voici une fonction plus simple avec un objectif similaire. L'utilisation de la mémoire peut être ordonnée par ordre alphabétique ou par taille, peut être limitée à un certain nombre d'objets et peut être ordonnée ascendante ou descendante. De plus, je travaille souvent avec des données de 1 Go +, donc la fonction change d'unité en conséquence.

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

Et voici un exemple de sortie:

> 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

Je ne sauvegarde jamais un espace de travail R. J'utilise des scripts d'importation et des scripts de données et je génère des objets de données particulièrement volumineux que je ne souhaite pas recréer souvent dans des fichiers. De cette façon, je commence toujours par un nouvel espace de travail et je n'ai pas besoin de nettoyer les gros objets. C'est une très belle fonction cependant.


30

Malheureusement, je n'ai pas eu le temps de le tester de manière approfondie mais voici une astuce mémoire que je n'ai jamais vue auparavant. Pour moi, la mémoire requise a été réduite de plus de 50%. Lorsque vous lisez des éléments dans R avec par exemple read.csv, ils nécessitent une certaine quantité de mémoire. Après cela, vous pouvez les enregistrer avec save("Destinationfile",list=ls()) La prochaine fois que vous ouvrirez R, vous pouvez utiliser load("Destinationfile") Maintenant, l'utilisation de la mémoire peut avoir diminué. Ce serait bien si quelqu'un pouvait confirmer si cela produit des résultats similaires avec un ensemble de données différent.


4
oui, j'ai vécu la même chose. L'utilisation de la mémoire tombe même à 30% dans mon cas. 1,5 Go de mémoire utilisée, enregistré dans .RData (~ 30 Mo). Nouvelle session après le chargement .RData utilise moins de 500 Mo de mémoire.
f3lix

J'ai essayé avec 2 jeux de données (100 Mo et 2,7 Go) chargés dans data.table à l'aide fread, puis enregistré sur .RData. Les fichiers RData étaient en effet environ 70% plus petits mais après le rechargement, la mémoire utilisée était exactement la même. J'espérais que cette astuce réduirait l'empreinte mémoire ... est-ce que je manque quelque chose?
NoviceProg

@NoviceProg Je ne pense pas que vous manquez quelque chose, mais c'est une astuce, je suppose que cela ne fonctionnera pas dans toutes les situations. Dans mon cas, la mémoire après le rechargement a été réduite comme décrit.
Dennis Jaheruddin

6
@NoviceProg Quelques choses. Tout d'abord, effrayez-vous, suivre le credo de data.table est probablement plus efficace en mémoire pour charger des fichiers que read.csv. Deuxièmement, les économies de mémoire que les gens remarquent ici sont principalement liées à la taille de la mémoire du processus R (qui se développe pour contenir des objets et se rétracte lorsque la collecte des ordures a lieu). Cependant, la récupération de place ne libère pas toujours toute la RAM sur le système d'exploitation. L'arrêt de la session R et le chargement de l'élément d'où il a été stocké libérera autant de RAM que possible ... mais si la surcharge était petite au début ... aucun gain.
russellpierce

27

Pour illustrer davantage la stratégie commune de redémarrages fréquents, nous pouvons utiliser littler qui nous permet d'exécuter des expressions simples directement à partir de la ligne de commande. Voici un exemple que j'utilise parfois pour chronométrer différents BLAS pour un simple crossprod.

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

Également,

 r -lMatrix -e'example(spMatrix)'

charge le package Matrix (via le commutateur --packages | -l) et exécute les exemples de la fonction spMatrix. Comme r démarre toujours «frais», cette méthode est également un bon test lors du développement d'un package.

Last but not least r fonctionne également très bien pour le mode batch automatisé dans les scripts utilisant l'en-tête de shebang '#! / Usr / bin / r'. Rscript est une alternative où littler n'est pas disponible (par exemple sous Windows).


23

Pour des raisons de vitesse et de mémoire, lors de la création d'un grand bloc de données via une série d'étapes complexes, je le viderai périodiquement (l'ensemble de données en cours de construction) sur le disque, en l'ajoutant à tout ce qui précède, puis en le redémarrant . De cette façon, les étapes intermédiaires ne fonctionnent que sur des trames de données de petite taille (ce qui est bon car, par exemple, rbind ralentit considérablement avec des objets plus gros). L'ensemble des données peut être relu à la fin du processus, lorsque tous les objets intermédiaires ont été supprimés.

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

Juste pour noter que les data.tablepaquets tables()semblent être un assez bon remplacement pour la .ls.objects()fonction personnalisée de Dirk (détaillée dans les réponses précédentes), bien que juste pour data.frames / tables et non par exemple des matrices, des tableaux, des listes.


cela ne répertorie aucun data.frames donc ce n'est pas génial
userJT

16
  1. J'ai de la chance et mes grands ensembles de données sont enregistrés par l'instrument en «morceaux» (sous-ensembles) d'environ 100 Mo (32 bits binaires). Ainsi, je peux effectuer des étapes de prétraitement (suppression de parties non informatives, sous-échantillonnage) séquentiellement avant de fusionner l'ensemble de données.

  2. Appeler gc ()"à la main" peut aider si la taille des données se rapproche de la mémoire disponible.

  3. Parfois, un algorithme différent a besoin de beaucoup moins de mémoire.
    Parfois, il y a un compromis entre la vectorisation et l'utilisation de la mémoire.
    comparer: split& lapplycontre une forboucle.

  4. Pour une analyse rapide et facile des données, je travaille souvent d'abord avec un petit sous-ensemble aléatoire ( sample ()) des données. Une fois que le script d'analyse de données / .Rnw est terminé, le code d'analyse de données et les données complètes sont envoyées au serveur de calcul pour le calcul pendant la nuit / le week-end / ....


11

L'utilisation d'environnements au lieu de listes pour gérer des collections d'objets qui occupent une quantité importante de mémoire de travail.

La raison: chaque fois qu'un élément d'une liststructure est modifié, toute la liste est temporairement dupliquée. Cela devient un problème si l'exigence de stockage de la liste représente environ la moitié de la mémoire de travail disponible, car les données doivent alors être échangées sur le disque dur lent. Les environnements, en revanche, ne sont pas soumis à ce comportement et ils peuvent être traités de manière similaire aux listes.

Voici un exemple:

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

En conjonction avec des structures telles que big.matrixou data.tablequi permettent de modifier leur contenu sur place, une utilisation de la mémoire très efficace peut être obtenue.


6
Ce n'est plus vrai: d'après le R avancé de Hadley , «les modifications apportées à la R 3.1.0 ont rendu cette utilisation [des environnements] beaucoup moins importante, car la modification d'une liste ne fait plus une copie complète.
petrelharp

8

La llfonction dans le gDatapackage peut également afficher l'utilisation de la mémoire de chaque objet.

gdata::ll(unit='MB')

Pas sur mon système: R version 3.1.1 (2014-07-10), x86_64-pc-linux-gnu (64 bits), gdata_2.13.3, gtools_3.4.1.
krlmlr

Tu as raison je le teste une fois qu'il a été commandé par hasard!
user1436187

1
veuillez modifier la fonction pour utiliser Gb, Mb
userJT

7

Si vous voulez vraiment éviter les fuites, vous devez éviter de créer de gros objets dans l'environnement global.

Ce que je fais habituellement, c'est d'avoir une fonction qui fait le travail et renvoie NULL- toutes les données sont lues et manipulées dans cette fonction ou d'autres qu'elle appelle.


7

Avec seulement 4 Go de RAM (exécutant Windows 10, alors faites-en environ 2 ou plus de manière réaliste 1 Go), j'ai dû être très prudent avec l'allocation.

J'utilise data.table presque exclusivement.

La fonction «fread» vous permet de sous-définir les informations par nom de champ lors de l'importation; importer uniquement les champs réellement nécessaires au départ. Si vous utilisez la lecture de base R, annulez les colonnes parasites immédiatement après l'importation.

Comme le suggère 42- , dans la mesure du possible, je vais ensuite sous-ensemble dans les colonnes immédiatement après l'importation des informations.

Je rm () fréquemment des objets de l'environnement dès qu'ils ne sont plus nécessaires, par exemple sur la ligne suivante après les avoir utilisés pour sous-définir autre chose, et appeler gc ().

'fread' et 'fwrite' de data.table peuvent être très rapides en comparaison avec les lectures et écritures de base R.

Comme le suggère kpierce8 , je réécris presque toujours tout ce qui se trouve dans l'environnement et je le redoute, même avec des milliers / centaines de milliers de petits fichiers à passer. Cela permet non seulement de maintenir l'environnement «propre» et de maintenir l'allocation de mémoire faible, mais, peut-être en raison du grave manque de RAM disponible, R a tendance à se planter fréquemment sur mon ordinateur; très fréquemment. La sauvegarde des informations sur le disque lui-même au fur et à mesure que le code progresse à travers différentes étapes signifie que je n'ai pas à recommencer dès le début s'il se bloque.

En 2017, je pense que les SSD les plus rapides tournent autour de quelques Go par seconde via le port M2. J'ai un SSD Kingston V300 50 Go (550 Mo / s) vraiment basique que j'utilise comme disque principal (avec Windows et R dessus). Je garde toutes les informations en vrac sur un plateau WD bon marché de 500 Go. Je déplace les ensembles de données sur le SSD lorsque je commence à travailler dessus. Ceci, combiné avec tout ce qui a «effrayé» et «écrit», a très bien fonctionné. J'ai essayé d'utiliser 'ff' mais je préfère le premier. Les vitesses de lecture / écriture 4K peuvent créer des problèmes avec cela; La sauvegarde d'un quart de million de fichiers 1k (valeur de 250 Mo) du SSD sur le plateau peut prendre des heures. Pour autant que je sache, il n'y a pas encore de package R disponible qui puisse optimiser automatiquement le processus de «chunkification»; par exemple, regardez combien de RAM un utilisateur a, tester les vitesses de lecture / écriture de la RAM / de tous les disques connectés puis suggérer un protocole de «chunkification» optimal. Cela pourrait produire des améliorations significatives du flux de travail / optimisations des ressources; par exemple, divisez-le en ... Mo pour le bélier -> divisez-le en ... Mo pour le SSD -> divisez-le en ... Mo sur le plateau -> divisez-le en ... Mo sur la bande. Il pourrait échantillonner des ensembles de données à l'avance pour lui donner un bâton de mesure plus réaliste à partir duquel travailler.

Beaucoup de problèmes sur lesquels j'ai travaillé dans R impliquent la formation de combinaisons et de permutations, de triplets, etc., ce qui ne fait que limiter la RAM, car ils s'étendent souvent au moins de manière exponentielle à un moment donné. Cela m'a fait me concentrer beaucoup d'attention sur la qualité plutôt que sur la quantité d'informations qui y sont fournies au départ, plutôt que d'essayer de les nettoyer par la suite, et sur la séquence des opérations de préparation des informations pour commencer (en commençant par l'opération la plus simple et l'augmentation de la complexité); par exemple sous-ensemble, puis fusionner / joindre, puis former des combinaisons / permutations, etc.

Il semble y avoir certains avantages à utiliser la lecture et l'écriture de base R dans certains cas. Par exemple, la détection d'erreurs dans 'fread' est si bonne qu'il peut être difficile d'essayer d'obtenir des informations vraiment en désordre dans R pour commencer pour les nettoyer. Base R semble également être beaucoup plus facile si vous utilisez Linux. La base R semble bien fonctionner sous Linux, Windows 10 utilise environ 20 Go d'espace disque alors qu'Ubuntu n'a besoin que de quelques Go, la RAM nécessaire avec Ubuntu est légèrement inférieure. Mais j'ai remarqué de grandes quantités d'avertissements et d'erreurs lors de l'installation de packages tiers dans (L) Ubuntu. Je ne recommanderais pas de dériver trop loin de (L) Ubuntu ou d'autres distributions de stock avec Linux car vous pouvez perdre tellement de compatibilité globale que cela rend le processus presque inutile (je pense que `` l'unité '' doit être annulée dans Ubuntu à partir de 2017 ).

Avec un peu de chance, cela pourrait aider les autres.


5

Cela n'ajoute rien à ce qui précède, mais est écrit dans le style simple et fortement commenté que j'aime. Il donne un tableau avec les objets classés en taille, mais sans certains détails donnés dans les exemples ci-dessus:

#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

Il s'agit d'une nouvelle réponse à cette excellente vieille question. De Advanced R de Hadley:

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

Si vous travaillez sur Linux et que vous souhaitez utiliser plusieurs processus et que vous n'avez qu'à effectuer des opérations de lecture sur un ou plusieurs objets volumineux, utilisez makeForkClusterau lieu de a makePSOCKcluster. Cela vous fait également gagner du temps lors de l'envoi du gros objet aux autres processus.


2

J'apprécie vraiment certaines des réponses ci-dessus, après @hadley et @Dirk qui suggèrent de fermer R et d'émettre sourceet d'utiliser la ligne de commande, je trouve une solution qui a très bien fonctionné pour moi. J'ai dû gérer des centaines de spectres de masse, chacun occupant environ 20 Mo de mémoire, j'ai donc utilisé deux scripts R, comme suit:

Tout d'abord un emballage:

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

avec ce script, je contrôle essentiellement ce que fait mon script principal runConsensus.ret j'écris la réponse des données pour la sortie. Avec cela, chaque fois que le wrapper appelle le script, il semble que le R soit rouvert et que la mémoire soit libérée.

J'espère que cela aide.


2

En plus des techniques de gestion de la mémoire plus générales données dans les réponses ci-dessus, j'essaie toujours de réduire autant que possible la taille de mes objets. Par exemple, je travaille avec des matrices très grandes mais très clairsemées, c'est-à-dire des matrices où la plupart des valeurs sont nulles. En utilisant le package «Matrix» (capitalisation importante), j'ai pu réduire la taille moyenne de mes objets de ~ 2 Go à ~ 200 Mo aussi simplement que:

my.matrix <- Matrix(my.matrix)

Le package Matrix comprend des formats de données qui peuvent être utilisés exactement comme une matrice régulière (pas besoin de changer votre autre code) mais sont capables de stocker des données éparses beaucoup plus efficacement, qu'elles soient chargées en mémoire ou enregistrées sur disque.

De plus, les fichiers bruts que je reçois sont au format «long» où chaque point de données a des variables x, y, z, i. Beaucoup plus efficace pour transformer les données en un x * y * ztableau de dimensions avec uniquement une variable i.

Connaissez vos données et utilisez un peu de bon sens.


2

Astuce pour traiter des objets nécessitant un calcul intermédiaire lourd: Lorsque vous utilisez des objets qui nécessitent beaucoup de calculs lourds et d'étapes intermédiaires pour créer, je trouve souvent utile d'écrire un morceau de code avec la fonction pour créer l'objet, puis un morceau séparé de code qui me donne la possibilité de générer et d'enregistrer l'objet en tant que rmdfichier, ou de le charger en externe à partir d'un rmdfichier que j'ai déjà enregistré précédemment. Ceci est particulièrement facile à faire en R Markdownutilisant la structure de blocs de code suivante.

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

```

Avec cette structure de code, tout ce que je dois faire est de changer LOADselon que je veux générer et enregistrer l'objet, ou le charger directement à partir d'un fichier enregistré existant. (Bien sûr, je dois le générer et l'enregistrer la première fois, mais après cela, j'ai la possibilité de le charger.)LOAD = TRUE contourne l'utilisation de ma fonction compliquée et évite tous les calculs lourds qui s'y trouvent. Cette méthode nécessite toujours suffisamment de mémoire pour stocker l'objet d'intérêt, mais elle vous évite d'avoir à le calculer chaque fois que vous exécutez votre code. Pour les objets qui nécessitent beaucoup de calculs lourds d'étapes intermédiaires (par exemple, pour les calculs impliquant des boucles sur de grands tableaux), cela peut économiser beaucoup de temps et de calcul.


1

Fonctionnement

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

De temps en temps, R aide également à libérer la mémoire inutilisée mais toujours non libérée.


Que fait la forboucle ici? Il n'y a pas idans l' gcappel.
Umaomamaomao

@qqq c'est là juste pour éviter le copier-coller gc(reset = T)neuf fois
Marcelo Ventura

14
Mais pourquoi voudriez-vous l'exécuter 9 fois? (curieux, pas critique)
Umaomamaomao

1

Vous pouvez également obtenir des avantages en utilisant knitr et en plaçant votre script dans des morceaux Rmd.

Je divise généralement le code en différents morceaux et sélectionne celui qui enregistrera un point de contrôle à mettre en cache ou dans un fichier RDS, et

Là-bas, vous pouvez définir un morceau à enregistrer dans le "cache", ou vous pouvez décider d'exécuter ou non un morceau particulier. De cette façon, lors d'une première exécution, vous ne pouvez traiter que la "partie 1", une autre exécution, vous ne pouvez sélectionner que la "partie 2", etc.

Exemple:

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

Comme effet secondaire, cela pourrait également vous éviter des maux de tête en termes de reproductibilité :)


1

Sur la base des réponses de @ Dirk et @ Tony, j'ai fait une légère mise à jour. Le résultat sortait [1]avant les jolies valeurs de taille, j'ai donc retiré le capture.outputqui a résolu le problème:

.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

J'essaie de garder la petite quantité d'objets lorsque je travaille dans un projet plus grand avec beaucoup d'étapes intermédiaires. Ainsi, au lieu de créer de nombreux objets uniques appelés

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

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

Je travaille avec des objets temporaires que j'appelle temp.

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

Ce qui me laisse moins de fichiers intermédiaires et plus d'aperçu.

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

Pour économiser plus de mémoire, je peux simplement le retirer templorsqu'il n'est plus nécessaire.

rm(temp)

Si j'ai besoin de plusieurs fichiers intermédiaires, je l' utilise temp1, temp2, temp3.

Pour tester , je l' utilise test, test2...

En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.