Scala: Berechnung der gleitenden Summe einer Liste mit einem festen Fenster

8

Ich bin neu in Scala und möchte eine bewegliche Summe mit einem festen Fenster für eine Liste berechnen.

Beispiel: Angesichts der Listenwerte (1.0, 2.0, 3.0, 6.0, 7.0, 8.0, 12.0, 9.0, 4.0, 1.0) und der Periode 4 sollte die Funktion Folgendes zurückgeben: (1.0, 3.0, 6.0, 12.0, 18.0, 24,0, 33,0, 36,0, 33,0, 26,0)

Wenn list.size <period ist, geben Sie einfach die kumulative Summe zurück.

Ich habe einige Versuche gemacht

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

Ich habe jedoch

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

das ist nicht richtig. Ich möchte Pyspark nicht verwenden, um die Ergebnisse zu erhalten. Kann jemand helfen?

Danke vielmals.

FlyUFalcon
quelle
sliding
Probieren
1
Ich stelle fest, dass das Fenster wächst (1. Element, 1. 2 Elemente, 1. 3 Elemente usw.), aber nicht schrumpft (letzte 4 Elemente, letzte 3 Elemente, letzte 2 Elemente usw.). Ist das beabsichtigt?
Jwvh

Antworten:

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
  }
User9123
quelle
super 👏 schöne lösung !!!!!
Raman Mishra
2
List(0.0)values = Seq()period > 1
Beachten Sie,
@CervEd danke für Ihren Hinweis, beheben Sie es
User9123
@ User9123, es könnte mehr geben. Musste in meiner Antwort selbst Akrobatik machen
CervEd
3

Hier ist eine Möglichkeit, dies zu beheben.

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

testen:

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)
jwvh
quelle
2

Dies ist eine andere Möglichkeit, wie Sie dies tun können:

  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)
Raman Mishra
quelle
1

Ein anderer Ansatz, ähnlich der Antwort von @ User9123

Der Unterschied besteht darin, dass nicht die Summe aller Elemente im Schiebefenster berechnet wird, sondern der Wert des letzten Fensterkopfs von seiner Summe subtrahiert und der Wert des nächsten Fensterkopfs addiert wird, um die nächste rollierende Summe zu erhalten. Dies sollte bei großen Fenstern effizienter sein.

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

Ich habe auch einige Schutzvorrichtungen für Sonderfälle hinzugefügt, die behandelt werden müssen, und es zu einer generischen Funktion für alle NumericTypen gemacht.

Hier ist ein laufendes Beispiel mit einigen Testfällen.

CervEd
quelle