Scala:固定ウィンドウでのリストの移動合計の計算


8

私は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 <periodの場合、累積合計を返します。

私はいくつかの試みをしました

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方法を試し
セス・Tisue

1
ウィンドウが拡大していることに気づきました(1番目の要素、1番目の2要素、1番目の3要素など)が縮小しません(最後の4要素、最後の3要素、最後の2要素など)。それは意図的ですか?
jwvh

回答:


5
  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
  }

素晴らしい👏素敵なソリューション!!!!!
Raman Mishra

2
注意して、この戻っList(0.0)たときvalues = Seq()period > 1
CervEd

@CervEd通知に感謝、修正
User9123

@ User9123、もっとあるかもしれません。私の答えでアクロバットを自分でやらなければならなかった
CervEd

3

これに取り組む1つの方法を次に示します。

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)

2

これは、これを実行できる別の方法です。

  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)

1

@ User9123による回答に類似した別のアプローチ

違いは、スライディングウィンドウのすべての要素の合計を計算するのではなく、その合計から最後のウィンドウヘッドの値を減算し、次のウィンドウヘッドの値を加算して次のローリング合計を生成することです。これは大きなウィンドウの場合により効率的です。

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タイプの汎用関数にしました。

以下は、いくつかのテストケースを使用した実行例です。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.