【问题标题】:R: Conditional cumulative sum/rollover along a columnR:沿列的条件累积总和/翻转
【发布时间】:2021-08-31 16:18:12
【问题描述】:

我有一个数据集,我试图在其中探索将变量限制在给定值并将超出部分滚动到后续区间的影响。从概念上讲,我可以看到一些使用 cumsum() 或类似方法的方法,但很难了解如何以合乎逻辑的方式实现它。

输入数据不大(10,000 行,而不是 100,000 行);所以效率并不重要。

表示输入数据:

interval starting kWh
2021-01-01 19:00 12.2
2021-01-01 19:30 14.7
2021-01-01 20:00 20.2
2021-01-01 20:30 30.7
2021-01-01 21:00 36.3
2021-01-01 21:30 36.7
2021-01-01 22:00 30.1
2021-01-01 22:30 26.3
2021-01-01 23:00 18.1
2021-01-01 23:30 15.8
2021-01-02 00:00 11.4
2021-01-02 00:30 10.2
2021-01-02 01:00 11.9
2021-01-02 01:30 12.3
2021-01-02 02:00 9.1
2021-01-02 02:30 8.6
2021-01-02 03:00 8.3
2021-01-02 03:30 10.1

而我想要做的是将 kWh 列中的值限制为最大 20.0;如果该值超过我想将超出部分滚动到下一个间隔,然后是下一个,依此类推,直到所有能量都被考虑在内(因此在足够宽的间隔内的总和始终相同),但峰值永远不会超过限制。

期望的输出:

interval starting kWh limit_kWh
2021-01-01 19:00 12.2 12.2
2021-01-01 19:30 14.7 14.7
2021-01-01 20:00 20.2 20.0
2021-01-01 20:30 30.7 20.0
2021-01-01 21:00 36.3 20.0
2021-01-01 21:30 36.7 20.0
2021-01-01 22:00 30.1 20.0
2021-01-01 22:30 26.3 20.0
2021-01-02 23:00 18.1 20.0
2021-01-02 23:30 15.8 20.0
2021-01-02 00:00 11.4 20.0
2021-01-02 00:30 10.2 20.0
2021-01-02 01:00 11.9 20.0
2021-01-02 01:30 12.3 20.0
2021-01-02 02:00 9.1 20.0
2021-01-02 02:30 8.6 17.7
2021-01-02 03:00 8.3 8.3
2021-01-02 03:30 10.1 10.1

因此,在这个时间段内,总能量是相同的,但峰值能量永远不会超过指定的限制。

任何帮助将不胜感激!

【问题讨论】:

    标签: r


    【解决方案1】:

    这只是一个基本循环,可以满足您的需求。它不是特别有效,但我想不出使用矢量化使其更快的好方法。

    overflow <- 0
    for (i in 1:nrow(d)) {
      if (d$kWh[i] + overflow > 20) {
        d$limit_kWh[i] <- 20
        overflow <- d$kWh[i] + overflow - 20
      }
      else {
        d$limit_kWh[i] <- d$kWh[i] + overflow
        overflow <- 0
      }
    }
    

    基本上超过 20 的数量(如果有)存储在 overflow 变量中,该变量在每个条目处更新。


    实际上,这是一种速度快约 2 倍的方法,它更多地依赖于矢量化。它涉及创建一个 overflow 向量,其中包含上一个日期的溢出量。

    overflow <- numeric(nrow(d))
    for (i in 2:nrow(d)) {
      overflow[i] <- max(d$kWh[i-1] + overflow[i-1] - 20, 0)
    }
    d$limit_kWh <- pmin(d$kWh + overflow, 20)
    

    【讨论】:

    • 谢谢!这很好用,我使用了矢量化版本。对于我大约 8,000 行的初始数据集,速度是可以接受的(相当快)。稍后将在一些更大的数据集上使用它。
    【解决方案2】:

    一种方法是将Reduceaccumulate 一起使用。该方法与@Noah给出的答案相同。

    x$limit_kWh <- pmin(20, x$kWh + head(Reduce(function(x, y)
        {max(0, x + y - 20)}, x$kWh, 0, accumulate = TRUE), -1))
    x
    #   interval starting  kWh limit_kWh
    #1   2021-01-01 19:00 12.2      12.2
    #2   2021-01-01 19:30 14.7      14.7
    #3   2021-01-01 20:00 20.2      20.0
    #4   2021-01-01 20:30 30.7      20.0
    #5   2021-01-01 21:00 36.3      20.0
    #6   2021-01-01 21:30 36.7      20.0
    #7   2021-01-01 22:00 30.1      20.0
    #8   2021-01-01 22:30 26.3      20.0
    #9   2021-01-01 23:00 18.1      20.0
    #10  2021-01-01 23:30 15.8      20.0
    #11  2021-01-02 00:00 11.4      20.0
    #12  2021-01-02 00:30 10.2      20.0
    #13  2021-01-02 01:00 11.9      20.0
    #14  2021-01-02 01:30 12.3      20.0
    #15  2021-01-02 02:00  9.1      20.0
    #16  2021-01-02 02:30  8.6      17.7
    #17  2021-01-02 03:00  8.3       8.3
    #18  2021-01-02 03:30 10.1      10.1
    

    数据:

    x <- read.table(header = TRUE, check.names =  FALSE,
                    text = '"interval starting"     kWh
    "2021-01-01 19:00"  12.2
    "2021-01-01 19:30"  14.7
    "2021-01-01 20:00"  20.2
    "2021-01-01 20:30"  30.7
    "2021-01-01 21:00"  36.3
    "2021-01-01 21:30"  36.7
    "2021-01-01 22:00"  30.1
    "2021-01-01 22:30"  26.3
    "2021-01-01 23:00"  18.1
    "2021-01-01 23:30"  15.8
    "2021-01-02 00:00"  11.4
    "2021-01-02 00:30"  10.2
    "2021-01-02 01:00"  11.9
    "2021-01-02 01:30"  12.3
    "2021-01-02 02:00"  9.1
    "2021-01-02 02:30"  8.6
    "2021-01-02 03:00"  8.3
    "2021-01-02 03:30"  10.1')
    

    【讨论】:

    • 不错的单行答案!
    【解决方案3】:

    我把@Noah 的基本逻辑放到了datastep() 中。这是相同的结果,并且不比 for 循环更有效。但它更容易阅读。

    这是输入数据:

    # Input data
    dt <- read.table(header = TRUE, text = '
    interval_starting   kWh
    "2021-01-01 19:00"  12.2
    "2021-01-01 19:30"  14.7
    "2021-01-01 20:00"  20.2
    "2021-01-01 20:30"  30.7
    "2021-01-01 21:00"  36.3
    "2021-01-01 21:30"  36.7
    "2021-01-01 22:00"  30.1
    "2021-01-01 22:30"  26.3
    "2021-01-01 23:00"  18.1
    "2021-01-01 23:30"  15.8
    "2021-01-02 00:00"  11.4
    "2021-01-02 00:30"  10.2
    "2021-01-02 01:00"  11.9
    "2021-01-02 01:30"  12.3
    "2021-01-02 02:00"  9.1
    "2021-01-02 02:30"  8.6
    "2021-01-02 03:00"  8.3
    "2021-01-02 03:30"  10.1')
    

    这是数据步:

    library(libr)
    
    # Run datastep
    res <- datastep(dt, 
                    retain = list(overflow = 0),
                    calculate = {limit = 20},
                    drop = c("limit", "overflow"),
        {
      
          if (kWh + overflow > limit) {
    
            limit_kWh  <- limit
            overflow <- kWh + overflow - limit
            
          } else {
            
            limit_kWh <- kWh + overflow
            overflow <- 0
          }
      
        })
    

    结果如下:

    # View results
    res
    #    interval_starting  kWh limit_kWh
    # 1   2021-01-01 19:00 12.2      12.2
    # 2   2021-01-01 19:30 14.7      14.7
    # 3   2021-01-01 20:00 20.2      20.0
    # 4   2021-01-01 20:30 30.7      20.0
    # 5   2021-01-01 21:00 36.3      20.0
    # 6   2021-01-01 21:30 36.7      20.0
    # 7   2021-01-01 22:00 30.1      20.0
    # 8   2021-01-01 22:30 26.3      20.0
    # 9   2021-01-01 23:00 18.1      20.0
    # 10  2021-01-01 23:30 15.8      20.0
    # 11  2021-01-02 00:00 11.4      20.0
    # 12  2021-01-02 00:30 10.2      20.0
    # 13  2021-01-02 01:00 11.9      20.0
    # 14  2021-01-02 01:30 12.3      20.0
    # 15  2021-01-02 02:00  9.1      20.0
    # 16  2021-01-02 02:30  8.6      17.7
    # 17  2021-01-02 03:00  8.3       8.3
    # 18  2021-01-02 03:30 10.1      10.1
    
    

    【讨论】:

    • 谢谢! datastep()不熟悉,和dplyr的rowwise()类似吗?
    • 是的,这是一个类似的想法。除了在 { } 内,您可以随意嵌套条件。 “保留”与 lag() 的作用相同。
    猜你喜欢
    • 1970-01-01
    • 2013-05-20
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2022-12-05
    • 1970-01-01
    • 2018-11-14
    相关资源
    最近更新 更多