2017-07-01 81 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」。