从另一个数据框给出的列表组合中选择数据框中的行
Selecting rows from a data frame from combinations of lists given by another dataframe
我有一个数据框,数据:
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)
}
结果应该return
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 循环有效,但我认为会有更有效的方法来执行此操作,可能使用 apply
或 do.call
.
的一些变体
使用 tidyverse
的解决方案。 dat2
是最终输出。这个想法是从 filter
数据框的列表列中提取值。将 filter
数据框转换为 filter2
格式,其中 col1
和 col2
列在 dat
数据框中具有相同的组件。最后用semi_join
过滤dat
得到dat2
.
顺便说一下,filter
是 dplyr
包中的预定义函数。在您的示例中,您使用了 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
列表恢复为标准 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
可以说,我会首先取消创建这些嵌套列表的任何过程。
我有一个数据框,数据:
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)
}
结果应该return
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 循环有效,但我认为会有更有效的方法来执行此操作,可能使用 apply
或 do.call
.
使用 tidyverse
的解决方案。 dat2
是最终输出。这个想法是从 filter
数据框的列表列中提取值。将 filter
数据框转换为 filter2
格式,其中 col1
和 col2
列在 dat
数据框中具有相同的组件。最后用semi_join
过滤dat
得到dat2
.
顺便说一下,filter
是 dplyr
包中的预定义函数。在您的示例中,您使用了 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
列表恢复为标准 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
可以说,我会首先取消创建这些嵌套列表的任何过程。