使用 R 的多线程计算:如何获得所有不同的随机数?
Multithread computation with R: how to get all different random numbers?
谁知道如何在下面的代码中让所有的随机数都不同?例如。使用 doRNG
包? 我不关心再现性。
编辑: 接受纯属偶然的重复。
rm(list = ls())
set.seed(666)
cat("4")
library(plyr)
library(dplyr)
library(doRNG)
# ====== Data Preparation ======
dt = data.frame(id = 1:10,
part = rep("dt",10),
HG = c(1,3,6,NA,NA,2,NA,NA,NA,NA),
random = NA)
# ====== Set Parallel Computing ======
library(foreach)
library(doParallel)
cl = makeCluster(3, outfile = "")
registerDoParallel(cl)
# ====== SIMULATION ======
nsim = 1000 # number of simulations
iterChunk = 100 # split nsim into this many chunks
out = data.frame() # prepare output DF
for(iter in 1:ceiling(nsim/iterChunk)){
strt = Sys.time()
out_iter =
foreach(i = 1:iterChunk, .combine = rbind, .multicombine = TRUE, .maxcombine = 100000, .inorder = FALSE, .verbose = FALSE,
.packages = c("plyr", "dplyr")) %dopar% {
# simulation number
id_sim = iterChunk * (iter - 1) + i
## Generate random numbers
tmp_sim = is.na(dt$HG) # no results yet
dt$random[tmp_sim] = runif(sum(tmp_sim))
dt$HG[tmp_sim] = 3
# Save Results
dt$id_sim = id_sim
dt$iter = iter
dt$i = i
print(Sys.time())
return(dt)
}#i;sim_forcycle
out = rbind.data.frame(out,subset(out_iter, !is.na(random)))
fnsh = Sys.time()
cat(" [",iter,"] ",fnsh - strt, sep = "")
}#iter
# ====== Stop Parallel Computing ======
stopCluster(cl)
# ====== Distinct Random Numbers ======
length(unique(out$random)) # expectation: 6000
我已经为此苦苦挣扎了 2 天。我 asked this question 早些时候只有关于随机数的一般回应。
在这里我想寻求一个解决方案(如果有人知道的话)如何设置 doRNG
包选项(或类似包),使 所有随机数都不同。遍历所有循环。
我已经尝试了很多 doRNG 设置,但仍然无法正常工作。在两台不同的计算机上尝试了 R 版本 3.5.3 和 3.6.3。
更新 与@Limey
讨论后
代码的目的是模拟足球比赛。由于模拟很大,我使用 iterChunk
将模拟“拆分”为可管理的部分,并且在每个 iter
之后将数据发送到 PostgreSQL 数据库,这样模拟就不会使 RAM 过载。一些比赛已经有了真实世界的结果,并填写了 HG
(主场进球)。我想模拟其余的。
将iterChunk
设置为1
时一切正常。增加 iterChunk
会导致在 iter
内生成相同的数字。例如,当我将 nsim
设置为 100
并将 iterChunk
设置为 10
时。 (所有比赛模拟100次,10次循环10次)。我期望 600 个随机数(每个匹配在所有循环中独立模拟)。但是我只得到 180 - 按照逻辑:3 个核心 * 6 个匹配项 * 10 个 iterChunks。)使用 2 个工作人员我得到 120 个不同的随机数 (2 * 6 * 10)
此外:排除 dt$HG[tmp_sim] = 3
无论设置如何,我都会得到不同的随机数。
为了理解问题,我建议:
- 运行 代码原样。 (可能设置
nsim
为 100
和 iterChunk
为 10
)你会得到 180 个不同的随机数。使用较少数量的 nsim 和 iterChunk,事情可能会按预期工作。
- 注释掉
dt$HG[tmp_sim] = 3
。
您将获得 6000 个不同的随机数(如果更改 nsim
和 iterChunk
则为 600 个)
第二步中的代码分配主队进球数。它看起来像某种我无法克服的错误。即使有人得到相同结果但不知道为什么的信息也会有所帮助 - 它会减轻我自己愚蠢的负担。
谢谢,我非常感谢任何努力。
修改“doParallel入门”中的“Hello World”示例vignette生成随机数,我想出了:
library(doParallel)
cl <- makeCluster(2)
registerDoParallel(cl)
myFunc <- function(n) {runif(n)}
foreach(i=1:3) %dopar% myFunc(10)
[[1]]
[1] 0.18492375 0.13388278 0.65455450 0.93093066 0.41157625 0.89479764 0.14736529 0.47935995 0.03062963 0.16110714
[[2]]
[1] 0.89245145 0.20980791 0.83828019 0.04411547 0.38184303 0.48110619 0.51509058 0.93732055 0.40159834 0.81414140
[[3]]
[1] 0.74393129 0.66999730 0.44411989 0.85040773 0.80224527 0.72483644 0.64566262 0.22546420 0.14526819 0.05931329
建议跨线程获取随机数很简单。事实上,doRNG reference manual 第 2 页和第 3 页的示例说明了同样的事情。
事实上,如果我理解正确的话,doRNG
的目的恰恰与您想要的相反:使随机进程可跨线程重现。
当然,这并不能保证所有线程中的所有数字都不同。但这使得复制的可能性很小。保证没有重复意味着过程中一定程度的确定性:完全随机的过程可能偶然产生重复。
更新
继我们在评论中的对话之后...
我们确定问题出在您的程序逻辑上,而不是并行化本身。所以我们需要重新关注这个问题:你想做什么。恐怕我一点也不清楚。所以那意味着我们需要简化。
我将 nsim
设置为 5
,将 iterChunk
设置为 1
。我得到 5 个数据框,看起来像
id part HG random id_sim iter i
1 1 dt 1 NA 1 1 1
2 2 dt 3 NA 1 1 1
3 3 dt 6 NA 1 1 1
4 4 dt 3 0.6919744 1 1 1
5 5 dt 3 0.5413398 1 1 1
6 6 dt 2 NA 1 1 1
7 7 dt 3 0.3983175 1 1 1
8 8 dt 3 0.3342174 1 1 1
9 9 dt 3 0.6126020 1 1 1
10 10 dt 3 0.4185468 1 1 1
在每个中,id_sim
和iter
的值总是相同的,并且运行从第一个数据帧中的1
到5
在第五。 i
对于所有数据框中的所有行都是 1
。 random
中的值似乎是随机的,并且在数据帧之间有所不同。但是 NA
在每个数据框中都位于相同的位置:第 1、2、3 和 6 行。 HG
的值如上所示所有五个数据帧。
这是您所期望的吗?如果没有,你期望什么?鉴于我们知道问题不是并行化,您需要向我们提供更多信息。
更新 2
你知道Arduan吗?他们在周末发布了一个相关问题...
我不会告诉你你的代码有什么问题。我将向您展示我将如何解决您的问题。如果没有别的,我希望你会同意它更具可读性。
所以,我们正在模拟一些足球比赛。我假设它是一种联赛形式,并以英超联赛为例。首先生成单个赛季的赛程表。
library(tidyverse)
teams <- c("Arsenal", "Aston Villa", "Bournemouth", "Brighton & Hove Albion",
"Burnley", "Chelsea", "Crystal Palace", "Everton", "Leicester City",
"Liverpool", "Manchester City", "Manchester United", "Newcastle United",
"Norwich City", "Sheffield United", "Southampton", "Tottenham Hotspur",
"Watford", "West Ham United", "Wolverhampton Wanderers")
fixtures <- tibble(HomeTeam=teams, AwayTeam=teams) %>%
complete(HomeTeam, AwayTeam) %>%
filter(HomeTeam != AwayTeam) # A team can't play itself
fixtures %>% head(5)
# A tibble: 5 x 2
HomeTeam AwayTeam
<chr> <chr>
1 Arsenal Aston Villa
2 Arsenal Bournemouth
3 Arsenal Brighton & Hove Albion
4 Arsenal Burnley
5 Arsenal Chelsea
假设我们知道一些结果。以昨天的比赛为例
knownResults <- tribble(~HomeTeam, ~AwayTeam, ~HomeGoals, ~AwayGoals,
"Burnley", "Sheffield United", 1, 1,
"Newcastle United", "West Ham United", 2, 2,
"Liverpool", "Aston Villa", 2, 0,
"Southampton", "Manchester City", 1, 0)
resultsSoFar <- fixtures %>%
left_join(knownResults, by=c("HomeTeam", "AwayTeam"))
resultsSoFar %>% filter(!is.na(HomeGoals))
# A tibble: 4 x 4
HomeTeam AwayTeam HomeGoals AwayGoals
<chr> <chr> <dbl> <dbl>
1 Burnley Sheffield United 1 1
2 Liverpool Aston Villa 2 0
3 Newcastle United West Ham United 2 2
4 Southampton Manchester City 1 0
现在一些实用函数。您当然可以将它们结合起来,但我认为将它们分开会更清楚,这样您就可以准确地看到每个人在做什么。
首先,模拟所有未知比赛结果的功能。您如何模拟分数的细节完全是任意的。我假设主队平均每场进 1.5 个球,客队平均每场进 1.2 个球。稍后,我将使用它来一次性模拟多个季节,因此我将添加一个变量(Iteration
)来索引季节。
simulateResults <- function(i=NA, data) {
n <- nrow(data)
data %>%
add_column(Iteration=i, .before=1) %>%
mutate(
# Give the home team a slight advantage
HomeGoals=ifelse(is.na(HomeGoals), floor(rexp(n, rate=1/1.5)), HomeGoals),
AwayGoals=ifelse(is.na(AwayGoals), floor(rexp(n, rate=1/1.2)), AwayGoals)
)
}
使用它,并检查我们是否没有覆盖已知结果:
simulateResults(1, resultsSoFar) %>% filter(HomeTeam=="Burnley", AwayTeam=="Sheffield United")
# A tibble: 1 x 5
Iteration HomeTeam AwayTeam HomeGoals AwayGoals
<dbl> <chr> <chr> <dbl> <dbl>
1 1 Burnley Sheffield United 1 1
我要并行化整个模拟,所以现在让我们使用一个函数来模拟大量模拟。同样,创建一个索引列来标识块。
simulateChunk <- function(chunkID=NA, n) {
bind_rows(lapply(1:n, simulateResults, data=resultsSoFar)) %>%
add_column(Chunk=chunkID, .before=1)
}
simulateChunk(chunkID=1, n=3)
# A tibble: 1,140 x 6
Chunk Iteration HomeTeam AwayTeam HomeGoals AwayGoals
<dbl> <int> <chr> <chr> <dbl> <dbl>
1 1 1 Arsenal Aston Villa 2 0
2 1 1 Arsenal Bournemouth 0 0
3 1 1 Arsenal Brighton & Hove Albion 2 0
4 1 1 Arsenal Burnley 2 0
5 1 1 Arsenal Chelsea 1 0
6 1 1 Arsenal Crystal Palace 0 0
7 1 1 Arsenal Everton 2 3
8 1 1 Arsenal Leicester City 2 0
9 1 1 Arsenal Liverpool 0 1
10 1 1 Arsenal Manchester City 4 0
好的。现在我已准备好进行主要的模拟工作。我将 运行 10 个 100 个模拟 eash 块,总共给出 1000 个模拟季节,和你一样。
library(doParallel)
cl <- makeCluster(3)
registerDoParallel(cl)
chunkSize <- 100
nChunks <- 10
startedAt <- Sys.time()
x <- bind_rows(foreach(i=1:nChunks, .packages=c("tidyverse")) %dopar% simulateChunk(i, n=chunkSize))
finishedAt <- Sys.time()
print(finishedAt - startedAt)
Time difference of 6.772928 secs
stopCluster(cl)
> x
# A tibble: 380,000 x 6
Chunk Iteration HomeTeam AwayTeam HomeGoals AwayGoals
<int> <int> <chr> <chr> <dbl> <dbl>
1 1 1 Arsenal Aston Villa 2 0
2 1 1 Arsenal Bournemouth 3 1
3 1 1 Arsenal Brighton & Hove Albion 0 1
4 1 1 Arsenal Burnley 3 0
5 1 1 Arsenal Chelsea 1 0
6 1 1 Arsenal Crystal Palace 0 0
7 1 1 Arsenal Everton 1 2
8 1 1 Arsenal Leicester City 0 0
9 1 1 Arsenal Liverpool 0 0
10 1 1 Arsenal Manchester City 0 0
让我们检查一下我得到了合理的结果。作为基本检查,我将查看阿森纳与阿斯顿维拉的比赛结果:
x %>%
filter(HomeTeam == "Arsenal", AwayTeam=="Aston Villa") %>%
group_by(HomeGoals, AwayGoals) %>%
summarise(N=n(), .groups="drop") %>%
pivot_wider(
values_from="N", names_prefix="AwayGoals",
names_sep="", names_from=AwayGoals
)
# A tibble: 8 x 10
HomeGoals AwayGoals0 AwayGoals1 AwayGoals2 AwayGoals3 AwayGoals4 AwayGoals5 AwayGoals6 AwayGoals8 AwayGoals7
<dbl> <int> <int> <int> <int> <int> <int> <int> <int> <int>
1 0 299 129 57 19 12 7 NA NA NA
2 1 135 63 25 6 4 4 1 2 NA
3 2 75 21 12 9 4 1 NA NA 1
4 3 30 13 10 1 NA NA NA NA NA
5 4 21 7 1 1 NA NA NA NA NA
6 5 11 2 1 NA 2 NA NA NA NA
7 6 4 2 2 NA NA NA NA NA NA
8 7 4 1 1 NA NA NA NA NA NA
这看起来很合理。现在确认具有已知结果的匹配没有变化。例如:
x %>%
filter(HomeTeam == "Liverpool", AwayTeam=="Aston Villa") %>%
group_by(HomeGoals, AwayGoals) %>%
summarise(N=n(), .groups="drop") %>%
pivot_wider(values_from="N", names_prefix="AwayGoals", names_sep="", names_from=AwayGoals)
HomeGoals AwayGoals0
<dbl> <int>
1 2 1000
一切顺利。
所以,这是 23 个语句来生成固定装置、考虑已知结果、模拟剩余的比赛并进行一些基本的健全性检查。如果必须的话,我可以很容易地将它减少到 20 个语句以下。这比您仅用于尝试模拟未知结果的数量少了大约三分之一。 [实际模拟不到 10 条语句。]我认为我的方法更容易理解:通过使用 tidy 动词,代码几乎是自我记录的。
我在洗澡的时候意识到了 OP 代码的问题所在。这很简单,回想起来也很明显:所有循环和并行进程都在同一个对象上工作——dt
数据框。所以他们不断地覆盖每个所做的更改,并且在外循环结束时,您只需要完成上一个循环所做更改的多个副本。解决方案同样简单:处理 dt
数据框的副本。
为了尽量减少更改,我将 dt
重命名为 baseDT
# ====== Data Preparation ======
baseDT = data.frame(id = 1:10,
part = rep("dt",10),
HG = c(1,3,6,NA,NA,2,NA,NA,NA,NA),
random = NA)
然后在 foreach
循环
的顶部复制了一份
out_iter = foreach(i = 1:iterChunk,
.combine = rbind, .multicombine = TRUE, .maxcombine = 100000,
.inorder = FALSE, .verbose = FALSE,
.packages = c("plyr", "dplyr")) %dopar% {
dt <- baseDT
这给
> length(unique(out$random)) # expectation: 6000
[1] 6000
符合预期。
谁知道如何在下面的代码中让所有的随机数都不同?例如。使用 doRNG
包? 我不关心再现性。
编辑: 接受纯属偶然的重复。
rm(list = ls())
set.seed(666)
cat("4")
library(plyr)
library(dplyr)
library(doRNG)
# ====== Data Preparation ======
dt = data.frame(id = 1:10,
part = rep("dt",10),
HG = c(1,3,6,NA,NA,2,NA,NA,NA,NA),
random = NA)
# ====== Set Parallel Computing ======
library(foreach)
library(doParallel)
cl = makeCluster(3, outfile = "")
registerDoParallel(cl)
# ====== SIMULATION ======
nsim = 1000 # number of simulations
iterChunk = 100 # split nsim into this many chunks
out = data.frame() # prepare output DF
for(iter in 1:ceiling(nsim/iterChunk)){
strt = Sys.time()
out_iter =
foreach(i = 1:iterChunk, .combine = rbind, .multicombine = TRUE, .maxcombine = 100000, .inorder = FALSE, .verbose = FALSE,
.packages = c("plyr", "dplyr")) %dopar% {
# simulation number
id_sim = iterChunk * (iter - 1) + i
## Generate random numbers
tmp_sim = is.na(dt$HG) # no results yet
dt$random[tmp_sim] = runif(sum(tmp_sim))
dt$HG[tmp_sim] = 3
# Save Results
dt$id_sim = id_sim
dt$iter = iter
dt$i = i
print(Sys.time())
return(dt)
}#i;sim_forcycle
out = rbind.data.frame(out,subset(out_iter, !is.na(random)))
fnsh = Sys.time()
cat(" [",iter,"] ",fnsh - strt, sep = "")
}#iter
# ====== Stop Parallel Computing ======
stopCluster(cl)
# ====== Distinct Random Numbers ======
length(unique(out$random)) # expectation: 6000
我已经为此苦苦挣扎了 2 天。我 asked this question 早些时候只有关于随机数的一般回应。
在这里我想寻求一个解决方案(如果有人知道的话)如何设置 doRNG
包选项(或类似包),使 所有随机数都不同。遍历所有循环。
我已经尝试了很多 doRNG 设置,但仍然无法正常工作。在两台不同的计算机上尝试了 R 版本 3.5.3 和 3.6.3。
更新 与@Limey
讨论后代码的目的是模拟足球比赛。由于模拟很大,我使用 iterChunk
将模拟“拆分”为可管理的部分,并且在每个 iter
之后将数据发送到 PostgreSQL 数据库,这样模拟就不会使 RAM 过载。一些比赛已经有了真实世界的结果,并填写了 HG
(主场进球)。我想模拟其余的。
将iterChunk
设置为1
时一切正常。增加 iterChunk
会导致在 iter
内生成相同的数字。例如,当我将 nsim
设置为 100
并将 iterChunk
设置为 10
时。 (所有比赛模拟100次,10次循环10次)。我期望 600 个随机数(每个匹配在所有循环中独立模拟)。但是我只得到 180 - 按照逻辑:3 个核心 * 6 个匹配项 * 10 个 iterChunks。)使用 2 个工作人员我得到 120 个不同的随机数 (2 * 6 * 10)
此外:排除 dt$HG[tmp_sim] = 3
无论设置如何,我都会得到不同的随机数。
为了理解问题,我建议:
- 运行 代码原样。 (可能设置
nsim
为100
和iterChunk
为10
)你会得到 180 个不同的随机数。使用较少数量的 nsim 和 iterChunk,事情可能会按预期工作。 - 注释掉
dt$HG[tmp_sim] = 3
。 您将获得 6000 个不同的随机数(如果更改nsim
和iterChunk
则为 600 个)
第二步中的代码分配主队进球数。它看起来像某种我无法克服的错误。即使有人得到相同结果但不知道为什么的信息也会有所帮助 - 它会减轻我自己愚蠢的负担。
谢谢,我非常感谢任何努力。
修改“doParallel入门”中的“Hello World”示例vignette生成随机数,我想出了:
library(doParallel)
cl <- makeCluster(2)
registerDoParallel(cl)
myFunc <- function(n) {runif(n)}
foreach(i=1:3) %dopar% myFunc(10)
[[1]]
[1] 0.18492375 0.13388278 0.65455450 0.93093066 0.41157625 0.89479764 0.14736529 0.47935995 0.03062963 0.16110714
[[2]]
[1] 0.89245145 0.20980791 0.83828019 0.04411547 0.38184303 0.48110619 0.51509058 0.93732055 0.40159834 0.81414140
[[3]]
[1] 0.74393129 0.66999730 0.44411989 0.85040773 0.80224527 0.72483644 0.64566262 0.22546420 0.14526819 0.05931329
建议跨线程获取随机数很简单。事实上,doRNG reference manual 第 2 页和第 3 页的示例说明了同样的事情。
事实上,如果我理解正确的话,doRNG
的目的恰恰与您想要的相反:使随机进程可跨线程重现。
当然,这并不能保证所有线程中的所有数字都不同。但这使得复制的可能性很小。保证没有重复意味着过程中一定程度的确定性:完全随机的过程可能偶然产生重复。
更新 继我们在评论中的对话之后...
我们确定问题出在您的程序逻辑上,而不是并行化本身。所以我们需要重新关注这个问题:你想做什么。恐怕我一点也不清楚。所以那意味着我们需要简化。
我将 nsim
设置为 5
,将 iterChunk
设置为 1
。我得到 5 个数据框,看起来像
id part HG random id_sim iter i
1 1 dt 1 NA 1 1 1
2 2 dt 3 NA 1 1 1
3 3 dt 6 NA 1 1 1
4 4 dt 3 0.6919744 1 1 1
5 5 dt 3 0.5413398 1 1 1
6 6 dt 2 NA 1 1 1
7 7 dt 3 0.3983175 1 1 1
8 8 dt 3 0.3342174 1 1 1
9 9 dt 3 0.6126020 1 1 1
10 10 dt 3 0.4185468 1 1 1
在每个中,id_sim
和iter
的值总是相同的,并且运行从第一个数据帧中的1
到5
在第五。 i
对于所有数据框中的所有行都是 1
。 random
中的值似乎是随机的,并且在数据帧之间有所不同。但是 NA
在每个数据框中都位于相同的位置:第 1、2、3 和 6 行。 HG
的值如上所示所有五个数据帧。
这是您所期望的吗?如果没有,你期望什么?鉴于我们知道问题不是并行化,您需要向我们提供更多信息。
更新 2
你知道Arduan吗?他们在周末发布了一个相关问题...
我不会告诉你你的代码有什么问题。我将向您展示我将如何解决您的问题。如果没有别的,我希望你会同意它更具可读性。
所以,我们正在模拟一些足球比赛。我假设它是一种联赛形式,并以英超联赛为例。首先生成单个赛季的赛程表。
library(tidyverse)
teams <- c("Arsenal", "Aston Villa", "Bournemouth", "Brighton & Hove Albion",
"Burnley", "Chelsea", "Crystal Palace", "Everton", "Leicester City",
"Liverpool", "Manchester City", "Manchester United", "Newcastle United",
"Norwich City", "Sheffield United", "Southampton", "Tottenham Hotspur",
"Watford", "West Ham United", "Wolverhampton Wanderers")
fixtures <- tibble(HomeTeam=teams, AwayTeam=teams) %>%
complete(HomeTeam, AwayTeam) %>%
filter(HomeTeam != AwayTeam) # A team can't play itself
fixtures %>% head(5)
# A tibble: 5 x 2
HomeTeam AwayTeam
<chr> <chr>
1 Arsenal Aston Villa
2 Arsenal Bournemouth
3 Arsenal Brighton & Hove Albion
4 Arsenal Burnley
5 Arsenal Chelsea
假设我们知道一些结果。以昨天的比赛为例
knownResults <- tribble(~HomeTeam, ~AwayTeam, ~HomeGoals, ~AwayGoals,
"Burnley", "Sheffield United", 1, 1,
"Newcastle United", "West Ham United", 2, 2,
"Liverpool", "Aston Villa", 2, 0,
"Southampton", "Manchester City", 1, 0)
resultsSoFar <- fixtures %>%
left_join(knownResults, by=c("HomeTeam", "AwayTeam"))
resultsSoFar %>% filter(!is.na(HomeGoals))
# A tibble: 4 x 4
HomeTeam AwayTeam HomeGoals AwayGoals
<chr> <chr> <dbl> <dbl>
1 Burnley Sheffield United 1 1
2 Liverpool Aston Villa 2 0
3 Newcastle United West Ham United 2 2
4 Southampton Manchester City 1 0
现在一些实用函数。您当然可以将它们结合起来,但我认为将它们分开会更清楚,这样您就可以准确地看到每个人在做什么。
首先,模拟所有未知比赛结果的功能。您如何模拟分数的细节完全是任意的。我假设主队平均每场进 1.5 个球,客队平均每场进 1.2 个球。稍后,我将使用它来一次性模拟多个季节,因此我将添加一个变量(Iteration
)来索引季节。
simulateResults <- function(i=NA, data) {
n <- nrow(data)
data %>%
add_column(Iteration=i, .before=1) %>%
mutate(
# Give the home team a slight advantage
HomeGoals=ifelse(is.na(HomeGoals), floor(rexp(n, rate=1/1.5)), HomeGoals),
AwayGoals=ifelse(is.na(AwayGoals), floor(rexp(n, rate=1/1.2)), AwayGoals)
)
}
使用它,并检查我们是否没有覆盖已知结果:
simulateResults(1, resultsSoFar) %>% filter(HomeTeam=="Burnley", AwayTeam=="Sheffield United")
# A tibble: 1 x 5
Iteration HomeTeam AwayTeam HomeGoals AwayGoals
<dbl> <chr> <chr> <dbl> <dbl>
1 1 Burnley Sheffield United 1 1
我要并行化整个模拟,所以现在让我们使用一个函数来模拟大量模拟。同样,创建一个索引列来标识块。
simulateChunk <- function(chunkID=NA, n) {
bind_rows(lapply(1:n, simulateResults, data=resultsSoFar)) %>%
add_column(Chunk=chunkID, .before=1)
}
simulateChunk(chunkID=1, n=3)
# A tibble: 1,140 x 6
Chunk Iteration HomeTeam AwayTeam HomeGoals AwayGoals
<dbl> <int> <chr> <chr> <dbl> <dbl>
1 1 1 Arsenal Aston Villa 2 0
2 1 1 Arsenal Bournemouth 0 0
3 1 1 Arsenal Brighton & Hove Albion 2 0
4 1 1 Arsenal Burnley 2 0
5 1 1 Arsenal Chelsea 1 0
6 1 1 Arsenal Crystal Palace 0 0
7 1 1 Arsenal Everton 2 3
8 1 1 Arsenal Leicester City 2 0
9 1 1 Arsenal Liverpool 0 1
10 1 1 Arsenal Manchester City 4 0
好的。现在我已准备好进行主要的模拟工作。我将 运行 10 个 100 个模拟 eash 块,总共给出 1000 个模拟季节,和你一样。
library(doParallel)
cl <- makeCluster(3)
registerDoParallel(cl)
chunkSize <- 100
nChunks <- 10
startedAt <- Sys.time()
x <- bind_rows(foreach(i=1:nChunks, .packages=c("tidyverse")) %dopar% simulateChunk(i, n=chunkSize))
finishedAt <- Sys.time()
print(finishedAt - startedAt)
Time difference of 6.772928 secs
stopCluster(cl)
> x
# A tibble: 380,000 x 6
Chunk Iteration HomeTeam AwayTeam HomeGoals AwayGoals
<int> <int> <chr> <chr> <dbl> <dbl>
1 1 1 Arsenal Aston Villa 2 0
2 1 1 Arsenal Bournemouth 3 1
3 1 1 Arsenal Brighton & Hove Albion 0 1
4 1 1 Arsenal Burnley 3 0
5 1 1 Arsenal Chelsea 1 0
6 1 1 Arsenal Crystal Palace 0 0
7 1 1 Arsenal Everton 1 2
8 1 1 Arsenal Leicester City 0 0
9 1 1 Arsenal Liverpool 0 0
10 1 1 Arsenal Manchester City 0 0
让我们检查一下我得到了合理的结果。作为基本检查,我将查看阿森纳与阿斯顿维拉的比赛结果:
x %>%
filter(HomeTeam == "Arsenal", AwayTeam=="Aston Villa") %>%
group_by(HomeGoals, AwayGoals) %>%
summarise(N=n(), .groups="drop") %>%
pivot_wider(
values_from="N", names_prefix="AwayGoals",
names_sep="", names_from=AwayGoals
)
# A tibble: 8 x 10
HomeGoals AwayGoals0 AwayGoals1 AwayGoals2 AwayGoals3 AwayGoals4 AwayGoals5 AwayGoals6 AwayGoals8 AwayGoals7
<dbl> <int> <int> <int> <int> <int> <int> <int> <int> <int>
1 0 299 129 57 19 12 7 NA NA NA
2 1 135 63 25 6 4 4 1 2 NA
3 2 75 21 12 9 4 1 NA NA 1
4 3 30 13 10 1 NA NA NA NA NA
5 4 21 7 1 1 NA NA NA NA NA
6 5 11 2 1 NA 2 NA NA NA NA
7 6 4 2 2 NA NA NA NA NA NA
8 7 4 1 1 NA NA NA NA NA NA
这看起来很合理。现在确认具有已知结果的匹配没有变化。例如:
x %>%
filter(HomeTeam == "Liverpool", AwayTeam=="Aston Villa") %>%
group_by(HomeGoals, AwayGoals) %>%
summarise(N=n(), .groups="drop") %>%
pivot_wider(values_from="N", names_prefix="AwayGoals", names_sep="", names_from=AwayGoals)
HomeGoals AwayGoals0
<dbl> <int>
1 2 1000
一切顺利。
所以,这是 23 个语句来生成固定装置、考虑已知结果、模拟剩余的比赛并进行一些基本的健全性检查。如果必须的话,我可以很容易地将它减少到 20 个语句以下。这比您仅用于尝试模拟未知结果的数量少了大约三分之一。 [实际模拟不到 10 条语句。]我认为我的方法更容易理解:通过使用 tidy 动词,代码几乎是自我记录的。
我在洗澡的时候意识到了 OP 代码的问题所在。这很简单,回想起来也很明显:所有循环和并行进程都在同一个对象上工作——dt
数据框。所以他们不断地覆盖每个所做的更改,并且在外循环结束时,您只需要完成上一个循环所做更改的多个副本。解决方案同样简单:处理 dt
数据框的副本。
为了尽量减少更改,我将 dt
重命名为 baseDT
# ====== Data Preparation ======
baseDT = data.frame(id = 1:10,
part = rep("dt",10),
HG = c(1,3,6,NA,NA,2,NA,NA,NA,NA),
random = NA)
然后在 foreach
循环
out_iter = foreach(i = 1:iterChunk,
.combine = rbind, .multicombine = TRUE, .maxcombine = 100000,
.inorder = FALSE, .verbose = FALSE,
.packages = c("plyr", "dplyr")) %dopar% {
dt <- baseDT
这给
> length(unique(out$random)) # expectation: 6000
[1] 6000
符合预期。