【问题标题】:Selecting rows from a data frame from combinations of lists given by another dataframe [duplicate]从另一个数据框给出的列表组合中选择数据框中的行[重复]
【发布时间】:2017-09-22 03:33:15
【问题描述】:

我有一个数据框,dat:

dat<-data.frame(col1=rep(1:4,3),
                col2=rep(letters[24:26],4),
                col3=letters[1:12])

我想仅使用数据框filter 中的行给出的组合在两个不同的列上过滤dat

filter<-data.frame(col1=1:3,col2=NA)
lists<-list(list("x","y"),list("y","z"),list("x","z"))
filter$col2<-lists

因此,例如,将选择包含 (1,x) 和 (1,y) 的行,但不会选择 (1,z)、(2,x) 或 (3,y)。

我知道如何使用 for 循环:

#create a frame to drop results in
results<-dat[0,]
for(f in 1:nrow(filter)){
  temp_filter<-filter[f,]
  temp_dat<-dat[dat$col1==temp_filter[1,1] &
                dat$col2%in%unlist(temp_filter[1,2]),]
  results<-rbind(results,temp_dat)
}

或者如果你更喜欢dplyr 风格:

require(dplyr)
results<-dat[0,]
for(f in 1:nrow(filter)){
  temp_filter<-filter[f,]
  temp_dat<-filter(dat,col1==temp_filter[1,1] & 
  col2%in%unlist(temp_filter[1,2])
  results<-rbind(results,temp_dat)
}

结果应该返回

  col1 col2 col3
1    1    x    a
5    1    y    e
2    2    y    b
6    2    z    f
3    3    z    c
7    3    x    g

我通常会使用合并进行过滤,但我现在不能,因为我必须根据列表而不是单个值检查 col2。 for 循环有效,但我认为会有更有效的方法来执行此操作,可能使用applydo.call 的一些变体。

【问题讨论】:

  • 最简单的答案是不要使用 filter 的格式,而是使用您上一个问题的答案 - stackoverflow.com/questions/46354932/…
  • 所以实际上,filter 的第 2 列中的列表是 10s 长的元素,因此通过合并进行过滤是行不通的。我知道过滤器的数据框可能不是最好的解决方案,但我需要将每个列表与单个元素配对,并同时按该元素和不同列上的配对列表过滤,我不相信前面的答案会请允许我这样做。
  • 如答案所示,它可以通过半连接实现,或者实际上可以通过像merge(dat, unique(filter2))这样的简单合并来实现

标签: r dataframe filter apply do.call


【解决方案1】:

使用tidyverse 的解决方案。 dat2 是最终输出。这个想法是从filter数据框的列表列中提取值。将filter 数据帧转换为filter2 格式,其中col1col2 列在dat 数据帧中具有相同的组件。最后,使用semi_join过滤dat创建dat2

顺便说一下,filterdplyr 包中的预定义函数。在您的示例中,您使用了dplyr 包,因此最好避免将数据框命名为filter

library(tidyverse)

filter2 <- filter %>%
  mutate(col2_a = map_chr(col2, 1),
         col2_b = map_chr(col2, 2)) %>%
  select(-col2) %>%
  gather(group, col2, -col1)

dat2 <- dat %>%
  semi_join(filter2, by = c("col1", "col2")) %>%
  arrange(col1)
dat2
  col1 col2 col3
1    1    x    a
2    1    y    e
3    2    y    b
4    2    z    f
5    3    z    c
6    3    x    g

更新

另一种准备filter2 包的方法,它不需要知道每个列表中有多少元素。其余同上一个方案。

library(tidyverse)

filter2 <- filter %>%
  rowwise() %>%
  do(data_frame(col1 = .$col1, col2 = flatten_chr(.$col2)))

dat2 <- dat %>%
  semi_join(filter2, by = c("col1", "col2")) %>%
  arrange(col1)

【讨论】:

  • filter 是 R 标准 stats::filter 中用于线性过滤的预定义函数,所以这是双重打击。
  • @thelatemail 感谢您分享此信息。
  • 谢谢!这是一个很好的解决方案,但它是否只有在 filter col2 中的每个列表都是 2 个元素长时才有效?对于我正在处理的问题,列表实际上从 5 到 30 多个元素不等,有什么想法吗?
  • @DevrajKori 请查看我的更新。我相信更新后的解决方案不需要知道每个列表中有多少元素。
【解决方案2】:

一旦您将filter 列表恢复为标准data.frame,就可以直接加入:

merge(
  dat,
  with(filter, data.frame(col1=rep(col1, lengths(col2)), col2=unlist(col2)))
)

#  col1 col2 col3
#1    1    x    a
#2    1    y    e
#3    2    y    b
#4    2    z    f
#5    3    x    g
#6    3    z    c

可以说,我首先会取消创建这些嵌套列表的任何过程。

【讨论】:

  • 谢谢!所以我的印象是,使用 %in% 运算符进行括号过滤比取消列出 col 2 以创建 2 列数据框然后合并更有效,不是这样吗?
  • @DevrajKori - 如果不运行基准测试,我无法确定,但我想循环和检查每个 list%in% 将比直接合并成本更高。将这种类型的逻辑转换为 dplyr/data.table/sql/python 等也更容易,因为它只是一个简单的连接操作。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-10-27
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-06-29
  • 1970-01-01
相关资源
最近更新 更多