2017-07-01 48 views
1

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

作为一个例子,包含元素类“字符”或“数字”

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

然后应成为“N”以下列表:

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似乎不起作用。

+0

我看不出如何完成任意的复杂性。您可以使用'rapply'将所有字符强制转换为数字,例如'rapply(x,as.numeric,how =“replace”)'。 –

+0

是的,但这是因为这个解决方案改变了所有的元素,它不会为任意的复杂性工作 - 我已经改变了这个例子,使这个更清晰 –

回答

1

使用lapply重复该过程在一个条件列表中的元素来检查您感兴趣的元素,这样你就不会无意中元素添加到您的子表:

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

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

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

    } 

    return(i) 

}) 
+0

我最初也试过这个,但这不幸的是不适用于N位于任意深度。我已经改变了这个例子,使这个方面更清晰。 –

+0

呵呵,它应该工作,不管每个列表中的“N”在哪里,或者是否有'N'。它如何失败? – ulfelder

+0

如果N嵌套深度超过一个级别,则它会失败。我想一个互惠版本可以在嵌套> 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 

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

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 
+0

这确实适用于此示例,但不适用于更复杂的列表。我已经改变了这个例子,使之更清晰。 –

+0

下次我会这样做。虽然我确实没有在这个例子中反映出这个问题,但最初的问题已经表明我正在寻找一个解决方案,它也可以用于更复杂的列表。 –

+0

@marcSandri非常感谢! –

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)和混合数据类型,如“data2005”。