Подтвердить что ты не робот

Есть ли более эффективный способ заменить NULL на NA в списке?

Я довольно часто сталкиваюсь с данными, которые структурированы примерно так:

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

Однако функция nullToNA очень медленная, когда применяется к 10 миллионам случаев, поэтому было бы хорошо, если бы был более эффективный подход.

Одна точка, которая, как представляется, замедляет процесс вниз, функция is.null может применяться только к одному элементу за раз (в отличие от is.na, который может сканировать полный список за один раз).

Любые советы по эффективному использованию этой операции в большом наборе данных?

4b9b3361

Ответ 1

Многие проблемы эффективности в 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"

Ответ 2

Двухступенчатый подход создает фрейм данных после расчёта с помощью rbind:

employee.df<-data.frame(do.call("rbind",employees))

Теперь замените NULL, я использую "NULL", так как R не помещает NULL при загрузке данных и читает его как символ при его загрузке.

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

Ответ 3

Все эти решения (я думаю) скрывают тот факт, что таблица данных все еще потеряна списком, а не списком векторов (я не заметил в своем приложении либо до тех пор, пока не начал бросать неожиданные ошибки во время :=). Попробуйте следующее:

data.table(t(sapply(employees, function(x) unlist(lapply(x, function(x) ifelse(is.null(x),NA,x))))))

Я считаю, что это работает отлично, но я не уверен, что он будет страдать от медлительности и может быть оптимизирован дальше.

Ответ 4

Я часто нахожу функции do.call(), которые трудно читать. Решение, которое я использую ежедневно (с выходом MySQL, содержащим значки "NULL"):

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

Но для всех решений: пожалуйста, помните, что NA нельзя использовать для расчета без na.rm = TRUE, но с NULL вы можете. NaN дает ту же проблему. Например:

> 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