data.table为唯一观察值赋值

时间:2019-06-26 22:23:33

标签: r data.table

一些示例数据:

library(data.table)

mydat <- data.table(id1=rep(c("A","B","C"),each=3),
                    id2=c("D","E","G", "D","E","F","G","E","D"),
                    val=c(1,2,4,1,2,3, 4,2,1))

哪个给

   id1 id2 val
1:   A   D   1
2:   A   E   2
3:   A   G   4
4:   B   D   1
5:   B   E   2
6:   B   F   3
7:   C   G   4
8:   C   E   2
9:   C   D   1

我的目标是获取id2,val的唯一值,然后生成一个依赖于唯一值的变量(例如,如下所示的唯一观察值之和)。然后,应将此变量放入原始data.table的一列中。我经常发现自己在编写如下代码:

## This is the most obvious way
tmp <- unique(mydat[,.(id2,val)])
tmp[,weight:=val/sum(val)]
tmp[,val:=NULL]
mydat <- merge(mydat,tmp,by="id2",all.x=TRUE)

## A second option which doesn't require merging
mydat[,first:=FALSE]
mydat[mydat[,.I[1],by=.(id2)]$V1,first:=TRUE]
mydat[first==TRUE,weight2:=val/sum(val)]
mydat[,weight2:=max(weight,na.rm = TRUE),by=.(id2)]
mydat[,first:=NULL]

这给

   id2 id1 val weight weight2
1:   D   A   1    0.1     0.1
2:   D   B   1    0.1     0.1
3:   D   C   1    0.1     0.1
4:   E   A   2    0.2     0.2
5:   E   B   2    0.2     0.2
6:   E   C   2    0.2     0.2
7:   F   B   3    0.3     0.3
8:   G   A   4    0.4     0.4
9:   G   C   4    0.4     0.4

完全出于好奇,是否有一种更清洁的方法(更多data.table)来做到这一点?也许有自我加入?性能非常重要,因为我正在使用的实际数据往往很大。

2 个答案:

答案 0 :(得分:4)

我同意@thelatemail的观点,OP中的方法已经非常干净了。

  

性能非常重要,因为我正在使用的实际数据往往很大。

如果必须使用此结构,则有:

setorder(mydat, id2)
mydat[unique(id2), on=.(id2), mult="first", w2 := val/sum(val)]
mydat[, w2 := nafill(w2, type="locf")]

我正在排序,因为这已显示在所需的输出中。要保持原始排序,请放下setorder并将最后一行更改为mydat[order(id2), w2 := nafill(w2, type="locf")]

nafill功能在1.12.3+中可用(因此在CRAN上尚不可用)。


我建议改为使用一组规范化/“整洁”的表:valid2的属性,因此您可以有一个包含此类内容的id2表。

# same as OP's tmp
id2DT = unique(mydat[, .(id2, val)])
setkey(id2DT, id2)
id2DT[, w := val/sum(val)]

# drop redundant repeating val unless you really need it there
# to save on space and improve readability
mydat[, val := NULL] 
# merge w in if/when needed
mydat[, w := id2DT[.SD, on=.(id2), x.w]]

答案 1 :(得分:2)

这是免合并选项:

total_val <- mydat[!duplicated(id2, val), sum(val)] # Just the scalar we are after
mydat[, `:=`(val = val[1], weight = val[1] / total_val), by = id2]

#    id1 id2 val weight
# 1:   A   D   1    0.1
# 2:   B   D   1    0.1
# 3:   C   D   1    0.1
# 4:   A   E   2    0.2
# 5:   B   E   2    0.2
# 6:   C   E   2    0.2
# 7:   B   F   3    0.3
# 8:   A   G   4    0.4
# 9:   C   G   4    0.4