【问题标题】:Scala: Calculating the Moving Sum of a List with a fixed windowScala:计算具有固定窗口的列表的移动和
【发布时间】:2020-08-14 03:57:14
【问题描述】:

我是 Scala 新手,我想用固定窗口计算列表的移动总和。

例如:给定列表值(1.0、2.0、3.0、6.0、7.0、8.0、12.0、9.0、4.0、1.0)和周期 4,函数应返回: (1.0, 3.0, 6.0, 12.0, 18.0, 24.0, 33.0, 36.0, 33.0, 26.0)

如果 list.size

我做了一些尝试

def mavg(values: List[Double], period: Int): List[Double] = {
  if (values.size <= period) (values.sum ) :: List.fill(period -1)(values.sum ) else {
      val rest: List[Double] = mavg(values.tail, period)
      (rest.head + ((values.head - values(period)))):: rest
  }
}

但是,我得到了

List(12.0, 18.0, 24.0, 33.0, 36.0, 33.0, 26.0, 26.0, 26.0, 26.0

这是不正确的。我不想使用 Pyspark 来获得结果。有人可以帮忙吗?

非常感谢。

【问题讨论】:

  • 试试sliding方法
  • 我注意到窗口增长了(第一个元素、第一个 2 个元素、第一个 3 个元素等),但它没有缩小(最后 4 个元素、最后 3 个元素、最后 2 个元素等)。 )。这是故意的吗?

标签: list scala sum rolling-sum


【解决方案1】:

另一种方法,类似于@User9123 的answer

不同之处在于它不计算滑动窗口中所有元素的总和,而是从总和中减去最后一个窗口头部的值,然后加上下一个窗口头部的值以产生下一个滚动总和。这对于大窗口应该更有效。

def rollingSum[N](values: Seq[N], period: Int)(
    implicit num: Numeric[N]
): Seq[N] = {
  import num._
  values match {
    case values if period == 1 => values // Can't slide on period 1
    case head :: tail if period < values.size =>
      (Seq.fill(period - 2)(num.zero) ++ (values)) // zero padding
        .sliding(period)
        .foldLeft((num.zero, Seq(head))) { // Use a tuple to store previous head
          case ((prevHead, acc), y) => {
            (y.head, acc :+ acc.last - prevHead + y.last) // do the magic
          }
        }
        ._2 // only return the result
    case head :: tail => tail.scanLeft(head)(_ + _) // Regular cummulative sum
    case Nil          => Nil
  }
}

我还为需要处理的特殊情况添加了一些保护,并使其成为所有Numeric 类型的通用函数。

Here's 带有一些测试用例的运行示例。

【讨论】:

    【解决方案2】:
      def mavg(values: Seq[Double], period: Int): Seq[Double] = {
        (Seq.fill(math.min(period - 1, values.length))(0.0) ++ values) // padding zeros
          .sliding(period)                  
          .map(_.sum)
          .toSeq
      }
    

    【讨论】:

    • 请注意,当values = Seq()period &gt; 1 时返回List(0.0)
    • @User9123,可能还有更多。在我的回答中不得不自己做一些杂技
    【解决方案3】:

    这是另一种方法:

      val l = List(1.0, 2.0, 3.0, 6.0, 7.0, 8.0, 12.0, 9.0, 4.0, 1.0,5.0,1.0,2.0)
      def mavg(step: Int, list: List[Double], ans: List[Double] = List.empty[Double], splitCount: Int = 0): List[Double] = {
        if (list.length > 1) {
          mavg(step - 1, list.take(step), list.sliding(step, 1).toList.map(_.sum) ::: ans, splitCount + 1)
        } else {
          ans.splitAt(splitCount + 2)._1.sliding(1, 2).toList.flatten ::: ans.drop(splitCount + 2)
        }
      }
    
      val ans = mavg(4, l)
      println(ans)
    

    【讨论】:

      【解决方案4】:

      这是解决问题的一种方法。

      def mavg(values: List[Double], period: Int): List[Double] =
        values.inits    //shrinking list of inits
              .toList   //result type
              .reverse  //growing list of inits
              .tail     //drop the empty one
              .map(_.takeRight(period).sum) //sum the window
      

      测试:

      mavg(List(1.0, 2.0, 3.0, 6.0, 7.0, 8.0, 12.0, 9.0, 4.0, 1.0), 4)
      //res0: List[Double] = List(1.0, 3.0, 6.0, 12.0, 18.0, 24.0, 33.0, 36.0, 33.0, 26.0)
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2021-06-09
        • 2018-09-29
        • 2017-10-19
        • 2014-02-22
        • 2012-01-27
        相关资源
        最近更新 更多