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

时间:2014-04-04 18:15:30

标签: r performance list null

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

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"))

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

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

library(data.table)
employee.df <- rbindlist(employees)

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

nullToNA <- function(x) {
    x[sapply(x, is.null)] <- NA
    return(x)
}

然后:

employees <- lapply(employees, nullToNA)
employee.df <- rbindlist(employees)

返回

   id dept age sportsteam
1:  1   IT  29   softball
2:  2   IT  30         NA
3:  3   IT  29     hockey
4:  4   NA  29   softball

然而,当应用于1000万个案例时,nullToNA函数非常慢,因此如果有更有效的方法,那就更好了。

似乎会使进程慢下来的一点是,is.null函数一次只能应用于一个项目(与可以一次扫描完整列表的is.na不同)。

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

5 个答案:

答案 0 :(得分:14)

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"

答案 1 :(得分:5)

两步法在与rbind

组合后创建数据框
employee.df<-data.frame(do.call("rbind",employees))

现在替换NULL,我正在使用&#34; NULL&#34;因为R在加载数据时不会输入NULL,并在加载数据时将其作为字符读取。

employee.df.withNA <- sapply(employee.df, function(x) ifelse(x == "NULL", NA, x))

答案 2 :(得分:2)

我觉得更易于阅读的整合解决方案是编写一个适用于单个元素的函数,并将其映射到所有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

然后我们可以使用深度为2的purrr::modify_depth()来应用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

答案 3 :(得分:1)

所有这些解决方案(我认为)都隐藏了这样一个事实:数据表仍然是列表的丢失而不是向量列表(我在我的应用程序中没有注意到它在{{1}期间开始抛出意外错误之前}})。 试试这个:

:=

我相信它运作良好,但我不确定它是否会受到缓慢影响并且可以进一步优化。

答案 4 :(得分:1)

我经常发现do.call()函数难以阅读。我每天使用的解决方案(MySQL输出包含"NULL"字符值):

NULL2NA <- function(df) {
  df[, 1:length(df)][df[, 1:length(df)] == 'NULL'] <- NA
  return(df)
}

但对于所有解决方案:请注意,NA不能在没有na.rm = TRUE的情况下用于计算,但可以使用NULLNaN给出了同样的问题。 例如:

> mean(c(1, 2, 3))
2

> mean(c(1, 2, NA, 3))
NA

> mean(c(1, 2, NULL, 3))
2

> mean(c(1, 2, NaN, 3))
NaN