【问题标题】:Using a loop (or vectorisation) to subset a list by multiple elements in a vector使用循环(或向量化)通过向量中的多个元素对列表进行子集化
【发布时间】:2016-08-15 11:23:54
【问题描述】:

我有 3 个data.frames 的列表:

my_list <- list(a = data.frame(value = c(1:5), class = c(letters[1:3],"a", "b")), b = data.frame (value = c(6:1),class=c(letters[1:4],"a", "b")),c=data.frame(value = c(1:7),class = c(letters[5:1],"a", "b")))

my_list

$a
  value class
1     1     a
2     2     b
3     3     c
4     4     a
5     5     b

$b
  value class
1     6     a
2     5     b
3     4     c
4     3     d
5     2     a
6     1     b

$c
  value class
1     1     e
2     2     d
3     3     c
4     4     b
5     5     a
6     6     a
7     7     b 

我想进入每个列表并通过class 列中的字母ab 对它们进行子集:

wanted_sub_class <- c("a", "b")

然后根据class 将结果放入my_list 的列表中。

编辑 - 预期输出:

$a class a
    value class
       1     a
       4     a

$a class b 
    value class
       2     b
       5     b

$b class a
    value class
      4     a
      2     a

$b class b
   value class
      5     b
      1     b
$c class a
  value class
    5     a
    6     b

$c class b
  value class
     4     b
     7     b

我尝试过使用双循环:

result <- list()

for (i in 1:length(my_list)) {
  for (j in wanted_sub_class {

    result [[i]] <- subset(my_list[[i]], my_list[[i]]$class == j)

  }
}

这应该给我 6 个列表元素(根据预期的输出),但它只给了 3 个并且只有元素 b

不过,理想情况下,如果确实可行,我想将结果放在my_list 的列表中,每个class。所以我想在列表中保留 3 个 data.frames 的结构,然后有一个列表,其中包含 ab 类的数据 - 否则,六个列表将起作用

我知道循环并不理想,但我无法真正理解 vecortisation(例如使用 lapply)。我将不胜感激循环(如果可能的话)和矢量化的答案。

【问题讨论】:

  • 试试lapply(my_list, function(x) { subset(x, class %in% c("a", "b")) })

标签: r loops vectorization


【解决方案1】:

我想进入每个列表并通过字母 ab 将它们子集来自 class

如果您想将您的data.frames 列表子集为class,您可以简单地这样做:

lapply(my_list, function(x) { subset(x, class %in% c("a", "b")) }) 

这给出了:

#$a
#  value class
#1     1     a
#2     2     b
#
#$b
#  value class
#1     4     a
#2     3     b
# 
#$c
#  value class
#4     4     b
#5     5     a

更新:重新阅读您的问题后,据我了解,您希望通过class 重塑您的实际列表:

但是,理想情况下,我想将结果放在my_list 的列表中 class 但我不知道如何循环执行此操作。

你可以试试:

library(dplyr)

data.table::melt(my_list) %>%
  filter(class %in% c("a", "b")) %>%
  select(class, value) %>%
  split(as.character(.$class))

这给出了:

#$a
#  class value
#1     a     1
#3     a     4
#6     a     5
#
#$b
#  class value
#2     b     2
#4     b     3
#5     b     4

根据@Sumedeh 提到的(在现已删除的评论中),您也可以使用purrr

library(purrr)
my_list %>% 
  map_df(function(x) x[x$class %in% c("a", "b"), ]) %>% 
  split(.$class)

这给出了:

#$a
#  value class
#1     1     a
#3     4     a
#6     5     a

#$b
#  value class
#2     2     b
#4     3     b
#5     4     b

【讨论】:

  • 我在发布答案时没有看到您的 purrr 方法。
  • 是的,我想重塑它,但保留 3 个 data.frames 的原始列表结构,并在每个列表中嵌套一个 a 类和 b 类列表
【解决方案2】:

如果我们使用 Hadleyverse 系列软件包中的 purrr

library(purrr)
my_list %>% 
      map(~ .[.$class %in% wanted_sub_class,])
#$a
#   value class
#1     1     a
#2     2     b

#$b
#  value class
#1     4     a
#2     3     b

#$c
#  value class
#4     4     b
#5     5     a

或者如果输出只需要 'a' 和 'b' list 元素

library(dplyr)
my_list %>%
       bind_rows %>%
       filter(class %in% wanted_sub_class) %>% 
       split(., .$class)
#$a
#  value class
#1     1     a
#3     4     a
#6     5     a

#$b
#  value class
#2     2     b
#4     3     b
#5     4     b

更新

基于 OP 的更新

my_list %>%
       map(~ .[.$class %in% wanted_sub_class,]) %>%
       map(~split(.x, seq_len(nrow(.x)))) %>%
       do.call("c", .)
#$a.1
#  value class
#1     1     a

#$a.2
#  value class
#2     2     b

#$b.1
#  value class
#1     4     a

#$b.2
#  value class
#2     3     b

#$c.1
#  value class
#4     4     b

#$c.2
#  value class
#5     5     a

或使用bind_rows 方法

my_list %>%
    bind_rows %>%
    filter(class %in% wanted_sub_class) %>% 
    split(., seq_len(nrow(.)))

更新2

如果我们需要for 循环

result <- setNames(vector('list', length(my_list)), names(my_list))
for(i in seq_along(my_list)){
  result[[i]] <- subset(my_list[[i]], class %in% wanted_sub_class)
  result[[i]] <- split(result[[i]], 1:nrow(result[[i]]))
 }

更新3

对于新的输出格式

 my_list %>% 
     bind_rows(.id = "id")  %>%
     filter(class %in% wanted_sub_class) %>% 
     split(., list(.$id, .$class))

或者使用for循环

result <- setNames(vector('list', length(my_list)), names(my_list))
for(i in seq_along(my_list)){
  result[[i]] <- subset(my_list[[i]], class %in% wanted_sub_class)
  result[[i]] <- split(result[[i]], result[[i]]$class, drop = TRUE)
}

【讨论】:

  • 我不认为这是 OP 想要的输出
  • @StevenBeaupré 您在帖子中也显示了相同的输出。
  • 是的,这是我对他的要求的初步理解,但重新阅读后,我认为我的答案的第二部分是 OP 想要的。
  • 我喜欢bind_rows而不是data.table::melt +1的方法
  • @Bonono 更新为 for 循环。
猜你喜欢
  • 1970-01-01
  • 2014-08-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-03-30
  • 2015-03-06
  • 1970-01-01
相关资源
最近更新 更多