Всеобъемлющий способ проверки функций, которые используют генератор случайных чисел в R script?

существует интеллектуальный способ идентифицировать все функции, которые используют .Random.seed (состояние генератора случайных чисел внутри R) в любой точке R script?

: у нас есть набор данных, который постоянно изменяется, как записи [строки], так и информация [столбцы] - мы часто добавляем новые записи, но мы также обновляем информацию в определенных столбцах. поэтому набор данных постоянно находится в движении. мы заполняем некоторые недостающие данные с вменением, что требует генерации случайных чисел с помощью функции sample(). поэтому всякий раз, когда мы добавляем новую строку или обновляем любую информацию в столбце, случайные вмененные числа все меняются - что и ожидается. мы используем set.seed() в начале каждого случайного вменения, поэтому, если столбец изменяется, но меняются нулевые строки, другие столбцы, генерируемые случайным образом, не затрагиваются.

У меня создается впечатление, что единственная функция внутри всей нашей кодовой базы, которая когда-либо затрагивает случайное семя, - это функция sample(), но я хотел бы это проверить как-то?

edit: даже то, что печатает вызов функции всякий раз, когда состояние случайного числа будет затронуто, было бы полезно, так же, как debug() оживает всякий раз, когда срабатывает отладочная функция? для наших целей довольно безопасно предположить, что если мы будем запускать наш script один раз для динамической оценки и никаких других случайных функций не запускаться, тогда мы в безопасности.

спасибо

Ответ 1

Несмотря на мой комментарий, это грубоватый способ проверить это:

rm(.Random.seed) # if it already exists
makeActiveBinding('.Random.seed',
                  function () stop('Something touched my seed', call. = FALSE),
                  globalenv())

Это сделает .Random.seed в активную привязку , которая вызывает ошибку при ее касании.

Это работает, но очень разрушительно. Heres более мягкий вариант. Он имеет несколько интересных особенностей:

  • Он позволяет включать и отключать отладку .Random.seed
  • Он поддерживает получение и установку семени
  • Он регистрирует вызов, но не останавливает выполнение
  • Он поддерживает "белый список" вызовов верхнего уровня, которые не должны регистрироваться

С этим вы можете написать следующий код, например:

# Ignore calls coming from sample.int
> debug_random_seed(ignore = sample.int)

> sample(5)
Getting .Random.seed
Called from sample(5)
Setting .Random.seed
Called from sample(5)
[1] 3 5 4 1 2

> sample.int(5)
[1] 5 1 2 4 3

> undebug_random_seed()

> sample(5)
[1] 2 1 5 3 4

Вот реализация во всей красе:

debug_random_seed = local({
    function (ignore) {
        seed_scope = parent.env(environment())

        if (is.function(ignore)) ignore = list(ignore)

        if (exists('.Random.seed', globalenv())) {
            if (bindingIsActive('.Random.seed', globalenv())) {
                warning('.Random.seed is already being debugged')
                return(invisible())
            }
        } else {
            set.seed(NULL)
        }

        # Save existing seed before deleting
        assign('random_seed', .Random.seed, seed_scope)
        rm(.Random.seed, envir = globalenv())

        debug_seed = function (new_value) {
            if (sys.nframe() > 1 &&
                ! any(vapply(ignore, identical, logical(1), sys.function(1)))
            ) {
                if (missing(new_value)) {
                    message('Getting .Random.seed')
                } else {
                    message('Setting .Random.seed')
                }
                message('Called from ', deparse(sys.call(1)))
            }

            if (! missing(new_value)) {
                assign('random_seed', new_value, seed_scope)
            }

            random_seed
        }

        makeActiveBinding('.Random.seed', debug_seed, globalenv())
    }
})

undebug_random_seed = function () {
    if (! (exists('.Random.seed', globalenv()) &&
           bindingIsActive('.Random.seed', globalenv()))) {
        warning('.Random.seed is not being debugged')
        return(invisible())
    }

    seed = suppressMessages(.Random.seed)
    rm('.Random.seed', envir = globalenv())
    assign('.Random.seed', seed, globalenv())
}

Некоторые примечания о коде:

  • Функция debug_random_seed определена внутри собственной частной среды. Эта среда обозначается символом seed_scope в коде. Это предотвращает утечку частной переменной random_seed в глобальную среду.
  • Функция защищает проверку того, включена ли отладка. Может быть, Overkill.
  • Отладочная информация выводится только при посещении семпла в вызове функции. Если пользователь проверяет .Random.seed непосредственно на консоли R, никаких протоколов не происходит.