Dostaję „gwałtowne” ładunki w rollapply PCA w R. Czy mogę to naprawić?

20

Mam 10 lat dziennych danych zwrotów dla 28 różnych walut. Chcę wyodrębnić pierwszy główny składnik, ale zamiast obsługiwać PCA przez całe 10 lat, chcę zastosować dwuletnie okno, ponieważ zachowania walut ewoluują, więc chcę to odzwierciedlić. Mam jednak poważny problem, to znaczy, że zarówno funkcje princomp (), jak i prcomp () często przeskakują od ładunków dodatnich do ujemnych w sąsiednich analizach PCA (tj. W odstępie 1 dnia). Zobacz tabelę ładowania dla waluty EUR:

wprowadź opis zdjęcia tutaj

Oczywiście nie mogę tego użyć, ponieważ sąsiednie ładunki będą przeskakiwać z dodatnich na ujemne, więc moja seria, która ich używa, będzie błędna. Teraz spójrz na wartość bezwzględną ładowania waluty EUR:

wprowadź opis zdjęcia tutaj

Problem polega oczywiście na tym, że nadal nie mogę tego użyć, ponieważ z górnego wykresu widać, że ładowanie zmienia się czasami z ujemnego na dodatnie i z powrotem, co jest cechą, którą muszę zachować.

Czy jest jakiś sposób na obejście tego problemu? Czy mogę wymusić, aby orientacja wektora własnego była zawsze taka sama w sąsiadujących PCA?

Nawiasem mówiąc, ten problem występuje również w funkcji FactoMineR PCA (). Kod rollapply znajduje się tutaj:

rollapply(retmat, windowl, function(x) summary(princomp(x))$loadings[, 1], by.column = FALSE, align = "right") -> princomproll
Thomas Browne
źródło
3
Czy możesz wyjaśnić, co rozumiesz przez „orientację” wektorów własnych? O ile mi wiadomo, nie ma czegoś takiego, co jest właściwe dla danych. (To jeden z powodów, dla których różne oprogramowanie będzie wytwarzać różne znormalizowane wektory własne.) Więc brzmi to tak, jakbyś prosił o coś, co nie istnieje i jest bez znaczenia.
whuber
1
Pewnego dnia dostanę takie ładunki: -0,2 EUR ZAR +0,8 USD +0,41 ..... 28 walut. A następnego dnia dostanę +0,21 EUR -0,79 USD -0,4 itd. Więc oś, na którą PCA zdecydowała się obrócić dane, jest zorientowana dokładnie odwrotnie w dniu 2, w porównaniu z dniem 1. To powoduje te ładunki podskakują i chcę tego uniknąć, jakoś ... Przepraszam, jeśli moja terminologia jest myląca. Rozumiem, że kod PCA tak naprawdę nie przejmuje się orientacją osi, o ile jest spójny dla obciążeń jednego dnia , ale potrzebuję, aby był spójny przez wiele dni.
Thomas Browne,
1
pamiętając, że z dnia na dzień, biorąc pod uwagę dwuletnie okno na codzienne dane, powinniśmy mieć bardzo, bardzo podobny PCA.
Thomas Browne,
Myślę, że powodem, dla którego masz problem, jest to, że ten rolowany pomysł nie ma sensu. Nie mam innego rozwiązania, jak poszukać czegoś innego, co może osiągnąć twoje cele (nie jestem pewien, jakie są) i jest rozsądne.
Michael R. Chernick,
EUR -0.2 ZAR +0.8 USD +0.41i EUR +0.21 ZAR -0.79 USD -0.4 bardzo bardzo podobne. Po prostu odwróć znak w jednym z dwóch wyników.
ttnphns

Odpowiedzi:

22

Ilekroć fabuła przeskakuje za dużo, odwróć orientację. Jednym skutecznym kryterium jest to: oblicz całkowitą liczbę skoków dla wszystkich komponentów. Oblicz całkowitą liczbę skoków, jeśli następny wektor własny jest zanegowany. Jeśli ten ostatni jest mniejszy, zaneguj następny wektor własny.

Oto implementacja. (Nie jestem zaznajomiony zoo, co może pozwolić na bardziej eleganckie rozwiązanie).

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)
}

Na przykład, poprowadźmy losowy spacer w grupie ortogonalnej i drżmy go trochę dla zainteresowania:

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,]
}

Oto krocząca PCA:

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

Oryginalny

Teraz poprawiona wersja:

plot(amend(result))

Poprawione

Whuber
źródło
tjavja+1ja+1vjaja1-1vja+1. Twój algorytm wydaje się nieco inny. Czy działałoby to w ten sam sposób?
ameba mówi Przywróć Monikę
@amoeba Chociaż nie jestem do końca pewien, co robisz, brzmi to jak niektóre pomysły omówione w odpowiedzi Davida J. Harrisa i komentarze po nim. Zobacz w szczególności mój komentarz na stronie stats.stackexchange.com/questions/34396/… .
whuber
2
@ Art, tak jak rozumiem, chcesz naprawić znak komponentu w oparciu o niektóre preferencje zewnętrzne (zewnętrzne względem PCA). W porządku, ale właśnie tak powinieneś do tego podejść. Najpierw wykonaj przesuwane PCA, upewniając się, że znaki są spójne. Następnie, w oparciu o dodatkowe kryteria, zdecyduj, czy przerzucić cały komponent, czy nie. Np. Możesz skorelować go z trendem euro, a jeśli korelacja jest ujemna, odwróć element. Czy jakoś tak. Zależy to całkowicie od konkretnej aplikacji i znajomości domeny.
ameba mówi Przywróć Monikę
1
Zgadzam się z interpretacją i zaleceniem @ amoeba.
whuber
1
@amoeba: tak, masz rację co do tego, chociaż naiwnie pomyślałem, że może być jakieś ogólne rozwiązanie, które nie zależy od konkretnych szeregów czasowych, coś w rodzaju „prawdziwej orientacji wektora” :) w każdym razie, dziękuję za pomoc i sugestie
Anonimowy
8

@ whuber ma rację, że nie ma orientacji właściwej dla danych, ale nadal możesz wymusić, że twoje wektory własne mają dodatnią korelację z jakimś wektorem odniesienia.

Na przykład, możesz sprawić, by ładunki dla USD były dodatnie na wszystkich swoich wektorach własnych (tj. Jeśli ładowanie USD jest ujemne, odwróć znaki całego wektora). Ogólny kierunek wektora jest nadal dowolny (ponieważ zamiast tego mógłbyś użyć EUR lub ZAR jako odniesienia), ale kilka pierwszych osi twojego PCA prawdopodobnie nie będzie przeskakiwać prawie tak bardzo - szczególnie dlatego, że twoje okna są tak zmienne długo.

David J. Harris
źródło
7
Dobry pomysł. Próbowałem tego pierwszy (prawdopodobnie podczas pisania tej odpowiedzi :-). Problem polega na tym, że inne ładunki mogą skakać. Aby to naprawić, oprzyj wybór znaku na największym obciążeniu. Nadal nie ma kości: ładunki mogą nadal skakać. Sztuką jest za każdym razem wybrać orientację, która powoduje najmniejsze zakłócenia w wektorze obciążeń z poprzedniego czasu.
whuber
4
@whuber Dobra robota.
David J. Harris
1
Prawidłowo, znak obciążeń nie ma znaczenia (orientacja). Coś, co nie zostało rozwiązane, polegało na tym, że jeśli wykonujesz to w różnych pakietach oprogramowania, różnice między pakietami polegają na tym, że jeden program może powodować ujemne (dodatnie) znaki na poszczególnych ładowaniach, podczas gdy inny skutkuje dodatnimi (ujemnymi) znakami dla tych samych ładunków. Dlatego znaki ostatecznych wyników na powyższym wykresie 3 serii mogą zostać odwrócone przy użyciu innego pakietu. Ładunki wektora odniesienia mogą również mieć zmianę znaku - i to rozwiązanie nie byłoby niepoprawne.
JoleT
@LEP: Napotkałem ten sam problem z inwersją, być może już znalazłeś rozwiązanie tego problemu - jak dowiedzieć się, czy pierwszy wektor jest poprawny i upewnić się, że reszta zostanie odpowiednio do niego dopasowana - quant.stackexchange.com/questions / 3094 /… ?
Anonimowy
Dopóki macierz nie jest pojedyncza i żadna z wartości własnych nie jest równa zero, większość wyników algorytmu powinna być taka sama, z wyjątkiem zmiany znaków o 180 stopni - co nie jest gwarantowane.
JoleT,
1

To, co zrobiłem, było obliczenie odległości L1 między kolejnymi wektorami własnymi. Po znormalizowaniu tej macierzy wybieram próg wyniku az, np. 1, tak więc jeśli przy każdym nowym toczeniu zmiana jest powyżej tego progu, przerzucam wektor własny, czynniki i obciążenia, aby uzyskać spójność w oknie toczenia. Osobiście nie lubię wymuszać danych znaków w niektórych korelacjach, ponieważ mogą one być bardzo niestabilne w zależności od makropoleceń.

Raul Muñoz
źródło