【问题标题】:How to sum on different intervals to find multi year peaks如何对不同的时间间隔求和以找到多年峰值
【发布时间】:2019-07-11 05:39:47
【问题描述】:

我正在尝试查找商品的历史连续多年销售高峰。我的问题是,有些商品过去已售出并已停产,但仍需要成为分析的一部分。例如:

我已经在 r 中完成了一些 for 循环,但是我不确定如何解决总结连续多年的问题,并将其与同一数据集中的其他局部最大值进行比较。

Year      Item            Sales
2001      Trash Can       100
2002      Trash Can       125
2003      Trash Can       90
2004      Trash Can       97
2002      Red Balloon     23
2003      Red Balloon     309
2004      Red Balloon     67
2005      Red Balloon     8
1998      Blue Bottle     600
1999      Blue Bottle     565

基于以上数据,如果要计算2年的销售高峰,我想输出Blue Bottle 1165(1998年和1999年之和),Red Balloon 376(2003年和2004年之和)和Trash Can 225(2001 年和 2002 年的总和)。但是,如果我想要一个 3 年的峰值,那么蓝瓶将不符合条件,因为它只有 2 年的数据。

如果我想计算 3 年的销售高峰,我想输出 Red Balloon 399(2002 年到 2004 年的总和)和 Trash Can 315(2001 年到 2003 年的总和)。

【问题讨论】:

    标签: sql r


    【解决方案1】:

    将数据dat(在最后的注释中重复显示)读入zoo系列,每个Item一列,然后转换为ts系列tt(这将用NA填充缺失的年份)。然后使用rollsumr 对每个Item 获取每个连续k 年的总和,找到每个Item 的最大值,将其堆叠到数据框中并省略任何NA 行。函数Maxmax(x, na.rm = TRUE) 类似,只是如果x 全部为NA,则返回NA 而不是-Inf,并且不会发出警告。 stack 第二个输出项目列,因此使用 2:1 反转列并添加更好的名称。

    library(zoo)
    
    Max <- function(x) if (all(is.na(x))) NA else max(x, na.rm = TRUE)
    
    peak <- function(data, k) {
      tt <- as.ts(read.zoo(data, split = "Item"))
      s <- na.omit(stack(apply(rollsumr(tt, k), 2, Max)))
      setNames(s[2:1], c("Item", "Sum"))
    }
    
    peak(dat, 2)
    ##          Item  Sum
    ## 1 Blue Bottle 1165
    ## 2 Red Balloon  376
    ## 3   Trash Can  225
    
    peak(dat, 3)
    ##          Item Sum
    ## 2 Red Balloon 399
    ## 3   Trash Can 315
    

    注意

    假设可重现形式的输入为:

    dat <- 
    structure(list(Year = c(2001L, 2002L, 2003L, 2004L, 2002L, 2003L, 
    2004L, 2005L, 1998L, 1999L), Item = c("Trash Can", "Trash Can", 
    "Trash Can", "Trash Can", "Red Balloon", "Red Balloon", "Red Balloon", 
    "Red Balloon", "Blue Bottle", "Blue Bottle"), Sales = c(100L, 
    125L, 90L, 97L, 23L, 309L, 67L, 8L, 600L, 565L)), row.names = c(NA, 
    -10L), class = "data.frame")
    

    【讨论】:

      【解决方案2】:

      在 R 中使用 tidyverseRcppRoll 的解决方案:

      #Loading the packages and your data as a `tibble`
      library("RcppRoll")
      library("dplyr")
      
      tbl <- tribble(
        ~Year,     ~Item,          ~Sales,
        2001,      "Trash Can",       100,
        2002,      "Trash Can",       125,
        2003,      "Trash Can",       90,
        2004,      "Trash Can",       97,
        2002,      "Red Balloon",     23,
        2003,      "Red Balloon",     309,
        2004,      "Red Balloon",      67,
        2005,      "Red Balloon",     8,
        1998,      "Blue Bottle",     600,
        1999,      "Blue Bottle",     565
      )
      
      # Set the number of consecutive years
      n <- 2
      
      # Compute the rolling sums (assumes data to be sorted) and take max
      res <- tbl %>% 
        group_by(Item) %>% 
        mutate(rollingsum = roll_sumr(Sales, n)) %>% 
        summarize(best_sum = max(rollingsum, na.rm = TRUE))
      print(res)
      ## A tibble: 3 x 2
      #  Item        best_sum
      #  <chr>          <dbl>
      #1 Blue Bottle     1165
      #2 Red Balloon      376
      #3 Trash Can        225
      

      设置n &lt;- 3 会产生不同的res

      print(res)
      ## A tibble: 3 x 2
      #  Item        best_sum
      #  <chr>          <dbl>
      #1 Blue Bottle     -Inf
      #2 Red Balloon      399
      #3 Trash Can        315
      

      【讨论】:

        【解决方案3】:

        我只能帮你处理SQL 部分;将GROUP BYHAVING 一起使用。使用HAVIG,它将过滤掉所有没有指定最小历史数据年数的项目。

        检查此查询是否调整了您的要求。

        SELECT 
             item
             , count(*) as num_years
             , sum(Sales) as local_max 
        from [your_table] 
        where year between [year_ini] and [year_end]
        group by item 
        having count(*) >= [number_of_years]
        

        【讨论】:

          【解决方案4】:

          在 SQL 中,您可以使用窗口函数。对于符合条件的 2 年销售:

              select item, sales, year
              from (select t.*,
                           sum(sales) over (partition by item order by year rows between 1 preceding and current row) as two_year_sales,
                           row_number() over (partition by item order by year) as seqnum
                    from t
                   ) t
              where seqnum >= 2;
          

          为了达到顶峰:

          select t.*   
          from (select item, two_year_sales, year,
                       max(two_year_sales) over (partition by item) as max_two_year_sales
                from (select t.*,
                             sum(sales) over (partition by item order by year rows between 1 preceding and current row) as two_year_sales,
                             row_number() over (partition by item order by year) as seqnum
                      from t
                     ) t
                where seqnum >= 2
               ) t
          where two_year_sales = max_two_year_sales;
          

          【讨论】:

          • 谢谢!这行得通!但是,回过头来解析一下,为什么需要“where seqnum >= 2”这行呢?当我注释掉该行时,代码仍然按预期工作。这个 where 子句是为什么指定的?
          • @chow 。 . .否则,它可能会选择第一年。窗口函数将计算总和,即使只有一行进入总和时也是第一行。
          猜你喜欢
          • 2014-11-28
          • 2016-07-05
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多