【问题标题】:group numerical and factor variables in a data.table在 data.table 中对数值和因子变量进行分组
【发布时间】:2021-03-22 07:11:33
【问题描述】:

我正在尝试对 data.table 的多个数字和因子列进行分组。 这是一些数据:

library(data.table)
dt <- data.table(Zeit = c(117.9, 118, 118, 118, 118.1, 118.1, 118.2, 118.2, 118.2, 118.3, 118.3), 
                 a = factor(c(15, 15, 1, 0, 0, 0, 1, 1, 15, 15, 15)), 
                 b = factor(c(1, 1, 1, 0, 0, 0, 1, 1, 1, 1, 1)))

我正在使用这个功能:

compr_uniq_data <- function(ecuData) {
  ecuData <- ecuData[, lapply(.SD, function(x) if (is.numeric(x)) mean(x, na.rm = TRUE)
                   else unlist(unique(x[!is.na(x)]))), by = Zeit]
  return(ecuData)
}

compr_uniq_data(dt)

但我收到以下错误: Supplied 2 items for column 2 of group 2 which has 3 rows. The RHS length must either be 1 (single values are ok) or match the LHS length exactly. If you wish to 'recycle' the RHS please use rep() explicitly to make this intent clear to readers of your code.

我做错了什么? 如果我只使用一个因子列,它就可以工作。

预期的结果应该是这样的:

dtres <- data.table(Zeit = c(117.9, 118, 118, 118, 118.1, 118.2, 118.2, 118.3), 
                    a = c(15, 15, 1, 0, 0, 1, 15, 15), 
                    b = c(1, 1, 1, 0, 0, 1, 1,  1))

【问题讨论】:

    标签: r data.table grouping


    【解决方案1】:

    length 应该与 data.table/data.frame 列相同,因此请将其包装在 listpaste

    compr_uniq_data <- function(ecuData) {
      ecuData <- ecuData[, lapply(.SD, function(x)
           if (is.numeric(x)) mean(x, na.rm = TRUE)
                    else list(unique(x[!is.na(x)]))), by = Zeit]
        return(ecuData)
     }
    
    compr_uniq_data(dt)
    #   Zeit        a    b
    #1: 117.9       15    1
    #2: 118.0 15, 1, 0 1, 0
    #3: 118.1        0    0
    #4: 118.2    1, 15    1
    #5: 118.3       15    1
    

    更新

    基于 cmets,我们可以更改函数以将不是numeric 的数据集列的unique 作为一个块,然后将onnumeric 列(如果存在)按“Zeit”分组'

    compr_uniq_data <- function(ecuData) {
         i1 <-  ecuData[, unlist(lapply(.SD, is.numeric))]
        
         nm1 <- setdiff(names(ecuData)[!i1], "Zeit")
         
         out1 <- unique(ecuData, by = c(nm1, "Zeit"))
         
         if(any(i1[names(i1) != "Zeit"])) {
              nm2 <- setdiff(names(ecuData)[i1], "Zeit")
              out2 <- ecuData[, lapply(.SD, mean, na.rm = TRUE), 
                     by = Zeit, .SDcols = nm2]
              out1 <- out1[out2, on = .(Zeit)]
         } else {
             out1
         }
        out1
    
    }
    

    -测试

    compr_uniq_data(dt)
    #    a b  Zeit
    #1: 15 1 117.9
    #2: 15 1 118.0
    #3:  1 1 118.0
    #4:  0 0 118.0
    #5:  0 0 118.1
    #6:  1 1 118.2
    #7: 15 1 118.2
    #8: 15 1 118.3
    

    或者另一种选择是将replace duplicated 元素与NA 然后删除那些包含所有NAs 的行用于除“Zeit”之外的列

    compr_uniq_data <- function(ecuData) {
      ecuData <- ecuData[, lapply(.SD, function(x)
           if (is.numeric(x)) mean(x, na.rm = TRUE)
                    else replace(x, duplicated(x), NA)), by = Zeit]
      
      ecuData[!ecuData[, Reduce(`&`, lapply(.SD, is.na)),
          .SDcols = setdiff(names(ecuData), 'Zeit')]]
     }
    
    
    compr_uniq_data(dt)
    

    【讨论】:

    • 非常感谢!但是有一个小问题。您的第一个更新解决方案没有像我预期的那样工作,因为它复制了数字列并放置了一个“i”。在每个数字列名之前。
    • 是否也可以插入数值而不是使用平均值?例如,如果我想要 Zeit = 117.90、117.91、117.92 等而不是 117.9、180 等。我尝试用 approx 函数替换 mean 函数,但它没有用。
    猜你喜欢
    • 1970-01-01
    • 2017-01-08
    • 2021-03-23
    • 2019-12-28
    • 2021-07-03
    • 1970-01-01
    • 2022-01-12
    • 2020-04-01
    • 1970-01-01
    相关资源
    最近更新 更多