【问题标题】:Extract elements by name from a nested list从嵌套列表中按名称提取元素
【发布时间】:2016-07-02 20:25:33
【问题描述】:

对于命名的嵌套列表,提取特定元素的最佳方法是什么?如果我有一个包含已知字段的列表(例如,来自 yaml 文件),我想提取一个元素(列表或其他),而无需搜索名称和索引或尝试跟踪 str 中的级别输出。

例如,我知道lm 返回一个包含qr 信息的嵌套列表。

fit <- lm(mpg ~ wt, mtcars)
fit$qr$qraux
# [1] 1.176777 1.046354

但是如果我不知道顺序,我只想指定列表以及元素的名称。理想情况下,一些东西会给我元素的索引路径和元素的名称路径和元素本身的

Relatedrelatedrelated

【问题讨论】:

标签: r list nested-lists


【解决方案1】:

我的递归版本 1 开始出现比我最初想象的更多的错误,所以我采取了一种简单的方法,基本上是 grepping 捕获的utils:::print.ls_str 的输出(我认为)。

到目前为止,这至少有两个缺点:捕获的输出和 eval-parse-texting,但它似乎适用于非常嵌套的列表,例如 ggplot2::ggplotGrob

这些只是一些辅助函数

unname2 <- function(l) {
  ## unname all lists
  ## str(unname2(lm(mpg ~ wt, mtcars)))
  l <- unname(l)
  if (inherits(l, 'list'))
    for (ii in seq_along(l))
      l[[ii]] <- Recall(l[[ii]])
  l
}

lnames <- function(l) {
  ## extract all list names
  ## lnames(lm(mpg ~ wt, mtcars))
  nn <- lpath(l, TRUE)
  gsub('\\[.*', '', sapply(strsplit(nn, '\\$'), tail, 1))
}

lpath <- function(l, use.names = TRUE) {
  ## return all list elements with path as character string
  ## l <- lm(mpg ~ wt, mtcars); lpath(l); lpath(l, FALSE)
  ln <- deparse(substitute(l))
  # class(l) <- NULL
  l <- rapply(l, unclass, how = 'list')
  L <- capture.output(if (use.names) l else unname2(l))
  L <- L[grep('^\\$|^[[]{2,}', L)]
  paste0(ln, L)
}

而这个正在返回有用的信息

lextract <- function(l, what, path.only = FALSE) {
  # stopifnot(what %in% lnames(l))
  ln1 <- eval(substitute(lpath(.l, TRUE), list(.l = substitute(l))))
  ln2 <- eval(substitute(lpath(.l, FALSE), list(.l = substitute(l))))
  cat(ln1[idx <- grep(what, ln1)], sep = '\n')
  cat('\n')
  cat(ln2[idx], sep = '\n')
  cat('\n')
  if (!path.only)
    setNames(lapply(idx, function(x) eval(parse(text = ln1[x]))), ln1[idx])
  else invisible()
}

fit <- lm(mpg ~ wt, mtcars)
lextract(fit, 'qraux')
# fit$qr$qraux
# 
# fit[[7]][[2]]
# 
# [1] 1.176777 1.046354

所以我可以直接使用该返回值,或者现在我有了索引。

fit[[7]][[2]]
# [1] 1.176777 1.046354


## etc
lextract(fit, 'qr', TRUE)

# fit$qr
# fit$qr$qr
# fit$qr$qraux
# fit$qr$pivot
# fit$qr$tol
# fit$qr$rank
# 
# fit[[7]]
# fit[[7]][[1]]
# fit[[7]][[2]]
# fit[[7]][[3]]
# fit[[7]][[4]]
# fit[[7]][[5]]

不过,我更喜欢内置或单线。

【讨论】:

    【解决方案2】:

    这是另一个递归尝试。我不确定应该如何构建输出,但我认为这提供了足够的信息来提取其余部分。

    这里的返回值是索引的向量,以及元素的长度。因此,对于fit 示例,它返回c(inds=7, len=5),对应于fit 中的第7 个位置,并且该元素的长度为5。

    rnames <- function(lst, item) {
      f <- function(ll, inds) {
        if ((ii <- match(item, names(ll), FALSE)))
          list(inds=c(inds, ii), len=length(ll[[ii]]))
        else if (all(is.atomic(unlist(ll, FALSE))) || !is.list(ll))
          NULL
        else
          lapply(seq_along(ll), function(i) f(ll[[i]], inds=c(inds, i)))
      }
      unlist(f(lst, NULL))
    }
    
    rnames(fit, "qr")
    # inds  len 
    #    7    5 
    

    这只会找到第一个匹配项,所以如果一个列表有多个同名元素,它将返回第一个匹配项的索引。一个稍微嵌套的例子,只返回第一个“d”。

    lst <- list(
      "a"=list("b"=1, "c"=2, "d"=list(1:5)), 
      "d"=list("f"=5),
      "g"=list("h"=list("i"=1:5), "k"=list(1:3, list(letters[1:4])))
    )
    
    rnames(lst, "d")
    # inds  len 
    #    2    1 
    

    而且,当有多层嵌套时

    rnames(lst, "k")
    # inds1 inds2   len 
    #     3     2     2 
    
    ## So, that would correspond to 
    lst[[3]][[2]][1:2]
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2023-03-22
      相关资源
      最近更新 更多