加载一些包影响随机数生成?

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 个!)。追踪这将是一个巨大的痛苦。我可能会尝试通过消除来做到这一点,采用这些包的名称并 排除 任何由(例如)sftidyverse 加载的内容,两者都不显示这个问题。然而,事实证明这不是必需的。

经过一些放屁(使用 R -d gdb 并在内部 unif_rand C 函数上设置断点),我意识到正在调用 sample(),然后是随机化步骤via sample()determine_tips_order (here) 中使用,从 .onLoad 调用。这用于设置向用户提供关于 tmap 的随机“提示”的顺序——在我看来,这真的应该在第一次调用 tmap_tip() 时调用,而不是在包加载时调用。 .(您可以在包的 github 回购协议中提出问题...)

(如果可以的话,我实际上会将其作为一个 CRAN 存储库策略,即包加载 不会 以这种方式扰乱随机数流 ...)