R:如何在没有替换且没有连续相同值的情况下进行采样
R: how to sample without replacement AND without consecutive same values
我花了一天多的时间试图完成一件看起来很简单的事情。我必须创建 300 'random' 个序列,其中数字 1、2、3 和 4 都恰好出现 12 次,但同一个数字从未连续使用两次 'in a row'/
我最好的尝试(我猜)是:
用R抽取48个项目不放回,用rle测试是否有连续值,然后只使用不包含连续值的序列。问题:几乎没有满足这个条件的随机序列,所以需要很长时间。
让 R 创建没有连续值的序列(见代码)。
pop<-rep(1:4,12)
y=c()
while(length(y)!=48)
{
y= c(y,sample(pop,48-length(y),replace=F))
y=y[!c(FALSE, diff(y) == 0)]
}
问题:这会创建每个值的数量不同的序列。然后我尝试只使用那些每个值恰好有 12 个的序列,但这只会让我回到问题 1:需要永远。
一定有一些简单的方法可以做到这一点,对吧?非常感谢任何帮助!
也许使用 replicate()
和 repeat
循环会更快。这里有一个 3
序列的例子。看起来这需要大约。 300
1490 秒(未测试)。
set.seed(42)
seqc <- rep(1:4, each=12) # starting sequence
system.time(
res <- replicate(3, {
repeat {
seqcs <- sample(seqc, 48, replace=FALSE)
if (!any(diff(seqcs) == 0)) break
}
seqcs
})
)
# user system elapsed
# 14.88 0.00 14.90
res[1:10, ]
# [,1] [,2] [,3]
# [1,] 4 2 3
# [2,] 1 1 4
# [3,] 3 2 1
# [4,] 1 1 4
# [5,] 2 3 1
# [6,] 4 1 2
# [7,] 3 4 4
# [8,] 2 1 1
# [9,] 3 4 4
# [10,] 4 3 2
您可以取出连续的值并将它们放在不连续的地方。
unConsecutive <- function(x) {
repeat{
tt <- c(FALSE, diff(x)==0)
if(any(tt)) {
y <- x[which(tt)]
x <- x[which(!tt)]
i <- x != y[1]
i <- which(c(c(TRUE, diff(i)==0) & i,FALSE)
| c(FALSE, c(diff(i)==0, TRUE) & i))
if(length(i) > 0) {
i <- i[1]-1
x <- c(x[seq_len(i)], y, x[i+seq_len(length(x)-i)])
} else {
x <- c(x, y)
break
}
} else {break}
}
x
}
unConsecutive(c(1,1,2))
#[1] 1 2 1
unConsecutive(c(1,1,1))
#[1] 1 1 1
set.seed(7)
system.time(
res <- replicate(300, unConsecutive(sample(rep(1:4,12))))
)
# user system elapsed
# 0.058 0.011 0.069
all(apply(res, 2, table) == 12)
#[1] TRUE
all(apply(res, 2, diff) != 0)
#[1] TRUE
另一种选择是使用马尔可夫链蒙特卡洛方法随机交换 2 个数字,仅当 1) 我们不交换相同数字和 2) 没有 2 个相同数字相邻时才移动到新样本。为了处理相关样本,我们可以生成大量样本,然后随机生成 select 300 个:
v <- rep(1:4, 12)
l <- 48
nr <- 3e5
m <- matrix(0, nrow=nr, ncol=l)
count <- 0
while(count < nr) {
i <- sample(l, 2)
if (i[1L] != i[2L]) {
v[i] = v[i[2:1]]
if (!any(diff(v)==0)) {
count <- count + 1
m[count, ] <- v
} else {
v[i] = v[i[2:1]]
}
}
}
a <- m[sample(nr, 300),]
a
我花了一天多的时间试图完成一件看起来很简单的事情。我必须创建 300 'random' 个序列,其中数字 1、2、3 和 4 都恰好出现 12 次,但同一个数字从未连续使用两次 'in a row'/
我最好的尝试(我猜)是:
用R抽取48个项目不放回,用rle测试是否有连续值,然后只使用不包含连续值的序列。问题:几乎没有满足这个条件的随机序列,所以需要很长时间。
让 R 创建没有连续值的序列(见代码)。
pop<-rep(1:4,12)
y=c()
while(length(y)!=48)
{
y= c(y,sample(pop,48-length(y),replace=F))
y=y[!c(FALSE, diff(y) == 0)]
}
问题:这会创建每个值的数量不同的序列。然后我尝试只使用那些每个值恰好有 12 个的序列,但这只会让我回到问题 1:需要永远。
一定有一些简单的方法可以做到这一点,对吧?非常感谢任何帮助!
也许使用 replicate()
和 repeat
循环会更快。这里有一个 3
序列的例子。看起来这需要大约。 300
1490 秒(未测试)。
set.seed(42)
seqc <- rep(1:4, each=12) # starting sequence
system.time(
res <- replicate(3, {
repeat {
seqcs <- sample(seqc, 48, replace=FALSE)
if (!any(diff(seqcs) == 0)) break
}
seqcs
})
)
# user system elapsed
# 14.88 0.00 14.90
res[1:10, ]
# [,1] [,2] [,3]
# [1,] 4 2 3
# [2,] 1 1 4
# [3,] 3 2 1
# [4,] 1 1 4
# [5,] 2 3 1
# [6,] 4 1 2
# [7,] 3 4 4
# [8,] 2 1 1
# [9,] 3 4 4
# [10,] 4 3 2
您可以取出连续的值并将它们放在不连续的地方。
unConsecutive <- function(x) {
repeat{
tt <- c(FALSE, diff(x)==0)
if(any(tt)) {
y <- x[which(tt)]
x <- x[which(!tt)]
i <- x != y[1]
i <- which(c(c(TRUE, diff(i)==0) & i,FALSE)
| c(FALSE, c(diff(i)==0, TRUE) & i))
if(length(i) > 0) {
i <- i[1]-1
x <- c(x[seq_len(i)], y, x[i+seq_len(length(x)-i)])
} else {
x <- c(x, y)
break
}
} else {break}
}
x
}
unConsecutive(c(1,1,2))
#[1] 1 2 1
unConsecutive(c(1,1,1))
#[1] 1 1 1
set.seed(7)
system.time(
res <- replicate(300, unConsecutive(sample(rep(1:4,12))))
)
# user system elapsed
# 0.058 0.011 0.069
all(apply(res, 2, table) == 12)
#[1] TRUE
all(apply(res, 2, diff) != 0)
#[1] TRUE
另一种选择是使用马尔可夫链蒙特卡洛方法随机交换 2 个数字,仅当 1) 我们不交换相同数字和 2) 没有 2 个相同数字相邻时才移动到新样本。为了处理相关样本,我们可以生成大量样本,然后随机生成 select 300 个:
v <- rep(1:4, 12)
l <- 48
nr <- 3e5
m <- matrix(0, nrow=nr, ncol=l)
count <- 0
while(count < nr) {
i <- sample(l, 2)
if (i[1L] != i[2L]) {
v[i] = v[i[2:1]]
if (!any(diff(v)==0)) {
count <- count + 1
m[count, ] <- v
} else {
v[i] = v[i[2:1]]
}
}
}
a <- m[sample(nr, 300),]
a