Как определить, является ли вектор символов допустимым числовым или целочисленным вектором

Я пытаюсь превратить структуру вложенного списка в фреймворк данных. Список похож на следующий (это сериализованные данные из разобранного JSON, считанного с использованием пакета httr).

  myList <- list(object1 = list(w=1, x=list(y=0.1, z="cat")), object2 = list(w=NULL, x=list(z="dog")))

EDIT: мои исходные данные примера были слишком простыми. Фактические данные оборваны, а это означает, что для каждого объекта существуют не все переменные, а некоторые из элементов списка - NULL. Я отредактировал данные, чтобы отразить это.

unlist(myList) отлично справляется с рекурсивным выравниванием списка, и затем я могу использовать lapply, чтобы сгладить все объекты.

  flatList <- lapply(myList, FUN= function(object) {return(as.data.frame(rbind(unlist(object))))}) 

И, наконец, я могу настроить его, используя plyr::rbind.fill

  myDF <- do.call(plyr::rbind.fill, flatList)
  str(myDF)

  #'data.frame':    2 obs. of  3 variables:
  #$ w  : Factor w/ 2 levels "1","2": 1 2
  #$ x.y: Factor w/ 2 levels "0.1","0.2": 1 2
  #$ x.z: Factor w/ 2 levels "cat","dog": 1 2

Проблема заключается в том, что w и x.y теперь интерпретируются как символьные векторы, которые по умолчанию обрабатываются как факторы в кадре данных. Я считаю, что unlist() является виновником, но я не могу найти другого способа рекурсивно сгладить структуру списка. Обходным решением было бы выполнить пост-обработку кадра данных и затем назначить типы данных. Каков наилучший способ определить, является ли вектор допустимым числовым или целочисленным вектором?

Ответ 1

Как обсуждалось здесь, проверка того, возвращает ли as.numeric значения NA, является простым подходом к проверке, содержит ли символьная строка числовые данные. Теперь вы можете сделать что-то вроде:

myDF2 <- lapply(myDF, function(col) {
  if (suppressWarnings(all(!is.na(as.numeric(as.character(col)))))) {
    as.numeric(as.character(col))
  } else {
    col
  }
})
str(myDF2)
# List of 3
#  $ w  : num [1:2] 1 2
#  $ x.y: num [1:2] 0.1 0.2
#  $ x.z: Factor w/ 2 levels "cat","dog": 1 2

Ответ 2

Вы можете использовать plyr::ldply:

ldply(myList,.fun=function(x)data.frame(x))

      .id w x.y x.z
1 object1 1 0.1 cat
2 object2 2 0.2 dog

Ответ 3

Я не вижу никакого преимущества plyr:: ldply над обычными базовыми методами R:

 do.call(rbind, lapply(myList, data.frame) )
#-------------

        w x.y x.z
object1 1 0.1 cat
object2 2 0.2 dog

Проблема возникла из-за ошибочной попытки "сгладить" данные без учета ее внутренней структуры.

Ответ 4

Если вы просто хотите преобразовать все числовые векторы, которые были ошибочно классифицированы как символ, когда они были прочитаны, вы также можете использовать функцию all.is.numeric из пакета Hmisc:

myDF2 <- lapply(myDF, Hmisc::all.is.numeric, what = "vector", extras = NA)

Выбор what = "vector" преобразует вектор в числовой, если он содержит только числа. NA или другие типы отсутствующих значений будут препятствовать преобразованию, если они не указаны в аргументе extras, как указано выше.

Обратите внимание, что если применить к целому data.frame, содержащему векторы Date или POSIXct, они также будут преобразованы в числовые. Чтобы предотвратить это, вы можете обернуть его в функцию, как показано ниже:

catchNumeric <- function(dtcol) {
  require(Hmisc)
  if (is.character(dtcol)) {
    dtcol1 = all.is.numeric(dtcol, what = "vector", extras = NA)
  } else {
    dtcol1 = dtcol
  }
  return(dtcol1)
}

Затем примените к вашему data.frame:

myDF2 <- lapply(myDF, catchNumeric)

Ответ 5

Если у вас есть список или вектор со строками, и вы хотите преобразовать только числа в числовые, возможно решение:

catchNumeric <- function(mylist) {
  newlist <- suppressWarnings(as.numeric(mylist))
  mylist <- as.list(mylist)
  mylist[!is.na(newlist)] <- newlist[!is.na(newlist)]
  mylist
}

> catchNumeric(c("123", "c12", "abc", "123.12"))
[[1]]
[1] 123

[[2]]
[1] "c12"

[[3]]
[1] "abc"

[[4]]
[1] 123.12

> catchNumeric(list("123", "c12", "abc", "123.12"))
[[1]]
[1] 123

[[2]]
[1] "c12"

[[3]]
[1] "abc"

[[4]]
[1] 123.12

Ответ 6

Когда NAs включены, функция @josliber не будет работать (хотя она отвечает на вопрос хорошо для данных образца). Функция @Amy M должна работать, но требует загрузки пакета Hmisc.

Что-то вроде этого:

can.be.numeric <- function(x) {
    stopifnot(is.atomic(x) || is.list(x)) # check if x is a vector
    numNAs <- sum(is.na(x))
    numNAs_new <- suppressWarnings(sum(is.na(as.numeric(x))))
    return(numNAs_new == numNAs)
}

Он рассчитывает NA во входном векторе и NA на выходе is.na и возвращает TRUE, если вектор можно "безопасно" преобразовать в numeric (т.е. без добавления каких-либо значений NA.