【问题标题】:recursively change names in nested lists in R递归更改R中嵌套列表中的名称
【发布时间】:2020-07-24 13:43:04
【问题描述】:

我在 R 的嵌套列表结构中有数据,我想使用查找表来更改名称,无论它们在结构中的什么位置。 示例

# build up an example
x <- as.list(c("a" = NA))
x[[1]] <- vector("list", 4)
names(x[[1]]) <- c("b","c","d","e")
x$a$b <- vector("list", 2)
names(x$a$b) <- c("d","f")
x$a$c <- 3
x$a$d <- 27
x$a$e <- "d"
x$a$b$d <- "data"
x$a$b$f <- "more data"

# make a lookup table for names I want to change from; to
lkp <- data.frame(matrix(data = c("a","z","b","bee","d","dee"), 
                         ncol = 2, 
                         byrow = TRUE), stringsAsFactors = FALSE)

names(lkp) <- c("from","to")

上面的输出

> x
$a
$a$b
$a$b$d
[1] "data"

$a$b$f
[1] "more data"


$a$c
[1] 3

$a$d
[1] 27

$a$e
[1] "d"


> lkp
  from  to
1    a   z
2    b bee
3    d dee

这是我想出的第一级:

> for(i in 1:nrow(lkp)){
+   names(x)[names(x) == lkp$from[[i]]] <- lkp$to[[i]]
+ }
> x
$z
$z$b
$z$b$d
[1] "data"

$z$b$f
[1] "more data"


$z$c
[1] 3

$z$d
[1] 27

$z$e
[1] "d"

这样可以正常工作,但使用循环并且只能到达第一级。我尝试了各种版本的 *apply 世界,但还没有得到有用的东西。

提前感谢您的任何想法

编辑: 有趣的是 rapply 在尝试访问和修改名称时惨败(或者,我的尝试惨败!)。这是一个尝试将所有名称更改为相同的示例

> namef <- function(x) names(x) <- "z"
> rapply(x, namef, how = "list")
$a
$a$b
$a$b$d
[1] "z"

$a$b$f
[1] "z"


$a$c
[1] "z"

$a$d
[1] "z"

$a$e
[1] "z"

【问题讨论】:

  • 请问你最后需要什么?你真的需要这样的嵌套列表吗?
  • @Roman,感谢您的提问。是的,我正在导出到 XML,所以我需要维护嵌套结构。大图:我正在将长名称更改为与 xml 标签匹配的短名称。我可以最初只用短名称构建嵌套列表,但我已经用长名称走了这么远,我希望有一种简单的方法来做到这一点。

标签: r list names


【解决方案1】:

我使用character 向量而不是data.frame 进行查找,但如果你真的想要data.frame,更改它很容易。

lkp2 <- lkp$to
names(lkp2) <- lkp$from

rename <- function(nested_list) {
    found <- names(nested_list) %in% names(lkp2)
    names(nested_list)[found] <- lkp2[names(nested_list)[found]]
    nested_list %>% map(~{
        if (is.list(.x)) {
            rename(.x)
        } else {
            .x
        }
    })
}
rename(x)
# $z
# $z$bee
# $z$bee$dee
# [1] "data"
#
# $z$bee$f
# [1] "more data"
#
#
# $z$c
# [1] 3
#
# $z$dee
# [1] 27
#
# $z$e
# [1] "d"

我不确定这是不是最好的方法,但它似乎可以完成这项工作,而且如果您只处理小列表(如 XML 文档),则无需过多担心性能。

您可能希望为函数命名一个更好的名称。

【讨论】:

  • 太棒了!谢谢,我认为这可以解决问题。如果其他人可能想尝试此操作,map 需要 library(purrr) 并且看起来 purrr 也可以处理 %&gt;%(加载 purrr 后我不必加载 magrittr)。
  • 如果你需要坚持原版R,可以使用lapply获得相同的结果。我习惯了使用tidyverse 的优秀部分,而purrr 是其中最好的部分。抱歉,我应该提到我正在使用它。
【解决方案2】:

使用外部包,您也可以使用rrapply-包中的rrapply 执行此操作(基础rapply 的扩展):

library(rrapply)  ## v1.2.1

rrapply(list(x), 
        classes = "list", 
        f = function(x) { 
          newnames <- lkp$to[match(names(x), lkp$from)]
          names(x)[!is.na(newnames)] <- newnames[!is.na(newnames)]
          return(x)
        },
        how = "recurse"
)[[1]]
#> $z
#> $z$bee
#> $z$bee$dee
#> [1] "data"
#> 
#> $z$bee$f
#> [1] "more data"
#> 
#> 
#> $z$c
#> [1] 3
#> 
#> $z$dee
#> [1] 27
#> 
#> $z$e
#> [1] "d"

这里,f 函数实现与 OP 的 for-loop 基本相同。 how = "recurse" 告诉函数在应用f 后继续递归。

请注意,输入被包装为list(x),因此f 函数也会修改列表本身的名称。

【讨论】:

  • 非常有趣,感谢您添加此解决方案。我不知道rrapply。我也喜欢使用match
猜你喜欢
  • 2019-02-15
  • 1970-01-01
  • 2020-10-06
  • 1970-01-01
  • 2015-05-28
  • 1970-01-01
  • 1970-01-01
  • 2017-01-13
  • 1970-01-01
相关资源
最近更新 更多