I have (1) a reference table of ratings, and (2) a function which randomly generates results based on these ratings and updates the ratings based upon the generated result.
Although there are easier solutions to the reproducible example below, the intended application is to simulate results between opponents based upon their Elo ratings, with ratings being updated after each round in order to run the simulations 'hot'.
Here, I have a reference table of ratings ref
and use the function genResult
to generate a random result and update the reference table using global assignment.
set.seed(123)
ref <- data.frame(id = LETTERS[1:5],
rating = round(runif(5, 100, 200)))
genResult <- function(ref) {
id_i <- LETTERS[floor(runif(1, 1, 5))]
score_i <- round(rnorm(1, 0, 20))
ref[ref$id == id_i,]$rating <- ref[ref$id == id_i,]$rating + score_i
result_i <- data.frame(id = id_i, score = score_i)
# assign('ref', ref, envir=.GlobalEnv)
ref <<- ref
return(list(result_i, ref))
}
Replicating this function twice, we can see ref
is updated as expected.
replicate(2, genResult(ref), simplify = F)
Returning this, where we can see reference table is updated in each of the two iterations.
[[1]]
[[1]][[1]]
id score
1 A 1
[[1]][[2]]
id rating
1 A 130
2 B 179
3 C 141
4 D 188
5 E 194
[[2]]
[[2]][[1]]
id score
1 C -2
[[2]][[2]]
id rating
1 A 130
2 B 179
3 C 139
4 D 188
5 E 194
Now let's say I want to replicate the above (replicated) function; simulating 3 separate instances of 5 results with dynamically updated ratings and outputting only the results. I make the reference table ref
again and define a similar function which uses global assignment:
set.seed(123)
ref <- data.frame(id = LETTERS[1:5],
rating = round(runif(5, 100, 200)))
genResult2 <- function(ref) {
id_i <- LETTERS[floor(runif(1, 1, 5))]
score_i <- round(rnorm(1, 0, 20))
ref[ref$id == id_i,]$rating <- ref[ref$id == id_i,]$rating + score_i
result_i <- data.frame(id = id_i, score = score_i)
ref <<- ref
return(result_i)
}
Then use an apply
loop and collapse the list of results to a dataframe:
lapply(1:3, function(i) {
ref_i <- ref
replicate(5, genResult2(ref_i), simplify = F) %>%
plyr::rbind.fill() %>%
mutate(i)
}) %>%
plyr::rbind.fill()
Returning:
id score i
1 A 1 1
2 C -2 1
3 B 9 1
4 A 26 1
5 A -9 1
6 D 10 2
7 D 8 2
8 C 5 2
9 A 36 2
10 C 17 2
11 B 14 3
12 B -15 3
13 B -4 3
14 A -22 3
15 B -13 3
Now this seems to work as expected, but (i) it feels really ugly, and (ii) I've read countless times that global assignment can and will cause unexpected injury.
Can anyone suggest a better solution?