更改嵌套列表中元素的数据类型

时间:2017-07-01 10:09:47

标签: r

是否可以扫描具有特定名称的元素的列表列表并更改其数据类型但保留其值?

例如,以下列表包含“character”或“numeric”

类的元素“N”
x = list(list(N=as.character(1)),
         list(a=1,b=2,c="another element",N=as.character(5)), 
         list(a=2,b=2,N=as.character(7),c=NULL), 
         list(a=2,b=2,list(N=as.character(3))))

应该成为:

x = list(list(N=as.numeric(1)),
         list(a=1,b=2,c="another element",N=as.numeric(5)), 
         list(a=2,b=2,N=as.numeric(7),c=NULL), 
         list(a=2,b=2,list(N=as.numeric(3))))

要清楚,解决方案应该允许更深的嵌套,并且尊重名称不是“N”的字段的数据类型。我无法找到适用于具有任意结构的列表的通用解决方案。

我尝试了this post中给出的解决方案:

a <- as.relistable(x)
u <- unlist(a)
u[names(u) == "N"] <- as.numeric(u[names(u) == "N"])
relist(u, a)

不幸的是,替换不适用于当前形式。此外,如果列表包含NULL元素,则relist似乎不起作用。

3 个答案:

答案 0 :(得分:1)

使用lapply在列表元素上重复此过程,条件是检查您感兴趣的元素,这样您就不会无意中将元素添加到子列表中:

x <- lapply(x, function(i) {

    if(length(i$N) > 0) {

        i$N <- as.numeric(i$N)

    }

    return(i)

})

答案 1 :(得分:0)

仅适用于包含带数字的数字或字符串的列表列表的解决方案:

x <- list(list(N=as.character(1)),
         list(a=1,b=2,N=as.character(5)), 
         list(a=2,b=2,N=as.character(7)), 
         list(a=2,b=2))

y1 <- lapply(x, function(y) lapply(y, as.numeric))

y2 <- list(list(N=as.numeric(1)),
         list(a=1,b=2,N=as.numeric(5)), 
         list(a=2,b=2,N=as.numeric(7)), 
         list(a=2,b=2))

identical(y1,y2)
# [1] TRUE

编辑。这是一个更通用的代码,适用于数字和字符串的嵌套列表。它使用递归函数as_numlist.apply包的rlist函数。

library(rlist)

x = list(list(N=as.character(1)),
         list(a=1,b=2,c="another element",N=as.character(5)), 
         list(a=2,b=2,N=as.character(7),c=NULL), 
         list(a=2,b=2,list(N=as.character(3))))

# Test if the string contains a number
is_num <- function(x) grepl("[-]?[0-9]+[.]?[0-9]*|[-]?[0-9]+[L]?|[-]?[0-9]+[.]?[0-9]*[eE][0-9]+",x)

# A recursive function for numeric convertion of strings containing numbers
as_num <- function(x) {
   if (!is.null(x)) {
     if (class(x)!="list") {
       y <- x
       if (is.character(x) & is_num(x)) y <- as.numeric(x)
     } else {
       y <- list.apply(x, as_num)
     }
   } else { 
     y <- x
   }
   return(y)
}

y <- list.apply(x, as_num)

z = list(list(N=as.numeric(1)),
         list(a=1,b=2,c="another element",N=as.numeric(5)), 
         list(a=2,b=2,N=as.numeric(7),c=NULL), 
         list(a=2,b=2,list(N=as.numeric(3))))

identical(y,z)
# [1] TRUE

答案 2 :(得分:0)

marco sandri提供的答案可以进一步概括为:

is_num <- function(x) grepl("^[-]?[0-9]+[.]?[0-9]*|^[-]?[0-9]+[L]?|^[-]?[0-9]+[.]?[0-9]*[eE][0-9]+",x)

as_num <- function(x) {
if (is.null(x)||length(x) == 0) return(x)
if (class(x)=="list") return(lapply(x, as_num))
if (is.character(x) & is_num(x)) return(as.numeric(x))
return(x)
}
y <- as_num(z)
identical(y,z)

此解决方案还允许列表元素包含数字(0)和混合数据类型,例如&#39; data2005&#39;。