Je reçois des charges «nerveuses» dans Rollapply PCA dans R. Puis-je le corriger?

20

J'ai 10 ans de données de retours quotidiens pour 28 devises différentes. Je souhaite extraire le premier composant principal, mais plutôt que d'exploiter PCA sur l'ensemble des 10 ans, je souhaite recaler une fenêtre de 2 ans, car les comportements des devises évoluent et je souhaite donc en tenir compte. Cependant, j'ai un problème majeur, c'est que les fonctions princomp () et prcomp () passeront souvent de charges positives à négatives dans les analyses PCA adjacentes (c'est-à-dire à 1 jour d'intervalle). Jetez un œil au tableau de chargement de la devise EUR:

entrez la description de l'image ici

De toute évidence, je ne peux pas l'utiliser car les chargements adjacents passeront du positif au négatif, donc ma série qui les utilise sera erronée. Jetez maintenant un œil à la valeur absolue du chargement de la devise EUR:

entrez la description de l'image ici

Le problème est bien sûr que je ne peux toujours pas l'utiliser car vous pouvez voir sur le graphique du haut que le chargement passe du négatif au positif et vice versa, une caractéristique que je dois préserver.

Existe-t-il un moyen de contourner ce problème? Puis-je forcer l'orientation du vecteur propre à être toujours la même dans les PCA adjacentes?

Par ailleurs, ce problème se produit également avec la fonction FactoMineR PCA (). Le code pour le rollapply est ici:

rollapply(retmat, windowl, function(x) summary(princomp(x))$loadings[, 1], by.column = FALSE, align = "right") -> princomproll
Thomas Browne
la source
3
Pourriez-vous expliquer ce que vous entendez par «orientation» de vecteur propre? Pour autant que je sache, rien de tel n'est intrinsèque aux données. (C'est une des raisons pour lesquelles différents logiciels produisent différents vecteurs propres normalisés.) Il semble donc que vous demandiez quelque chose qui n'existe pas et qui n'a pas de sens.
whuber
1
Eh bien un jour, je recevrai des chargements comme celui-ci: EUR -0,2 ZAR +0,8 USD +0,41 ..... 28 devises. Et le lendemain, j'obtiendrai EUR +0,21 ZAR -0,79 USD -0,4 etc. ces sauts de chargement et je souhaite l'éviter, en quelque sorte ... Excusez-moi si ma terminologie est trompeuse. Je comprends que le code PCA ne se soucie pas vraiment de l'orientation de l'axe tant qu'il est cohérent entre les chargements sur une journée , mais j'ai besoin qu'il soit cohérent sur plusieurs jours.
Thomas Browne
1
en gardant à l'esprit que du jour au lendemain, étant donné une fenêtre glissante de 2 ans sur les données quotidiennes, nous devrions avoir une ACP très, très similaire.
Thomas Browne
Je pense que la raison pour laquelle vous avez un problème est que cette idée rollapply n'a pas de sens. Je n'ai pas d'autre solution que de chercher quelque chose de différent qui puisse atteindre vos objectifs (pas sûr de ce qu'ils sont) et qui est sensé.
Michael R. Chernick
EUR -0.2 ZAR +0.8 USD +0.41et EUR +0.21 ZAR -0.79 USD -0.4 sont très très similaires. Vous inversez simplement le signe dans l'un des deux résultats.
ttnphns

Réponses:

22

Chaque fois que l'intrigue saute trop, inversez l'orientation. Un critère efficace est le suivant: calculer le nombre total de sauts sur tous les composants. Calculez le nombre total de sauts si le vecteur propre suivant est annulé. Si ce dernier est inférieur, annulez le vecteur propre suivant.

Voici une implémentation. (Je ne connais paszoo , ce qui pourrait permettre une solution plus élégante.)

require(zoo)
amend <- function(result) {
  result.m <- as.matrix(result)
  n <- dim(result.m)[1]
  delta <- apply(abs(result.m[-1,] - result.m[-n,]), 1, sum)
  delta.1 <- apply(abs(result.m[-1,] + result.m[-n,]), 1, sum)
  signs <- c(1, cumprod(rep(-1, n-1) ^ (delta.1 <= delta)))
  zoo(result * signs)
}

Par exemple, exécutons une marche aléatoire dans un groupe orthogonal et agitons-le un peu par intérêt:

random.rotation <- function(eps) {
  theta <- rnorm(3, sd=eps)
  matrix(c(1, theta[1:2], -theta[1], 1, theta[3], -theta[2:3], 1), 3)
}
set.seed(17)
n.times <- 1000
x <- matrix(1., nrow=n.times, ncol=3)
for (i in 2:n.times) {
  x[i,] <- random.rotation(.05) %*% x[i-1,]
}

Voici le PCA roulant:

window <- 31
data <- zoo(x)
result <- rollapply(data, window, 
  function(x) summary(princomp(x))$loadings[, 1], by.column = FALSE, align = "right")
plot(result)

Original

Maintenant, la version fixe:

plot(amend(result))

Modifié

Whuber
la source
tjevje+1je+1vjeje1-1vje+1. Votre algorithme semble être un peu différent. Cela fonctionnerait-il de la même manière?
amibe dit Réintégrer Monica le
@amoeba Bien que je ne sois pas tout à fait sûr de ce que vous faites, cela ressemble à certaines des idées discutées dans la réponse de David J. Harris et les commentaires qui la suivent. Voir, en particulier, mon commentaire sur stats.stackexchange.com/questions/34396/… .
whuber
2
@Art, donc si je comprends bien, vous voulez corriger le signe du composant en fonction de certaines préférences externes (externes à PCA). C'est bien, mais c'est comme ça que vous devriez l'aborder. Faites d'abord la chose coulissante PCA, en vous assurant que les signes sont cohérents. Et puis décidez, sur la base de certains critères supplémentaires, de retourner ou non l'ensemble du composant. Par exemple, vous pouvez le corréler avec la tendance de l'euro et si la corrélation est négative, inversez la composante. Ou quelque chose comme ça. Cela dépend entièrement de votre application spécifique et de votre connaissance du domaine.
Amoeba dit Reinstate Monica
1
Je suis d'accord avec l'interprétation et la recommandation de @ amoeba.
whuber
1
@amoeba: oui, vous avez raison à ce sujet, bien que, naïvement, j'ai pensé qu'il pourrait y avoir une solution générique qui ne dépend pas de séries temporelles spécifiques, quelque chose comme "l'orientation réelle du vecteur" :) de toute façon, merci pour l'aide et suggestions
Anonyme
8

@whuber a raison de dire qu'il n'y a pas d'orientation intrinsèque aux données, mais vous pouvez toujours faire en sorte que vos vecteurs propres aient une corrélation positive avec un vecteur de référence.

Par exemple, vous pouvez rendre les chargements pour l'USD positifs sur tous vos vecteurs propres (c.-à-d., Si le chargement de l'USD est négatif, inversez les signes du vecteur entier). La direction générale de votre vecteur est toujours arbitraire (puisque vous auriez pu utiliser EUR ou ZAR comme référence à la place), mais les premiers axes de votre PCA ne sauteront probablement pas autant - en particulier parce que vos fenêtres roulantes sont si longue.

David J. Harris
la source
7
Bonne idée. J'ai essayé ceci en premier (probablement pendant que vous publiez cette réponse :-). Le problème est que les autres chargements peuvent sauter. Pour résoudre ce problème, basez le choix du panneau sur le plus grand chargement. Toujours pas de dés: les chargements peuvent encore sauter. L'astuce consiste à chaque fois à choisir l'orientation qui crée le moins de perturbation dans le vecteur de chargements de l'époque précédente.
whuber
4
@whuber Beau travail.
David J. Harris
1
Correct, le signe des chargements n'a pas d'importance (orientation). Quelque chose qui n'a pas été résolu est que si vous effectuez cette opération sur différents logiciels, les différences entre les packages sont qu'un programme peut entraîner des signes négatifs (positifs) sur des chargements particuliers tandis qu'un autre entraîne des signes positifs (négatifs) pour les mêmes chargements. Par conséquent, les signes des résultats finaux dans le graphique de la série 3 ci-dessus pourraient être inversés lors de l'utilisation d'un autre package. Les chargements vectoriels de référence pourraient également avoir un changement de signe - et cette solution ne serait pas incorrecte.
JoleT
@LEP: J'ai rencontré le même problème avec l'inversion, peut-être avez-vous déjà trouvé une solution à ce problème - comment savoir que le premier vecteur est correct et vous assurer que le reste y sera correctement aligné - quant.stackexchange.com/questions / 3094 /… ?
Anonyme
Tant que la matrice n'est pas singulière et qu'aucune des valeurs propres n'est nulle, la plupart des résultats de l'algorithme doivent être les mêmes, à l'exception d'un changement de 180 degrés dans les signes - ce qui n'est pas garanti.
JoleT
1

Ce que j'ai fait était de calculer la distance L1 entre les vecteurs propres successifs. Après normalisation de cette matrice, je choisis un seuil de score az, par exemple 1, de sorte que si dans tout nouveau roulement le changement est supérieur à ce seuil, je retourne le vecteur propre, les facteurs et les charges afin d'avoir une cohérence dans la fenêtre de roulement. Personnellement, je n'aime pas forcer les signes donnés dans certaines corrélations car ils peuvent être très volatils en fonction des macro-moteurs.

Raul Muñoz
la source