【问题标题】:How to do operations on list columns in an R data.table to output another list column?如何对 R data.table 中的列表列进行操作以输出另一个列表列?
【发布时间】:2018-10-01 01:41:21
【问题描述】:

我仍然很难考虑如何使用作为列表的 R data.table 列。

这是一个 R 数据表:

library(data.table)
dt = data.table(
      numericcol = rep(42, 8),
      listcol = list(c(1, 22, 3), 6, 1, 12, c(5, 6, 1123), 3, 42, 1)
  )
> dt
   numericcol        listcol
1:         42        1,22, 3
2:         42              6
3:         42              1
4:         42             12
5:         42    5,   6,1123
6:         42              3
7:         42             42
8:         42              1

我想为numericcollistcol 的元素之间的绝对值创建一个列:

> dt
   numericcol        listcol    absvals 
1:         42        1,22, 3    41, 20, 39
2:         42              6    36
3:         42              1    41
4:         42             12    30
5:         42    5,   6,1123    37, 36, 1081
6:         42              3    39
7:         42             42    0
8:         42              1    41

所以,我的第一个想法是使用sapply(),如下所示:

dt[, absvals := sapply(listcol, function(x) abs(x-numericcol))]

这会输出以下内容:

> dt
   numericcol        listcol absvals
1:         42        1,22, 3      41
2:         42              6      20
3:         42              1      39
4:         42             12      41
5:         42    5,   6,1123      20
6:         42              3      39
7:         42             42      41
8:         42              1      20

所以,absvals 现在是一列未列出的元素,每行都有一个单独的元素,并且是与 data.table 不同的维度。

(1) 如何创建absvals 来保留listcol 的列表结构?

(2) 在这种情况下,如果我只对值的向量感兴趣,R data.table 用户如何创建这样的数据结构?

也许

vec = as.vector(dt[, absvals := sapply(listcol, function(x) abs(x-numericcol))])

?

【问题讨论】:

    标签: r data.table


    【解决方案1】:

    使用mapply的另一种解决方案:

    dt[, absvals := mapply(listcol, numericcol, FUN = function(x, y) abs(x-y))]
    
    #output
    dt
       numericcol        listcol        absvals
    1:         42        1,22, 3       41,20,39
    2:         42              6             36
    3:         42              1             41
    4:         42             12             30
    5:         42    5,   6,1123   37,  36,1081
    6:         42              3             39
    7:         42             42              0
    8:         42              1             41
    

    【讨论】:

    • 优秀的答案。很不错
    【解决方案2】:

    也许你真的不需要列表栏? 看起来您可以更简单地完成所有这些操作。

    # convert to long format:
    dt2 <- dt[, .(var = unlist(listcol)), by = numericcol]
    dt2[, absval := abs(var - numericcol)]
    dt2
        numericcol  var absval
     1:         42    1     41
     2:         42   22     20
     3:         42    3     39
     4:         42    6     36
     5:         42    1     41
     6:         42   12     30
     7:         42    5     37
     8:         42    6     36
     9:         42 1123   1081
    10:         42    3     39
    11:         42   42      0
    12:         42    1     41
    

    根据我的经验,使用列表对象比使用简单的 data.tables 更难,也更慢。

    【讨论】:

      【解决方案3】:

      我认为,这基本上是一种逐行操作,所以这种方法肯定会有点不稳定。在data.table 中使用list 列要记住的关键是[.data.table 假定j 的任何输出是list 指的是列——所以你需要在list 中包装任何list再次让j 明白只有一列。

      我认为这适用于您的情况:

      dt[ , abs_vals := list(lapply(seq_along(.I), function(ii) 
        abs(listcol[[ii]] - numericcol[ii])))][]
      #    numericcol        listcol       abs_vals
      # 1:         42        1,22, 3       41,20,39
      # 2:         42              6             36
      # 3:         42              1             41
      # 4:         42             12             30
      # 5:         42    5,   6,1123   37,  36,1081
      # 6:         42              3             39
      # 7:         42             42              0
      # 8:         42              1             41
      

      seq_along(.I) 部分正在处理逐行方面。

      【讨论】:

        【解决方案4】:

        你可以使用apply()逐行遍历你的data.table,得到numericollistcol的每个元素之差的绝对值,如下所示;

        dt[, absvals := apply(.SD, 
                              1, 
                              function(x) abs(x$numericcol - x$listcol))]
        

        输出是这样的:

           numericcol        listcol        absvals
        1:         42        1,22, 3       41,20,39
        2:         42              6             36
        3:         42              1             41
        4:         42             12             30
        5:         42    5,   6,1123   37,  36,1081
        6:         42              3             39
        7:         42             42              0
        8:         42              1             41
        

        【讨论】:

          【解决方案5】:

          我们可以使用Map

          dt[, absvals := Map(function(x, y) abs(x-y), listcol, numericcol)]
          dt
          #    numericcol        listcol        absvals
          #1:         42        1,22, 3       41,20,39
          #2:         42              6             36
          #3:         42              1             41
          #4:         42             12             30
          #5:         42    5,   6,1123   37,  36,1081
          #6:         42              3             39
          #7:         42             42              0
          #8:         42              1             41
          

          purrr::map

          dt[, absvals := map2(listcol, numericcol, ~ abs(.x -.y))]
          

          除了循环多次之外,还有一个选项unlist 并根据'listvals' 的lengths 获得与replicated 'numericol' 的绝对差异。效率可能更高

          dt[, absvals := relist(abs(rep(numericcol, lengths(listcol)) - 
                             unlist(listcol)), skeleton = listcol)]
          

          注意:这里不需要复制,因为它与 'numericol' 的值相同,但 rep 用于一般情况

          【讨论】:

            猜你喜欢
            • 2019-11-22
            • 1970-01-01
            • 2018-02-01
            • 2011-11-21
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2021-05-02
            • 1970-01-01
            相关资源
            最近更新 更多