Sztuczki dotyczące zarządzania dostępną pamięcią w sesji R.

490

Jakich sztuczek używają ludzie do zarządzania dostępną pamięcią interaktywnej sesji R. Korzystam z poniższych funkcji [na podstawie wpisów Petra Pikala i Davida Hindsa do listy pomocy w 2004 r.], Aby wyświetlić (i / lub posortować) największe obiekty oraz sporadycznie rm()niektóre z nich. Jednak zdecydowanie najskuteczniejszym rozwiązaniem było ... uruchomienie pod 64-bitowym Linuksem z dużą ilością pamięci.

Jakieś inne fajne sztuczki, którymi ludzie chcą się podzielić? Poproszę jeden na post.

# 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)
}
Dirk Eddelbuettel
źródło
Uwaga, nie wątpię w to, ale jaki jest pożytek z tego? Jestem całkiem nowy w problemach z pamięcią w R, ale ostatnio mam pewne problemy (dlatego szukałem tego postu :) - więc zaczynam od tego wszystkiego. Jak to pomaga mojej codziennej pracy?
Matt Bannert
4
jeśli chcesz zobaczyć obiekty w obrębie funkcji, musisz użyć: lsos (pos = environment ()), w przeciwnym razie pokażą tylko zmienne globalne. Aby zapisać błąd standardowy: write.table (lsos (pos = environment ()), stderr (), quote = FALSE, sep = '\ t')
Michael Kuhn
Dlaczego 64-bitowy system Linux, a nie 64-bitowy system Windows? Czy wybór systemu operacyjnego nie jest trywialną różnicą, gdy mam 32 GB pamięci RAM?
Jase
3
@pepsimax: Zostało to zapakowane w multilevelPSApaczkę . Pakiet jest przeznaczony do czegoś innego, ale możesz użyć funkcji stamtąd bez ładowania pakietu, mówiąc requireNamespace(multilevelPSA); multilevelPSA::lsos(...). Lub w Dmiscpakiecie (nie w CRAN).
krlmlr
1
Jeśli zestaw danych jest w zarządzalnym rozmiarze, zwykle przechodzę do R studio> Środowisko> Widok siatki. Tutaj możesz zobaczyć i posortować wszystkie elementy w bieżącym środowisku na podstawie rozmiaru.
kRazzy R

Odpowiedzi:

197

Upewnij się, że nagrywasz swoją pracę w powtarzalnym skrypcie. Od czasu do czasu ponownie otwórz R, a następnie source()skrypt. Wyczyścisz wszystko, czego już nie używasz, a dodatkową korzyścią będzie przetestowanie kodu.

Hadley
źródło
58
Moją strategią jest rozkładanie skryptów wzdłuż linii load.R i do.R, gdzie load.R ładowanie danych z plików lub bazy danych może trochę potrwać, a wszelkie wstępne przetwarzanie / łączenie te dane. Ostatni wiersz load.R służy do zapisania stanu obszaru roboczego. Więc do.R to mój notatnik, w którym buduję funkcje analizy. Często przeładowuję do.R (z lub bez przeładowywania stanu obszaru roboczego z load.R w razie potrzeby).
Josh Reich
32
To dobra technika. Gdy pliki są uruchamiane w określonym porządku takim, często poprzedzić je z numerem: 1-load.r, 2-explore.r, 3-model.r- w ten sposób, że to oczywiste dla innych, że istnieje jakiś obecny porządek.
hadley,
4
Nie mogę wystarczająco poprzeć tego pomysłu. Nauczyłem R kilku osób i jest to jedna z pierwszych rzeczy, które mówię. Dotyczy to również każdego języka, w którym programowanie zawiera REPL i edytowany plik (tj. Python). rm (ls = list ()) i source () również działają, ale ponowne otwarcie jest lepsze (pakiety również wyczyszczone).
Vince
53
Fakt, że najczęściej głosowana odpowiedź wymaga ponownego uruchomienia R, jest najgorszą możliwą krytyką R.
sds
7
@ MartínBel, który usuwa tylko obiekty utworzone w środowisku globalnym. Nie zwalnia pakietów, obiektów S4 ani wielu innych rzeczy.
hadley,
160

Korzystam z pakietu data.table . Za pomocą jego :=operatora możesz:

  • Dodaj kolumny przez odniesienie
  • Zmodyfikuj podzbiory istniejących kolumn według referencji i grupuj według referencji
  • Usuń kolumny przez odniesienie

Żadna z tych operacji nie kopiuje (potencjalnie dużych) data.tablew ogóle, ani razu.

  • Agregacja jest również szczególnie szybka, ponieważ data.tablezużywa znacznie mniej pamięci roboczej.

Powiązane linki :

Matt Dowle
źródło
109

Widziałem to na Twitterze i myślę, że to świetna funkcja Dirka! Kontynuując odpowiedź JD Long, zrobiłbym to dla przyjaznego czytania:

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

Co powoduje coś takiego:

                      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

UWAGA: Główną częścią, którą dodałem, było (ponownie, dostosowane z odpowiedzi JD):

obj.prettysize <- napply(names, function(x) {
                           print(object.size(x), units = "auto") })
Tony Breyal
źródło
można tę funkcję dodać do dplyr lub innego pakietu kluczy.
userJT
1
Warto zauważyć, że (przynajmniej w wersji 3.3.2) capture.outputnie jest już konieczne i obj.prettysize <- napply(names, function(x) {format(utils::object.size(x), units = "auto") })zapewnia czysty wynik. W rzeczywistości nie usunięcie go powoduje powstanie niechcianych cudzysłowów na wyjściu, tj. [1] "792.5 Mb"Zamiast 792.5 Mb.
Nutle 27.04.17
@Nutle Doskonale, zaktualizowałem kod odpowiednio :)
Tony Breyal
Zmieniłbym również obj.class <- napply(names, function(x) as.character(class(x))[1])na, obj.class <- napply(names, function(x) class(x)[1]) ponieważ teraz classzawsze zwraca wektor znaków (base-3.5.0).
DeltaIV
49

Agresywnie używam subsetparametru z wyborem tylko wymaganych zmiennych przy przekazywaniu ramek danych do data=argumentu funkcji regresji. Powoduje to pewne błędy, jeśli zapomnę dodać zmienne zarówno do formuły, jak i do select=wektora, ale nadal oszczędza dużo czasu ze względu na zmniejszone kopiowanie obiektów i znacznie zmniejsza powierzchnię pamięci. Powiedzmy, że mam 4 miliony rekordów ze 110 zmiennymi (i mam.) Przykład:

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

Poprzez ustawienie kontekstu i strategii: gdlab2zmienna jest logicznym wektorem, który został skonstruowany dla pacjentów w zbiorze danych, który miał wszystkie normalne lub prawie normalne wartości dla szeregu testów laboratoryjnych i HIVfinalbył wektorem znaków, który podsumował wstępne i potwierdzające testy na HIV .

IRTFM
źródło
48

Uwielbiam skrypt .ls.objects () Dirka, ale wciąż zezowałem, żeby policzyć znaki w kolumnie wielkości. Więc zrobiłem kilka brzydkich hacków, aby zaprezentować się z ładnym formatowaniem dla rozmiaru:

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

To dobra sztuczka.

Inną sugestią jest użycie obiektów, które są wydajne pod względem pamięci, tam gdzie to możliwe: na przykład użyj matrycy zamiast data.frame.

To tak naprawdę nie odnosi się do zarządzania pamięcią, ale jedną ważną funkcją, która nie jest powszechnie znana, jest memory.limit (). Możesz zwiększyć wartość domyślną za pomocą tego polecenia memory.limit (rozmiar = 2500), gdzie rozmiar jest w MB. Jak wspomniał Dirk, musisz korzystać z wersji 64-bitowej, aby naprawdę z tego skorzystać.

Shane
źródło
25
Czy nie dotyczy to tylko systemu Windows?
Christopher DuBois,
4
> memory.limit () [1] Inf Komunikat ostrzegawczy: „memory.limit ()” jest specyficzny dla systemu Windows
LJT
Czy używanie tibble zamiast data.frame pomaga nam jeszcze bardziej oszczędzać pamięć?
32

Bardzo podoba mi się ulepszona funkcja obiektów opracowana przez Dirka. Przez większość czasu wystarcza mi bardziej podstawowe wyjście z nazwą i rozmiarem obiektu. Oto prostsza funkcja z podobnym celem. Wykorzystanie pamięci można uporządkować alfabetycznie lub według wielkości, może być ograniczone do określonej liczby obiektów i może być uporządkowane rosnąco lub malejąco. Poza tym często pracuję z danymi o wielkości 1 GB +, więc funkcja odpowiednio zmienia jednostki.

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

A oto przykładowe dane wyjściowe:

> 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
Michael Hallquist
źródło
30

Nigdy nie zapisuję obszaru roboczego R. Używam skryptów importu i skryptów danych i wysyłam do szczególnie dużych obiektów danych, których nie chcę często odtwarzać. W ten sposób zawsze zaczynam od świeżego obszaru roboczego i nie muszę czyścić dużych obiektów. To jednak bardzo fajna funkcja.

kpierce8
źródło
30

Niestety nie zdążyłem go dokładnie przetestować, ale oto wskazówka dotycząca pamięci, której wcześniej nie widziałem. Dla mnie wymagana pamięć została zmniejszona o ponad 50%. Kiedy czytasz rzeczy do R np. Read.csv, wymagają one pewnej ilości pamięci. Następnie możesz zapisać je przy save("Destinationfile",list=ls()) następnym otwarciu R, możesz użyć load("Destinationfile") Teraz użycie pamięci mogło się zmniejszyć. Byłoby miło, gdyby ktoś mógł potwierdzić, czy daje to podobne wyniki z innym zestawem danych.

Dennis Jaheruddin
źródło
4
tak, doświadczyłem tego samego. W moim przypadku zużycie pamięci spada nawet do 30%. Wykorzystano 1,5 GB pamięci zapisanej w .RData (~ 30 MB). Nowa sesja po załadowaniu .RData zużywa mniej niż 500 MB pamięci.
f3lix,
Próbowałem z 2 zestawami danych (100 MB i 2,7 GB) załadowanymi do data.table przy użyciu fread, a następnie zapisanych w .RData. Pliki RData były rzeczywiście o około 70% mniejsze, ale po ponownym załadowaniu wykorzystana pamięć była dokładnie taka sama. Miałem nadzieję, że ta sztuczka zmniejszy ślad pamięci ... coś mi brakuje?
NoviceProg
@NoviceProg Nie sądzę, że czegoś brakuje, ale jest to sztuczka, myślę, że to nie zadziała we wszystkich sytuacjach. W moim przypadku pamięć po ponownym załadowaniu została faktycznie zmniejszona zgodnie z opisem.
Dennis Jaheruddin,
6
@NoviceProg Kilka rzeczy. Po pierwsze, fread, śledzenie credo data.table jest prawdopodobnie bardziej wydajne pod względem pamięci podczas ładowania plików niż read.csv. Po drugie, oszczędność pamięci, którą zauważają ludzie, dotyczy przede wszystkim wielkości pamięci procesu R (który rozszerza się, aby pomieścić obiekty i cofa się, gdy odbywa się wyrzucanie elementów bezużytecznych). Jednak czyszczenie pamięci nie zawsze zwalnia całą pamięć RAM z powrotem do systemu operacyjnego. Zatrzymanie sesji R i załadowanie elementu z miejsca, w którym był przechowywany, zwolni tyle pamięci RAM, ile to możliwe ... ale jeśli narzut byłby mały na początek ... nie zyska.
russellpierce
27

Aby dodatkowo zilustrować wspólną strategię częstych restartów, możemy użyć littler, który pozwala nam uruchamiać proste wyrażenia bezpośrednio z wiersza poleceń. Oto przykład, którego czasami używam do mierzenia czasu dla różnych BLAS dla prostej krzyżówki.

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

Również,

 r -lMatrix -e'example(spMatrix)'

ładuje pakiet Matrix (za pomocą przełącznika --packages | -l) i uruchamia przykłady funkcji spMatrix. Ponieważ r zawsze zaczyna się od „świeżości”, ta metoda jest również dobrym testem podczas opracowywania pakietu.

Wreszcie, r również działa świetnie w trybie automatycznego wsadowego w skryptach przy użyciu nagłówka shebang '#! / Usr / bin / r'. Rscript jest alternatywą, w której littler jest niedostępny (np. W systemie Windows).

Dirk Eddelbuettel
źródło
23

Zarówno w przypadku szybkości, jak i pamięci, budując dużą ramkę danych za pomocą złożonej serii kroków, okresowo opróżnię ją (budowany zestaw danych w toku) na dysk, dołączając do wszystkiego, co było wcześniej, a następnie uruchom ją ponownie . W ten sposób pośrednie kroki działają tylko na małych ramkach danych (co jest dobre, ponieważ np. Rbind spowalnia znacznie przy większych obiektach). Cały zestaw danych można odczytać z powrotem pod koniec procesu, gdy wszystkie pośrednie obiekty zostaną usunięte.

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 )
Ben BL
źródło
17

Należy zauważyć, że data.tablepakiet tables()wydaje się być całkiem dobrym zamiennikiem .ls.objects()niestandardowej funkcji Dirka (szczegółowo opisanej we wcześniejszych odpowiedziach), chociaż tylko dla data.frames / tabel, a nie np. Macierzy, tablic, list.

geotheory
źródło
to nie wyświetla żadnych ramek data.frame, więc nie jest tak świetne
userJT
16
  1. Mam szczęście i moje duże zestawy danych są zapisywane przez instrument w „kawałkach” (podzbiorach) o wielkości około 100 MB (32-bitowy plik binarny). W ten sposób mogę wykonać etapy przetwarzania wstępnego (usuwanie nieinformacyjnych części, próbkowanie w dół) sekwencyjnie przed stopieniem zestawu danych.

  2. Wywołanie gc ()„ręcznie” może pomóc, jeśli rozmiar danych zbliży się do dostępnej pamięci.

  3. Czasami inny algorytm wymaga znacznie mniej pamięci.
    Czasami występuje kompromis między wektoryzacją a wykorzystaniem pamięci.
    porównaj: spliti lapplyvs. forpętla.

  4. Ze względu na szybką i łatwą analizę danych często najpierw pracuję z małym losowym podzbiorem ( sample ()) danych. Po zakończeniu skryptu analizy danych / .Rnw kod analizy danych i kompletne dane są przesyłane do serwera obliczeniowego w celu wykonania obliczeń za noc / w weekend / ...

cbeleites niezadowoleni z SX
źródło
11

Wykorzystanie środowisk zamiast list do obsługi kolekcji obiektów, które zajmują znaczną ilość pamięci roboczej.

Powód: za każdym razem, gdy element liststruktury jest modyfikowany, cała lista jest tymczasowo powielana. Staje się to problemem, jeśli zapotrzebowanie na miejsce na liście wynosi około połowy dostępnej pamięci roboczej, ponieważ wtedy dane muszą zostać zamienione na wolny dysk twardy. Z drugiej strony środowiska nie podlegają temu zachowaniu i można je traktować podobnie jak listy.

Oto przykład:

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

W połączeniu ze strukturami takimi jak big.matrixlub, data.tablektóre pozwalają na zmianę ich zawartości w miejscu, można osiągnąć bardzo wydajne wykorzystanie pamięci.

Georg Schnabel
źródło
6
To już nie jest prawdą: od zaawansowanego R Hadleya „zmiany w R 3.1.0 spowodowały, że korzystanie z [środowisk] było znacznie mniej ważne, ponieważ modyfikacja listy nie powoduje już głębokiej kopii”.
petrelharp
8

llFunkcji w gDatapakiecie mogą wykazywać wykorzystania pamięci każdego obiektu, jak również.

gdata::ll(unit='MB')
użytkownik1436187
źródło
Nie w moim systemie: wersja R 3.1.1 (2014-07-10), x86_64-pc-linux-gnu (64-bit), gdata_2.13.3, gtools_3.4.1.
krlmlr
Masz rację, testuję go, gdy został zamówiony przypadkowo!
user1436187
1
Proszę zmodyfikować funkcję, aby używać Gb, Mb
userJT
7

Jeśli naprawdę chcesz uniknąć wycieków, powinieneś unikać tworzenia dużych obiektów w środowisku globalnym.

To, co zwykle robię, to mieć funkcję, która wykonuje zadanie i zwraca NULL- wszystkie dane są odczytywane i przetwarzane w tej funkcji lub w innych wywoływanych przez nią funkcjach.

Alexander Radev
źródło
7

Z tylko 4 GB pamięci RAM (z systemem Windows 10, więc upewnij się, że około 2 lub więcej realistycznie 1 GB) musiałem być bardzo ostrożny z alokacją.

Używam data.table prawie wyłącznie.

Funkcja „fread” pozwala na podzbiór informacji według nazw pól podczas importu; importuj tylko pola, które są naprawdę potrzebne na początek. Jeśli używasz odczytu R, zeruj fałszywe kolumny natychmiast po zaimportowaniu.

Jak sugeruje 42 , tam, gdzie to możliwe, podzbiorę w kolumnach natychmiast po zaimportowaniu informacji.

Często rm () obiekty ze środowiska, gdy tylko nie są już potrzebne, np. W następnym wierszu po użyciu ich do podzbioru czegoś innego i wywołanie gc ().

„fread” i „fwrite” z data.table mogą być bardzo szybkie w porównaniu z bazowymi odczytami i odczytami R.

Jak sugeruje kpierce8 , prawie zawsze piszę wszystko ze środowiska i przeglądam je z powrotem, nawet z tysiącami / setkami tysięcy małych plików do przejścia. To nie tylko utrzymuje czystość środowiska i utrzymuje niski przydział pamięci, ale prawdopodobnie z powodu poważnego braku dostępnej pamięci RAM R ma skłonność do częstych awarii na moim komputerze; bardzo często. Tworzenie kopii zapasowej informacji na samym dysku, gdy kod przechodzi przez różne etapy, oznacza, że ​​nie muszę zaczynać od samego początku, jeśli ulegnie awarii.

Według stanu na 2017 r. Najszybsze dyski SSD pracują z prędkością około kilku GB na sekundę przez port M2. Mam naprawdę podstawowy dysk SSD Kingston V300 o pojemności 50 GB (550 MB / s), którego używam jako mojego głównego dysku (na nim jest Windows i R). Przechowuję wszystkie informacje masowe na tanim talerzu WD o pojemności 500 GB. Przenoszę zestawy danych na dysk SSD, kiedy zaczynam nad nimi pracować. To, w połączeniu z „fread” i „fwrite”, wszystko działało świetnie. Próbowałem użyć „ff”, ale wolę ten pierwszy. Prędkość odczytu / zapisu 4K może jednak powodować problemy; tworzenie kopii zapasowej ćwierć miliona plików 1k (o wartości 250 MB) z dysku SSD na talerz może zająć wiele godzin. O ile mi wiadomo, nie ma jeszcze dostępnego pakietu R, który mógłby automatycznie zoptymalizować proces „chunkification”; np. sprawdź, ile pamięci RAM ma użytkownik, przetestuj prędkości odczytu / zapisu pamięci RAM / wszystkich podłączonych napędów, a następnie zaproponuj optymalny protokół „chunkification”. Może to spowodować znaczące ulepszenia przepływu pracy / optymalizację zasobów; np. podziel go na ... MB dla pamięci RAM -> podziel na ... MB na dysk SSD -> podziel na ... MB na talerzu -> podziel na ... MB na taśmie. Może wcześniej próbkować zestawy danych, aby nadać mu bardziej realistyczny wskaźnik do pracy.

Wiele problemów, nad którymi pracowałem w R, polega na tworzeniu par kombinacji i permutacji, potrójnych itd., Co sprawia, że ​​posiadanie ograniczonej pamięci RAM jest bardziej ograniczeniem, ponieważ często będą one przynajmniej wykładniczo rozszerzane w pewnym momencie. To sprawiło, że skupiłem dużo uwagi na jakości, a nie na ilości informacji, które trafiają do nich na początku, zamiast próbować je później wyczyścić, a także na sekwencji operacji przygotowujących informacje na początek (zaczynając od najprostsza operacja i zwiększenie złożoności); np. podzbiór, następnie scalanie / łączenie, a następnie tworzenie kombinacji / permutacji itp.

Wydaje się, że korzystanie z podstawowego odczytu i zapisu w niektórych przypadkach przynosi pewne korzyści. Na przykład wykrywanie błędów w ramach „fread” jest tak dobre, że próba wprowadzenia naprawdę niechlujnych informacji do R na początek może być trudna. Baza R wydaje się również znacznie łatwiejsza, jeśli używasz Linuksa. Baza R wydaje się działać dobrze w systemie Linux, Windows 10 zużywa ~ 20 GB miejsca na dysku, podczas gdy Ubuntu potrzebuje tylko kilku GB, pamięć RAM potrzebna dla Ubuntu jest nieco niższa. Ale zauważyłem duże ilości ostrzeżeń i błędów podczas instalowania pakietów stron trzecich w (L) Ubuntu. Nie polecałbym odchodzenia zbyt daleko od (L) Ubuntu lub innych dystrybucji akcji z Linuksem, ponieważ możesz stracić tyle ogólnej kompatybilności, że czyni to proces prawie bezcelowym (myślę, że „jedność” ma zostać anulowana w Ubuntu od 2017 roku ).

Mam nadzieję, że niektóre z nich mogą pomóc innym.

bg49ag
źródło
5

To nic nie dodaje do powyższego, ale jest napisane w prostym i mocno komentowanym stylu, który lubię. Daje tabelę z obiektami uporządkowanymi pod względem wielkości, ale bez niektórych szczegółów podanych w powyższych przykładach:

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

To jest nowa odpowiedź na to wspaniałe stare pytanie. Z 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 )

Chris Beeley
źródło
3

Jeśli pracujesz na systemie Linux i chcą korzystać z kilku procesów i mają tylko zrobić odczytu operacji na jednym lub więcej dużych obiektów użyć makeForkClusterzamiast makePSOCKcluster. Oszczędza to również czas wysyłania dużego obiektu do innych procesów.

gdkrmr
źródło
2

Naprawdę doceniam niektóre z powyższych odpowiedzi, następujące po @hadley i @Dirk, które sugerują zamknięcie R i wydanie source i użycie wiersza poleceń, wpadłem na rozwiązanie, które działało bardzo dobrze dla mnie. Musiałem poradzić sobie z setkami widm masowych, z których każde zajmuje około 20 Mb pamięci, więc użyłem dwóch skryptów R w następujący sposób:

Najpierw opakowanie:

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

za pomocą tego skryptu w zasadzie kontroluję to, co robi mój główny skrypt runConsensus.r, i piszę odpowiedź na dane wyjściowe. Dzięki temu za każdym razem, gdy otoki wywołuje skrypt, wydaje się, że R jest ponownie otwierane i pamięć jest zwalniana.

Mam nadzieję, że to pomoże.

użytkownik1265067
źródło
2

Oprócz bardziej ogólnych technik zarządzania pamięcią podanych w powyższych odpowiedziach, zawsze staram się jak najbardziej zmniejszyć rozmiar moich obiektów. Na przykład pracuję z bardzo dużymi, ale bardzo rzadkimi macierzami, innymi słowy macierzami, w których większość wartości wynosi zero. Korzystając z pakietu „Matrix” (ważne wielkie litery) udało mi się zmniejszyć moje średnie rozmiary obiektów z ~ 2 GB do ~ 200 MB, tak jak:

my.matrix <- Matrix(my.matrix)

Pakiet Matrix zawiera formaty danych, których można używać dokładnie tak jak zwykłą matrycę (nie trzeba zmieniać innego kodu), ale są one w stanie znacznie wydajniej przechowywać rzadkie dane, niezależnie od tego, czy są załadowane do pamięci, czy zapisane na dysku.

Dodatkowo, pliki surowe, które otrzymuję, mają „długi” format, w którym każdy punkt danych ma zmienne x, y, z, i. Znacznie bardziej wydajne przekształcanie danych w x * y * ztablicę wymiarów z tylko zmienną i.

Poznaj swoje dane i zachowaj zdrowy rozsądek.

D Greenwood
źródło
2

Wskazówka dotycząca postępowania z obiektami wymagającymi ciężkich obliczeń pośrednich: Podczas tworzenia obiektów wymagających dużej ilości obliczeń i pośrednich kroków często przydaje mi się napisanie fragmentu kodu z funkcją utworzenia obiektu, a następnie oddzielnego fragmentu kodu, który daje mi opcję albo wygenerowania i zapisania obiektu jako rmdpliku, albo załadowania go zewnętrznie z rmdpliku, który już wcześniej zapisałem. Jest to szczególnie łatwe w R Markdownużyciu poniższej struktury fragmentu kodu.

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

```

W tej strukturze kodu wszystko, co muszę zrobić, to zmienić w LOADzależności od tego, czy chcę wygenerować i zapisać obiekt, czy załadować go bezpośrednio z istniejącego zapisanego pliku. (Oczywiście, muszę go wygenerować i zapisać za pierwszym razem, ale potem mam opcję załadowania go.) Ustawienie LOAD = TRUEpomija użycie mojej skomplikowanej funkcji i pozwala uniknąć wszystkich związanych z tym ciężkich obliczeń. Ta metoda wciąż wymaga wystarczającej ilości pamięci do przechowywania interesującego obiektu, ale oszczędza ci konieczności obliczania go za każdym razem, gdy uruchamiasz kod. W przypadku obiektów wymagających dużej ilości obliczeń pośrednich kroków (np. Do obliczeń obejmujących pętle na dużych tablicach) może to zaoszczędzić znaczną ilość czasu i obliczeń.

Ben - Przywróć Monikę
źródło
1

Bieganie

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

od czasu do czasu pomaga również R uwolnić nieużywaną, ale wciąż nie zwolnioną pamięć.

Marcelo Ventura
źródło
Co robi fortutaj pętla? Nie ma się iw gcrozmowy.
Umaomamaomao
@qqq jest tam tylko po to, aby uniknąć kopiowania i wklejania gc(reset = T)dziewięć razy
Marcelo Ventura
14
Ale dlaczego miałbyś uruchamiać go 9 razy? (ciekawy, niekrytyczny)
Umaomamaomao
1

Możesz również uzyskać pewne korzyści, używając knitr i umieszczając swój skrypt w chmurze Rmd.

Zwykle dzielę kod na różne części i wybieram, który z nich zapisze punkt kontrolny w pamięci podręcznej lub w pliku RDS, a

Tam możesz ustawić, aby fragment był zapisywany w „pamięci podręcznej” lub możesz zdecydować, czy chcesz uruchomić określony fragment, czy nie. W ten sposób w pierwszym uruchomieniu możesz przetworzyć tylko „część 1”, w innym wykonaniu możesz wybrać tylko „część 2” itp.

Przykład:

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

Efektem ubocznym może być również oszczędność bólu głowy w zakresie powtarzalności :)

Matias Thayer
źródło
1

Na podstawie odpowiedzi @ Dirka i @ Tony'ego dokonałem niewielkiej aktualizacji. Wynik był generowany [1]przed ładnymi wartościami wielkości, więc wyjąłem ten, capture.outputktóry rozwiązał problem:

.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()
ilyas
źródło
-1

Staram się utrzymywać małą liczbę obiektów podczas pracy w większym projekcie z wieloma pośrednimi krokami. Zamiast tworzyć wiele unikalnych obiektów o nazwie

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

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

Pracuję z tymczasowymi obiektami, które nazywam temp.

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

Co pozostawia mi mniej pośrednich plików i więcej informacji.

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

Aby zaoszczędzić więcej pamięci, mogę po prostu usunąć, tempgdy nie jest już potrzebny.

rm(temp)

Jeśli muszę kilka plików pośrednich, używam temp1, temp2, temp3.

Do testowania używam test, test2...

Buzdygan
źródło