如何在data.table中按变量分组并按随机顺序列出?

5

我有一个变量需要分组。这很容易。然而,我希望结果表按随机顺序列出其行。实际上,我想做的事情有点更复杂。但是让我给你展示一个简化版本。

mydf = data.table(
   x = rep(1:4, each = 5),
   y = rep(c('A', 'B','c','D', 'E'), times = 2),
   v = rpois(20, 30)
)

mydf[,list(sum(x),sum(v)), by=y]
mydf[,list(sum(x),sum(v)), by=list(y=sample(y))]

#to list all the raw data in order of y


mydf[,list(x,v), by=y]
mydf[,list(x,v), by=list(y=sample(y))]

如果您查看生成的输出,您会注意到Y确实是随机排序的,但它已经与包含它的行中的数据脱节了。我该怎么办?

1
为什么不正常返回 data.table 然后再进行随机排序呢? - Justin
@Justin,对于显示总和的表格来说,随机排序返回的表格可能有效,但对于下一个集合,其中想要列出原始数据但仍希望按y组进行列出的情况,这种方法不适用。 - Farrel
2个回答

5
我会执行这个操作,然后随机排序:
mydf[,list(x,v),by=y][sample(seq_len(nrow(mydf)),replace=FALSE)]

注意:分组后的随机重排:

mydf[,list(sum(x),sum(v)), by=y][sample(seq_len(length(y)),replace=FALSE)]

您可以像这样对内容进行分组和随机排序,看起来它确实保留了更改后的顺序:
mydf[order(setNames(sample(unique(y)),unique(y))[y])]
mydf[order(setNames(sample(unique(y)),unique(y))[y]),list(sum(x),sum(v)),by=y]

#perhaps more readable:
mydf[{z <- unique(y); order(setNames(sample(z),z)[y])}]
mydf[{z <- unique(y); order(setNames(sample(z),z)[y])},list(sum(x),sum(v)),by=y]

在排序之前先添加一列,这样更加透明。

mydf[,new.y := setNames(sample(unique(y)),unique(y))[y]][order(new.y)]

分解一下:

##a random ordering of the elements of y 
##(set.seed is used here to get consistent results)
set.seed(1); mydf[,{z <- unique(y);sample(z)}]
# [1] "B" "E" "D" "c" "A"
##assigning names to the elements of y
##creating a 1-1 bijective function between the elements of y
set.seed(1); mydf[,{z <- unique(y);setNames(sample(z),z)}]
#  A   B   c   D   E 
#"B" "E" "D" "c" "A" 
##subsetting by y puts y through the map
##in effect every element of y is posing as an element of y, picked at random
##notice that the names (top row) are the original y
##the values (bottom row) are the mapped-to values
#  A   B   c   D   E   A   B   c   D   E   A   B   c   D   E   A   B   c   D   E 
#"B" "E" "D" "c" "A" "B" "E" "D" "c" "A" "B" "E" "D" "c" "A" "B" "E" "D" "c" "A"
##ordering by this now orders by the mapped-to values
set.seed(1); mydf[{z <- unique(y);order(setNames(sample(z),z)[y])}]

编辑:根据评论中Arun的建议使用setattr设置名称:

mydf[{z <- unique(y); order(setattr(sample(z),'names',z)[y])}]
mydf[{z <- unique(y); order(setattr(sample(z),'names',z)[y])},list(sum(x),sum(v)),by=y]

该代码只是以随机顺序返回原始数据。然而,我希望所有特定 y 的行(随机排序)能够一起列出。 - Farrel
@BlueMagister,如果你正在使用data.table,那么你应该使用setnames(而不是setNames)。 - Arun
@Arun setnames 是针对 data.table 而非普通向量的。 - Blue Magister
1
@BlueMagister,不太确定你的意思。我的意思是,例如最后一行(或任何其他行),在其中你已经在DT内部设置了setNamesset.seed(1); mydf[{z <- unique(y); order(setattr(sample(z), 'names', z)[y])}]。这是通过引用完成的,没有进行任何副本。 setNames会复制对象。尝试这个:x <- sample(10); tracemem(x); setattr(x, 'names', letters[1:10]); tracemem(x)。现在,将setattr替换为setNames(x, letters[1:10]),并观察它进行复制。 - Arun
1
@Farrel,我会这样做:x <- sample(10); setNames(x, letters[1:10])。它输出什么?与第一个赋值的 x 相比有什么变化?我看到每个向量元素都被分配了名称。啊,等等,现在文档有意义了!它只是设置对象上的名称并返回该命名对象。 - Arun
显示剩余4条评论

3
我想这就是你要找的内容吧...?
mydf[,.SD[sample(.N)],by=y]

受@BlueMagister第二个解决方案的启发,这里是先随机化的方法:
mydf[sample(nrow(mydf)),.SD,by=y]

在这里,如果你想让分组按照字母顺序出现,请使用keyby而不是by

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