【问题标题】:Passing value in pipeline operator在管道运算符中传递值
【发布时间】:2017-04-21 01:54:28
【问题描述】:

我在 R 中使用两个通过 id 变量连接的表(出于某些原因,我不想合并它们)。示例对象如下所示:

a <- data.frame(id=c(1L,2L,3L),
            var1=c(0,1,3))
b <- data.frame(id=c(1L,1L,2L,2L,3L,3L),
            var2=rnorm(6))

我想要做的是在第一个数据库中查找与 var1 上的给定条件相关的行,仅选择 Id,然后使用这些 id 值过滤数据库 2 中的观察结果。我想知道我是否可以在一个管道中执行此操作,因为如下:

a %>% 
filter(var1==1) %>% 
select(id) %>%
filter(b,id==.)

或者

a %>% 
filter(var1==1) %>% 
select(id) %>% c() %>% unlist()
filter(b,id==.)

这两个示例都不起作用,可能是因为我只能通过管道运算符传递 data.frames 或其他对象,而不能传递原子值。我说的对吗?

【问题讨论】:

    标签: r dplyr pipeline


    【解决方案1】:

    我只是把它变成一个两阶段的过程:

    selected_ids = a %>% filter(var1 == 1) %>% select(id) %>% unlist()
    b %>% filter(id %in% selected_ids)
    #   id       var2
    # 1  2  0.8054040
    # 2  2 -0.5000918
    

    或者合并数据集,直接进行操作:

    merged_data = merge(a, b)
    merged_data %>% filter(var1 == 1)
    #   id var1       var2
    # 1  2    1  0.8054040
    # 2  2    1 -0.5000918
    

    我更喜欢第二种选择。

    【讨论】:

      【解决方案2】:

      我们可以从a 那里得到id var = 1 并选择b 中与id 匹配的所有行

      b[b$id %in% a$id[a$var1 == 1], ]
      
      #  id   var2
      #3  2 1.0294
      #4  2 0.7369
      

      类似的事情可以在dplyr by

      library(dplyr)
      b %>%
      filter(id == a$id[a$var1 == 1])
      

      【讨论】:

      • 我同意这会产生正确的答案。 OP 正在请求基于dplyr 的答案...
      • @PaulHiemstra 更新了答案。
      【解决方案3】:

      其他答案提供了如何获得所需结果的良好解决方案。要回答您在问题末尾提出的问题:

      您的示例不会失败,因为管道在某种程度上受限于它通过的管道。问题在于管道操作员%&gt;% 的实际操作。无论您在哪里使用.,它将左侧的结果作为右侧的第一个参数传递。所以你有filter(b,id==.) 它不会过滤b,它实际上是过滤你之前语句的结果。当您在第一个示例中调用 traceback() 时,您可以看到这一点。如果我们看两个相关的结果:

      ....
      9: filter(., b, id == .)
      ....
      1: a %>% filter(var1 == 1) %>% select(id) %>% filter(b, id == .)
      

      1: 我们看到您的代码,但在 9: 我们看到 R 实际读取的内容。filter(b, id == .) 实际上被读取为 filter(., b, id == .)

      【讨论】:

      • 这里有一个问题是b 应该是group_by 对吧?如果我错了,请纠正我
      • 我不确定你的意思。我认为group_by 不会在这里发挥作用,group_by 主要是如果您在一个 data.frame 中有多个分组变量,而不是用于处理多个 data.frames。
      • 还有人可以解释否决票吗? (最好是反对者)
      【解决方案4】:

      这是一个使用data.table的选项

      setDT(a)[b, on = "id"][var1==1]
      

      或使用dplyr

      left_join(b, a, by = "id") %>% 
                           filter(var1==1)
      

      【讨论】:

      • 由于某些原因我不想加入 - 但在 99% 的情况下,这是一个最佳解决方案。
      【解决方案5】:

      看来您正在寻找semi_join

      a %>% filter(var1 == 1) %>% semi_join(b, ., by = "id")
      #  id       var2
      # 1  2  0.8283845
      # 2  2 -0.5286006
      

      半加入

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

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

      【讨论】:

      • dplyr 只是继续给予。我会在您的回答中添加semi_join 来自dplyr
      • 谢谢,我想通了,但我还是想知道问题的答案。
      【解决方案6】:

      您可以完成这项工作(尽管我同意其他人的观点,我更喜欢某种合并或联接)。

      您可以通过在表达式周围包含括号 ({}) 来绕过包含 . 作为第一个参数的问题。然后,将. 视为data.frame(即使在select 之后也是如此),并调用所需的列。像这样:

      a %>% 
        filter(var1==1) %>% 
        {filter(b,id==.$id)}
      

      返回:

        id       var2
      1  2 -0.2992151
      2  2 -0.4115108
      

      【讨论】:

      • 谢谢,似乎是最好的答案:)
      猜你喜欢
      • 2018-04-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2022-06-29
      • 2017-06-23
      • 2011-03-19
      • 1970-01-01
      相关资源
      最近更新 更多