在 R 中无重复地改组数据帧

Shuffling a dataframe in R with no repeats

有没有人知道如何在 R 中编写数据集随机播放脚本,这样如果我在数据框中有 25 个数字(5 行 x 5 列),并且我分别随机播放 25 次,每个数字都出现在每个位置恰好一次?

因此它不是完全随机的,至少在第一次洗牌之后不是,因为随着每次洗牌,任何数字的潜在位置都会减少。

谢谢!

我将在 3 x 3 数据集上演示解决方案。我要做的第一件事是将 data.frame 转换为矩阵,以便能够轻松应用排列。

假设我们有一个 3x3 矩阵:

set.seed(1)
m <- matrix(sample(1:100, 9), nrow = 3)
m
#>      [,1] [,2] [,3]
#> [1,]   68   34   14
#> [2,]   39   87   82
#> [3,]    1   43   59

然后每个洗牌可以通过数字 1 到 9 的排列来定义。

shuffle <- c(9, 4, 7, 1, 8, 3, 2, 5, 6)
matrix(m[shuffle], nrow = 3)
#>      [,1] [,2] [,3]
#> [1,]   59   68   39
#> [2,]   34   82   87
#> [3,]   14    1   43

因此我们的任务是生成 9 个这样的排列,其中每个数字在每个位置上只出现一次。例如。第一次洗牌c(9, 4, 7, 1, 8, 3, 2, 5, 6),我们不能c(9, 2, 7, 3, 8, 5, 4, 6, 1),因为9已经排在第一位,7排在第三位,8排在第五位。

基本上我们需要的是 9 x 9 latin square。幸好有这样的包:

library(magic)
#> Loading required package: abind
set.seed(1)
shuffles_matrix <- rlatin(9)
shuffles_matrix
#>       [,1] [,2] [,3] [,4] [,5] [,6] [,7] [,8] [,9]
#>  [1,]    6    5    4    2    3    9    8    1    7
#>  [2,]    4    2    7    6    9    8    1    3    5
#>  [3,]    8    3    1    5    2    7    9    4    6
#>  [4,]    5    1    9    7    6    2    4    8    3
#>  [5,]    3    6    5    1    8    4    7    9    2
#>  [6,]    9    7    8    3    1    6    5    2    4
#>  [7,]    7    9    3    4    5    1    2    6    8
#>  [8,]    2    8    6    9    4    5    3    7    1
#>  [9,]    1    4    2    8    7    3    6    5    9

现在我们可以将这个正方形的每一行视为我们原始 3x3 矩阵的洗牌:

shuffles <- split(shuffles_matrix, 1:9)
shuffles
#> $`1`
#> [1] 6 5 4 2 3 9 8 1 7
#> 
#> $`2`
#> [1] 4 2 7 6 9 8 1 3 5
#> 
#> $`3`
#> [1] 8 3 1 5 2 7 9 4 6
#> 
#> $`4`
#> [1] 5 1 9 7 6 2 4 8 3
#> 
#> $`5`
#> [1] 3 6 5 1 8 4 7 9 2
#> 
#> $`6`
#> [1] 9 7 8 3 1 6 5 2 4
#> 
#> $`7`
#> [1] 7 9 3 4 5 1 2 6 8
#> 
#> $`8`
#> [1] 2 8 6 9 4 5 3 7 1
#> 
#> $`9`
#> [1] 1 4 2 8 7 3 6 5 9

这就是我们将这些洗牌应用到矩阵的方式:

library(purrr)
shuffles %>% 
  map(~matrix(m[.], nrow = 3))
#> $`1`
#>      [,1] [,2] [,3]
#> [1,]   43   39   82
#> [2,]   87    1   68
#> [3,]   34   59   14
#> 
#> $`2`
#>      [,1] [,2] [,3]
#> [1,]   34   43   68
#> [2,]   39   59    1
#> [3,]   14   82   87
#> 
#> $`3`
#>      [,1] [,2] [,3]
#> [1,]   82   87   59
#> [2,]    1   39   34
#> [3,]   68   14   43
#> 
#> $`4`
#>      [,1] [,2] [,3]
#> [1,]   87   14   34
#> [2,]   68   43   82
#> [3,]   59   39    1
#> 
#> $`5`
#>      [,1] [,2] [,3]
#> [1,]    1   68   14
#> [2,]   43   82   59
#> [3,]   87   34   39
#> 
#> $`6`
#>      [,1] [,2] [,3]
#> [1,]   59    1   87
#> [2,]   14   68   39
#> [3,]   82   43   34
#> 
#> $`7`
#>      [,1] [,2] [,3]
#> [1,]   14   34   39
#> [2,]   59   87   43
#> [3,]    1   68   82
#> 
#> $`8`
#>      [,1] [,2] [,3]
#> [1,]   39   59    1
#> [2,]   82   34   14
#> [3,]   43   87   68
#> 
#> $`9`
#>      [,1] [,2] [,3]
#> [1,]   68   82   43
#> [2,]   34   14   87
#> [3,]   39    1   59

我认为 Iaroslav 的回答非常好。我使用了一些不同的函数来基本上做同样的事情,所以我想我会分享一些其他的代码。基本上我还创建了一个拉丁方阵,但我没有意识到这是名字。我用

做到了
roll <- function(x, i) {
  if (i==0) return(x)
  c(x[-(1:i)], x[1:i])
}
m <- sapply(0:24, function(i) roll(1:25, i))

这里我只用了数字1:25。它创建一个矩阵,其中每一行或每一列都是一组可用于排列您的值的索引。如果看起来太有序,还可以用另一个辅助函数

打乱矩阵的行列
shuffle_mat <- function(x, N=50, margin=c(1,2)) {
  mg <- sample(margin, N, replace=TRUE)
  n_row_swap = sum(mg==1)
  sr <- replicate(n_row_swap, sample.int(nrow(x), 2))
  for(i in 1:ncol(sr)) {
    x[sr[,i],]<-x[rev(sr[,i]),]
  }
  n_col_swap = sum(mg==2)
  sc <- replicate(n_col_swap, sample.int(ncol(x), 2))
  for(i in 1:ncol(sc)) {
    x[,sc[,i]]<-x[,rev(sc[,i])]
  }
  x
}    
rr <- shuffle_mat(m)

然后您可以再次将这些 rows/columns 中的每一个组成一个 5x5 矩阵。