我正在使用dplyr过滤数据框。问题是列之间的谓词不同。 请在下面找到一个包含三列和三个谓词的最小示例:
library(tidyverse)
set.seed(123)
dframe <- rerun(3, rnorm(5)) %>%
set_names(paste0("var", 1:3)) %>%
data.frame
cond <- c(2, 1, -1.4)
dframe %>% filter(var1 < cond[1] & var2 < cond[2] & var3 > cond[3])
有没有办法过滤数据集而不明确说明filter
中的谓词?
编辑:问题的一个潜在解决方案显然是使用for循环,请参阅下面的代码。但是,可能会有更优雅的解决方案。
dframe_help <- dframe
cond <- c(2, 1, -1.4)
isSmaller <- c(TRUE, TRUE, FALSE)
for(i in seq_along(cond)) {
if (isSmaller[i])
dframe_help <- dframe_help %>% filter_at(.vars = vars(num_range(prefix = "var", range = i)),
.vars_predicate = all_vars(. < cond[i]))
else
dframe_help <- dframe_help %>% filter_at(.vars = vars(num_range(prefix = "var", range = i)),
.vars_predicate = all_vars(. > cond[i]))
}
答案 0 :(得分:2)
您需要某种对象来指定是使用<
还是>
。我创建了一个名为less
的{{1}},1
为<
,>
为0。
require(purrr); require(magrittr)
filter2 <- function(dframe, cond, less){
rows <- pmap(list(cond, less, dframe),
function(cond, less, x) if(less) x < cond else x > cond
) %>%
pmap_lgl(all)
dframe[rows,]
}
dframe %>% filter2(cond = c(2, 1, -1.4), less = c(1, 1, 0))
或者,明确传递要用于每个变量的函数。
filter3 <- function(df, y, fun){
df[pmap(list(df, y, fun), function(x, y, fun) fun(x, y)) %>%
pmap_lgl(all)
,]
}
dframe %>% filter3(y = c(2, 1, -1.4), fun = list(`<`, `<`, `>`))
答案 1 :(得分:0)
不确定您的意思是&#39;自动化&#39;这个过程,但这里有几个选项。
如果您希望以更加清晰的方式过滤多个要素,可以创建独立过滤功能:
cond <- c(2, 1, -1.4)
filter_using_conditions <- function(df) {
df[df$var1 < cond[1] & df$var2 < cond[2] & df$var3 > cond[3],]
}
dframe %>%
filter_using_conditions()
var1 var2 var3
2 0.4978505 -0.2179749 0.8377870
3 -1.9666172 -1.0260044 0.1533731
4 0.7013559 -0.7288912 -1.1381369
5 -0.4727914 -0.6250393 1.2538149
如果要使用运算符和值的向量实现解决方案,可以尝试执行一些字符串操作,并使用base::eval()
或glue::eval()
生成逻辑向量以对数据帧进行子集化。以下是使用purrr:map
和purrr:map2
的示例(它不是很优雅,但希望得到重点):
cond <- c(2, 1, -1.4)
operators <- c("<", "<", ">")
filter_conditions <- function(dframe, conds, operators) {
x <- paste(operators, conds, sep = " ")
rows_to_use <- map2(dframe, x, paste) %>%
map(map_lgl, glue::evaluate, NULL) %>%
as_tibble() %>%
na_if(FALSE) %>%
complete.cases()
dframe[rows_to_use,]
}
filter_conditions(dframe, cond, operators)
var1 var2 var3
2 0.4978505 -0.2179749 0.8377870
3 -1.9666172 -1.0260044 0.1533731
4 0.7013559 -0.7288912 -1.1381369
5 -0.4727914 -0.6250393 1.2538149
此示例使用purrr:map2()
使用指定的运算符条件对为每个数据点生成单个字符串,然后使用glue::evaluate()
和purrr:map2()
将这些字符串作为命令执行并返回逻辑向量。使用dplyr::na_if()
以便稍后使用complete.cases()
来获取与行索引相对应的逻辑向量。
map2(dframe, x, paste)
$var1
[1] "1.78691313680308 < 2" "0.497850478229239 < 2" "-1.96661715662964 < 2" "0.701355901563686 < 2"
[5] "-0.472791407727934 < 2"
$var2
[1] "-1.06782370598685 < 1" "-0.217974914658295 < 1" "-1.02600444830724 < 1" "-0.72889122929114 < 1"
[5] "-0.625039267849257 < 1"
$var3
[1] "-1.68669331074241 > -1.4" "0.837787044494525 > -1.4" "0.153373117836515 > -1.4"
[4] "-1.13813693701195 > -1.4" "1.25381492106993 > -1.4"