【问题标题】:Count logical values in subset of columns using .SDcols argument to data.table使用 data.table 的 .SDcols 参数计算列子集中的逻辑值
【发布时间】:2015-08-25 08:40:11
【问题描述】:

我有一个data.table 的逻辑值如下:

library(data.table)
set.seed(1)
myDt <- data.table(id = paste0("id", 1:10))
myDt[, paste0(letters[1:3], sample(1:10, 9, replace = FALSE)) :=
       lapply(1:9, function(i) sample(c(TRUE, FALSE), 10, replace = TRUE))]
myDt
      id    a3    b4    c5    a7    b2    c8    a9    b6   c10
 1:  id1  TRUE FALSE  TRUE  TRUE FALSE  TRUE FALSE FALSE  TRUE
 2:  id2  TRUE FALSE  TRUE FALSE  TRUE FALSE  TRUE  TRUE  TRUE
 3:  id3  TRUE  TRUE FALSE FALSE FALSE  TRUE FALSE FALSE  TRUE
 4:  id4 FALSE FALSE  TRUE FALSE  TRUE  TRUE  TRUE  TRUE FALSE
 5:  id5  TRUE  TRUE  TRUE FALSE  TRUE  TRUE  TRUE  TRUE FALSE
 6:  id6 FALSE  TRUE FALSE FALSE  TRUE FALSE  TRUE FALSE FALSE
 7:  id7  TRUE  TRUE FALSE FALSE  TRUE  TRUE FALSE  TRUE FALSE
 8:  id8 FALSE  TRUE FALSE  TRUE  TRUE  TRUE FALSE FALSE  TRUE
 9:  id9 FALSE  TRUE  TRUE  TRUE FALSE FALSE  TRUE  TRUE  TRUE
10: id10  TRUE FALSE FALSE FALSE FALSE  TRUE FALSE  TRUE FALSE

id 之外的列是三个类别(abc),每个类别有 3 个重复(整数)。我需要在事先不知道重复数的情况下计算每个类别的逻辑值。

我可以获取类别a 的列,如下所示:

aCols <- grep("^a", names(myDt), value = TRUE)
myDt[, .SD, .SDcols = aCols, by = id]
      id    a3    a7    a9
 1:  id1  TRUE  TRUE FALSE
 2:  id2  TRUE FALSE  TRUE
 3:  id3  TRUE FALSE FALSE
 4:  id4 FALSE FALSE  TRUE
 5:  id5  TRUE FALSE  TRUE
 6:  id6 FALSE FALSE  TRUE
 7:  id7  TRUE FALSE FALSE
 8:  id8 FALSE  TRUE FALSE
 9:  id9 FALSE  TRUE  TRUE
10: id10  TRUE FALSE FALSE

但是当我试图计算逻辑值时我被卡住了。到目前为止,我已经尝试过:

myDt[, sum(.SD), .SDcols = aCols, by = id]
Error in gsum(.SD) : 
  GForce sum can only be applied to columns, not .SD or similar. To sum all items in a list such as .SD, either add the prefix base::sum(.SD) or turn off GForce optimization using options(datatable.optimize=1). More likely, you may be looking for 'DT[,lappy(.SD,sum),by=,.SDcols=]'

myDt[, base::sum(.SD), .SDcols = aCols, by = id]
Error in FUN(X[[i]], ...) : 
  only defined on a data frame with all numeric variables

我确实用数字而不是逻辑尝试了后一种代码,它给了我预期的结果。

如果有任何建议,我将不胜感激。感谢阅读!

> sessionInfo()
R version 3.2.2 (2015-08-14)
Platform: x86_64-pc-linux-gnu (64-bit)
Running under: Ubuntu 14.04.3 LTS

locale:
 [1] LC_CTYPE=en_AU.UTF-8       LC_NUMERIC=C               LC_TIME=en_AU.UTF-8       
 [4] LC_COLLATE=en_AU.UTF-8     LC_MONETARY=en_AU.UTF-8    LC_MESSAGES=en_AU.UTF-8   
 [7] LC_PAPER=en_AU.UTF-8       LC_NAME=C                  LC_ADDRESS=C              
[10] LC_TELEPHONE=C             LC_MEASUREMENT=en_AU.UTF-8 LC_IDENTIFICATION=C       

attached base packages:
[1] stats     graphics  grDevices utils     datasets  methods   base     

other attached packages:
[1] data.table_1.9.4

loaded via a namespace (and not attached):
[1] magrittr_1.5   plyr_1.8.3     tools_3.2.2    reshape2_1.4.1 Rcpp_0.12.0    stringi_0.5-5 
[7] stringr_1.0.0  chron_2.3-47  

【问题讨论】:

    标签: r data.table


    【解决方案1】:

    当您有许多相同类型的列并且想要一次对它们进行操作时,通常最好将您的数据整理起来并再次传播它。这是使用meltdcast 组合的可能解决方案

    # melt by the "id" column
    res <- melt(myDt, id = "id") 
    # Remove numeric values from column names
    res[, indx := sub("\\d+", "", variable)] 
    # Spread the data again according to the new index while counting `TRUE`s
    dcast(res, id ~ indx, value.var = "value", fun.aggregate = function(x) sum(x == "TRUE"))
    #       id a b c
    #  1:  id1 2 0 3
    #  2: id10 1 1 1
    #  3:  id2 2 2 2
    #  4:  id3 1 1 2
    #  5:  id4 1 2 2
    #  6:  id5 2 3 2
    #  7:  id6 1 2 0
    #  8:  id7 1 3 1
    #  9:  id8 1 2 2
    # 10:  id9 2 2 2
    

    我使用了development version here (v 1.9.5),如果您使用 v 1.9.4,您可能需要使用dcast.data.table 而不是仅使用dcast


    另外,您提到您有 logical 值,但如果您的真实数据集确实如此,您的示例包含 character 值(sample(c("TRUE", "FALSE"), 10, replace = TRUE)) 而不仅仅是 sample(c(TRUE, FALSE), 10, replace = TRUE)))有 逻辑 值,那么最后一步可以简化为

    dcast(res, id ~ indx, value.var = "value", sum)
    

    【讨论】:

    • 完美,谢谢。示例已编辑以使值 真正 合乎逻辑:)
    • 尽管我在下面添加了另一个答案,但这是更优雅的方法。除了优雅之外,它更通用。例如,如果数据是数字而不是逻辑,则可以将 sum() 函数更改为 dcasting 为例如count values > 0 only,或其他一些需要的标准。
    【解决方案2】:

    我喜欢@David Arenburg 的回答。只是添加另一个选项——使用rowSums() 而不是sum()。使用更新的数据,使用

    myDt[, a_cols := rowSums(.SD), .SDcols = aCols]
    myDt
              id    a3    b4    c5    a7    b2    c8    a9    b6   c10 a_cols
         1:  id1  TRUE FALSE  TRUE  TRUE FALSE  TRUE FALSE FALSE  TRUE      2
         2:  id2  TRUE FALSE  TRUE FALSE  TRUE FALSE  TRUE  TRUE  TRUE      2
         3:  id3  TRUE  TRUE FALSE FALSE FALSE  TRUE FALSE FALSE  TRUE      1
         4:  id4 FALSE FALSE  TRUE FALSE  TRUE  TRUE  TRUE  TRUE FALSE      1
         5:  id5  TRUE  TRUE  TRUE FALSE  TRUE  TRUE  TRUE  TRUE FALSE      2
         6:  id6 FALSE  TRUE FALSE FALSE  TRUE FALSE  TRUE FALSE FALSE      1
         7:  id7  TRUE  TRUE FALSE FALSE  TRUE  TRUE FALSE  TRUE FALSE      1
         8:  id8 FALSE  TRUE FALSE  TRUE  TRUE  TRUE FALSE FALSE  TRUE      1
         9:  id9 FALSE  TRUE  TRUE  TRUE FALSE FALSE  TRUE  TRUE  TRUE      2
        10: id10  TRUE FALSE FALSE FALSE FALSE  TRUE FALSE  TRUE FALSE      1
    

    【讨论】:

    • 是的,这就是为什么我说我更喜欢你的答案,它更优雅并解决了实际问题。我添加了rowSums() 方法作为答案,因为它解决了隐含的问题“为什么sum() 在操作的用例中不起作用?”
    • @Peter 我同意您关于其他答案更好、更通用的评论。虽然我走错了路,但我提到.SDcolsrowSums() 会按照我提出的方式回答问题,所以你也可以投票。谢谢!
    猜你喜欢
    • 2015-09-28
    • 2013-04-17
    • 1970-01-01
    • 1970-01-01
    • 2021-12-18
    • 2021-08-21
    • 1970-01-01
    • 2021-07-23
    • 2022-07-11
    相关资源
    最近更新 更多