如何高效地重塑数据表格

3

我有一个数据表 DT

set.seed(1)
DT <- data.table(x=rep(c(1,2,3),each=4), y=c("A","B"), v=sample(1:100,12))
DT
    x y   v
 1: 1 A  29
 2: 1 B  92
 3: 1 A 100
 4: 1 B  82
 5: 2 A  28
 6: 2 B  26
 7: 2 A  18
 8: 2 B  22
 9: 3 A  30
10: 3 B  96
11: 3 A  15
12: 3 B   4

我希望将其扩展如下,为每个x值创建一个新列,并报告v值,不应期望数据中的结构(不是按照下面的块)。
    x y v.1  v.2 v.3
 1: 1 A  29   NA  NA
 2: 1 B  92   NA  NA
 3: 1 A 100   NA  NA
 4: 1 B  82   NA  NA
 5: 2 A  NA   28  NA
 6: 2 B  NA   26  NA
 7: 2 A  NA   18  NA
 8: 2 B  NA   22  NA
 9: 3 A  NA   NA  30
10: 3 B  NA   NA  96
11: 3 A  NA   NA  15
12: 3 B  NA   NA   4

我曾在这里问过一个非常相似的问题here,但无法适应当时G Grothendieck给出的答案...

编辑: 像往常一样,在写帖子后,我几乎就得到了答案... 我只需要用NA替换那些0(我可能会在v中得到0,我想能够将v == 0与缺失项区分开来)

DT2 <- DT[, {SUM.<-factor(x); data.table(model.matrix(~ SUM.:v + 0))}]
txtR) DT2
    SUM.1:v SUM.2:v SUM.3:v
 1:      29       0       0
 2:      92       0       0
 3:     100       0       0
 4:      82       0       0
 5:       0      28       0
 6:       0      26       0
 7:       0      18       0
 8:       0      22       0
 9:       0       0      30
10:       0       0      96
11:       0       0      15
12:       0       0       4
3个回答

4
set.seed(1)
DT <- data.table(x=rep(c(1,2,3),each=4), y=c("A","B"), v=sample(1:100,12))

这将提供
    x y  v
 1: 1 A 27
 2: 1 B 37
 3: 1 A 57
 4: 1 B 89
 5: 2 A 20
 6: 2 B 86
 7: 2 A 97
 8: 2 B 62
 9: 3 A 58
10: 3 B  6
11: 3 A 19
12: 3 B 16

接下来,答案如下:
ux <- unique(DT$x)
DT[,c(v.=lapply(ux,function(i)v[x==i])),by="x,y"]

这提供了

    x y v.1 v.2 v.3
 1: 1 A  27  NA  NA
 2: 1 A  57  NA  NA
 3: 1 B  37  NA  NA
 4: 1 B  89  NA  NA
 5: 2 A  NA  20  NA
 6: 2 A  NA  97  NA
 7: 2 B  NA  86  NA
 8: 2 B  NA  62  NA
 9: 3 A  NA  NA  58
10: 3 A  NA  NA  19
11: 3 B  NA  NA   6
12: 3 B  NA  NA  16

那个答案可能在后续版本的R中会出问题,但是原帖指出这个方法也可以,并且可能更快:

DT[,paste0("v.",ux):=lapply(ux,function(i)v[x==i]),by="x"]

1
哦,期望的输出结果无论如何都有一个“y”列。如果不将它放在“by”中,我是否需要执行类似于合并或者“DT[,c(list(y=y),v.=lapply(1:3,function(i)v[x==i])),by="x"]”这样的操作呢? - Frank
@statquant 好的,你是对的。我已经将它更改为遍历所有x值。 - Frank
@Arun 我还是看不到。你的有一个“v”列;0代替了NA;而且行的顺序也不同,但除此之外,它们在我看来是一样的...顺便说一句,我没有读取OP的数据,而是用种子重新运行了生成代码。 - Frank
1
DT[,paste0("v.",ux):=lapply(ux,function(i)v[x==i]),by="x"] 运行效果更好(可能也更快)。 - statquant
我在...R版本2.15.2(2012-10-26)上没有看到警告; 平台:x86_64-w64-mingw32 / x64(64位); data.table_1.8.6。 - Frank
显示剩余4条评论

3
这里有一种方法:
tt <- model.matrix(data=DT, ~ factor(x):rep(1, nrow(DT)) + 0)
tt[tt==0] <- NA
cbind(DT, DT$v * tt)
#     x y   v factor(x)1:v factor(x)2:v factor(x)3:v
#  1: 1 A  69           69           NA           NA
#  2: 1 B  39           39           NA           NA
#  3: 1 A  76           76           NA           NA
#  4: 1 B  49           49           NA           NA
#  5: 2 A 100           NA          100           NA
#  6: 2 B  95           NA           95           NA
#  7: 2 A  36           NA           36           NA
#  8: 2 B  73           NA           73           NA
#  9: 3 A  86           NA           NA           86
# 10: 3 B  20           NA           NA           20
# 11: 3 A  59           NA           NA           59
# 12: 3 B  12           NA           NA           12

你看到有什么技巧可以用NA替换那些0吗?太糟糕了,因为它非常快... - statquant
@statquant,不是很直接,但我已经处理好了,请检查编辑。 - Arun
@statquant,您是指在运行时间或代码行数方面更加高效(或两者兼备)? - Arun

3

您可以简单地循环遍历x并使用data.table分配:

setkey(DT, x)
for (i in unique(DT$x)) {
  DT[J(i), paste0("v.", i) := v]
}

附注:我真的希望以下内容能够起作用,但是.GRP在那里不可用:

DT[, paste0("v.", .GRP) := v, by = x]

编辑另一种解决方案(尝试以某种方式运用上面提到的.GRP的想法),使用rbind.fill(我没有运行非常仔细的台式车,但这似乎可以很好地扩展)

library(plyr)

cbind(DT,
      rbind.fill(DT[, list(list(setnames(data.table(v), paste0("v.", .GRP)))),
                      by = x]$V1))

2
+1. 关于第二个想法,听起来像是一个不错的功能请求,是吧? - Frank
如果请求启用 DT[, paste0("v.", .GRP) := v, by = x] 功能,那么最好包括一种工具来指定填充值,以防需要使用除 NA 之外的其他值。 - G. Grothendieck
@Frank,我赞同这个观点。但我记得有人在之前提过这个问题。而且我认为这将是迄今为止最快的方法,特别是因为数据表中的列已经被分配了。 - Arun

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