2009-06-21 14 views
5

Bei einer Tabelle, in der die erste Spalte Sekunden über einen bestimmten Bezugspunkt ist, und das zweite ist ein willkürliches Maß:Glättung unregelmäßig abgetastete Zeitdaten

6 0.738158581 
21 0.801697222 
39 1.797224596 
49 2.77920469 
54 2.839757536 
79 3.832232283 
91 4.676794376 
97 5.18244704 
100 5.521878863 
118 6.316630137 
131 6.778507504 
147 7.020395216 
157 7.331607129 
176 7.637492223 
202 7.848079136 
223 7.989456499 
251 8.76853608 
278 9.092367123 
    ... 

Wie man sieht, werden die Messungen in unregelmäßigen Zeitpunkten abgetastete . Ich muss die Daten glätten, indem ich den Messwert bis zu 100 Sekunden vor jeder Messung mittle (in Python). Da die Datentabelle riesig ist, wird eine Iterator-basierte Methode wirklich bevorzugt. Leider kann ich nach zwei Stunden Kodierung keine effiziente und elegante Lösung finden.

Kann mir jemand helfen?

EDIT s

  1. ich für jede rohe Lesung eine geglättete Lesung wollen, und die geglättete Lesung ist das arithmetische Mittel der rohen Lesen und alle anderen in den vorangegangenen 100 (Delta) Sekunden sein . (John, sind Sie rechts)

  2. Huge ~ 1e6 - 10e6 Linien + müssen mit engen RAM

  3. Die Daten sind etwa Irrfahrt arbeiten

  4. Die Daten sortiert ist

AUFLÖSUNG

Ich habe Lösungen von J Machin und yairchu getestet. Sie beide haben die gleichen Ergebnisse, jedoch auf meinem Datensatz, J Machins Version exponentiell, während die von Yairchu linear ist. Im folgenden werden als Ausführungszeiten von % timeit des IPython gemessen (in Mikrosekunden):

data size J Machin yairchu 
10  90.2  55.6 
50   930   258 
100   3080  514 
500   64700  2660 
1000  253000  5390 
2000  952000  11500 

Vielen Dank für die Hilfe.

+1

ist es zu groß, um in numply arrays behandelt zu werden? Wie viele Gegenstände hast du? –

+0

Ist diese lineare Interpolation, um Punkte zu finden, die Vielfache von 100 sind? –

+0

Wenn Sie Glättungsanforderungen haben, bitte etwas mehr ausarbeiten. Ich habe es ein paar Mal versucht, aber ich kann diese Beschreibung nicht analysieren: "Ich muss die Daten glätten, indem ich den Messwert bis zu 100 Sekunden vor jeder Messung mittle." – rix0rrr

Antwort

2

Ich verwende ein Summenergebnis, zu dem ich die neuen Mitglieder hinzufüge und die alten subtrahiere. Auf diese Weise kann man jedoch akkumulierende Gleitkomma-Ungenauigkeiten erleiden.

Daher implementiere ich eine "Deque" mit einer Liste. Und immer wenn meine Deque auf eine kleinere Größe umschreibt. Ich berechne die Summe bei derselben Gelegenheit neu.

Ich berechne auch den Durchschnitt bis Punkt x einschließlich Punkt x, so gibt es mindestens einen Stichprobenpunkt zu durchschnittlich.

def getAvgValues(data, avgSampleTime): 
    lastTime = 0 
    prevValsBuf = [] 
    prevValsStart = 0 
    tot = 0 
    for t, v in data: 
    avgStart = t - avgSampleTime 
    # remove too old values 
    while prevValsStart < len(prevValsBuf): 
     pt, pv = prevValsBuf[prevValsStart] 
     if pt > avgStart: 
     break 
     tot -= pv 
     prevValsStart += 1 
    # add new item 
    tot += v 
    prevValsBuf.append((t, v)) 
    # yield result 
    numItems = len(prevValsBuf) - prevValsStart 
    yield (t, tot/numItems) 
    # clean prevVals if it's time 
    if prevValsStart * 2 > len(prevValsBuf): 
     prevValsBuf = prevValsBuf[prevValsStart:] 
     prevValsStart = 0 
     # recalculate tot for not accumulating float precision error 
     tot = sum(v for (t, v) in prevValsBuf) 
+0

(1) Das ist eine sehr interessante Implementierung einer Deque. (2) Ich bezweifle, dass das OP sehr besorgt ist, dass Gleitkomma-Rundungsfehler akkumulieren; sie wären sicherlich sehr kleine Schnitte im Vergleich zu den gravierenden Änderungen der Glättung ... aber wenn er es ist, könnte man einen Kahan-Addierer verwenden, um die laufende Summe aufrechtzuerhalten. –

+0

Beachten Sie, dass dies im Vergleich zu einem exponentiellen gleitenden Durchschnitt sehr rechenintensiv ist (http://stackoverflow.com/questions/1023860/exponential-moving-average-sampled-at-varying-times/1024008). Sofern Sie nicht besonders darauf achten müssen, dass alle Samples innerhalb des Zeitbereiches gleichberechtigt sind und ältere keinen Beitrag leisten, würde ich mit dem viel effizienteren EMA fortfahren. –

+0

@Curt Sampson: Das OP hat speziell danach gefragt – yairchu

-1

was über so etwas, speichern Sie Werte, bis Zeitunterschied mit dem letzten Mal ist> 100, Durchschnitt und geben solche Werte z.

def getAvgValues(data): 
    lastTime = 0 
    prevValues = [] 
    avgSampleTime=100 

    for t, v in data: 
     if t - lastTime < avgSampleTime: 
      prevValues.append(v) 
     else: 
      avgV = sum(prevValues)/len(prevValues) 
      lastTime = t 
      prevValues = [v] 
      yield (t,avgV) 

for v in getAvgValues(data): 
    print v 
+0

fragte er für alle Zeiten der ursprünglichen Messungen nach Durchschnitten der vorherigen 100 Sekunden. Sie geben nur 2 Ergebnisse für sein Beispiel – yairchu

+0

hmm ich missverstanden es, jeder Weg sieht aus wie Sie es für die richtige Lösung geändert –

+0

Ich habe es wirklich nicht geändert. Ich habe nur deine Variablennamen verwendet. – yairchu

2

Sie haben nicht genau gesagt, wann Sie die Ausgabe möchten. Ich nehme an, dass Sie eine geglättete Lesung für jede rohe Lesung wünschen, und die geglättete Lesung soll das arithmetische Mittel der rohen Ablesung und aller anderen in den vorherigen 100 (Delta) Sekunden sein.

Kurze Antwort: Verwenden Sie ein Collections.deque ... es wird nie mehr als "Delta" Sekunden von Lesungen halten. So wie ich es eingerichtet habe, kann man die Deque wie eine Liste behandeln und einfach den Mittelwert oder ein schickes Gizmoid berechnen, das den neueren Lesungen mehr Gewicht verleiht.

Lange Antwort:

>>> the_data = [tuple(map(float, x.split())) for x in """\ 
... 6  0.738158581 
... 21  0.801697222 
[snip] 
... 251  8.76853608 
... 278  9.092367123""".splitlines()] 
>>> import collections 
>>> delta = 100.0 
>>> q = collections.deque() 
>>> for t, v in the_data: 
...  while q and q[0][0] <= t - delta: 
...   # jettison outdated readings 
...   _unused = q.popleft() 
...  q.append((t, v)) 
...  count = len(q) 
...  print t, sum(item[1] for item in q)/count, count 
... 
... 
6.0 0.738158581 1 
21.0 0.7699279015 2 
39.0 1.112360133 3 
49.0 1.52907127225 4 
54.0 1.791208525 5 
79.0 2.13137915133 6 
91.0 2.49500989771 7 
97.0 2.8309395405 8 
100.0 3.12993279856 9 
118.0 3.74976297144 9 
131.0 4.41385300278 9 
147.0 4.99420529389 9 
157.0 5.8325615685 8 
176.0 6.033109419 9 
202.0 7.15545189083 6 
223.0 7.4342562845 6 
251.0 7.9150342134 5 
278.0 8.4246097095 4 
>>> 

bearbeiten

One-Stop-Shop: Holen Sie Ihre Phantasie hier gizmoid.Hier ist der Code:

numerator = sum(item[1] * upsilon ** (t - item[0]) for item in q) 
denominator = sum(upsilon ** (t - item[0]) for item in q) 
gizmoid = numerator/denominator 

wo Ypsilon ein wenig geringer sein sollte als 1,0 (< = Null ist illegal, knapp über Null wenig Glättung der Fall ist, man bekommt man das arithmetische Mittelwert plus verschwendet CPU-Zeit, und größer als eins gibt das Gegenteil von Ihrem Zweck).

+0

Es scheint mir, eine normale Liste würde hier funktionieren, mit .pop (0) anstelle von .popleft(). Was ist der Vorteil von collections.deque? – Paul

+2

Die linke Seite einer Python-Liste ist O (N); links von einer Deque ist O (1) –

0

scheint Ihre Daten in etwa linear zu sein:

Plot of your data http://rix0r.nl/~rix0r/share/shot-20090621.144851.gif

Welche Art von Glättung dem Sie suchen? Eine Anpassung der kleinsten Quadrate einer Linie an diesen Datensatz? Eine Art Tiefpassfilter? Oder etwas anderes?

Bitte teilen Sie uns die Anwendung mit, damit wir Sie ein wenig besser beraten können.

EDIT: Zum Beispiel, abhängig von der Anwendung, Interpolation einer Linie zwischen dem ersten und letzten Punkt möglicherweise gut genug für Ihre Zwecke.

+0

Diesmal kann es linear sein. – Nosredna

0

Dieses macht es linear:

def process_data(datafile): 
    previous_n = 0 
    previous_t = 0 
    for line in datafile: 
     t, number = line.strip().split() 
     t = int(t) 
     number = float(number) 
     delta_n = number - previous_n 
     delta_t = t - previous_t 
     n_per_t = delta_n/delta_t 
     for t0 in xrange(delta_t): 
      yield previous_t + t0, previous_n + (n_per_t * t0) 
     previous_n = n 
     previous_t = t 

f = open('datafile.dat') 

for sample in process_data(f): 
    print sample 
+0

(1) Die .strip() ist redundant. (2) Sie scheinen vergessen zu haben, previous_ * jedesmal zu aktualisieren. (3) Selbst dann ist es nicht offensichtlich, was das bedeutet ... es scheint eine lineare Interpolation zwischen dem vorherigen und dem aktuellen Messwert zu geben, in Intervallen von einer Sekunde - eine interessante Interpretation der Anforderungen des OP. (3) Ich glaube, du meintest 'für t0 in xrange (1, delta_t + 1)' –

-2

Klingt wie Sie eine einfache Rundung Formel benötigen. Zur Abrundung eine beliebige Anzahl an einem beliebigen Intervall:

round (num/Intervall) * Intervall

können Sie ersetzen Runde mit Boden oder Decke für „führende bis zu“ oder „da“ beeinflusst. Es kann in jeder Sprache, einschließlich SQL, arbeiten.

0

O (1) Speicher für den Fall, dass Sie die Eingabe mehr als einmal durchlaufen können - Sie können einen Iterator für "links" und einen für "rechts" verwenden.

def getAvgValues(makeIter, avgSampleTime): 
    leftIter = makeIter() 
    leftT, leftV = leftIter.next() 
    tot = 0 
    count = 0 
    for rightT, rightV in makeIter(): 
    tot += rightV 
    count += 1 
    while leftT <= rightT - avgSampleTime: 
     tot -= leftV 
     count -= 1 
     leftT, leftV = leftIter.next() 
    yield rightT, tot/count 
+1

Ich denke, das OP möchte den geglätteten Wert in Echtzeit anzeigen ... denke Herzschlagmonitor auf der Intensivstation. –

0

Während es eine exponentiell abfallende Durchschnitt gibt, anstatt eine durchschnittliche, ich glaube, Sie möchten, was ich eine exponential moving average with varying alpha genannt, die Tiefpassfilter wirklich eine einpolige ist. Es gibt jetzt eine Lösung für diese Frage, und sie läuft zeitlich linear zur Anzahl der Datenpunkte. Sehen Sie, ob es für Sie funktioniert.

Verwandte Themen