是否有更有效的方法在列表中用NA替换NULL?

Jon*_*n M 32 performance null r list

我经常遇到这样的结构数据:

employees <- list(
    list(id = 1,
             dept = "IT",
             age = 29,
             sportsteam = "softball"),
    list(id = 2,
             dept = "IT",
             age = 30,
             sportsteam = NULL),
    list(id = 3,
             dept = "IT",
             age = 29,
             sportsteam = "hockey"),
    list(id = 4,
             dept = NULL,
             age = 29,
             sportsteam = "softball"))
Run Code Online (Sandbox Code Playgroud)

在许多情况下,此类列表可能长达数千万个项目,因此内存问题和效率始终是一个问题.

我想将列表转换为数据帧,但如果我运行:

library(data.table)
employee.df <- rbindlist(employees)
Run Code Online (Sandbox Code Playgroud)

由于NULL值,我得到错误.我的正常策略是使用如下函数:

nullToNA <- function(x) {
    x[sapply(x, is.null)] <- NA
    return(x)
}
Run Code Online (Sandbox Code Playgroud)

然后:

employees <- lapply(employees, nullToNA)
employee.df <- rbindlist(employees)
Run Code Online (Sandbox Code Playgroud)

返回

   id dept age sportsteam
1:  1   IT  29   softball
2:  2   IT  30         NA
3:  3   IT  29     hockey
4:  4   NA  29   softball
Run Code Online (Sandbox Code Playgroud)

但是,当应用于1000万个案例时,nullToNA函数非常慢,因此如果有更高效的方法则会很好.

有一点似乎减慢了它的进程,is.null函数一次只能应用于一个项目(与可以一次扫描完整列表的is.na不同).

有关如何在大型数据集上有效执行此操作的任何建议?

Ric*_*ven 16

R中的许多效率问题通过首先将原始数据更改为使得后续过程尽可能快速和简单的形式来解决.通常,这是矩阵形式.

如果将所有数据放在一起rbind,则您的nullToNA函数不再需要搜索嵌套列表,因此sapply更有效地实现其目的(通过矩阵查找).从理论上讲,这应该会使流程更快.

顺便问一下好问题.

> dat <- do.call(rbind, lapply(employees, rbind))
> dat
     id dept age sportsteam
[1,] 1  "IT" 29  "softball"
[2,] 2  "IT" 30  NULL      
[3,] 3  "IT" 29  "hockey"  
[4,] 4  NULL 29  "softball"

> nullToNA(dat)
     id dept age sportsteam
[1,] 1  "IT" 29  "softball"
[2,] 2  "IT" 30  NA        
[3,] 3  "IT" 29  "hockey"  
[4,] 4  NA   29  "softball"
Run Code Online (Sandbox Code Playgroud)

  • 这将非常整洁,但结果列(转换为 data.frame 后)是列表,这会产生问题。见`dat = data.frame(dat); 数据[,1]`.. (2认同)

小智 7

我发现更容易阅读的 tidyverse 解决方案是编写一个适用于单个元素的函数并将其映射到所有 NULL。

我将使用 @rich-scriven 的 rbind 和 lapply 方法来创建一个矩阵,然后将其转换为数据帧。

library(magrittr)

dat <- do.call(rbind, lapply(employees, rbind)) %>% 
  as.data.frame()

dat
#>   id dept age sportsteam
#> 1  1   IT  29   softball
#> 2  2   IT  30       NULL
#> 3  3   IT  29     hockey
#> 4  4 NULL  29   softball
Run Code Online (Sandbox Code Playgroud)

然后我们可以使用purrr::modify_depth()深度为2来应用replace_x()

replace_x <- function(x, replacement = NA_character_) {
  if (length(x) == 0 || length(x[[1]]) == 0) {
    replacement
  } else {
    x
  }
}

out <- dat %>% 
  purrr::modify_depth(2, replace_x)

out
#>   id dept age sportsteam
#> 1  1   IT  29   softball
#> 2  2   IT  30         NA
#> 3  3   IT  29     hockey
#> 4  4   NA  29   softball
Run Code Online (Sandbox Code Playgroud)


inf*_*ner 5

结合使用后,两步法创建数据框rbind:

employee.df<-data.frame(do.call("rbind",employees))
Run Code Online (Sandbox Code Playgroud)

现在替换NULL,我使用"NULL",因为R在加载数据时没有放置NULL,并且在加载数据时将其作为字符读取.

employee.df.withNA <- sapply(employee.df, function(x) ifelse(x == "NULL", NA, x))
Run Code Online (Sandbox Code Playgroud)