检查在 R 脚本中使用随机数生成器的函数的综合方法?
comprehensive way to check for functions that use the random number generator in an R script?
是否有一种聪明的方法来识别在 R 脚本中的任何时候使用 .Random.seed
(R 中的随机数生成器状态)的所有函数?
用例:我们有一个不断变化的数据集,包括记录 [行] 和信息 [列] - 我们经常添加新记录,但我们也会更新某些列中的信息。所以数据集在不断变化。我们用插补填充一些缺失的数据,这需要使用 sample()
函数生成随机数。因此,每当我们添加新行或更新列中的任何信息时,随机估算的数字都会发生变化——这是预期的。我们在每个随机插补的开始使用 set.seed()
,因此如果一列发生变化但零行发生变化,则其他随机生成的列不受影响。
我的印象是,我们整个代码库中唯一接触随机种子的函数是 sample()
函数,但我想以某种方式验证这一点?
编辑:即使是在随机数状态被触及时打印函数调用的东西也会有所帮助,同样的方式 debug()
在被调试的函数被触发时出现?出于我们的目的,可以很安全地假设如果我们 运行 我们的脚本一次用于动态评估并且没有其他随机函数被触发,那么我们是安全的。
谢谢
尽管有我的评论,但这里有一种蛮力检查方法:
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
,则不会发生日志记录。
是否有一种聪明的方法来识别在 R 脚本中的任何时候使用 .Random.seed
(R 中的随机数生成器状态)的所有函数?
用例:我们有一个不断变化的数据集,包括记录 [行] 和信息 [列] - 我们经常添加新记录,但我们也会更新某些列中的信息。所以数据集在不断变化。我们用插补填充一些缺失的数据,这需要使用 sample()
函数生成随机数。因此,每当我们添加新行或更新列中的任何信息时,随机估算的数字都会发生变化——这是预期的。我们在每个随机插补的开始使用 set.seed()
,因此如果一列发生变化但零行发生变化,则其他随机生成的列不受影响。
我的印象是,我们整个代码库中唯一接触随机种子的函数是 sample()
函数,但我想以某种方式验证这一点?
编辑:即使是在随机数状态被触及时打印函数调用的东西也会有所帮助,同样的方式 debug()
在被调试的函数被触发时出现?出于我们的目的,可以很安全地假设如果我们 运行 我们的脚本一次用于动态评估并且没有其他随机函数被触发,那么我们是安全的。
谢谢
尽管有我的评论,但这里有一种蛮力检查方法:
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
,则不会发生日志记录。