如何对数据框进行行列随机化(或排列)?

121

我有一个数据框(df1),长这样。

     f1   f2   f3   f4   f5
d1   1    0    1    1    1  
d2   1    0    0    1    0
d3   0    0    0    1    1
d4   0    1    0    0    1

d1至d4列是行名称,f1至f5行是列名称。

使用sample(df1)函数,可以得到一个计算1的新数据框。因此,整个数据框中的1的数量保持不变,但每一行或每一列中的1的数量并未保持不变。

是否可以按行或列进行随机化?

我想要对df1按列逐列进行随机化,即每一列中1的数量保持不变,每一列至少需要发生一次更改。例如,我可以得到一个类似于以下内容的随机化的df2:(注意到每一列中1的数量保持不变,但每一行中1的数量不同)

     f1   f2   f3   f4   f5
d1   1    0    0    0    1  
d2   0    1    0    1    1
d3   1    0    0    1    1
d4   0    0    1    1    0
同样地,我也想对df1进行逐行随机化处理,即每行中的1的数量保持不变,并且需要更改每一行(但是更改的条目数可能不同)。例如,随机化后的 df3 可能如下所示:
     f1   f2   f3   f4   f5
d1   0    1    1    1    1  <- two entries are different
d2   0    0    1    0    1  <- four entries are different
d3   1    0    0    0    1  <- two entries are different
d4   0    0    1    0    1  <- two entries are different

顺便说一下,非常感谢Gavin Simpson、Joris Meys和Chase对我之前有关随机化两列的问题所作的帮助。


你想同时对行和列进行排列吗?重新阅读这个问题,似乎第二个例子中排列行并没有遵守每列相同数量1的限制。 - Gavin Simpson
1
请不要注册多个账号。我已经要求管理员将您在此处使用的帐户与之前 Q 中使用的帐户合并。 - Gavin Simpson
9个回答

267

给定 R 数据框:

> df1
  a b c
1 1 1 0
2 1 0 0
3 0 1 0
4 0 0 0

随机重排行:

> df2 <- df1[sample(nrow(df1)),]
> df2
  a b c
3 0 1 0
4 0 0 0
2 1 0 0
1 1 1 0

默认情况下,sample() 会随机重新排列作为第一个参数传递的元素。这意味着默认大小是传递数组的大小。向 sample(...) 传递参数 replace=FALSE(默认值)可以确保无替换采样,从而完成行级洗牌。

按列随机洗牌:

> df3 <- df1[,sample(ncol(df1))]
> df3
  c a b
1 0 1 1
2 0 1 0
3 0 0 1
4 0 0 0

5
我觉得很有趣,这不是最热门的评论,但它比去学习其他包要简单。对于几乎所有关于排列的问题来说都是如此。只需使用SAMPLE()函数即可。 - Brash Equilibrium
我猜测这个方法会保留行名称,我的理解正确吗? - tumultous_rooster
在这种情况下,使用“=”而不是标准的“<-”有什么原因吗? - Christian
7
好的,这是改变行和列的顺序,但 OP 想要的是不同的:独立地洗牌每一列/行。 - JelenaČuklina
正是我所需要的! - ChuckCottrill

30

使用包dplyr,这是另一种对data.frame进行洗牌(shuffle)的方法:

按行进行:

df2 <- slice(df1, sample(1:n()))
或者
df2 <- sample_frac(df1, 1L)

逐列地:

df2 <- select(df1, one_of(sample(names(df1)))) 

12

请查看vegan包中的permatswap()函数。这里提供了一个维护行和列总和的示例,但您也可以只固定行或列之一的总和。

mat <- matrix(c(1,1,0,0,0,0,0,1,1,0,0,0,1,1,1,0,1,0,1,1), ncol = 5)
set.seed(4)
out <- permatswap(mat, times = 99, burnin = 20000, thin = 500, mtype = "prab")
这将给出:
R> out$perm[[1]]
     [,1] [,2] [,3] [,4] [,5]
[1,]    1    0    1    1    1
[2,]    0    1    0    1    0
[3,]    0    0    0    1    1
[4,]    1    0    0    0    1
R> out$perm[[2]]
     [,1] [,2] [,3] [,4] [,5]
[1,]    1    1    0    1    1
[2,]    0    0    0    1    1
[3,]    1    0    0    1    0
[4,]    0    0    1    0    1
为了解释这个调用:
out <- permatswap(mat, times = 99, burnin = 20000, thin = 500, mtype = "prab")
  1. times指的是您希望获得的随机矩阵数量,这里为99。
  2. burnin指的是在开始取每个随机矩阵之前进行的交换次数。这使得我们从中进行抽样的矩阵可以变得相当随机,然后再开始取每个随机矩阵。
  3. thin表示只在每隔thin次交换时进行一次随机抽取。
  4. mtype = "prab"表示将矩阵视为存在/不存在,即二进制0/1数据。

有几点需要注意的是,这并不能保证任何列或行已被随机化,但如果burnin足够长,应该有很好的机会发生这种情况。另外,您可以绘制比所需更多的随机矩阵,并丢弃不符合所有要求的矩阵。

您对每行的更改次数有不同要求,这也在此处未涉及。同样,您可以随机抽取更多的矩阵,然后丢弃不满足此要求的矩阵。


9
你可以在R包picante中使用randomizeMatrix函数。
示例:
test <- matrix(c(1,1,0,1,0,1,0,0,1,0,0,1,0,1,0,0),nrow=4,ncol=4)
> test
     [,1] [,2] [,3] [,4]
[1,]    1    0    1    0
[2,]    1    1    0    1
[3,]    0    0    0    0
[4,]    1    0    1    0

randomizeMatrix(test,null.model = "frequency",iterations = 1000)

     [,1] [,2] [,3] [,4]
[1,]    0    1    0    1
[2,]    1    0    0    0
[3,]    1    0    1    0
[4,]    1    0    1    0

randomizeMatrix(test,null.model = "richness",iterations = 1000)

     [,1] [,2] [,3] [,4]
[1,]    1    0    0    1
[2,]    1    1    0    1
[3,]    0    0    0    0
[4,]    1    0    1    0
> 

选项null.model="frequency"维护列总和,而richness则维护行总和。虽然主要用于随机化物种存在与缺失数据集的社区生态学,但在此处也表现良好。
此函数还有其他空模型选项,请查看以下链接以获取更多详细信息(第36页):picante文档

4
当然,您可以对每一行进行采样:
sapply (1:4, function (row) df1[row,]<<-sample(df1[row,]))

这个算法会自动重排行,所以每行中数字 1 的数量不会改变。对于列的小变化同样适用,但这是留给读者练习的 :-P


2
在这里面没有任何尝试实现原帖想要强加的限制条件。 - Gavin Simpson

3

如果目标是随机打乱每一列,那么上面的一些答案不起作用,因为列是联合随机化的(这保留了列间相关性)。另外一些方法则需要安装包。但是,有一种只有一行的方法:

df2 = lapply(df1, function(x) { sample(x) })

2
您可以使用以下方式在数据框中“抽样”相同数量的项目:
nr<-dim(M)[1]
random_M = M[sample.int(nr),]

你可以使用nrow(M)代替dim(M)[1],这样整个过程就变成了一行代码:random_M <- M[nrow(M),] - Agile Bean

0

数据框中的随机样本和排列 如果是矩阵形式,则转换为数据框 使用基础包中的sample函数 索引 = sample(1:nrow(df1), size=1*nrow(df1)) 随机样本和排列


0
这里是一个使用.Nsampledata.table选项的示例。
library(data.table)
setDT(df)
df[sample(.N)]
#>    a b c
#> 1: 0 1 0
#> 2: 1 1 0
#> 3: 1 0 0
#> 4: 0 0 0

使用reprex v2.0.2于2023年1月28日创建


数据:

df <- read.table(text = "  a b c
1 1 1 0
2 1 0 0
3 0 1 0
4 0 0 0", header = TRUE)

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接