【问题标题】:Translating dplyr to data.table将 dplyr 转换为 data.table
【发布时间】:2019-11-23 04:39:22
【问题描述】:

所以我正在尝试翻译一些 dplyr 代码。我试图从将 dplyr 转换为 data.table 的包中获得帮助,但它仍然不起作用。错误是来自row_numberdplyr..

我需要dplyr 代码中的所有步骤(即使它们在这里对mtcars 没有意义)

library(dplyr)
library(dtplyr) # from https://github.com/tidyverse/dtplyr
library(data.table)

mtcars %>% 
  distinct(mpg, .keep_all = TRUE) %>% 
  group_by(am) %>% 
  arrange(mpg, .by_group = TRUE) %>% 
  mutate(row_num = LETTERS[row_number()]) %>% 
  ungroup() 

# using dtplyr
dt <- lazy_dt(mtcars)

dt %>% 
  distinct(mpg, .keep_all = TRUE) %>% 
  group_by(am) %>% 
  arrange(mpg, .by_group = TRUE) %>% 
  mutate(row_num = LETTERS[row_number()]) %>% 
  ungroup() %>% 
  show_query()
#> unique(`_DT1`, by = "mpg")[order(am, mpg)][, `:=`(row_num = c("A", 
#> "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M", "N", 
#> "O", "P", "Q", "R", "S", "T", "U", "V", "W", "X", "Y", "Z")[row_number()]), 
#>     keyby = .(am)]

# I then use the query from dtplyr 
DT <- as.data.table(mtcars)
unique(DT, by = "mpg")[order(am, mpg)][, `:=`(row_num = c("A", 
                                                              "B", "C", "D", "E", "F", "G", 
                                                              "H", "I", "J", "K", "L", "M", 
                                                              "N", "O", "P", "Q", "R", "S", 
                                                              "T", "U", "V", "W", "X", "Y", 
                                                              "Z")[row_number()]), keyby = .(am)]

#> row_number() should only be called in a data context

reprex package (v0.3.0) 于 2019 年 7 月 14 日创建

【问题讨论】:

    标签: r dplyr data.table dtplyr


    【解决方案1】:

    我们可以使用seq_len(.N)

    unique(DT, by = "mpg")[order(am, mpg)][, 
         `:=`(row_num = LETTERS[seq_len(.N)]), by = .(am)][]
    

    【讨论】:

      【解决方案2】:

      我可以推荐 rowid 函数吗?它在“幕后”执行分组步骤,您可能会发现它看起来更干净:

      unique(DT, by='mpg')[order(am, mpg), row_num := LETTERS[rowid(am)]]
      

      如果你喜欢链接,你也可以在 [] 中获取所有内容:

      DT[ , .SD[1L], by = mpg
         ][order(am, mpg), row_num := LETTERS[rowid(am)]]
      

      【讨论】:

      • 我认为您需要在第一个表达式中将ij 分开,否则它会就地修改unique() 生成的副本? (这当然是我的实验所建议的)。我认为rowid()row_number() 不同?我正在翻译为frank(x, ties.method = "first", na.last = "keep")
      • 哦,你只需要最后一个[],或者将它分配给一个变量?
      • @hadley 在第一种情况下分离 i/j 应该没有实质性区别——是的,:= 正在通过引用进行修改,但它正在修改一个不是尚未分配(unique() 的结果)。推迟到第二个 [ 将通过引用分配给尚未分配的不同对象(unique()[] 的结果)
      • 回复:row_number() 哦!我没有意识到row_number() 默认情况下可能不是唯一的。我猜rowid() 相当于dense_rank()?您对frank 的使用一目了然。
      【解决方案3】:

      我正在尝试对翻译进行一些调整,以便 dtplyr 自动生成更像您想要的内容:

      library(dtplyr)
      library(dplyr, warn.conflicts = FALSE)
      
      dt <- lazy_dt(mtcars)
      
      dt %>% 
        distinct(mpg, .keep_all = TRUE) %>% 
        group_by(am) %>% 
        arrange(mpg, .by_group = TRUE) %>% 
        mutate(row_num = LETTERS[row_number()]) %>% 
        ungroup() %>% 
        show_query()
      #> unique(`_DT1`, by = "mpg")[order(am, mpg)][, `:=`(row_num = ..LETTERS[seq_len(.N)]), 
      #>    keyby = .(am)]
      

      或者像@MichaelChirico 建议的那样避免分组:

      dt %>% 
        distinct(mpg, .keep_all = TRUE) %>% 
        arrange(am, mpg) %>% 
        mutate(row_num = LETTERS[row_number(am)]) %>% 
        ungroup() %>% 
        show_query()
      #> unique(`_DT1`, by = "mpg")[order(am, mpg)][, `:=`(row_num =  ..LETTERS[frank(am, 
      #>    ties.method = "first", na.last = "keep")])]
      

      (在 LETTERS 前面使用 .. 是一个 data.table 功能,可以清楚地表明您指的是数据框之外的变量;这里可能没有必要,但我认为最好比抱歉更安全。)

      【讨论】:

      • 谢谢。还要感谢您提供的所有书籍和包裹。是否也可以在某些时候包含spread()gather() 的翻译?
      • 我不确定 OP 想要哪个,但 Michael 的 DT[order(...), v := stuff] 和您的 DT[order(...)][, v := stuff] 之间存在行为差异——后者是创建新表的永久重新排序,而前者只是现有表中分配的临时性。我怀疑 dplyr 需要新的动词来涵盖这种情况...... mutate_on_filter、mutate_on_slice、mutate_on_arrange。 (续……)
      • (..cont) 对于永久重新排序x[order(...)] 惯用且更有效的是setorder(x, ...),尽管这可能会降低链接的可读性。 (实际上,我认为大多数人会将它放在多行而不是链接上,fwiw,所以 show_query 仍然不太习惯。)由于 unique() 已经创建了一个新表,因此不需要安排也复制/创建一个新表。编辑:我有点惊讶 frank 出现在 row_number 的翻译中,而不是 rowid 或 seq_len(.N)。
      • 显然 dtplyr 只能翻译现有的 dplyr 动词,所以我认为将arrange() 翻译成x[order(...)] 最准确地捕捉了原始代码的意图。我也很惊讶需要frank(),但这就是单元测试所揭示的。分组 rowid() 做了一些我没想到的事情(也没有花时间去弄清楚)。
      【解决方案4】:

      由于data.table 语法受到严重批评,下面是两个版本的 akrun 答案,恕我直言,语法更清晰。

      [ 多次通过管道传输时,我发现要遵循data.table 代码要困难得多,特别是当有:= 调用时(dplyr 中的mutate)。

      library(data.table)
      dt = as.data.table(mtcars)
      
      dt = unique(dt, by = "mpg")
      dt = dt[order(am, mpg)]
      dt[, row_num:=LETTERS[seq_len(.N)], by=am]
      dt[1:3]
      
          mpg cyl disp  hp drat   wt  qsec vs am gear carb row_num
      1: 10.4   8  472 205 2.93 5.25 17.98  0  0    3    4       A
      2: 13.3   8  350 245 3.73 3.84 15.41  0  0    3    4       B
      3: 14.3   8  360 245 3.21 3.57 15.84  0  0    3    4       C
      

      另一种选择是使用%&gt;% 管道。

      library(magrittr)
      
      dt = as.data.table(mtcars)
      dt = unique(dt, by = "mpg") %>%
        .[order(am, mpg)] %>%
        .[, row_num:=LETTERS[seq_len(.N)], by=am]
      dt[1:3]
      
      #     mpg cyl disp  hp drat   wt  qsec vs am gear carb row_num
      # 1: 10.4   8  472 205 2.93 5.25 17.98  0  0    3    4       A
      # 2: 13.3   8  350 245 3.73 3.84 15.41  0  0    3    4       B
      # 3: 14.3   8  360 245 3.21 3.57 15.84  0  0    3    4       C
      

      【讨论】:

      • 必须在'am'前订购吗?因为 'am' 将在 'by' 语句中使用。
      • 这在您的代码中,在 arrange 调用中。如果您不订购,它会更改输出。
      猜你喜欢
      • 2020-02-06
      • 2015-02-16
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2022-01-07
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多