【问题标题】:comprehensive way to check for functions that use the random number generator in an R script?检查在 R 脚本中使用随机数生成器的函数的综合方法?
【发布时间】:2017-09-24 03:02:30
【问题描述】:

是否有一种智能方法可以识别在 R 脚本中的任何位置使用.Random.seed(R 中的随机数生成器状态)的所有函数?

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

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

编辑:即使是在随机数状态被触及时打印函数调用的东西也会有所帮助,就像debug() 在被调试的函数被触发时出现一样?出于我们的目的,可以假设如果我们运行一次脚本进行动态评估并且没有触发其他随机函数,那么我们是安全的。

谢谢

【问题讨论】:

  • 我不得不猜测投反对票的懒人认为您可以进一步简化您的问题。不幸的是,仍然有可能在不提供原因的情况下对问题投反对票。祝你好运!
  • 不,根本没有办法。由于 R 是动态评估的,您不能编写静态分析器来全面检查这一点。启发式方法可能会让您接近,但为此构建一个将非常困难。我会说你运气不好。如果您想确保没有任何东西触及随机数生成器的状态,请不要依赖 R,编写一个使用基于 <random> 标准标头的生成器的小型 C++ 函数,然后将该函数导入 R。跨度>
  • 感谢@KonradRudolph,我添加了一个编辑,但如果我误解了某些内容,请见谅..
  • 这不是用户友好的,但我唯一能想到的就是在整个脚本中检查.Random.seed(将结果保存在矩阵或其他东西中)并寻找它何时发生变化。至于代码扫描器/功能检查器,我同意 Konrad 的观点,认为这可能是不可能的。想出病态的例子太容易了。
  • @AnthonyDamico:我注意到您花费了大部分代表。如果您需要“获得奖励”的问题,我很乐意使用我的一些代表给您的问题添加通知。我怀疑你知道我的“真名”。

标签: r random random-seed


【解决方案1】:

尽管有我的评论,但这是一种蛮力检查的方法:

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

这将使.Random.seed 变成active binding,当它被触摸时会引发错误。

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

  • 它允许启用和禁用.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 变量泄漏到全局环境中。
  • 该函数防御性地检查是否已启用调试。也许矫枉过正。
  • 只有在函数调用中访问种子时才会打印调试信息。如果用户直接在 R 控制台上检查 .Random.seed,则不会发生日志记录。

【讨论】:

  • 这几乎是完美的。有没有办法让function() stop() 忽略sample() 功能?我认为像if( !any( unlist( lapply( c( "sample" , "runif" ) , function( w ) grepl( w , paste( as.character( sys.calls() ) , collapse = "" ) ) ) ) ) ) 这样的丑陋解决方案可以跳过这两个功能,但也许有更清洁的方法
  • @AnthonyDamico 它在紧要关头工作。我更愿意比较实际的 call 符号而不是看起来相似的字符串,但不幸的是我现在在移动设备上,所以我无法编写示例。
  • @AnthonyDamico 显然我没有什么好做的了。 ;-) 检查答案中的新代码。这应该可以满足您的每一个梦想。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2020-05-12
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-07-23
  • 2023-03-13
相关资源
最近更新 更多