【问题标题】:Group_by then filter with dplyrGroup_by 然后用 dplyr 过滤
【发布时间】:2018-03-21 11:33:24
【问题描述】:

首先,请让我知道我正在做的是否是对 dplyr 的使用不当,因为我不确定我是否以最好的方式处理这个问题。我有以下数据框:

mydf = data.frame(user = c(7,7,7,7,7,7,7,8,8,8,8,8,8),
                  col1 = c('0','0','1','1','0','3','NULL','3','3','0','1','0','0'),
                  col2 = runif(n=13),
                  col3 = letters[1:13], 
                  stringsAsFactors = FALSE)

> mydf
   user col1      col2 col3
1     7    0 0.7607907    a
2     7    0 0.1580448    b
3     7    1 0.8063540    c
4     7    1 0.7331512    d
5     7    0 0.2433631    e
6     7    3 0.2357065    f
7     7 NULL 0.4864172    g
8     8    3 0.6806089    h
9     8    3 0.2229874    i
10    8    0 0.6187911    j
11    8    1 0.7617177    k
12    8    0 0.5884821    l
13    8    0 0.4985750    m

我想做的过滤有点冗长,但我会尝试 - 我想通过删除 col1 == '0' 的所有行来过滤数据帧(如果该行出现)在 col1 == '1' 的用户的第一行之后。 (粗体表示我搞砸了原来的问题,并切换了 0 和 1)。

例如,对于用户 7,第 3 行 col1 == '1',所以我想过滤第 3 行之后 col1 == '0' 的所有行(在这种情况下,只有第 5 行)。然后,对于用户 8,第 11 行是该用户的第一行 col1 == '1',所以我想过滤第 12 行和第 13 行,因为 col1 == '0'。

我的最终输出应该是这样的:

> mydf
   user col1      col2 col3
1     7    0 0.7607907    a
2     7    0 0.1580448    b
3     7    1 0.8063540    c
4     7    1 0.7331512    d
6     7    3 0.2357065    f
7     7 NULL 0.4864172    g
8     8    3 0.6806089    h
9     8    3 0.2229874    i
10    8    0 0.6187911    j
11    8    1 0.7617177    k

我尝试了以下方法,但没有成功。我认为添加一个 rownums 列,然后按用户分组,然后过滤我所描述的方式会起作用。我的想法是我的过滤器调用有问题:

mydf %>%
  mutate(rownums = 1:nrow(mydf)) %>%
  group_by(user) %>%
  filter(!(col1 == "0" & rownums > min(which(col1 == "1"))))

# A tibble: 9 x 5
# Groups:   col0 [2]
   user  col1      col2  col3 rownums
  <dbl> <chr>     <dbl> <chr>   <int>
1     7     0 0.2088034     a       1
2     7     0 0.2081894     b       2
3     7     1 0.1825428     c       3
4     7     1 0.2143353     d       4
5     7     3 0.1979774     f       6
6     7  NULL 0.2990799     g       7
7     8     3 0.7808038     h       8
8     8     3 0.1694272     i       9
9     8     1 0.1526450     k      11

此输出与正确输出之间的区别在于,此输出还错误地过滤了原始数据帧的第 10 行。

对此的任何帮助表示赞赏!

编辑 - 如果 group_by() %>% filter() 在 R 中对于 dplyr 是不好的做法,我特别好奇。我的 group_by() 的 99% 后面是 summarise(),这显然更有意义。

EDIT2 - 我想我明白了!

mydf %>%
  group_by(col0) %>%
  mutate(rownums = 1:length(col0)) %>%
  filter(!(col1 == "0" & rownums > min(which(col1 == "1"))))

只需翻转 mutate() 和 group_by() 调用的顺序,并稍微调整 mutate() 调用,看起来就完成了。不过,我愿意听取更好的方法。

【问题讨论】:

    标签: r dplyr


    【解决方案1】:

    有一个cumany 函数,对这些顺序条件很有用,如下所示:

    mydf %>%
      group_by(user) %>% 
      mutate(seen_one = cumany(col1 == "1")) %>%
      filter(!seen_one | col1 != "0")
    

    即标记"1" 已在“流”中seen_one 之后的所有行,然后保留不满足其中一个条件的行。 (filter 的语义需要反转条件以“摆脱”行,!(A &amp; B) == !A | !B。)

    【讨论】:

    • 我的想法很棒,尽管输出与 OP 的预期有很大不同
    • OP 不是很符合要求,他说先删除 col1 == 1 的行,然后删除 col1 == 0 的第 12 行和第 13 行。
    • 就像我说的过滤很罗嗦,乍一看我把它搞砸了
    • 好的,我的代码是否符合您的预期?如果没有,请修正您的“冗长”描述。
    • 它已修复,但我想要的输出仍然是发布的内容。
    【解决方案2】:

    这是dplyr的一个想法

    library(dplyr)
    
    df %>% 
     group_by(user) %>% 
     mutate(id1 = row_number(), new_col = max(which(col1 == 1)+1)) %>% 
     filter(!(col1 == 0 & id1 >= new_col))
    

    给出,

    # A tibble: 10 x 6
    # Groups:   user [2]
        user  col1       col2  col3   id1 new_col
       <dbl> <chr>      <dbl> <chr> <int>   <dbl>
     1     7     0 0.54742608     a     1       5
     2     7     0 0.89271859     b     2       5
     3     7     1 0.48999057     c     3       5
     4     7     1 0.17163211     d     4       5
     5     7     3 0.96146770     f     6       5
     6     7  NULL 0.31368382     g     7       5
     7     8     3 0.82051455     h     1       5
     8     8     3 0.30705440     i     2       5
     9     8     0 0.18545358     j     3       5
    10     8     1 0.04834678     k     4       5
    

    【讨论】:

    • 我喜欢在 mutate() 中创建附加列的想法,而不是使用过于复杂的 filter()。 mutate() %>% filter() 使过滤器更易于阅读。
    【解决方案3】:

    可以通过稍微更新您的尝试来解决:

    library(dplyr)
    mydf %>%
       group_by(user) %>%
       filter(col1 != 0 | row_number() < which.max(col1 == 1))
    
    
    #    user   col1        col2  col3
    #   <dbl> <chr>       <dbl> <chr>
    # 1     7     0 0.756522673     a
    # 2     7     0 0.168314555     b
    # 3     7     1 0.977254798     c
    # 4     7     1 0.722721694     d
    # 5     7     3 0.407849378     f
    # 6     7  NULL 0.245335151     g
    # 7     8     3 0.003423735     h
    # 8     8     3 0.191716738     i
    # 9     8     0 0.626846893     j
    #10     8     1 0.546459621     k
    

    使用filter,我们选择col1 不等于0 的所有行或当前行小于该组第一次出现1 的索引的那些行。

    【讨论】:

      【解决方案4】:

      或者,以@liborm 的回答提供的方向为基础:

      mydf %>%
        group_by(user) %>%
        mutate(k = cumany(col1 == '0'), j = cumany(col1 == '1')) %>%
        filter(!(col1 == 0 & k == TRUE & j == TRUE)) %>%
        select(-k,-j)
      

      返回:

          user  col1  col2  col3
         <dbl> <chr> <dbl> <chr>
       1     7     0     1     a
       2     7     0     1     b
       3     7     1     0     c
       4     7     1     0     d
       5     7     3     0     f
       6     7  NULL     1     g
       7     8     3     0     h
       8     8     3     1     i
       9     8     0     1     j
      10     8     1     0     k
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2022-08-18
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多