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

问题描述

我发现加载一些包会影响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),它将在第一次加载包时运行(技术上,调用 {{ 1}} 第二次不加载包,因为它已经加载了...)

深入一点,我们可以诊断出这些问题

library(tmap)

然后在每一步后检查set.seed(1) r <- .Random.seed f <- function() identical(r,.Random.seed) ;它将是 f() 除非随机数流已被更改。 我在 this answer ...

中使用了这种方法

这很难弄清楚,因为几乎不可能执行所有 TRUE 函数(例如,如果您调用 .onLoad首先 加载包以访问该函数),并且在 tmap:::working_internet() 函数本身上设置调试标志并不容易(不可能?)(因为在加载包之前无法访问它)。

我最初被误导,认为是由 .onLoad 间接加载的other 包之一导致了问题(tmap 显示其中有 52 个!)。追踪这将是一个巨大的痛苦。我可能会尝试通过消除来做到这一点,采用这些包的名称并排除任何加载的(例如)names(sessionInfo()$loadedOnly)sf,这两个都没有显示此问题。然而,事实证明这没有必要。

在放屁之后(使用 tidyverse 并在内部 R -d gdb C 函数上设置断点),我意识到 unif_rand 被调用,然后通过 { 进行随机化步骤{1}} 正用于从 sample() 调用的 sample() (here)。这用于设置向用户提供有关 tmap 的随机“提示”的顺序 - 在我看来,这确实应该在第一次调用 determine_tips_order 时调用,而不是在包加载时调用。 .(你可以在包的 github repo 上提出问题......)

(如果可以的话,我实际上会使其成为 CRAN 存储库策略,包加载不会以这种方式与随机数流混淆...)