加载一些包影响随机数生成?
Loading some packages affects random number generation?
我发现加载一些包会影响R中的随机数生成,问题可以重现如下
(1) 打开一个新的 R 会话。 (我的案例:R 4.x + RStudio)
(2)执行以下代码:
set.seed(1)
library(sf)
library(tmap)
sample(1:10, 5)
(3) 第一次的结果是:
5 10 2 8 6
(4) 但是,如果多次执行整个代码,结果(在第一次之后)总是:
9 4 7 1 2
为什么第一次的结果不一样?貌似第一次加载sf和tmap库会影响随机数生成。好奇怪。
一些实验表明问题出在 tmap
包上,而不是 sf
(即从干净的 R 会话中,set.seed(1); library(sf); sample(1:10, 5)
给出 9 4 7 1 2
。
如果我们去它的 GitHub 存储库,我们可以看到 tmap 包有一个 .onLoad
函数(here),它会在第一次得到 运行包已加载(从技术上讲,第二次调用 library(tmap)
不会加载包,因为它已经加载...)
再深入一点,我们可以诊断出这类问题
set.seed(1)
r <- .Random.seed
f <- function() identical(r, .Random.seed)
然后在每一步之后检查f()
;它将是 TRUE
除非 随机数流已被更改。
我在 ...
中使用了这种方法
这很难弄清楚,因为几乎不可能 不 执行所有 .onLoad
函数(例如,如果您调用 tmap:::working_internet()
,那first 加载包以便访问该函数),并且在 .onLoad
函数本身上设置调试标志并不容易(不可能?)(因为它不是在加载包之前访问。
我最初误以为它是由 tmap
间接加载的 other 包之一导致了问题(names(sessionInfo()$loadedOnly)
表明有 52 个!)。追踪这将是一个巨大的痛苦。我可能会尝试通过消除来做到这一点,采用这些包的名称并 排除 任何由(例如)sf
或 tidyverse
加载的内容,两者都不显示这个问题。然而,事实证明这不是必需的。
经过一些放屁(使用 R -d gdb
并在内部 unif_rand
C 函数上设置断点),我意识到正在调用 sample()
,然后是随机化步骤via sample()
在 determine_tips_order
(here) 中使用,从 .onLoad
调用。这用于设置向用户提供关于 tmap 的随机“提示”的顺序——在我看来,这真的应该在第一次调用 tmap_tip()
时调用,而不是在包加载时调用。 .(您可以在包的 github 回购协议中提出问题...)
(如果可以的话,我实际上会将其作为一个 CRAN 存储库策略,即包加载 不会 以这种方式扰乱随机数流 ...)
我发现加载一些包会影响R中的随机数生成,问题可以重现如下
(1) 打开一个新的 R 会话。 (我的案例:R 4.x + RStudio)
(2)执行以下代码:
set.seed(1)
library(sf)
library(tmap)
sample(1:10, 5)
(3) 第一次的结果是: 5 10 2 8 6
(4) 但是,如果多次执行整个代码,结果(在第一次之后)总是: 9 4 7 1 2
为什么第一次的结果不一样?貌似第一次加载sf和tmap库会影响随机数生成。好奇怪。
一些实验表明问题出在 tmap
包上,而不是 sf
(即从干净的 R 会话中,set.seed(1); library(sf); sample(1:10, 5)
给出 9 4 7 1 2
。
如果我们去它的 GitHub 存储库,我们可以看到 tmap 包有一个 .onLoad
函数(here),它会在第一次得到 运行包已加载(从技术上讲,第二次调用 library(tmap)
不会加载包,因为它已经加载...)
再深入一点,我们可以诊断出这类问题
set.seed(1)
r <- .Random.seed
f <- function() identical(r, .Random.seed)
然后在每一步之后检查f()
;它将是 TRUE
除非 随机数流已被更改。
我在
这很难弄清楚,因为几乎不可能 不 执行所有 .onLoad
函数(例如,如果您调用 tmap:::working_internet()
,那first 加载包以便访问该函数),并且在 .onLoad
函数本身上设置调试标志并不容易(不可能?)(因为它不是在加载包之前访问。
我最初误以为它是由 tmap
间接加载的 other 包之一导致了问题(names(sessionInfo()$loadedOnly)
表明有 52 个!)。追踪这将是一个巨大的痛苦。我可能会尝试通过消除来做到这一点,采用这些包的名称并 排除 任何由(例如)sf
或 tidyverse
加载的内容,两者都不显示这个问题。然而,事实证明这不是必需的。
经过一些放屁(使用 R -d gdb
并在内部 unif_rand
C 函数上设置断点),我意识到正在调用 sample()
,然后是随机化步骤via sample()
在 determine_tips_order
(here) 中使用,从 .onLoad
调用。这用于设置向用户提供关于 tmap 的随机“提示”的顺序——在我看来,这真的应该在第一次调用 tmap_tip()
时调用,而不是在包加载时调用。 .(您可以在包的 github 回购协议中提出问题...)
(如果可以的话,我实际上会将其作为一个 CRAN 存储库策略,即包加载 不会 以这种方式扰乱随机数流 ...)