模拟不平衡的集群数据

Simulate unbalanced clustered data

我想模拟一些不平衡的聚类数据。聚类数为 20,平均观察数为 30。但是,我想为每个聚类创建一个不平衡的聚类数据,其中观察值比指定的多 10%(即 33 而不是 30)。然后我想随机排除适当数量的观察值(即 60)以达到每个集群指定的平均观察值数量(即 30)。在每个聚类中排除观察值的概率不一致(即,一些聚类没有删除案例,而其他聚类则排除了更多)。所以最后我总共还有600个观察值。任何人都知道如何在 R 中实现这一点?这是一个较小的示例数据集。虽然每个簇的观察数不符合上面指定的条件,但我只是用它来传达我的想法。

> y <- rnorm(20)
> x <- rnorm(20)
> z <- rep(1:5, 4)
> w <- rep(1:4, each=5)
> df <- data.frame(id=z,cluster=w,x=x,y=y) #this is a balanced dataset
> df
   id cluster           x           y
1   1       1  0.30003855  0.65325768
2   2       1 -1.00563626 -0.12270866
3   3       1  0.01925927 -0.41367651
4   4       1 -1.07742065 -2.64314895
5   5       1  0.71270333 -0.09294102
6   1       2  1.08477509  0.43028470
7   2       2 -2.22498770  0.53539884
8   3       2  1.23569346 -0.55527835
9   4       2 -1.24104450  1.77950291
10  5       2  0.45476927  0.28642442
11  1       3  0.65990264  0.12631586
12  2       3 -0.19988983  1.27226678
13  3       3 -0.64511396 -0.71846622
14  4       3  0.16532102 -0.45033862
15  5       3  0.43881870  2.39745248
16  1       4  0.88330282  0.01112919
17  2       4 -2.05233698  1.63356842
18  3       4 -1.63637927 -1.43850664
19  4       4  1.43040234 -0.19051680
20  5       4  1.04662885  0.37842390

随机增删一些数据后,不平衡的数据变成了这样:

            id   cluster   x     y
       1     1       1  0.895 -0.659 
       2     2       1 -0.160 -0.366 
       3     1       2 -0.528 -0.294 
       4     2       2 -0.919  0.362 
       5     3       2 -0.901 -0.467 
       6     1       3  0.275  0.134 
       7     2       3  0.423  0.534 
       8     3       3  0.929 -0.953 
       9     4       3  1.67   0.668 
      10     5       3  0.286  0.0872
      11     1       4 -0.373 -0.109 
      12     2       4  0.289  0.299 
      13     3       4 -1.43  -0.677 
      14     4       4 -0.884  1.70  
      15     5       4  1.12   0.386 
      16     1       5 -0.723  0.247 
      17     2       5  0.463 -2.59  
      18     3       5  0.234  0.893 
      19     4       5 -0.313 -1.96  
      20     5       5  0.848 -0.0613

编辑 这部分问题已解决(致谢 jay.sf)。接下来,我想重复此过程 1000 次,并对每个生成的数据集进行 运行 回归。但是,我不想 运行 对整个数据集进行回归,而是对随机选择的某些选定集群进行回归(可以使用此函数:df[unlist(cluster[sample.int(k, k, replace = TRUE)], use.names = TRUE), ]。最后,我想从这 1000 个回归中得到置信区间。如何进行?

ncl 为所需的簇数。我们可能会生成一个采样 space S,它是围绕每个聚类 mnobs 的平均观察值的一系列公差 tol。我们从中抽取 repeat 大小为 1 的随机样本以获得集群列表 CL。如果集群 lengths 的总和满足 ncl*mnobs 我们 break 循环,向集群添加随机数据并 rbind 结果。

FUN <- function(ncl=20, mnobs=30, tol=.1) {
  S <- do.call(seq.int, as.list(mnobs*(1 + tol*c(-1, 1))))
  repeat({
    CL <- lapply(1:ncl, function(x) rep(x, sample(S, 1, replace=T)))
    if (sum(lengths(CL)) == ncl*mnobs) break
  })
  L <- lapply(seq.int(CL), function(i) {
    id <- seq.int(CL[[i]])
    cbind(id, cluster=i, 
          matrix(rnorm(max(id)*2),,2, dimnames=list(NULL, c("x", "y"))))
  })
  do.call(rbind.data.frame, L)
}

用法

set.seed(42)
res <- FUN()  ## using defined `arg` defaults
dim(res)
# [1] 600   4

(res.tab <- table(res$cluster))
#  1  2  3  4  5  6  7  8  9 10 11 12 13 14 15 16 17 18 19 20 
# 29 29 31 31 30 32 31 30 32 28 28 27 28 31 32 33 31 30 27 30

table(res.tab)
# 27 28 29 30 31 32 33 
#  2  3  2  4  5  3  1

sapply(c("mean", "sd"), function(x) do.call(x, list(res.tab)))
#      mean        sd 
# 30.000000  1.747178 

可显示示例

set.seed(42)
FUN(4, 5, tol=.3)  ## tol needs to be adjusted for smaller samples
#    id cluster           x          y
# 1   1       1  1.51152200 -0.0627141
# 2   2       1 -0.09465904  1.3048697
# 3   3       1  2.01842371  2.2866454
# 4   1       2 -1.38886070 -2.4404669
# 5   2       2 -0.27878877  1.3201133
# 6   3       2 -0.13332134 -0.3066386
# 7   4       2  0.63595040 -1.7813084
# 8   5       2 -0.28425292 -0.1719174
# 9   6       2 -2.65645542  1.2146747
# 10  1       3  1.89519346 -0.6399949
# 11  2       3 -0.43046913  0.4554501
# 12  3       3 -0.25726938  0.7048373
# 13  4       3 -1.76316309  1.0351035
# 14  5       3  0.46009735 -0.6089264
# 15  1       4  0.50495512  0.2059986
# 16  2       4 -1.71700868 -0.3610573
# 17  3       4 -0.78445901  0.7581632
# 18  4       4 -0.85090759 -0.7267048
# 19  5       4 -2.41420765 -1.3682810
# 20  6       4  0.03612261  0.4328180

根据 Ben Bolker 的要求,我发布了我的解决方案,但请参阅 jay.sf 以获得更通用的答案。

#First create an oversampled dataset: 
  y <- rnorm(24)
  x <- rnorm(24)
  z <- rep(1:6, 4)
  w <- rep(1:4, each=6)
  df <- data.frame(id=z,cluster=w,x=x,y=y)
#Then just slice_sample to arrive at the sample size as desired
  df %>% slice_sample(n = 20) %>%
  arrange(cluster)
#Or just use base R
  a <- df[sample(nrow(df), 20), ]  
  df2 <- a[order(a$cluster), ]