【问题标题】:Using filter inside filter in dplyr gives unexpected results在 dplyr 中使用过滤器内的过滤器会产生意想不到的结果
【发布时间】:2015-01-14 06:52:01
【问题描述】:

使用R 3.1.2dplyr 0.4.0

我正在尝试在filter 中使用filter,这听起来很简单,但我不明白为什么它没有给我预期的结果。这是我大约 6 个月前编写的代码,我相当确定它可以工作,所以它要么因为更新的 R 版本或 dplyr 或其他一些依赖关系而停止工作。无论如何,这里有一些简单的代码,它根据在 df2 的列上找到 filter 的条件过滤来自 df1 的行。

df1 <- data.frame(x = c("A", "B"), stringsAsFactors = FALSE)
df2 <- data.frame(x = "A", y = TRUE, stringsAsFactors = FALSE)
dplyr::filter(df1, x %in% (dplyr::filter(df2, y)$x))

我希望这会显示df1 的第一行,但我却得到了

# [1] x
# <0 rows> (or 0-length row.names)

我不知道该怎么做。为什么它返回一个向量和一个空的data.frame?

如果我将过滤器代码分成两个单独的语句,我会得到我所期望的

xval <- dplyr::filter(df2, y)$x
dplyr::filter(df1, x %in% xval)

#   x
# 1 A

谁能帮我弄清楚为什么会发生这种行为?我不是说这是一个错误,但我不明白。

【问题讨论】:

  • 我无法真正解释为什么,但就dplyr 语法而言,filter(df1, x %in% (filter(df2, y) %&gt;% .$x)) 似乎更合适,并产生所需的输出
  • 是的,我注意到这是可行的,但它似乎更冗长,对我来说可读性稍差(尽管这是有争议的)。但我会接受你的回答:) 你还回答了我一直有的另一个问题 - 提取列的正确 dplyr 方法是什么?我有时使用filter(df2, y) %&gt;% select(x) %&gt;% as.characterfilter(df2, y) %&gt;% select(x) %&gt;% first
  • @RichardScriven,我假设您使用的是 dplyr 0.4.0?在 0.3.0.2 中它显然不起作用。另一种选择是filter(df2, y) %&gt;% select(x) %&gt;% inner_join(df1)
  • @docendodiscimus - 是的。其实今天才更新
  • @daattali FWIW:(1)当我运行时:filter(df1, x %in% filter(df2, y)$x),我没有得到与你相同的输出,我得到一个错误:Error: incompatible expression in filter。 (2) 代码在data.tables: dt1 &lt;- as.data.table(df1) 上运行良好; dt2 &lt;- as.data.table(df2); dt1 %&gt;% filter(x == (filter(dt2, y)$x)),这可能表明 data.frame 版本中发生的事情是“未预料到的”。

标签: r dplyr


【解决方案1】:

这是一个有效的问题,为什么您的方法不起作用(显然不再起作用)。我不能回答这个问题,但我会建议一种不同的方法,如上所述,它避免了嵌套函数调用(filter inside 另一个 filter),IMO 是 dplyr 的用途:通过易于阅读和理解的语法表达,从左到右,从上到下。

因此,对于您的示例,因为您感兴趣的列都命名为“x”,您可以这样做:

filter(df2, y) %>% select(x) %>% inner_join(df1)
  • 按“y”列过滤 df2 数据
  • 仅选择“x”列
  • 在公用列 ("x") 上使用 df1 执行 inner_join。 inner_join 的意思是:“返回 x 中在 y 中有匹配值的所有行,以及 x 和 y 中的所有列。”

如果它们不同,例如“z”和“x”,您可以使用:

filter(df2, y) %>% select(x) %>% inner_join(df1, by = c("z" = "x"))

正如 Hadley 在下面的评论中所指出的,在这里使用 semi_join 而不是 inner_join 会更安全。文档说:

semi_join: 从 x 返回所有在 y 中有匹配值的行, 只保留来自 x 的列。

半连接与内连接不同,因为内连接将 为 y 的每个匹配行返回一行 x,其中半连接将 永远不要重复 x 行。

因此,您可以为示例案例做:

filter(df2, y) %>% select(x) %>% semi_join(df1)

【讨论】:

  • semi_join()inner_join() 更安全
  • @hadley,这是有道理的。我更新了我的答案并包含了有关 semi_join 的文档。
猜你喜欢
  • 2021-12-28
  • 1970-01-01
  • 1970-01-01
  • 2016-01-28
  • 1970-01-01
  • 1970-01-01
  • 2014-10-23
  • 1970-01-01
  • 2019-09-21
相关资源
最近更新 更多