2010-09-21 18 views
41

Sto codificando qualcosa nel momento in cui sto prendendo un sacco di valori nel tempo da una bussola hardware. Questa bussola è molto accurata e si aggiorna molto spesso, con il risultato che se si muove leggermente, finisco con il valore dispari che è selvaggiamente incoerente con i suoi vicini. Voglio smussare quei valori.Livelli di arrotondamento nel tempo: media mobile o qualcosa di meglio?

Avendo letto un po ', sembrerebbe che quello che voglio sia un filtro passa-alto, un filtro passa-basso o una media mobile. La media mobile con cui posso scendere, basta tenere una cronologia degli ultimi 5 valori o qualsiasi altra cosa, e usare la media di quei valori a valle nel mio codice in cui una volta usavo solo il valore più recente.

Questo dovrebbe, penso, appianare bene quei jiggles, ma mi sembra che sia probabilmente abbastanza inefficiente, e questo è probabilmente uno di quei problemi noti ai programmatori corretti a cui c'è una soluzione di Clever Math davvero pulita.

Io sono, tuttavia, uno di quegli orribili programmatori autodidatti senza un briciolo di educazione formale in qualcosa anche solo vagamente collegato a CompSci o Math. Leggere un po 'suggerisce che questo possa essere un filtro passa alto o basso, ma non riesco a trovare nulla che spieghi in termini comprensibili a un hack come me, quale sarebbe l'effetto di questi algoritmi su una serie di valori, per non parlare di come la matematica funziona. La risposta data here, ad esempio, risponde tecnicamente alla mia domanda, ma solo in termini comprensibili a quelli che probabilmente saprebbero già come risolvere il problema.

Sarebbe davvero una persona molto gentile e intelligente che potrebbe spiegare il tipo di problema e come funzionano le soluzioni, in termini comprensibili per un laureato in lettere.

risposta

34

Se la media mobile deve essere lungo al fine di raggiungere il livellamento desiderato, e non si ha realmente bisogno di alcuna particolare forma di kernel, allora siete meglio se si utilizza un diminuisce in modo esponenziale media mobile:

a(i+1) = tiny*data(i+1) + (1.0-tiny)*a(i) 

dove si sceglie tiny essere una costante appropriata (ad esempio, se si sceglie piccolo = 1- 1/N, avrà la stessa quantità di media come una finestra di dimensione N, ma distribuita in modo diverso sui punti più anziani).

In ogni caso, poiché il valore successivo della media mobile dipende solo dal precedente e dai dati, non è necessario mantenere una coda o altro. E puoi pensare a questo come a qualcosa del tipo: "Beh, ho un nuovo punto, ma non mi fido davvero, quindi terrò l'80% della mia vecchia stima della misurazione, e solo fidati di questo nuovo data point del 20% ". Questo è più o meno lo stesso di "Beh, mi fido di questo nuovo punto del 20%, e userò altri 4 punti di cui mi fido lo stesso importo", tranne che invece di prendere esplicitamente gli altri 4 punti, sei partendo dal presupposto che la media che hai fatto l'ultima volta sia stata ragionevole, puoi usare il tuo lavoro precedente.

+6

Buona spiegazione, grazie Rex. Avrei ragione nel pensare che un altro modo di esprimere la stessa cosa sarebbe: workingAverage = (newValue * smoothingFactor) + (workingAverage * (1.0 - smoothingFactor))? –

+4

@Henry - Giusto, questo è il modo per farlo senza utilizzare alcuna memoria aggiuntiva. –

+2

Ehi, so che sono in ritardo di 5 anni, ma grazie per una risposta fantastica. Sto lavorando a un gioco in cui il suono cambia in base alla velocità, ma a causa dell'esecuzione del gioco su un computer lento, la velocità fluttuerebbe selvaggiamente, il che andava bene per lo sterzo, ma super fastidioso in termini di suono. Questa era una soluzione davvero semplice ed economica per qualcosa che pensavo sarebbe stato un problema davvero complesso. – Adam

6

media mobile posso scendere con ... ma mi colpisce che probabilmente è abbastanza inefficiente.

Non c'è davvero alcun motivo per cui una media mobile dovrebbe essere inefficiente. Si mantiene il numero di punti dati che si desidera in un buffer (come una coda circolare). Su ogni nuovo punto dati, si inserisce il valore più vecchio e lo si sottrae da una somma, e si spinge il più recente e lo si aggiunge alla somma. Quindi ogni nuovo punto dati comporta solo un pop/push, un'aggiunta e una sottrazione. La tua media mobile è sempre questa somma variabile divisa per il numero di valori nel tuo buffer.

Ottiene un piccolo ingannevole se si ricevono dati simultaneamente da più thread, ma poiché i dati provengono da un dispositivo hardware che mi sembra altamente dubbio.

Oh e anche: terribili programmatori autodidatti si uniscono! ;)

+0

La media mobile mi è sembrata inefficiente perché è necessario memorizzare un buffer di valori, meglio fare solo alcuni Clever Math con il valore di input e il valore corrente di lavoro? Penso che sia così che funziona la media mobile esponenziale. Un'ottimizzazione che ho visto per questo tipo di media mobile comporta l'uso di una coda a lunghezza fissa e un puntatore al punto in cui ci si trova in quella coda, e semplicemente avvolgendo il puntatore (con% o un if). Ecco! Nessun push/pop costoso. Potere ai dilettanti, fratello! –

+0

@Henry: per una media mobile di tipo straight-up è necessario semplicemente il buffer in modo da sapere quale valore viene visualizzato quando viene premuto il valore successivo. Detto questo, la "coda di lunghezza fissa e un puntatore" che stai descrivendo è esattamente ciò che intendevo per "coda circolare". Ecco perché stavo dicendo che non è inefficiente. Cosa pensavi * intendevo? E se la tua risposta è "un array che sposta i suoi valori su ogni rimozione indicizzata" (come 'std :: vector' in C++) ... beh, allora, sono così ferito che non voglio nemmeno parlare con tu più;) –

+0

no, pensavo intendessi un array.push()/array.unshift() di alto livello o qualcosa del genere, come farebbe un programmatore AS3 o Java. Perdonami. Laurea in lettere, ricordi? –

53

Se si sta tentando di rimuovere il valore dispari occasionale, un filtro passa-basso è la migliore delle tre opzioni identificate. I filtri passa-basso consentono modifiche a bassa velocità come quelle causate dalla rotazione manuale di una bussola, mentre, ad esempio, rifiutano le modifiche ad alta velocità come quelle provocate da urti sulla strada.

Una media mobile non sarà probabilmente sufficiente, dal momento che gli effetti di un singolo "blip" nei dati influenzeranno diversi valori successivi, a seconda delle dimensioni della finestra della media mobile.

Se vengono facilmente rilevati i valori dispari, si può anche essere meglio con un algoritmo di problema tecnico-rimozione che li ignora completamente:

if (abs(thisValue - averageOfLast10Values) > someThreshold) 
{ 
    thisValue = averageOfLast10Values; 
} 

Ecco un grafico guick per illustrare:

graph comparison

Il primo grafico è il segnale di ingresso, con uno spiacevole inconveniente. Il secondo grafico mostra l'effetto di una media mobile a 10 campioni. Il grafico finale è una combinazione della media di 10 campioni e dell'algoritmo di rilevamento del glitch semplice mostrato sopra. Quando viene rilevato il problema tecnico, viene utilizzata la media a 10 campioni anziché il valore effettivo.

+0

Ben spiegato, e punti bonus per il grafico;) –

+0

Wow .. Raramente ho visto una risposta così bella! – Muis

+2

La media mobile * è * un filtro passa-basso. – nomen

2

Una media mobile in decadenza esponenziale può essere calcolata "a mano" con la tendenza solo se si utilizzano i valori corretti. Vedere http://www.fourmilab.ch/hackdiet/e4/ per un'idea su come eseguire questa operazione rapidamente con carta e penna se si sta cercando "una media mobile esponenzialmente levigata con un livellamento del 10%". Ma dal momento che si dispone di un computer, probabilmente si desidera eseguire lo spostamento binario anziché lo spostamento decimale;)

In questo modo, tutto ciò che serve è una variabile per il valore corrente e una per la media. La media successiva può quindi essere calcolata da quella.

+0

Buona spiegazione di EMA, grazie. –

1

c'è una tecnica chiamata gate di intervallo che funziona bene con campioni spuri a bassa insorgenza. supponendo l'uso di una delle tecniche di filtro menzionate sopra (media mobile, esponenziale), una volta che hai una storia "sufficiente" (una costante di tempo) puoi testare il nuovo campione di dati in entrata per ragionevolezza, prima dello viene aggiunto al calcolo.

è richiesta una certa conoscenza della massima velocità di variazione del segnale. il campione grezzo viene confrontato con il valore livellato più recente e se il valore assoluto di tale differenza è maggiore dell'intervallo consentito, tale campione viene espulso (o sostituito con un certo euristico, ad esempio una previsione basata su pendenza, differenziale o valore di previsione "trend" da doppio livellamento esponenziale)