计算由其他变量子集化的多个变量的特定值的计数和比例

时间:2014-05-28 14:15:44

标签: r dataframe data.table

我的data.table看起来像这样:

DT <- data.table(Feature1 = c("yes", "yes", "yes", "no", "no"),
                 Feature2 = c("yes", "yes", "yes", "yes", "no"),
                 Feature3 = c("yes", "yes", "yes", "yes", "no"),
                 Var1 = c("yes", "yes", "no", "yes", "no"),
                 Var2 = c("yes", "yes", "yes", "yes", "yes"))


DT

##   Feature1 Feature2 Feature3 Var1 Var2
##1:       no       no       no   no  yes
##2:       no      yes      yes  yes  yes
##3:      yes      yes      yes  yes  yes
##4:      yes      yes      yes  yes  yes
##5:      yes      yes      yes   no  yes

现在我想计算&#34; Var1&#34;的出现次数和比例。正在&#34;是&#34;对于所有可能的功能组合,&#34; Var2&#34;正在&#34;是&#34;通过这些组合等我需要计算以及每个组合再次使用&#34; yes&#34; -answers的比例。

计算一个变量很容易。由于我不想删除任何组合,因此我使用CJ而不是by

DT[,`:=`(Feature1 = as.factor(Feature1),
         Feature2 = as.factor(Feature2),
         Feature3 = as.factor(Feature3))]

(顺便说一句,是否有更好的方法可以立即将多个列设置为因子?)

setkeyv(DT, c("Feature1", "Feature2", "Feature3", "Var1"))
DT2 <- DT[CJ(levels(Feature1), levels(Feature2), levels(Feature3), "yes"),
          list(Var1.count = .N)]
DT2[, Var1 := NULL]

但是,使用CJ意味着我必须为每个变量设置一个新密钥。如果我有100个怎么办?有没有更好的方法来做到这一点,而不是设置for - 循环?另外,我如何从这里获得比例?例如,对于特征的组合&#34;是,是,是&#34;,Var1是&#34;是&#34;两次&#34;没有&#34;曾经,所以我想在相应的行中获得另一个名为Var1.prop的列,其值为0.66。

从本质上讲,这就是我的目标:

   Feature1 Feature2 Feature3 Var1 Var1.count Var1.prop Var2.count Var2.prop
1:       no       no       no  yes          0        NA         1        1.00
2:       no       no      yes  yes          0        NA         0        NA
3:       no      yes       no  yes          0        NA         0        NA
4:       no      yes      yes  yes          1        1.00       1        1.00
5:      yes       no       no  yes          0        NA         0        NA
6:      yes       no      yes  yes          0        NA         0        NA
7:      yes      yes       no  yes          0        NA         0        NA
8:      yes      yes      yes  yes          2        0.66       3        1.00

解决方案应该可以扩展以适应大量不同的功能和变量。我更喜欢使用data.table,因为它比普通data.frame操作快得多,因为我发现与dplyr相比,它更容易在函数中使用。话虽如此,我也会接受data.frame的简洁而不太低效的解决方案。


@ Arun的回答后更新。这真的很整洁,但它不是可以扩展的,比方说,100个变量。我一直在尝试以这种方式建立Arun的答案,但它只返回一个空的data.table以及警告:

vars <- c("Var1", "Var2")
tmps <- paste0(vars, ".tmp")

ans <- DTn[, { for (var in vars){
  assign(paste0(var, ".tmp"), sum(var == "yes", na.rm = TRUE));
  list(assign(paste0(var, ".count"), get(paste0(var, ".tmp"))),
       assign(paste0(var, ".prop"), get(paste0(var, ".tmp"))/.N)
  )
}}, by = key(DT), with = FALSE]

这里出了什么问题?

1 个答案:

答案 0 :(得分:3)

您不必将列转换为factors。事实上,data.table建议尽可能避免因素,因为它还会提高速度。但是,我将说明如何在未来更轻松地转换为factor

sd_cols = c("Feature1", "Feature2", "Feature3")
DT[, c(sd_cols) := lapply(.SD, as.factor), .SDcols=sd_cols]

好的,现在解决方案。当然,我们需要在这里使用CJ,因为您还需要缺少组合。所以,我们首先要生成它。

uvals = c("no", "yes")
setkey(DT, Feature1, Feature2, Feature3)
DTn = DT[CJ(uvals, uvals, uvals), allow.cartesian=TRUE]

allow.cartesian=TRUE是必要的,因为联接会在联接max(nrow(x), nrow(i))中产生比x[i]更多的行。有关allow.cartesian的更多信息,请阅读this post

现在我们已经完成了所有组合,我们可以分组/聚合它们,以便以您需要的方式获得结果。

ans = DTn[, { tmp1 = sum(Var1 == "yes", na.rm=TRUE);
             tmp2 = sum(Var2 == "yes", na.rm=TRUE);
           list(Var1.count = tmp1, 
                Var1.prop  = tmp1/.N, 
                Var2.count = tmp2,
                Var2.prop  = tmp2/.N * 100)
           }, by=key(DT)]

#    Feature1 Feature2 Feature3 Var1.count Var1.prop Var2.count Var2.prop
# 1:       no       no       no          0 0.0000000          1         1
# 2:       no       no      yes          0 0.0000000          0         0
# 3:       no      yes       no          0 0.0000000          0         0
# 4:       no      yes      yes          1 1.0000000          1         1
# 5:      yes       no       no          0 0.0000000          0         0
# 6:      yes       no      yes          0 0.0000000          0         0
# 7:      yes      yes       no          0 0.0000000          0         0
# 8:      yes      yes      yes          2 0.6666667          3         1

我认为如果那个值真的那么重要的话,你可以把这些值变成NA而不是0?


在获得DTn之后,在评论+编辑下关注OP的问题:

vars = c("Var1", "Var2")
ans = DTn[, c(N=.N, lapply(.SD, function(x) sum(x=="yes", na.rm=TRUE))), 
               by=key(DTn), .SDcols=vars]
N = ans$N
ans[, N := NULL]
ans[, c(paste(vars, "prop", sep=".")) := .SD/N, .SDcols=vars]
setnames(ans, vars, paste(vars, "count", sep="."))

ans
#    Feature1 Feature2 Feature3 Var1.count Var2.count Var1.prop Var2.prop
# 1:       no       no       no          0          1 0.0000000         1
# 2:       no       no      yes          0          0 0.0000000         0
# 3:       no      yes       no          0          0 0.0000000         0
# 4:       no      yes      yes          1          1 1.0000000         1
# 5:      yes       no       no          0          0 0.0000000         0
# 6:      yes       no      yes          0          0 0.0000000         0
# 7:      yes      yes       no          0          0 0.0000000         0
# 8:      yes      yes      yes          2          3 0.6666667         1

这个怎么样?