R中的条件随机样本
Conditional Random Sample in R
我想知道解决这个问题的最佳方法是什么。本质上,我想生成 20 个样本,这些样本加起来是 100,但也是 (x1+x2>20)。我正在努力获得快速高效的东西。我意识到我可以过滤掉不符合此条件的行,但如果我生成 10,000 个而不是 20 个,效率不高。
代码如下:
n = 20
x1 = sample(0:100,n,replace = TRUE)
x2 = sample(0:100,n,replace = TRUE)
x3 = sample(0:100,n,replace = TRUE)
index = (x1+x2+x3)>100
G=(x1+x2)>20
while(sum(index)>0&&sum(G)>0){
x1[index&&G] = sample(0:100,n,replace = TRUE)
x2[index&&G] = sample(0:100,n,replace = TRUE)
x3[index&&G] = sample(0:100,n,replace = TRUE)
index =(x1+x2+x3)>100
G=(x1+x2)>20
}
x4=rep(100,n)-x1-x2-x3
df <- data.frame(x1,x2,x3,x4)
提前致谢。
要生成一个这样的向量,您可以这样做:
# generate x1+x2
x1_plus_x2 <- sample.int(79,1) + 20
# generate x1 and x2
x1x2 <- rmultinom(1, x1_plus_x2, c(1,1))
# generate x3 and x4
x3x4 <- rmultinom(1, 100-x1_plus_x2, c(1,1))
# generated x1,x2,x3,x4
x <- c(x1x2, x3x4)
您可以循环生成 n
个样本。您可以通过在开头生成 x1+x2
的 n
值来提高速度:
n <- 20
# matrix to store the simulations
x <- matrix(NA_integer_, nrow=n, ncol=4)
# generate all the x1+x2's
x1_plus_x2 <- sample.int(79, n, replace=TRUE) + 20
# loop
for(j in 1:n){
# generate x1 and x2
x1x2 <- rmultinom(1, x1_plus_x2[j], c(1,1))
# generate x3 and x4
x3x4 <- rmultinom(1, 100-x1_plus_x2[j], c(1,1))
#
x[j,] <- c(x1x2,x3x4)
}
> rowSums(x)
[1] 100 100 100 100 100 100 100 100 100 100 100 100 100 100 100 100 100 100 100 100
另一种可能:
选择序列 0:100
的三个 中断点 。
然后在这些中断之间生成 x1、x2、x3 和 x4。如果x1 + x2
小于20,那么x3 + x4
大于20,所以我们可以交换它们。
generate_four_numbers <- function(from = 0, to = 100) {
breaks <- sort(sample(seq(from, to), 3 ,replace = TRUE))
x1 <- breaks[1]
x2 <- breaks[2] - breaks[1]
x3 <- breaks[3] - breaks[2]
x4 <- 100 - breaks[3]
if (x1 + x2 <= 20) {
return(data.frame(x1 = x4, x2 = x3, x3 = x2, x4 = x1)
}
data.frame(x1, x2, x3, x4)
}
res <- do.call(rbind, lapply(1:10000, function(x) generate_four_numbers()))
table(rowSums(res)) # all at 100
length(which(res$x1 + res$x2 > 20)) / nrow(res) # 100 % acceptable
这是一种在 0:n 范围内选择 k 个数字的无偏方法,其总和为 n。它基于 stars and bars encoding:
#picks k random numbers in range 0:n which sum to n:
pick <- function(k,n){
m <- n + k - 1 #number of stars and bars
bars <- sort(sample(1:m,k-1)) #positions of the bars
c(bars,m+1)-c(0,bars)-1
}
这会生成一个示例,返回一个向量。正如@Guillaume Devailly 在他们的回答中观察到的那样,大多数样本将满足前 2 个数字之和的附加约束,因此您可以过滤掉那些不满足的。
请注意,如果您想要 1:100 范围内的 4 个数字,总和为 100,您可以只使用 1 + pick(4,96)
。
要对前两个数字强制执行约束:
pick.sample <- function(){
while(TRUE){
x <- pick(4,100)
if(sum(x[1:2]) >20) return(x)
}
}
然后
df <- data.frame(t(replicate(10000,pick.sample())))
将创建一个 10,000 行的数据框,其中每一行都是一个满足约束的样本。
你可以很容易地暴力破解这个,如下所示
#####
# Brute force solution
set.seed(28550697)
n <- 100000L
time. <- proc.time() # to measure time difference
brute <- t(replicate(
n, {
repeat {
xs <- sample.int(101L, 4, replace = TRUE) - 1L
if(xs[1] + xs[2] > 20L && sum(xs) == 100L)
break
}
xs
}))
proc.time() - time. # time taken
#R user system elapsed
#R 192.76 0.13 196.74
# check result
stopifnot(
all(rowSums(brute) == 100L),
all(brute %in% 0:100),
all(brute[, 1] + brute[, 2] > 20L))
# only the first two columns should be able to take values in 0:100
apply(brute, 2, range)
#R [,1] [,2] [,3] [,4]
#R [1,] 0 0 0 0
#R [2,] 99 99 79 79
以上我在合理的时间内模拟了 100,000 对(比你要求的多 10 倍)。你当然可以用更聪明的方法做得更好,但这里很明显分布是正确的。
我想知道解决这个问题的最佳方法是什么。本质上,我想生成 20 个样本,这些样本加起来是 100,但也是 (x1+x2>20)。我正在努力获得快速高效的东西。我意识到我可以过滤掉不符合此条件的行,但如果我生成 10,000 个而不是 20 个,效率不高。
代码如下:
n = 20
x1 = sample(0:100,n,replace = TRUE)
x2 = sample(0:100,n,replace = TRUE)
x3 = sample(0:100,n,replace = TRUE)
index = (x1+x2+x3)>100
G=(x1+x2)>20
while(sum(index)>0&&sum(G)>0){
x1[index&&G] = sample(0:100,n,replace = TRUE)
x2[index&&G] = sample(0:100,n,replace = TRUE)
x3[index&&G] = sample(0:100,n,replace = TRUE)
index =(x1+x2+x3)>100
G=(x1+x2)>20
}
x4=rep(100,n)-x1-x2-x3
df <- data.frame(x1,x2,x3,x4)
提前致谢。
要生成一个这样的向量,您可以这样做:
# generate x1+x2
x1_plus_x2 <- sample.int(79,1) + 20
# generate x1 and x2
x1x2 <- rmultinom(1, x1_plus_x2, c(1,1))
# generate x3 and x4
x3x4 <- rmultinom(1, 100-x1_plus_x2, c(1,1))
# generated x1,x2,x3,x4
x <- c(x1x2, x3x4)
您可以循环生成 n
个样本。您可以通过在开头生成 x1+x2
的 n
值来提高速度:
n <- 20
# matrix to store the simulations
x <- matrix(NA_integer_, nrow=n, ncol=4)
# generate all the x1+x2's
x1_plus_x2 <- sample.int(79, n, replace=TRUE) + 20
# loop
for(j in 1:n){
# generate x1 and x2
x1x2 <- rmultinom(1, x1_plus_x2[j], c(1,1))
# generate x3 and x4
x3x4 <- rmultinom(1, 100-x1_plus_x2[j], c(1,1))
#
x[j,] <- c(x1x2,x3x4)
}
> rowSums(x)
[1] 100 100 100 100 100 100 100 100 100 100 100 100 100 100 100 100 100 100 100 100
另一种可能:
选择序列 0:100
的三个 中断点 。
然后在这些中断之间生成 x1、x2、x3 和 x4。如果x1 + x2
小于20,那么x3 + x4
大于20,所以我们可以交换它们。
generate_four_numbers <- function(from = 0, to = 100) {
breaks <- sort(sample(seq(from, to), 3 ,replace = TRUE))
x1 <- breaks[1]
x2 <- breaks[2] - breaks[1]
x3 <- breaks[3] - breaks[2]
x4 <- 100 - breaks[3]
if (x1 + x2 <= 20) {
return(data.frame(x1 = x4, x2 = x3, x3 = x2, x4 = x1)
}
data.frame(x1, x2, x3, x4)
}
res <- do.call(rbind, lapply(1:10000, function(x) generate_four_numbers()))
table(rowSums(res)) # all at 100
length(which(res$x1 + res$x2 > 20)) / nrow(res) # 100 % acceptable
这是一种在 0:n 范围内选择 k 个数字的无偏方法,其总和为 n。它基于 stars and bars encoding:
#picks k random numbers in range 0:n which sum to n:
pick <- function(k,n){
m <- n + k - 1 #number of stars and bars
bars <- sort(sample(1:m,k-1)) #positions of the bars
c(bars,m+1)-c(0,bars)-1
}
这会生成一个示例,返回一个向量。正如@Guillaume Devailly 在他们的回答中观察到的那样,大多数样本将满足前 2 个数字之和的附加约束,因此您可以过滤掉那些不满足的。
请注意,如果您想要 1:100 范围内的 4 个数字,总和为 100,您可以只使用 1 + pick(4,96)
。
要对前两个数字强制执行约束:
pick.sample <- function(){
while(TRUE){
x <- pick(4,100)
if(sum(x[1:2]) >20) return(x)
}
}
然后
df <- data.frame(t(replicate(10000,pick.sample())))
将创建一个 10,000 行的数据框,其中每一行都是一个满足约束的样本。
你可以很容易地暴力破解这个,如下所示
#####
# Brute force solution
set.seed(28550697)
n <- 100000L
time. <- proc.time() # to measure time difference
brute <- t(replicate(
n, {
repeat {
xs <- sample.int(101L, 4, replace = TRUE) - 1L
if(xs[1] + xs[2] > 20L && sum(xs) == 100L)
break
}
xs
}))
proc.time() - time. # time taken
#R user system elapsed
#R 192.76 0.13 196.74
# check result
stopifnot(
all(rowSums(brute) == 100L),
all(brute %in% 0:100),
all(brute[, 1] + brute[, 2] > 20L))
# only the first two columns should be able to take values in 0:100
apply(brute, 2, range)
#R [,1] [,2] [,3] [,4]
#R [1,] 0 0 0 0
#R [2,] 99 99 79 79
以上我在合理的时间内模拟了 100,000 对(比你要求的多 10 倍)。你当然可以用更聪明的方法做得更好,但这里很明显分布是正确的。