Extraire des points de données de la moyenne mobile?

15

Est-il possible d'extraire des points de données de données moyennes mobiles?

En d'autres termes, si un ensemble de données n'a que des moyennes mobiles simples des 30 points précédents, est-il possible d'extraire les points de données d'origine?

Si c'est le cas, comment?


la source
1
La réponse est oui, mais la procédure exacte dépend de la façon dont le segment initial de données est traité. Si elle est simplement supprimée, vous avez effectivement perdu 15 données, vous laissant avec un système indéterminé d'équations linéaires. Le résultat est qu'il existe de nombreuses réponses valables en général, mais vous pouvez toujours faire des progrès si soit (a) des fenêtres plus courtes (ou une telle procédure) sont utilisées pour les 15 moyennes mobiles initiales ou (b) vous pouvez spécifier des contraintes supplémentaires sur la solution (environ 15 dimensions de contraintes ...). Dans quelle situation êtes-vous?
whuber
@whuber Merci beaucoup d'avoir cherché! J'ai 2000 points. Le premier point MA est très probablement une moyenne des 30 premiers points d'origine. La précision est en second lieu à un résultat généralement correct, en particulier de bonnes suppositions sur les points les plus "récents". Pouvez-vous recommander une méthode relativement simple? Merci d'avance!
1
(si vous prenez plus de cinq minutes pour écrire un commentaire ...). Ce que je voulais écrire, c'est que vous pouvez considérer la moyenne comme une multiplication matricielle. Les rangées du milieu auront 1/30 * [1 1 1 ...] avant la diagonale. La question est de savoir comment gérer les points aux frontières de votre vecteur afin de rendre la matrice inversible. Vous pouvez le faire en supposant qu'ils sont le résultat d'une moyenne sur moins d'éléments ou que vous pensez à d'autres contraintes. Notez que si une inversion de matrice est un moyen facile de la comprendre, elle n'est pas la plus efficace. Vous voulez probablement utiliser une FFT pour cela.
fabee

Réponses:

4

+1 à la réponse de fabee, qui est complète. Juste une note pour le traduire en R, basé sur les packages que j'ai trouvés pour effectuer les opérations à portée de main. Dans mon cas, j'avais des données qui sont des prévisions de température de la NOAA sur une base de trois mois: janvier-février-mars, février-mars-avril, mars-avril-mai, etc., et je voulais les détailler (approximativement) valeurs mensuelles, en supposant que la température de chaque période de trois mois est essentiellement une moyenne.

library (Matrix)
library (matrixcalc)

# Feb-Mar-Apr through Nov-Dec-Jan temperature forecasts:

qtemps <- c(46.0, 56.4, 65.8, 73.4, 77.4, 76.2, 69.5, 60.1, 49.5, 41.2)

# Thus I need a 10x12 matrix, which is a band matrix but with the first
# and last rows removed so that each row contains 3 1's, for three months.
# Yeah, the as.matrix and all is a bit obfuscated, but the results of
# band are not what svd.inverse wants.

a <- as.matrix (band (matrix (1, nrow=12, ncol=12), -1, 1)[-c(1, 12),])
ai <- svd.inverse (a)

mtemps <- t(qtemps) %*% t(ai) * 3

Ce qui fonctionne très bien pour moi. Merci @fabee.

EDIT: OK, en rétraduisant mon R en Python, j'obtiens:

from numpy import *
from numpy.linalg import *

qtemps = transpose ([[46.0, 56.4, 65.8, 73.4, 77.4, 76.2, 69.5, 60.1, 49.5, 41.2]])

a = tril (ones ((12, 12)), 2) - tril (ones ((12, 12)), -1)
a = a[0:10,:]

ai = pinv (a)

mtemps = dot (ai, qtemps) * 3

(Ce qui a pris beaucoup plus de temps pour déboguer que la version R. D'abord parce que je ne suis pas aussi familier avec Python qu'avec R, mais aussi parce que R est beaucoup plus utilisable de manière interactive.)

Wayne
la source
@Gracchus: Désolé, pas un gars C ++, mais vous pouvez trouver ce dont vous avez besoin dans la bibliothèque d'algèbre linéaire Armadillo C ++ ( arma.sourceforge.net ), qui est également disponible en R via le package RcppArmadillo.
Wayne
OK, voyez si cela fonctionne pour vous. Si oui, vous pouvez choisir ma réponse ;-)
Wayne
Les meilleures pratiques FYI en Python sont de faire des importations absolues: python.org/dev/peps/pep-0008/#imports, ce qui facilite la lecture du code des autres, car vous savez réellement d'où viennent les fonctions au lieu de devoir recherchez chacun que vous ne connaissez pas. J'aurais aimé qu'il soit standard en R de faire de même. Devoir rechercher toutes les petites fonctions dans le code de quelqu'un d'autre me fait vraiment
grincer des dents
En outre, les blocs-notes Jupyter pour l'interactivité Python ou IPython.
wordsforhewise
17

J'essaie de mettre ce que whuber a dit dans une réponse. Disons que vous avez un grand vecteur avec n = 2000 entrées. Si vous calculez une moyenne mobile avec une fenêtre de longueur = 30 , vous pouvez l'écrire comme une multiplication matricielle vectorielle y = A x du vecteur x avec la matricexn=2000=30y=Axx

A=130(1...10...001...10...0...1...100...01...1)

qui en a qui sont décalés à mesure que vous avancez dans les rangées jusqu'à ce que les 30 atteignent l'extrémité de la matrice. Ici, le vecteur moyen y a 1970 dimensions. La matrice a 1970 lignes et 2000 colonnes. Par conséquent, il n'est pas inversible.3030y19702000

Si vous n'êtes pas familier avec les matrices, pensez-y comme un système d'équations linéaires: vous recherchez des variables x1,...,x2000y1y2

Le problème avec le système d'équations (et la matrice) est qu'il a plus d'inconnues que d'équations. Par conséquent, vous ne pouvez pas identifier de manière unique vos inconnues x1,...,xnxyx

UNE3030AA

Un autre, peut - être plus facile, est d'utiliser la pseudo - de A . Cela génère un vecteur zAAz=AyXyAz

2000x

reconstruction du signal d'origine à partir de la moyenne mobile à l'aide de la pseudoinverse

De nombreux programmes numériques offrent des pseudo-inverses (par exemple Matlab, numpy en python, etc.).

Voici le code python pour générer les signaux de mon exemple:

from numpy import *
from numpy.linalg import *
from matplotlib.pyplot import *
# get A and its inverse     
A = (tril(ones((2000,2000)),-1) - tril(ones((2000,2000)),-31))/30.
A = A[30:,:]
pA = pinv(A) #pseudo inverse

# get x
x = random.randn(2000) + 5
y = dot(A,x)

# reconstruct
x2 = dot(pA,y)

plot(x,label='original x')
plot(y,label='averaged x')
plot(x2,label='reconstructed x')
legend()
show()

J'espère que cela pourra aider.

fabee
la source
C'est une excellente réponse, mais je pense que vous vous trompez lorsque vous avez dit que "cela minimise la distance quadratique entre y et Az". En fait, y et Az sont la même chose. Ce qui est minimisé, c'est la norme de z qui fonctionne bien pour les signaux du monde réel que j'ai essayé, mais qui n'est pas si bonne si votre signal d'origine a de nombreuses valeurs aberrantes.
gdelfino
Je ne sais pas si je suis. y et Ax sont la même chose, mais pas y et Az Il est vrai que cela minimise également la norme de z. Je ne vois pas non plus pourquoi cela ne fonctionne pas pour mes exemples. Le bleu et le rouge correspondent assez bien. Suis-je en train de manquer quelque chose dans votre commentaire?
fabee
y est la moyenne mobile calculée à partir du signal d'origine x en multipliant par A. Cette procédure nous donne un signal z qui a la même moyenne mobile y. Donc y = Az Donc, seule la norme de z est minimisée. Si le signal d'origine a une valeur normale élevée, la procédure ne donnera pas de bons résultats. Un exemple de signal avec une grande valeur normale est ci-dessous:
gdelfino
{42.8, -33.7, 13.2, -45.6, 10.2, 35.8, -41.4, 20.253, 43.3429, -33.2735, 13.6135, -45.1067, 10.6346, 36.1352, -40.9703, 20.6616, 43.6796, -32.8966, 14.0406, -44.7001, 10.9988 , 36.4675, -40.7277, 20.8823, 43.7878, -32.7415, 13.9951, -44.7947, 11.044, 36.3873, -40.7117, 20.7505, 43.8204, -32.9399, 13.9129, -44.9549, 10.8703, 36.1559, -40.889, 43,46 , 13.5468, -45.2374, 10.3787, 35.8235, -41.5161, 19.9717, 43.0658, -33.7125, 13.0321}
gdelfino
Veuillez utiliser une taille de fenêtre de 8 pour le signal ci-dessus. De cette façon, le signal filtré est de forme très différente du signal d'origine.
gdelfino