在 R 中 data.table 中基于另一个因素聚合一个因素

Aggregating one factor based on another in data.table in R

我有一个 R data.table 对象 DT 如下:

library(ggplot2)
library(data.table)

DT <- movies[movies$mpaa %in% c("NC-17", "PG", "PG-13", "R"), c("rating", "title", "mpaa")]
setDT(DT)
setnames(DT, colnames(DT), c("Gp", "ID", "FACTOR"))
DT[, FACTOR := droplevels(FACTOR)]
DT[, Gp := as.numeric(as.factor(Gp))]
setkey(DT, ID)
DT <- unique(DT)

DT
      Gp                       ID FACTOR
   1: 43                  $windle      R
   2: 61             'A' gai waak  PG-13
   3: 62    'A' gai waak juk jaap  PG-13
   4: 39                  'R Xmas      R
   5: 38       'Til There Was You  PG-13
  ---                                   
4899: 57                  Zuotian      R
4900: 27 Zyosyuu syukeininn Maria      R
4901: 57                 eXistenZ      R
4902: 45                      xXx  PG-13
4903: 29  xXx: State of the Union  PG-13

我正在尝试根据列 Gp 中的元素聚合 FACTOR 列中的数据。我已经能够实现如下。

k <- vector("list", max(DT$Gp))
for (i in 1:max(DT$Gp)) {
  names(k)[i] <- i
  k[[i]] <- DT[Gp == i, as.vector(table(FACTOR))]
}
k <- lapply(k, function(x) as.data.frame(t(x)))
k <- rbindlist(k)
setnames(k, old = colnames(k), new = c("NC-17", "PG", "PG-13", "R"))
k$Gp <- row.names(k)
setcolorder(k, c("Gp","NC-17", "PG", "PG-13", "R"))

head(k)
   Gp NC-17 PG PG-13 R
1:  1     0  0     0 2
2:  2     0  0     0 1
3:  3     0  0     0 2
4:  4     0  0     0 1
5:  5     0  0     0 2
6:  6     0  0     0 3

对于Gp的每个级别,我想获得FACTOR每个级别的记录数。如何单独使用 data.table 更优雅地获得想要的结果 k

您可以在每组 Gp 中使用 as.list(table(FACTOR))

DT[, as.list(table(FACTOR)), by = Gp]

这给出了结果:

    Gp NC-17 PG PG-13  R
 1: 43     1  8    26 79
 2: 61     2  9    22 77
 3: 62     0  7    18 63
 4: 39     0 17    24 52
 5: 38     1 14    13 77
 6: 57     1 11    23 72
 7: 48     0 16    29 78
...

(要按照示例输出中的 Gp 对它们进行排序,您可以将其更改为 DT[order(Gp), as.list(table(FACTOR)), by = Gp])。

我建议为此使用 dcast

require(data.table) # v1.9.4
dcast.data.table(DT, Gp ~ FACTOR, fun.aggregate = length)

或者从current devel, v1.9.5(以及以后的版本)开始,我们可以直接使用dcast():

require(data.table) # v1.9.5+
dcast(DT, Gp ~ FACTOR, fun.aggregate = length)

as.list() 是一个 S3 通用的,并且随着更多的组会变得相当慢(由于为每个组调度正确的方法所花费的时间)。 table() 是另一个慢函数。

dcast() 也会自动按 Gp 列对结果进行排序。


这里有一个足够大的基准来突出差异:

set.seed(1L)
bmark = data.table(Gp = sample(1e5, 1e7, TRUE), 
                   FACTOR = sample(levels(DT$FACTOR), 1e7, TRUE))
print(object.size(bmark), units="Mb")
# 114.4 Mb

system.time(ans1 <- dcast(bmark, Gp ~ FACTOR, fun.aggregate = length))
#    user  system elapsed 
#   0.998   0.026   1.030 

system.time(ans2 <- bmark[, as.list(table(FACTOR)), by=Gp])
#    user  system elapsed 
#  14.666   0.141  15.078 

identical(ans1, setkey(ans2, Gp))
# [1] TRUE

数据大小是~114MB,不算大,速度是~15x。