如何删除嵌套列表中的元素?

3

How can I go from

x <- list(p1 = list(type='A',score=list(c1=10,c2=8,c3=data.frame(a=1, b=3, c=5))),
       p2 = list(type='B',score=list(c1=9,c2=9,c3=data.frame(a=2, b=2))),
       p3 = list(type='B',score=list(c1=9,c2=7,c3=data.frame(a=2, b=2))))

如何从数据框中删除“c3”元素,以便将其转换为列表?
最好使用tidyverse或可以放在管道中间的方法来完成。
我已经尝试过list.remove、嵌套lapply、rapply和Filter,但似乎无法使它们正常工作...而且我不想取消嵌套列表结构。
(编辑:抱歉,在我的原始问题样本数据中有一个拼写错误(请参见下文),但如果您的解决方案适用于这两种情况,则非常好!)
x <- list(p1 = list(type='A',score=list(c1=10,c2=8,c3=data.frame(a=1, b=3, c=5))),
       p2 = list(type='B',score=list(c1=9,c2=9,c3=data.frame(a=2, b=2)),
       p3 = list(type='B',score=list(c1=9,c2=7,c3=data.frame(a=2, b=2)))))

你是否打算将 p3 放在 p2 元素内而不是在 p 层级上? - Parfait
哎呀,你说得对,我是想让p1、p2和p3处于同一级别,但无论如何,我更喜欢适用于任何复杂嵌套的解决方案。 - Arthur Yip
3个回答

2
你可以编写自己的函数来实现这个功能:

最初的回答

check = function(x,name){
  m = names(x)%in% name
  x = if(any(m)) x[!m] else x
  if(is.list(x)) sapply(x,check,name)
  else x
}

dput(check(x,'c3'))
list(p1 = list(type = "A", score = list(c1 = 10, c2 = 8)), p2 = list(
    type = "B", score = list(c1 = 9, c2 = 9), p3 = list(type = "B", 
        score = list(c1 = 9, c2 = 7))))

这也可以向量化,它可以丢弃你不需要的所有内容。例如尝试 check(x,c('c1','c3')),这是最初的回答。

酷,我应该在编程课上更多地练习递归!我改进了你的答案,以便保持列表结构和准则为is.data.frame而不是其他。 removeDataFramesFromList <- function(x) { m <- sapply(x, is.data.frame) x <- if(any(m)) x[!m] else x if(is.list(x)) lapply(x, check) else x } - Arthur Yip

2
这是使用`modify_depth`的正确场景,它可作为对深度嵌套列表进行一系列`modify`操作的快捷方式。在这个问题中,`modify`比`map`更有优势,因为它会保留输入的类型,而不是将所有内容强制转换为列表,如果您的列表结构中有向量元素,这可能是相关的。
使用您提供的输入(其中包含一个`p3`元素,而不是与`p2`处于同一级别),如下所示丢弃第二层和第三层的数据框元素。为了搜索嵌套列表的所有级别,我们可以设置一个`while`循环来迭代各级别,丢弃数据框时继续进行。我们需要设置`.ragged = TRUE`以处理列表深度错误。此版本从底部向上搜索,但您也可以将其更改为从顶部向下搜索。
library(tidyverse)
x <- list(
  p1 = list(type = "A", score = list(c1 = 10, c2 = 8, c3 = data.frame(a = 1, b = 3, c = 5))),
  p2 = list(
    type = "B", score = list(c1 = 9, c2 = 9, c3 = data.frame(a = 2, b = 2)),
    p3 = list(type = "B", score = list(c1 = 9, c2 = 7, c3 = data.frame(a = 2, b = 2)))
  )
)

remove_dataframes <- function(input_list) {
  current_list <- input_list
  current_depth <- vec_depth(current_list)
  # current_depth <- max_depth
  while (current_depth > 1) {
    current_list <- modify_depth(
      .x = current_list,
      .depth = current_depth,
      .f = ~ discard(., is.data.frame),
      .ragged = TRUE
    )
  current_depth <- current_depth - 1
  }
  return(current_list)
}

x %>%
  remove_dataframes %>%
  glimpse
#> List of 2
#>  $ p1:List of 2
#>   ..$ type : chr "A"
#>   ..$ score:List of 2
#>   .. ..$ c1: num 10
#>   .. ..$ c2: num 8
#>  $ p2:List of 3
#>   ..$ type : chr "B"
#>   ..$ score:List of 2
#>   .. ..$ c1: num 9
#>   .. ..$ c2: num 9
#>   ..$ p3   :List of 2
#>   .. ..$ type : chr "B"
#>   .. ..$ score:List of 2

创建于2019年2月20日,使用reprex package(v0.2.1)生成。

你是指最低层级的 p3 中的那一个,还是其他什么? - Calum You
是的,我正在尝试摆脱所有c3数据框。我尝试使用您的代码处理p1 p2 p3位于同一级别的数据集,但在两种情况下,我都得到了“Error in range[1] : object of type 'closure' is not subsettable”的错误提示。 - Arthur Yip
已更新以解决多层问题。 - Calum You
出于某种原因,我仍然收到“范围[1]中的错误:类型为'closure'的对象不可子集化”的错误。我知道你正在使用reprex,所以这很奇怪...感谢向我展示purrr函数modify、modify_depth和discard。 - Arthur Yip

2

这里有另一种替代方法,使用rrapply()rrapply包中,适用于任意嵌套级别:

library(rrapply)

x1 <- rrapply(x, condition = function(x, .xparents) !any(.xparents == "c3"), how = "prune")
str(x1)  
#> List of 3
#>  $ p1:List of 2
#>   ..$ type : chr "A"
#>   ..$ score:List of 2
#>   .. ..$ c1: num 10
#>   .. ..$ c2: num 8
#>  $ p2:List of 2
#>   ..$ type : chr "B"
#>   ..$ score:List of 2
#>   .. ..$ c1: num 9
#>   .. ..$ c2: num 9
#>  $ p3:List of 2
#>   ..$ type : chr "B"
#>   ..$ score:List of 2
#>   .. ..$ c1: num 9
#>   .. ..$ c2: num 7

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接