如何将区间随机划分为不重叠、间隔相等的区间

How to randomly divide interval into non overlapping, spaced bins of equal length

我有一个间隔,例如从 1 到 671。我想将它分成 5 个随机的非重叠 bin,长度为 50,但间隔最小为 51。

interval <- 1:671  (example, it does not need to be 671)

结果(这是一个例子,因为 bins 应该是随机的,但在间隔内,等长和间隔如定义):

bin1 <- 3:52
bin2 <- 103:152
bin3 <- 209:258
bin4 <- 425:474
bin5 <- 610:659

我希望输出是数据帧(bin、startOfbin、endOfbin),但其他类型如列表也可以。

我目前正在用 R 编写一个函数,它将在大量间隔中使用此采样,但我无法提出合理的解决方案。提前谢谢你。

我不知道这是否具有所需的随机性:

interval <- 1:671 

set.seed(42)

repeat { #rejection sampling
  int <- list(interval)
  s <- integer(5) * NA

  for (i in 1:5) {
    #sample an interval from the list
    sel <- sample(length(int), 1)
    isel <- int[[sel]]

    #sample start value
    s[[i]] <- sample(head(isel,-49), 1)

    #remove sampled values from interval
    sp <-
      split(isel, findInterval(isel, c(0, s[[i]], s[[i]] + 50, Inf)))
    if (s[[i]] > isel[1] &&
        s[[i]] < length(isel) - 49)
      sp <- sp[-2]
    else
      if (s[[i]] == isel[1])
        sp <- sp[-1]
    else
      if (s[[i]] == length(isel) - 49)
        sp <- head(sp,-1)
    sp <- sp[lengths(sp) >= 50]
    int <- c(sp, int[-sel])

    #break out of for loop 
    #if not enough intervals of sufficient length left
    if (length(int) < 1) break
  }
  if (!anyNA(s)) break
}

s
#[1] 321  74 245 170 441

library(ggplot2)
ggplot(data.frame(s = s, e = s + 49), aes(x = s, xend = e, y = 0, yend = 0)) +
  geom_segment(size = 3) +
  theme_minimal() +
  theme(axis.text.y = element_blank(),
        axis.ticks.y = element_blank(),
        panel.grid.major.y = element_blank()) +
  xlab("") + ylab("")

如果我对您的问题的理解正确,您需要间隔的 5 个部分,长度为 50,最小距离为 51。

所以你的随机性在于每个距离比 51 大多少。

这意味着您要计算 space 实际需要分配的数量。

intervalLength <- 671
nBins <- 5
binWidth <- 50
binMinDistance <- 51

spaceToDistribute <- intervalLength - (nBins * binWidth + (nBins - 1) * binMinDistance)

计算这个值的随机拆分

distances <- diff(floor(c(0, sort(runif(nBins))) * spaceToDistribute))

并构建您想要的 data.frame

startOfBin <- cumsum(distances) + (0:(nBins-1)) * 101
result <- data.frame(bin = 1:nBins, startOfBin = startOfBin, endOfBin = startOfBin + 49)

类似这样的方法可行:

set.seed(111)

n_bins <- 5
bl <- 50
spacing <- 51

start <- 1
end <- 671


end_int <- end - n_bins*bl - (n_bins-1)*spacing
first_bin_start <- sample(start:end_int, 1)
first_bin_end <- first_bin_start + bl
avail_spacing <- end - first_bin_end - (n_bins-1)*bl - (n_bins-1)*spacing

sp <- c()
for (i in 1:(n_bins-1)){
  end <- sample(1:avail_spacing, 1)
  sp <- c(sp, end)
  avail_spacing <- avail_spacing - end
}


bin_start <- c(first_bin_start, first_bin_start + cumsum(spacing + bl + sp))
bin_end <- bin_start + bl

df <- data.frame(bin = 1:n_bins,
                 bin_start = bin_start,
                 bin_end = bin_end)

df