Варианты кеширования / запоминания / хеширования в R

Я пытаюсь найти простой способ использовать что-то вроде хеш-функций Perl в R (по сути, кеширование), так как я собирался выполнять как хеширование в стиле Perl, так и писать свои собственные запоминания вычислений. Тем не менее, другие избили меня до отказа и имеют пакеты для запоминания. Чем больше я копаю, тем больше нахожу, например memoise а также R.cache, но различия не всегда понятны. Кроме того, неясно, как еще можно получить хеши в стиле Perl (или словари в стиле Python) и написать свои собственные памятки, кроме как использовать hash пакет, который, кажется, не лежит в основе двух пакетов памятки.

Поскольку я не могу найти информацию о CRAN или где-либо еще, чтобы различать варианты, возможно, это должен быть вопрос вики сообщества для SO: Какие есть варианты для запоминания и кэширования в R, и каковы их различия?


В качестве основы для сравнения, вот список вариантов, которые я нашел. Кроме того, мне кажется, что все зависит от хеширования, поэтому я также отмечу варианты хеширования. Хранилище ключей / значений в некоторой степени связано, но открывает огромную кучу червей в отношении систем БД (например, BerkeleyDB, Redis, MemcacheDB и множество других).

Похоже, варианты:

хеширования

  • digest - обеспечивает хеширование для произвольных объектов R.

мемоизации

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

Кэширование

  • hash - обеспечивает функциональность кэширования, аналогичную хешам Perl и словарям Python.

Хранение ключа / значения

Это базовые параметры для внешнего хранения объектов R.

Checkpointing

Другой

  • Base R поддерживает: именованные векторы и списки, имена строк и столбцов фреймов данных и имена элементов в средах. Мне кажется, что использование списка является чем-то вроде клочья. (Есть также pairlist, но это устарело.)
  • Пакет data.table поддерживает быстрый поиск элементов в таблице данных.

Случай использования

Хотя я в основном заинтересован в знании опций, у меня есть два основных варианта использования:

  1. Кэширование: простой подсчет строк. [Примечание: это не для NLP, а для общего использования, поэтому библиотеки NLP излишни; таблицы неадекватны, потому что я предпочитаю не ждать, пока весь набор строк будет загружен в память. Хеши в стиле Perl находятся на правильном уровне полезности.]
  2. Запоминание чудовищных расчетов.

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


Примечание 1. В представлении задач CRAN в разделе " Воспроизводимые исследования" перечислено несколько пакетов (cacher а также R.cache), но нет подробного описания вариантов использования.

Примечание 2: Чтобы помочь другим, ищущим связанный код, вот несколько заметок о некоторых авторах или пакетах. Некоторые из авторов используют SO.:)

  • Дирк Эддельбюттель: digest - от этого зависит множество других пакетов.
  • Роджер Пэн: cacher, filehash, stashR - они решают разные проблемы по-разному; см . сайт Роджера для большего количества пакетов.
  • Кристофер Браун: hash - Кажется, полезный пакет, но ссылки на ODG, к сожалению, не работают.
  • Хенрик Бенгтссон: R.cache И Хэдли Уикхем: memoise - пока не ясно, когда отдать предпочтение одному пакету над другим.

Примечание 3: Некоторые люди используют памятку / памятку, другие используют памятку / памятку. Просто заметка, если вы ищете вокруг. Хенрик использует "z", а Хэдли - "s".

3 ответа

Мне не повезло с memoise потому что это дало too deep recursive проблема с какой-то функцией упакованного я пробовал. С R.cache Мне повезло больше Ниже приведен более аннотированный код, который я адаптировал из R.cache документация. В коде показаны разные варианты кеширования.

# Workaround to avoid question when loading R.cache library
dir.create(path="~/.Rcache", showWarnings=F) 
library("R.cache")
setCacheRootPath(path="./.Rcache") # Create .Rcache at current working dir
# In case we need the cache path, but not used in this example.
cache.root = getCacheRootPath() 
simulate <- function(mean, sd) {
    # 1. Try to load cached data, if already generated
    key <- list(mean, sd)
    data <- loadCache(key)
    if (!is.null(data)) {
        cat("Loaded cached data\n")
        return(data);
    }
    # 2. If not available, generate it.
    cat("Generating data from scratch...")
    data <- rnorm(1000, mean=mean, sd=sd)
    Sys.sleep(1) # Emulate slow algorithm
    cat("ok\n")
    saveCache(data, key=key, comment="simulate()")
    data;
}
data <- simulate(2.3, 3.0)
data <- simulate(2.3, 3.5)
a = 2.3
b = 3.0
data <- simulate(a, b) # Will load cached data, params are checked by value
# Clean up
file.remove(findCache(key=list(2.3,3.0)))
file.remove(findCache(key=list(2.3,3.5)))

simulate2 <- function(mean, sd) {
    data <- rnorm(1000, mean=mean, sd=sd)
    Sys.sleep(1) # Emulate slow algorithm
    cat("Done generating data from scratch\n")
    data;
}
# Easy step to memoize a function
# aslo possible to resassign function name.
This would work with any functions from external packages. 
mzs <- addMemoization(simulate2)

data <- mzs(2.3, 3.0)
data <- mzs(2.3, 3.5)
data <- mzs(2.3, 3.0) # Will load cached data
# aslo possible to resassign function name.
# but different memoizations of the same 
# function will return the same cache result
# if input params are the same
simulate2 <- addMemoization(simulate2)
data <- simulate2(2.3, 3.0)

# If the expression being evaluated depends on
# "input" objects, then these must be be specified
# explicitly as "key" objects.
for (ii in 1:2) {
    for (kk in 1:3) {
        cat(sprintf("Iteration #%d:\n", kk))
        res <- evalWithMemoization({
            cat("Evaluating expression...")
            a <- kk
            Sys.sleep(1)
            cat("done\n")
            a
        }, key=list(kk=kk))
        # expressions inside 'res' are skipped on the repeated run
        print(res)
        # Sanity checks
        stopifnot(a == kk)
        # Clean up
        rm(a)
    } # for (kk ...)
} # for (ii ...)

Для простого подсчета строк (а не использования table или аналогичный), многосетевая структура данных выглядит как подходящая. environment объект может быть использован для эмуляции этого.

# Define the insert function for a multiset
msetInsert <- function(mset, s) {
    if (exists(s, mset, inherits=FALSE)) {
        mset[[s]] <- mset[[s]] + 1L
    } else {
        mset[[s]] <- 1L 
    }
}

# First we generate a bunch of strings
n <- 1e5L  # Total number of strings
nus <- 1e3L  # Number of unique strings
ustrs <- paste("Str", seq_len(nus))

set.seed(42)
strs <- sample(ustrs, n, replace=TRUE)


# Now we use an environment as our multiset    
mset <- new.env(TRUE, emptyenv()) # Ensure hashing is enabled

# ...and insert the strings one by one...
for (s in strs) {
    msetInsert(mset, s)
}

# Now we should have nus unique strings in the multiset    
identical(nus, length(mset))

# And the names should be correct
identical(sort(ustrs), sort(names(as.list(mset))))

# ...And an example of getting the count for a specific string
mset[["Str 3"]] # "Str 3" instance count (97)

Связано с решением @biocyperman. R.cache имеет функцию обертки, позволяющую избежать загрузки, сохранения и оценки кэша. Смотрите измененную функцию:

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

simulate <- function(mean, sd) {
key <- list(mean, sd)
data <- evalWithMemoization(key = key, expr = {
    cat("Generating data from scratch...")
    data <- rnorm(1000, mean=mean, sd=sd)
    Sys.sleep(1) # Emulate slow algorithm
    cat("ok\n")
    data})
}
Другие вопросы по тегам