data.table的等效tidyr::complete()函数

30

tidyr::complete()函数会为数据中缺失的列值组合添加行,以便填充 data.frame。例如:

library(dplyr)
library(tidyr)

df <- data.frame(person = c(1,2,2),
                 observation_id = c(1,1,2),
                 value = c(1,1,1))
df %>%
  tidyr::complete(person,
                  observation_id,
                  fill = list(value=0))

产量
# A tibble: 4 × 3
  person observation_id value
   <dbl>          <dbl> <dbl>
1      1              1     1
2      1              2     0
3      2              1     1
4      2              2     1

df 中缺失的组合 person == 1observation_id == 2value 已经被填充为 0。

data.table 中,相当于什么?

3个回答

29

我认为data.table的哲学是在特定任务中需要比tidyverse更少的专门命名的函数,因此需要一些额外的编码,例如:

res = setDT(df)[
  CJ(person = person, observation_id = observation_id, unique=TRUE), 
  on=.(person, observation_id)
]

接下来,您仍需要手动处理缺失级别的值填充。在data.table的最新版本中,我们可以使用setnafill高效地通过引用来处理此操作:

setnafill(res, fill = 0, cols = 'value')

请查看@Jealie的答案,其中提到了一个可以规避此问题的功能。


确实,这里必须输入三次列名,这很疯狂。但是另一方面,可以编写一个包装器:

completeDT <- function(DT, cols, defs = NULL){
  mDT = do.call(CJ, c(DT[, ..cols], list(unique=TRUE)))
  res = DT[mDT, on=names(mDT)]
  if (length(defs)) 
    res[, names(defs) := Map(replace, .SD, lapply(.SD, is.na), defs), .SDcols=names(defs)]
  res[]
} 

completeDT(setDT(df), cols = c("person", "observation_id"), defs = c(value = 0))

   person observation_id value
1:      1              1     1
2:      1              2     0
3:      2              1     1
4:      2              2     1

为了避免在第一步中重复三次输入名称的快捷方式,这里是 @thelatemail 的想法:

vars <- c("person","observation_id")
df[do.call(CJ, c(mget(vars), unique=TRUE)), on=vars]

# or with magrittr...
c("person","observation_id") %>% df[do.call(CJ, c(mget(.), unique=TRUE)), on=.]
更新:感谢@MichaelChirico和@MattDowle的改进,现在在CJ中您不需要再输入两次名称了。

8

可能有更好的答案,但这个方法有效:

dt[CJ(person=unique(dt$person), 
      observation_id=unique(dt$observation_id)),
   on=c('person','observation_id')]

这将会得到:

   person observation_id value
1:      1              1     1
2:      2              1     1
3:      1              2    NA
4:      2              2     1

现在,如果您想填充任何值(而不是NA),我建议等待相应的功能完成或为其做出贡献:)。请参考此链接

2
值得注意的是,上面的 completeDT 函数并不包含 tidyr :: complete 的许多特性。 特别是,空因子水平被丢弃-与 tidyr :: complete 不同,它会保留它们。 如果您确实想要保留空因子,则可以将函数编辑如下。 下面的 make_vals 函数可以更加复杂,以处理其他变量类别,例如整数的完整序列。

library(magrittr)
library(data.table)


dat <- data.frame(
  person = c(1,2,2),
  observation_id = factor(c(1,1,2), 1:3),
  value = c(1,1,1))

dat %>%
  tidyr::complete(
    person, observation_id, fill = list(value=0))
#> # A tibble: 6 x 3
#>   person observation_id value
#>    <dbl> <fct>          <dbl>
#> 1      1 1                  1
#> 2      1 2                  0
#> 3      1 3                  0
#> 4      2 1                  1
#> 5      2 2                  1
#> 6      2 3                  0

completeDT <- function(DT, cols, defs = NULL){
  
  make_vals <- function(col) {
    if(is.factor(col)) factor(levels(col))
    else unique(col)
  }
  
  mDT = do.call(CJ, c(lapply(DT[, ..cols], make_vals), list(unique=TRUE)))
  res = DT[mDT, on=names(mDT)]
  if (length(defs)) 
    res[, names(defs) := Map(replace, .SD, lapply(.SD, is.na), defs), .SDcols=names(defs)]
  res[]
} 

completeDT(DT = setDT(dat), cols = c("person", "observation_id"), defs = c(value = 0))
#>    person observation_id value
#> 1:      1              1     1
#> 2:      1              2     0
#> 3:      1              3     0
#> 4:      2              1     1
#> 5:      2              2     1
#> 6:      2              3     0

此文档由reprex软件包(v0.3.0)于2021年03月08日创建。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接