【问题标题】:Applying a function to each row of a data.table将函数应用于 data.table 的每一行
【发布时间】:2013-03-18 09:48:04
【问题描述】:

我正在寻找一种方法来有效地将函数应用于 data.table 的每一行。让我们考虑以下数据表:

library(data.table)
library(stringr)

x <- data.table(a = c(1:3, 1), b = c('12 13', '14 15', '16 17', '18 19'))
> x
   a     b
1: 1 12 13
2: 2 14 15
3: 3 16 17
4: 1 18 19

假设我想按空格分割b 列的每个元素(从而为原始数据中的每一行生成两行)并连接生成的数据表。对于上面的示例,我需要以下结果:

   a V1
1: 1 12
2: 1 13
3: 2 14
4: 2 15
5: 3 16
6: 3 17
7: 1 18
8: 1 19

如果列 a 仅具有唯一值,则以下方法将起作用:

x[, list(str_split(b, ' ')[[1]]), by = a]

以下几乎可以工作(除非原始数据表中有一些相同的行),但是当x 有很多列并将b列复制到结果时,我想要避免。

>     x[, list(str_split(b, ' ')[[1]]), by = list(a,b)]
   a     b V1
1: 1 12 13 12
2: 1 12 13 13
3: 2 14 15 14
4: 2 14 15 15
5: 3 16 17 16
6: 3 16 17 17
7: 1 18 19 18
8: 1 18 19 19

解决这个问题最有效和最惯用的方法是什么?

【问题讨论】:

    标签: r data.table


    【解决方案1】:

    一种选择是添加行号

    x[, r := 1:nrow(x)]
    

    然后按r分组:

    x[, list(a, str_split(b, ' ')[[1]]), by = r]
    

    我想知道是否有更好的解决方案?

    【讨论】:

    • 也许更习惯用法,您可以在by 中包含对rownames 的调用(或者更好的是keyby):x[, list(str_split(b, ' ')[[1]]), keyby=list(a, rownames(x))]。
    • 是的,我喜欢。如果您将其发布为答案,我将很乐意接受。我不确定我是否需要 keyby(因为我只想遍历数据表一次)并且您不需要 a 键 - 只需 rownames(x) 就足以满足我的目的。
    【解决方案2】:

    最有效和惯用的方法是使用矢量化函数。

    在这种情况下,某种regex 会做你想做的事

     x[, V1 := gsub(" [[:alnum:]]*", "", b)]
    
       a     b V1
    1: 1 12 13 12
    2: 2 14 15 14
    3: 3 16 17 16
    4: 1 18 19 18
    

    如果你想返回每个拆分的组件,并且你知道每个都有两个,你可以使用Map 将strsplit 的结果强制转换为正确的形式

    x[, c('b1','b2')  := do.call(Map, c(f = c, strsplit(b, ' ')))]
    
    
    
    x
       a     b b1 b2
    1: 1 12 13 12 13
    2: 2 14 15 14 15
    3: 3 16 17 16 17
    4: 1 18 19 18 19
    

    【讨论】:

    • 我可能没有正确解释我想要什么。我需要的是问题底部的结果,但没有列b。在我的特定示例中,原始数据表中的每一行应在结果中产生两行,因为b 中的每个值都拆分为两个子字符串。
    • @mnel 虽然可以说是一种更合理的格式,但这并不能达到 OP 的预期结果。
    【解决方案3】:

    查看输入和所需的输出,这应该可行 -

    x <- data.frame(a=c(1,2,3,1),b=c("12 13","14 15","16 17","18 19"))
    data.frame(a=rep(x$a,each=2), new_b=unlist(strsplit(as.character(x$b)," ")))
    

    【讨论】:

    • 这不会返回请求的输出(注意a 的排序)。
    • 哦,好的。这很容易解决:)
    • 为了概括结果,将 each=2 替换为 - each = length(unlist(strsplit(as.character(x$b))))
    【解决方案4】:

    怎么样:

    x
       a     b
    1: 1 12 13
    2: 2 14 15
    3: 3 16 17
    4: 1 18 19
    
    x[,list(a=rep(a,each=2), V1=unlist(strsplit(b," ")))]
       a V1
    1: 1 12
    2: 1 13
    3: 2 14
    4: 2 15
    5: 3 16
    6: 3 17
    7: 1 18
    8: 1 19
    

    给出评论的通用解决方案:

    x[,{s=strsplit(b," ");list(a=rep(a,sapply(s,length)), V1=unlist(s))}]
    

    【讨论】:

    • 谢谢 Matthew - 这在我的特定示例中有效(每个 b 中有两个组件,用空格分隔)但在更一般的情况下不起作用,每个 b 可以有 1 到 10 个组件.这表明有时很难准确地说明您的问题:)。
    • Matt,这是一个完美的解决方案,节省了大量时间并且执行效率很高。它表明您的 DT 确实必须在 r-base 中替换 DF。我将在我的大数据分析课上引用这一点。一个问题,我们如何通过在多核上并行运行来提高效率?我检查了 htop 并运行了一个核心。
    • 嗨@SerhatCevikel。万分感谢。 DT 已经开始使用 OpenMP。例如,试试 fwrite,你会看到你所有的核心都被使用了。尝试 setkey() ,您会看到它部分使用了所有内核。在 dev 中尝试 fread,它使用所有内核。
    【解决方案5】:

    dplyr/tidyr 方法也适用于数据表。

    library(dplyr)
    library(tidyr)
    x %>% 
      separate(b, into = c("b1", "b2")) %>% 
      gather(b, "V1", b1:b2) %>%
      arrange(V1) %>%
      select(a, V1)
    

    或者,使用标准评估表:

    x %>% 
      separate_("b", into = c("b1", "b2")) %>% 
      gather_("b", "V1", c("b1", "b2")) %>%
      arrange_(~ V1) %>%
      select_(~ a, ~ V1)
    

    b 列中值不同数量的情况只是稍微复杂一些。

    library(stringr)
    
    x2 <- data.table(
      a = c(1:3, 1), 
      b = c('12 13', '14', '15 16 17', '18 19')
    )
    
    n <- max(str_count(x2$b, " ")) + 1
    b_cols <- paste0("b", seq_len(n))
    x2 %>% 
      separate_("b", into = b_cols, extra = "drop") %>% 
      gather_("b", "V1", b_cols) %>%
      arrange_(~ V1) %>%
      select_(~ a, ~ V1)
    

    【讨论】:

      【解决方案6】:
      x[, .(a,strsplit(b,' ')), by=1:nrow(x)]
      

      by=nrow(x) 是一种简单的方法来强制每组 1 行

      【讨论】:

        【解决方案7】:
        x[, .(a,strsplit(b,' ')), by = .I]
        

        看起来更优雅

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2023-03-06
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2015-12-15
          • 1970-01-01
          • 2014-04-30
          • 1970-01-01
          相关资源
          最近更新 更多