【问题标题】:Getting top_n when multiple groups and keep subgroups多个组时获取top_n并保留子组
【发布时间】:2017-04-24 20:06:41
【问题描述】:

我有一个 df,其中包含 main 中每个子组的营业额。现在我想获得营业额最高的两个主要群体。

df <- data.frame(
  grp = gl(5, 5, labels = c("A", "B", "C", "D", "E")),
  sub_grp = gl(5, 1),
  turnover = rnorm(25, mean = 100, sd = 15))
 > df
   grp sub_grp  turnover
1    A       1  98.14430
2    A       2 107.90811
3    A       3 103.93973
4    A       4  95.78222
5    A       5  63.19635
6    B       1  97.85688
7    B       2  92.65572
8    B       3  86.02872
9    B       4 101.88177
10   B       5 120.66959
11   C       1 125.93533
12   C       2  98.49771
13   C       3  77.28770
14   C       4 101.44822
15   C       5 107.08171
16   D       1  77.73252
17   D       2 107.49374
18   D       3  87.46436
19   D       4 101.49984
20   D       5  99.13047
21   E       1  91.48636
22   E       2 115.63716
23   E       3  99.34567
24   E       4 104.65408
25   E       5 121.41820

我知道如何获得两个营业额最高的主要群体,但不知道如何让我的子群体和营业额仍然分散在子群体上。

df %>%
  group_by(grp) %>%
  summarise(total.turnover = sum(turnover)) %>%
  top_n(n = 2)

     grp total.turnover
  (fctr)          (dbl)
1      C       510.2507
2      E       532.5415

我想从这个例子中得到结果。

   grp sub_grp  turnover
1    C       1 125.93533
2    C       2  98.49771
3    C       3  77.28770
4    C       4 101.44822
5    C       5 107.08171
6    E       1  91.48636
7    E       2 115.63716
8    E       3  99.34567
9    E       4 104.65408
10   E       5 121.41820

【问题讨论】:

    标签: r dplyr


    【解决方案1】:

    以下是dplyr 的几种不同方法。

    重新加入原始对象

    df %>%
        group_by(grp) %>%
        summarise(total.turnover = sum(turnover)) %>%
        top_n(n = 2) %>% 
        inner_join(df, by = "grp") %>% 
        select(grp, sub_grp, turnover)
    # # A tibble: 10 × 3
    #       grp sub_grp  turnover
    #    <fctr>  <fctr>     <dbl>
    # 1       A       1  91.59287
    # 2       A       2  96.54734
    # 3       A       3 123.38062
    # 4       A       4 101.05763
    # 5       A       5 101.93932
    # 6       C       1 118.36123
    # 7       C       2 105.39721
    # 8       C       3 106.01157
    # 9       C       4 101.66024
    # 10      C       5  91.66238
    

    使用窗口函数 (dense_rank)

    df %>%
        group_by(grp) %>%
        mutate(total.turnover = sum(turnover)) %>%
        ungroup() %>%
        filter(dense_rank(desc(total.turnover)) < 3) %>%
        select(grp, sub_grp, turnover)
    # # A tibble: 10 × 3
    #       grp sub_grp  turnover
    #    <fctr>  <fctr>     <dbl>
    # 1       A       1  91.59287
    # 2       A       2  96.54734
    # 3       A       3 123.38062
    # 4       A       4 101.05763
    # 5       A       5 101.93932
    # 6       C       1 118.36123
    # 7       C       2 105.39721
    # 8       C       3 106.01157
    # 9       C       4 101.66024
    # 10      C       5  91.66238
    

    使用data.table(类似于dplyr开窗函数方式)

    library(data.table)
    dt <- data.table(df)
    
    dt[,total.turnover := sum(turnover), by = .(grp)
        ][,rank := frank(-total.turnover, ties.method = "dense")
        ][rank < 3, .(grp, sub_grp, turnover)]
    #     grp sub_grp  turnover
    #  1:   A       1  91.59287
    #  2:   A       2  96.54734
    #  3:   A       3 123.38062
    #  4:   A       4 101.05763
    #  5:   A       5 101.93932
    #  6:   C       1 118.36123
    #  7:   C       2 105.39721
    #  8:   C       3 106.01157
    #  9:   C       4 101.66024
    # 10:   C       5  91.66238
    

    library(dplyr)
    set.seed(123)
    
    df <- data.frame(
        grp = gl(5, 5, labels = c("A", "B", "C", "D", "E")),
        sub_grp = gl(5, 1),
        turnover = rnorm(25, mean = 100, sd = 15)
    )
    

    【讨论】:

      【解决方案2】:

      一个选项是 dplyr,我们在汇总输出对象上使用 filter

      df %>% 
        filter(grp %in% df1$grp)
      

      其中 'df1' 是汇总的输出对象


      或者如果我们想要在同一个链中

      df %>% 
        group_by(grp) %>% 
        summarise(val = sum(turnover)) %>%
        top_n(2)  %>%
        semi_join(df, .) 
      #   grp sub_grp  turnover
      #1    C       1 125.93533
      #2    C       2  98.49771
      #3    C       3  77.28770
      #4    C       4 101.44822
      #5    C       5 107.08171
      #6    E       1  91.48636
      #7    E       2 115.63716
      #8    E       3  99.34567
      #9    E       4 104.65408
      #10   E       5 121.41820
      

      或者另一种单行选项是data.table

      library(data.table)
      setDT(df)[grp %in% df[, sum(turnover), grp][order(-V1), head(grp, 2)]]
      #      grp sub_grp  turnover
      # 1:   C       1 125.93533
      # 2:   C       2  98.49771
      # 3:   C       3  77.28770
      # 4:   C       4 101.44822
      # 5:   C       5 107.08171
      # 6:   E       1  91.48636
      # 7:   E       2 115.63716
      # 8:   E       3  99.34567
      # 9:   E       4 104.65408
      #10:   E       5 121.41820
      

      或者我们可以使用 base R

      轻松做到这一点
      subset(df, grp %in% names(tail(sort(xtabs(turnover~grp , df)),2)))
      #   grp sub_grp  turnover
      #11   C       1 125.93533
      #12   C       2  98.49771
      #13   C       3  77.28770
      #14   C       4 101.44822
      #15   C       5 107.08171
      #21   E       1  91.48636
      #22   E       2 115.63716
      #23   E       3  99.34567
      #24   E       4 104.65408
      #25   E       5 121.41820
      

      数据

      df <- structure(list(grp = c("A", "A", "A", "A", "A", "B", "B", "B", 
      "B", "B", "C", "C", "C", "C", "C", "D", "D", "D", "D", "D", "E", 
      "E", "E", "E", "E"), sub_grp = c(1L, 2L, 3L, 4L, 5L, 1L, 2L, 
      3L, 4L, 5L, 1L, 2L, 3L, 4L, 5L, 1L, 2L, 3L, 4L, 5L, 1L, 2L, 3L, 
      4L, 5L), turnover = c(98.1443, 107.90811, 103.93973, 95.78222, 
      63.19635, 97.85688, 92.65572, 86.02872, 101.88177, 120.66959, 
      125.93533, 98.49771, 77.2877, 101.44822, 107.08171, 77.73252, 
      107.49374, 87.46436, 101.49984, 99.13047, 91.48636, 115.63716, 
      99.34567, 104.65408, 121.4182)), .Names = c("grp", "sub_grp", 
      "turnover"), 
       class = "data.frame", row.names = c(NA, -25L), 
       index = structure(integer(0), "`__grp`" = integer(0)))
      

      【讨论】:

      • 我不能在一个链中完成这个?
      • @TomasEricsson 抱歉,我不在。我更新了一些紧凑的选项
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2016-12-14
      • 2023-03-23
      • 1970-01-01
      • 2017-05-03
      • 2015-12-27
      • 2011-03-14
      相关资源
      最近更新 更多