Wie welchen Durchschnittswert von unregelmäßigen (Zeit,Betrag)-Wertepaaren berechnen?

OpaK

Aktives Mitglied
Hallo,

ich habe in etwa so etwas:
Code:
Index   0 1 2 3 4 5
Zeit    1 6 7 8 9 9.5
Betrag  5 8 7 6 5 4
Der Chart verläuft also über 5,8,7,6,5,4. Man sieht es schon, zwischen Index 0 und 1 liegen 5 Stunden, zwischen Index 4 und 5 nur eine halbe Stunde, und zwischen den übrigen Beträgen eine Stunde.

Wie kann ich das bei der Durchschnittswertberechnung berücksichtigen? Und welche "Durchschnitts-Arten" gäbe es hierbei im Allgemeinen?

Hintergrund: Ich speichere in unregelmäßigen Zeitabständen einen Kursverlauf, und möchte nun einen Basiswert finden, um mehrere unterschiedliche Kursverläufe miteinander in Relation zu setzten und diese zu vergleichen. Leider sind meine Mathekenntnisse nicht mehr ganz frisch.

Danke für jede Antwort.
 
Oh, die KI war nun schneller...

Wenn die Messpunkte in unregelmäßigen Zeitabständen kommen, ist der „normale“ arithmetische Mittelwert der Beträge

\[
\bar y=\frac{1}{n}\sum_{i=0}^{n-1} y_i
\]

meist nicht das, was du willst – denn damit zählt jeder Messpunkt gleich stark, egal ob er für 5 Stunden gilt oder nur für 0,5 Stunden.

Sinnvoll ist stattdessen i.d.R. ein zeitgewichteter Mittelwert („time-weighted average“):

1) Zeitgewichteter Mittelwert (stückweise konstant / Treppenfunktion)

Annahme: Der Betrag \(y_i\) gilt von \(t_i\) bis \(t_{i+1}\) konstant weiter.

\[
\bar y=\frac{\sum_{i=0}^{n-2} y_i\cdot (t_{i+1}-t_i)}{t_{n-1}-t_0}
\]

Für deine Daten:

  • Zeiten: 1, 6, 7, 8, 9, 9.5
  • Beträge: 5, 8, 7, 6, 5, 4
  • Zeitdifferenzen: 5, 1, 1, 1, 0.5

Gewichtete Summe:
- \(5\cdot 5 + 8\cdot 1 + 7\cdot 1 + 6\cdot 1 + 5\cdot 0.5 = 48.5\)

Gesamtzeit: \(9.5-1=8.5\)

\[
\bar y = 48.5/8.5 \approx 5.7059
\]

2) Zeitgewichteter Mittelwert (linear zwischen Punkten / Trapezregel)

Annahme: Zwischen \(t_i\) und \(t_{i+1}\) verläuft der Kurs linear. Dann nimmst du die Fläche der Trapeze:

\[
\bar y=\frac{\sum_{i=0}^{n-2}\frac{y_i+y_{i+1}}{2}\cdot (t_{i+1}-t_i)}{t_{n-1}-t_0}
\]

Das ist oft näher an „echter“ Kursentwicklung, wenn man annimmt, dass sich der Kurs zwischen Messungen kontinuierlich bewegt.

Java-Skizze

Treppenfunktion (piecewise constant):
Java:
double[] t = {1, 6, 7, 8, 9, 9.5};
double[] y = {5, 8, 7, 6, 5, 4};

double weighted = 0.0;
for (int i = 0; i < t.length - 1; i++) {
    double dt = t[i+1] - t[i];
    weighted += y[i] * dt;
}
double avg = weighted / (t[t.length - 1] - t[0]);
System.out.println(avg);

Linear (Trapezregel):
Java:
double area = 0.0;
for (int i = 0; i < t.length - 1; i++) {
    double dt = t[i+1] - t[i];
    area += (y[i] + y[i+1]) * 0.5 * dt;
}
double avgLin = area / (t[t.length - 1] - t[0]);

Welche „Durchschnitts-Arten“ gibt es hier allgemein?

Für deinen Hintergrund („Basiswert“ zum Vergleichen mehrerer Verläufe) sind typisch:

  • Arithmetisches Mittel der Samples (ignoriert Zeitabstände) – meist ungeeignet bei unregelmäßigem Sampling
  • Zeitgewichteter Mittelwert (wie oben) – sehr passend, wenn „Kurs über Zeit“ verglichen werden soll
  • Median (robuster gegen Ausreißer)
  • Min/Max, Quantile (Spannweite/Verteilung)
  • Durchschnitt der Renditen statt der Kurse (wenn „Performance“ verglichen werden soll; dann aber sauber definieren, z.B. log returns)
 
Hat geklappt... meine gewichtete Trapez-Durchschnittswertmethode mit Halbwertszeit sieht nun wie folgt aus:

Java:
  private static double avg(List<TimeEntry> all, long halfLifeMs, Function<TimeEntry, Double> fy) {
    if (all.size() < 2) {
      return Double.NaN;
    }

    final double LN2 = Math.log(2.0);
    long tn = all.get(all.size() - 1).date.getTime();
    DoubleUnaryOperator w = (time) -> Math.exp((time - tn) * LN2 / halfLifeMs);

    double num = 0, den = 0;
    for (int i = 0; i < all.size() - 1; i++) {
      TimeEntry te0 = all.get(i);
      TimeEntry te1 = all.get(i + 1);
      long t0 = te0.date.getTime();
      long t1 = te1.date.getTime();
      long dt = t1 - t0;
      if (dt <= 0) {
        continue; // falls doch unsortiert/gleichzeitig
      }
      double y0 = fy.apply(te0);
      double y1 = fy.apply(te1);
      double w0 = w.applyAsDouble(t0);
      double w1 = w.applyAsDouble(t1);
      num += 0.5 * (y0 * w0 + y1 * w1) * dt;
      den += 0.5 * (w0 + w1) * dt;
    }
    return den == 0 ? Double.NaN : (num / den);
  }

Bis auf, ds ich den Paramter "halfLifeMs" nicht prüfe, sollte es auch korrekt sein.
 
Danke. Leider ist das ganze Functions-Gedöns in Java ein wenig undurchsichtig entworfen worden. Aber ToDoubleFunction statt Function<TimeEntry, Double> wäre hier naheliegender.
 

Neue Themen


Zurück
Oben