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

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

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


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

Похоже, что параметры:

хэширования

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

запоминание

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

Кэширование

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

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

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

Checkpointing

Другие

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

Использовать регистр

Хотя мне больше всего интересно узнать параметры, у меня возникают два основных варианта использования:

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

Это действительно происходит потому, что я вникаю в профилирование некоторого кода slooooow, и мне бы очень хотелось просто подсчитать простые строки и посмотреть, могу ли я ускорить некоторые вычисления посредством memoization. Имея возможность хешировать входные значения, даже если я не memoize, позволит мне увидеть, может ли memoization помочь.


Примечание 1: CRAN Task View on Reproducible Research содержит несколько пакетов (cacher и R.cache), но не существует необходимости в разработке вариантов использования.

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

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

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

Ответ 1

Для простого подсчета строк (и не используя table или аналогичных) структура данных multiset кажется хорошей. Объект 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)

Ответ 2

Мне не повезло с 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 ...)

Ответ 3

Относится к @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})
}