Как определить, является ли вектор символов допустимым числовым или целочисленным вектором
Я пытаюсь превратить структуру вложенного списка в фреймворк данных. Список похож на следующий (это сериализованные данные из разобранного 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
.