检查在R脚本中使用随机数生成器的函数的综合方法?

Ant*_*ico 11 random r random-seed

是否有一种智能的方法来识别.Random.seedR脚本中任何一点使用的所有函数(R中的随机数生成器状态)?

用例:我们有一个不断变化的数据集,记录[行]和信息[列] - 我们经常添加新记录,但我们也更新某些列中的信息.所以数据集不断变化.我们用一个插补来填充一些缺失的数据,这需要随sample()函数生成随机数.因此,每当我们添加新行或更新列中的任何信息时,随机推算的数字都会发生变化 - 这是预期的.我们set.seed()在每个随机插补的开始使用,因此如果列更改但零行更改,则其他随机生成的列不受影响.

我的印象是我们整个代码库中唯一触及随机种子的sample()函数是函数,但我想以某种方式验证这一点?

编辑:即使在触摸随机数状态时打印函数调用的东西也会有所帮助,debug()每当调试函数被触发时,生活方式都会生效吗?出于我们的目的,可以非常安全地假设,如果我们运行一次脚本进行动态评估并且没有其他随机函数被触发,那么我们是安全的.

谢谢

Kon*_*lph 19

尽管我发表了评论,但这是一种蛮力的方式来检查:

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

这将.Random.seed成为一个活动绑定,在触摸时抛出错误.

这有效,但它非常具有破坏性.这是一个更温和的变体.它有一些有趣的功能:

  • 它允许启用和禁用调试 .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
Run Code Online (Sandbox Code Playgroud)

这是它所有荣耀的实现:

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())
}
Run Code Online (Sandbox Code Playgroud)

关于代码的一些注意事项:

  • debug_random_seed函数在其自己的私有环境中定义.此环境seed_scope在代码中指定.这可以防止私有random_seed变量泄漏到全局环境中.
  • 该功能防御性地检查是否已启用调试.也许是矫枉过正.
  • 只有在函数调用中访问种子时才会打印调试信息.如果用户.Random.seed直接在R控制台上进行检查,则不会进行日志记录.

  • @AnthonyDamico显然我没有更好的事情要做.;-)检查答案中的新代码.这应该满足你的每一个梦想. (3认同)