2016-04-14 21 views
1

这应该是显而易见的,但我还是没有得到我的脑海围绕lapply及其亲属,而且我还没有找到一个明确而简单的答案,这对SO或Google。因此,与道歉noobness,这里有云:列名tolower的()递归(在dataframes列表)

我有多重的嵌入列表,有点像这样(抓获来自here重复的例子):

lst <- structure(list(Df1 = structure(list(Df1 = structure(list(Date = structure(c(14611, 14612), class = "Date"), Ta_200 = c(10.0067787761421, 5.9095282339839)), .Names = c("Date", "Ta_200"), row.names = c(NA, -2L), class = "data.frame"), Df2 = structure(list(Date = structure(c(14611, 14612), class = "Date"), rH_200 = c(64.9115310510325, 90.8615907551521)), .Names = c("Date", "rH_200"), row.names = c(NA, -2L), class = "data.frame")), .Names = c("Df1", "Df2")), Df2 = structure(list(Df1 = structure(list(Date = structure(c(14642, 14643), class = "Date"), Ta_200 = c(9.91976687351846, 8.79129183854663)), .Names = c("Date", "Ta_200"), row.names = c(NA, -2L), class = "data.frame"), Df2 = structure(list(Date = structure(c(14642, 14643), class = "Date"), rH_200 = c(76.9297879127307, 75.8021788747459)), .Names = c("Date", "rH_200"), row.names = c(NA, -2L), class = "data.frame")), .Names = c("Df1", "Df2"))), .Names = c("Df1", "Df2")) 

的目标是:让所有列名都使用tolower(),为2或3级深度的递归列表(如此处)使用小写字母。我可以遍历列表元素,但我试图按照我在任何地方看到的建议来避免循环,并使用apply系列和dplyr中的东西。

我可以使用lapply第二水平下降这样的:

lapply(lst, function(x) { names(x) <- tolower(names(x)) }) 

但是:(1)我不知道如何递归的第一和第二(和第三)水平做到这一点, (2)我很想念如何实际将新的小写名称写入列表项(上面的行只返回它们)。

+2

'setNames'是有帮助的:'lapply(LST,函数(X){setNames(X,tolower的(名称(X)))})'这只做一次在一个级别,虽然。 – alistaire

回答

3

因为 你对各级所有名称不反对被送回 小写的是递归调用自己会做的伎俩的函数,...

all_to_lower <- function(x) { 
    ## Update the name at the desired level, when present. 
    if (! is.null(names(x))) 
     names(x) <- tolower(names(x)) 
    ## Decide if an iterative step should be used. 
    if (is.list(x) & ! is.data.frame(x)) 
     x <- lapply(
      X = x, 
      FUN = all_to_lower) 
    ## Return to workflow. 
    x 
} 

all_to_lower(lst) 
$df1 
$df1$df1 
     date ta_200 
1 2010-01-02 10.006779 
2 2010-01-03 5.909528 

$df1$df2 
     date rh_200 
1 2010-01-02 64.91153 
2 2010-01-03 90.86159 


$df2 
$df2$df1 
     date ta_200 
1 2010-02-02 9.919767 
2 2010-02-03 8.791292 

$df2$df2 
     date rh_200 
1 2010-02-02 76.92979 
2 2010-02-03 75.80218 

编辑:这是当然可以调整函数,以便只影响数据帧的名称。只需将(! is.null(names(x)))替换为(! is.null(names(x)) & is.data.frame(x))即可。

+0

可爱。我想这就是'rapply'(作为'递归应用')的意思是能够做到的,但我无法让它工作,而你的解决方案是优雅的,概括性很好,适用于我的数据。 – strangeloop

+0

我也试过'rapply',起初有点困惑,为什么它不起作用。但是,正如@alistaire的回答中所评论的那样,'rapply'在这里遇到了问题,因为'data.frame'也是一个'list',然后它“走下来”比我们想要做的还要多一步。对于其他数据类型,我认为'rapply'应该像我们预期的那样工作。 –

1

下面是一个两级问题的解决方案。它有点密集

# double lapply with as.data.frame wrapping second lapply 
noCapsData <- lapply(lst, function(level2) lapply(level2, 
        function(dfnames) setNames(dfnames, tolower(names(dfnames))))) 

此例程检查字符向量,如果您的data.frames中有多个数据类型,这可能会有用。

+0

在示例中提供的'lst'调用时会返回不同的东西。 – alistaire

+0

@alistaire对不起。我读了标题并写了一些内容来改变data.frames的内容。但是,OP在正文中询问变量的名称。我会做出改变。 – lmo

+0

是的,对不起,我已编辑标题以反映问题及其答案。 – strangeloop

0

可以嵌套lapply S的setNames,这取决于你的名单多少层深。如果所有的data.frames都在一个级别,你可以逃脱像

lapply(lst, function(x){lapply(x, function(y){setNames(y, tolower(names(y)))})}) 

如果您有不同程度的data.frames,您可能需要检查你迭代的项目是否有过data.frames:

lapply(lst, function(x){if(is.data.frame(x)){ 
    setNames(x, tolower(names(x))) 
    } else { 
    lapply(x, function(y){if(is.data.frame(y)){ 
     setNames(y, tolower(names(y))) 
    }}) 
}}) 

如果您的列表变得更深,再次迭代。

确实存在rapplylapply的递归版本,但让它在data.frames上正常工作是一个痛苦,因为data.frames是列表。

+0

事实上,我最初认为'rapply'是我所需要的,虽然这可能是真的,但我无法让它工作。我接受了最普遍的解决方案,即@ lars-arne-jordanger的递归函数。 – strangeloop