Leemos primero datos del paro registrado (mensuales):
library(zoo)
##
## Attaching package: 'zoo'
## The following objects are masked from 'package:base':
##
## as.Date, as.Date.numeric
system(command="head -n 10 ParoReg.txt", intern=TRUE)
## [1] " BE_24_15_3" "" "1939:09 632510"
## [4] "1939:10 608040" "1939:11 464140" "1939:12 441388"
## [7] "1940:01 479338" "1940:02 486834" "1940:03 474129"
## [10] "1940:04 462540"
<- read.table(file="ParoReg.txt",skip=2,header=FALSE,dec=",")
temp head(temp)
## V1 V2
## 1 1939:09 632510
## 2 1939:10 608040
## 3 1939:11 464140
## 4 1939:12 441388
## 5 1940:01 479338
## 6 1940:02 486834
tail(temp)
## V1 V2
## 890 2013:10 4811383
## 891 2013:11 4808908
## 892 2013:12 4701338
## 893 2014:01 4814435
## 894 2014:02 4812486
## 895 2014:03 4795866
<- zoo(temp[,2],order.by=as.yearmon(1939 + 8/12 + seq(0,894)/12))
ParoReg head(ParoReg)
## sep 1939 oct 1939 nov 1939 dic 1939 ene 1940 feb 1940
## 632510 608040 464140 441388 479338 486834
tail(ParoReg)
## oct 2013 nov 2013 dic 2013 ene 2014 feb 2014 mar 2014
## 4811383 4808908 4701338 4814435 4812486 4795866
Ahora los de paro EPA, procedentes del INE.
system("head -n 10 ParadosEPA.csv", intern=TRUE)
## [1] "Encuesta de Población Activa"
## [2] "Parados"
## [3] "Parados por sexo y grupo de edad. Valores absolutos y porcentajes respecto del total de cada sexo"
## [4] "Ambos sexos;Valor absoluto;2014T3;2014T2;2014T1;2013T4;2013T3;2013T2;2013T1;2012T4;2012T3;2012T2;2012T1;2011T4;2011T3;2011T2;2011T1;2010T4;2010T3;2010T2;2010T1;2009T4;2009T3;2009T2;2009T1;2008T4;2008T3;2008T2;2008T1;2007T4;2007T3;2007T2;2007T1;2006T4;2006T3;2006T2;2006T1;2005T4;2005T3;2005T2;2005T1;2004T4;2004T3;2004T2;2004T1;2003T4;2003T3;2003T2;2003T1;2002T4;2002T3;2002T2;2002T1;Total;"
## [5] "5.427,7;5.622,9;5.933,3;5.935,6;5.943,4;6.047,3;6.278,2;6.021,0;5.824,2;5.731,0;5.667,9;5.287,3;4.998,0;4.844,2;4.921,2;4.702,2;4.585,4;4.655,3;4.617,7;4.335,0;4.121,4;4.139,6;4.018,2;3.206,8;2.600,7;2.385,7;2.190,5;1.942,0;1.806,2;1.773,2;1.863,2;1.819,4;1.766,9;1.834,4;1.942,8;1.860,3;1.783,5;1.969,1;2.121,3;2.176,9;2.199,8;2.247,6;2.309,8;2.276,7;2.247,5;2.216,0;2.328,5;2.232,4;2.196,0;2.103,3;2.152,8;"
Un poco difícil de leer:
<- read.csv(file="ParadosEPA.csv",skip=4,header=FALSE,dec=",",sep=";",stringsAsFactors=FALSE)
temp head(temp)
## V1 V2 V3 V4 V5 V6 V7 V8 V9
## 1 5.427,7 5.622,9 5.933,3 5.935,6 5.943,4 6.047,3 6.278,2 6.021,0 5.824,2
## V10 V11 V12 V13 V14 V15 V16 V17 V18
## 1 5.731,0 5.667,9 5.287,3 4.998,0 4.844,2 4.921,2 4.702,2 4.585,4 4.655,3
## V19 V20 V21 V22 V23 V24 V25 V26 V27
## 1 4.617,7 4.335,0 4.121,4 4.139,6 4.018,2 3.206,8 2.600,7 2.385,7 2.190,5
## V28 V29 V30 V31 V32 V33 V34 V35 V36
## 1 1.942,0 1.806,2 1.773,2 1.863,2 1.819,4 1.766,9 1.834,4 1.942,8 1.860,3
## V37 V38 V39 V40 V41 V42 V43 V44 V45
## 1 1.783,5 1.969,1 2.121,3 2.176,9 2.199,8 2.247,6 2.309,8 2.276,7 2.247,5
## V46 V47 V48 V49 V50 V51 V52
## 1 2.216,0 2.328,5 2.232,4 2.196,0 2.103,3 2.152,8 NA
Notad las contorsiones que es preciso realizar con los puntos y comas:
<- gsub("\\.","",temp)
temp <- gsub("\\,",".",temp)
temp <- as.numeric(temp[-length(temp)])
temp head(temp)
## [1] 5427.7 5622.9 5933.3 5935.6 5943.4 6047.3
tail(temp)
## [1] 2216.0 2328.5 2232.4 2196.0 2103.3 2152.8
length(temp)
## [1] 51
<- zoo(rev(temp),order.by=as.yearqtr(2002 + seq(0,50)/4))
ParoEPA head(ParoEPA)
## 2002 Q1 2002 Q2 2002 Q3 2002 Q4 2003 Q1 2003 Q2
## 2152.8 2103.3 2196.0 2232.4 2328.5 2216.0
tail(ParoEPA)
## 2013 Q2 2013 Q3 2013 Q4 2014 Q1 2014 Q2 2014 Q3
## 6047.3 5943.4 5935.6 5933.3 5622.9 5427.7
Observemos que el fechado de las dos series de paro es inconsistente:
head(ParoReg)
## sep 1939 oct 1939 nov 1939 dic 1939 ene 1940 feb 1940
## 632510 608040 464140 441388 479338 486834
head(ParoEPA)
## 2002 Q1 2002 Q2 2002 Q3 2002 Q4 2003 Q1 2003 Q2
## 2152.8 2103.3 2196.0 2232.4 2328.5 2216.0
Ahora dataremos cada periodo en el último día para poder alinear las series. (¡Ojo! La función as.Date
utilizada aquí es la del paquete zoo
, que enmascara a la del mismo nombre en R base. Para resolver cualquier ambigüedad podemos emplear el operador ::
como en zoo::as.Date
, base::as.Date
.)
index(ParoReg) <- as.Date(index(ParoReg), frac=1)
index(ParoEPA) <- as.Date(index(ParoEPA), frac=1)
frac=1
significa: “Alinea con el día que corresponde a dejar pasar la totalidad del periodo”. Esto alinea con el último dia del mes, del trimestre, etc. Observemos ahora:
head(ParoReg)
## 1939-09-30 1939-10-31 1939-11-30 1939-12-31 1940-01-31 1940-02-29
## 632510 608040 464140 441388 479338 486834
head(ParoEPA)
## 2002-03-31 2002-06-30 2002-09-30 2002-12-31 2003-03-31 2003-06-30
## 2152.8 2103.3 2196.0 2232.4 2328.5 2216.0
Ahora va a haber fechas identicas en ambas series que van a permitir el empalme (aunque no todas las fechas de una serie aparezcan en la otra). ¡SOLO el poder hacer uso de la línea a continuación justifica el trabajo que nos hemos tomado en fechar!
<- cbind(ParoReg/1000,ParoEPA)
paro <- window(paro,start="2002-03-31",end="2013-07-31")
paro plot(na.approx(paro),screens=1,col=1:2)
<- log(paro)
paro plot(na.approx(paro),screens=1,col=1:2)
Creemos un modelo bivariante:
library(dlm)
<- function(p) {
crearMod <- exp(p)
ep <- dlmModPoly(1)
mod1 <- dlmModPoly(1)
mod2 <- mod1 %+% mod2
mod $FF <- matrix(c(1,1),2,1)
mod$GG <- matrix(1,1,1)
mod$W <- matrix(ep[1],1,1)
mod$V <- diag(rep(ep[2],2))
modreturn(mod)
}<- crearMod(rep(-5,2))
modelo modelo
## $FF
## [,1]
## [1,] 1
## [2,] 1
##
## $V
## [,1] [,2]
## [1,] 0.006737947 0.000000000
## [2,] 0.000000000 0.006737947
##
## $GG
## [,1]
## [1,] 1
##
## $W
## [,1]
## [1,] 0.006737947
##
## $m0
## [1] 0 0
##
## $C0
## [,1] [,2]
## [1,] 1e+07 0e+00
## [2,] 0e+00 1e+07
¡ %+% es diferente de + !
<- dlmMLE(paro,c(0,0),build=crearMod)
estim <- crearMod(estim$par)
modelo #
dlmFilter(paro,modelo)$m[1:6]
## 2002-03-31 2002-04-30 2002-05-31 2002-06-30 2002-07-31
## 0.000000 7.658069 7.645967 7.627698 7.621299 7.606837
1:6,] paro[
## ParoReg/1000 ParoEPA
## 2002-03-31 7.641614 7.674525
## 2002-04-30 7.630495 NA
## 2002-05-31 7.602363 NA
## 2002-06-30 7.582210 7.651263
## 2002-07-31 7.581644 NA
## 2002-08-31 7.592861 NA
<- dlmSmooth(paro,modelo)$s[-1] suavizado.dlm
require(KFAS)
## Loading required package: KFAS
## Please cite KFAS in publications by using:
##
## Jouni Helske (2017). KFAS: Exponential Family State Space Models in R. Journal of Statistical Software, 78(10), 1-39. doi:10.18637/jss.v078.i10.
<- SSModel(paro ~
mod1 -1 # Sin el nivel "de oficio"
+ SSMcustom(
Z=matrix(c(1,1), 2, 1), # Matriz de observación
T=diag(1), # Matriz de transición
Q=diag(1), # Matriz covarianzas del estado
P1inf=matrix(1,1,1) # Priori difusa
),H=diag(2) # Matriz de covarianzas obs
)
Examinemos la estructura del objeto resultante:
mod1
## Call:
## SSModel(formula = paro ~ -1 + SSMcustom(Z = matrix(c(1, 1), 2,
## 1), T = diag(1), Q = diag(1), P1inf = matrix(1, 1, 1)), H = diag(2))
##
## State space model object of class SSModel
##
## Dimensions:
## [1] Number of time points: 137
## [1] Number of time series: 2
## [1] Number of disturbances: 1
## [1] Number of states: 1
## Names of the states:
## [1] custom1
## Distributions of the time series:
## [1] gaussian
##
## Object is a valid object of class SSModel.
$Z mod1
## , , 1
##
## custom1
## ParoReg/1000 1
## ParoEPA 1
$T mod1
## , , 1
##
## custom1
## custom1 1
$Q mod1
## , , 1
##
## [,1]
## [1,] 1
$H mod1
## , , 1
##
## [,1] [,2]
## [1,] 1 0
## [2,] 0 1
Para ajustar el modelo, necesitamos la updatefn
más abajo, cuyo cometido es reemplazar valores de los parametros en lugares específicos de las matrices que hay que estimar. Nuestro modelo tiene dos parámetros, una varianza en Q
y dos varianzas en H
que supondremos iguales.
Obsérvese que estamos imponiendo que las dos varianzas de la ecuación de observación sean iguales, lo que tiene sentido si les atribuimos la misma precisión: no tiene porqué ser el caso.
<- function(pars, model) {
updatefn <- exp(pars)
ep "Q"] <- matrix(ep[1],1,1)
model["H"] <- diag(c(ep[2],ep[2]))
model[return(model)
}
Una vez definida esta función, proporcionamos valores iniciales para los dos parámetros en init
e invocamos fitSSM
.
<- rep(0.1, 2)
init <- fitSSM(mod1, inits=init, updatefn=updatefn) fit
Los resultados se obtienen en fit
cuyo contenido es:
fit
## $optim.out
## $optim.out$par
## [1] -6.935134 -5.670112
##
## $optim.out$value
## [1] -216.5231
##
## $optim.out$counts
## function gradient
## 115 NA
##
## $optim.out$convergence
## [1] 0
##
## $optim.out$message
## NULL
##
##
## $model
## Call:
## SSModel(formula = paro ~ -1 + SSMcustom(Z = matrix(c(1, 1), 2,
## 1), T = diag(1), Q = diag(1), P1inf = matrix(1, 1, 1)), H = diag(2))
##
## State space model object of class SSModel
##
## Dimensions:
## [1] Number of time points: 137
## [1] Number of time series: 2
## [1] Number of disturbances: 1
## [1] Number of states: 1
## Names of the states:
## [1] custom1
## Distributions of the time series:
## [1] gaussian
##
## Object is a valid object of class SSModel.
En $optim.out$value
vemos el valor de la verosimilitud maximizada y en $optim.out$par
los valores “brutos” de los parámetros (sin hacer las transformaciones indicadas más arriba). Podemos reemplazarlos en el modelo mediante,
<- updatefn(pars = fit$optim.out$par, model = mod1)
mod1 $H mod1
## , , 1
##
## [,1] [,2]
## [1,] 0.003447479 0.000000000
## [2,] 0.000000000 0.003447479
$Q mod1
## , , 1
##
## [,1]
## [1,] 0.0009729929
Con los valores de los parámetros ya estimados podemos correr el filtro de Kalman para obtener los valores suavizados del estado:
<- KFS(mod1) mod1.out
En mod1.out
tenemos ahora bastante información, que incluye los valores suavizados del estado (en el componente alphahat
).
str(mod1.out$V)
## num [1, 1, 1:137] 0.00099 0.0009 0.000832 0.000702 0.000803 ...
<- mod1.out$alphahat suavizado.kfas
Podemos comparar con el resultado de dlm
. La inicialización aproximada de dlm
y la exacta de KFAS
proporcionan resultados prácticamente indistinguibles.
cbind(suavizado.kfas,suavizado.dlm)
## Time Series:
## Start = 1
## End = 137
## Frequency = 1
## suavizado.kfas suavizado.dlm
## 1 7.641083 7.641083
## 2 7.631495 7.631495
## 3 7.622190 7.622190
## 4 7.618479 7.618480
## 5 7.615753 7.615754
## 6 7.622653 7.622654
## 7 7.637961 7.637962
## 8 7.646846 7.646846
## 9 7.659738 7.659738
## 10 7.673170 7.673169
## 11 7.678990 7.678989
## 12 7.681860 7.681859
## 13 7.683229 7.683228
## 14 7.665970 7.665970
## 15 7.652705 7.652706
## 16 7.649080 7.649081
## 17 7.640845 7.640846
## 18 7.644453 7.644455
## 19 7.658010 7.658010
## 20 7.665335 7.665335
## 21 7.677532 7.677532
## 22 7.691838 7.691837
## 23 7.696418 7.696417
## 24 7.696911 7.696910
## 25 7.695135 7.695134
## 26 7.681382 7.681382
## 27 7.668308 7.668308
## 28 7.661740 7.661741
## 29 7.649038 7.649039
## 30 7.647921 7.647922
## 31 7.653156 7.653156
## 32 7.653972 7.653972
## 33 7.659265 7.659265
## 34 7.664441 7.664440
## 35 7.666086 7.666085
## 36 7.662248 7.662247
## 37 7.653295 7.653294
## 38 7.637564 7.637564
## 39 7.618956 7.618957
## 40 7.604402 7.604403
## 41 7.599789 7.599790
## 42 7.596358 7.596359
## 43 7.588961 7.588963
## 44 7.605291 7.605291
## 45 7.615496 7.615495
## 46 7.616644 7.616644
## 47 7.632950 7.632948
## 48 7.635081 7.635079
## 49 7.623927 7.623926
## 50 7.613741 7.613740
## 51 7.596696 7.596696
## 52 7.577827 7.577828
## 53 7.576062 7.576062
## 54 7.573711 7.573712
## 55 7.565999 7.566001
## 56 7.578376 7.578376
## 57 7.585407 7.585407
## 58 7.584816 7.584816
## 59 7.598645 7.598644
## 60 7.600427 7.600426
## 61 7.591648 7.591647
## 62 7.589374 7.589373
## 63 7.580601 7.580602
## 64 7.569903 7.569904
## 65 7.580533 7.580534
## 66 7.589633 7.589633
## 67 7.591586 7.591587
## 68 7.614606 7.614607
## 69 7.634721 7.634722
## 70 7.651355 7.651356
## 71 7.687060 7.687059
## 72 7.712347 7.712346
## 73 7.727766 7.727766
## 74 7.749553 7.749553
## 75 7.769161 7.769161
## 76 7.790312 7.790313
## 77 7.818280 7.818281
## 78 7.852993 7.852995
## 79 7.892510 7.892512
## 80 7.945718 7.945719
## 81 7.999469 7.999469
## 82 8.052285 8.052284
## 83 8.100327 8.100325
## 84 8.145619 8.145617
## 85 8.188173 8.188170
## 86 8.198995 8.198993
## 87 8.209229 8.209228
## 88 8.223685 8.223684
## 89 8.221244 8.221244
## 90 8.232407 8.232408
## 91 8.253639 8.253639
## 92 8.264905 8.264905
## 93 8.281803 8.281803
## 94 8.304646 8.304645
## 95 8.316213 8.316213
## 96 8.330635 8.330634
## 97 8.346313 8.346312
## 98 8.339446 8.339445
## 99 8.335516 8.335516
## 100 8.338656 8.338656
## 101 8.325404 8.325405
## 102 8.327527 8.327528
## 103 8.341247 8.341247
## 104 8.341810 8.341810
## 105 8.349850 8.349850
## 106 8.365962 8.365961
## 107 8.370044 8.370044
## 108 8.379729 8.379729
## 109 8.393233 8.393232
## 110 8.381614 8.381615
## 111 8.376317 8.376317
## 112 8.381163 8.381163
## 113 8.372672 8.372673
## 114 8.380799 8.380801
## 115 8.404320 8.404320
## 116 8.411657 8.411658
## 117 8.427805 8.427806
## 118 8.453495 8.453494
## 119 8.462108 8.462108
## 120 8.478718 8.478718
## 121 8.501207 8.501205
## 122 8.493710 8.493710
## 123 8.494406 8.494406
## 124 8.505287 8.505286
## 125 8.493533 8.493534
## 126 8.499405 8.499407
## 127 8.522222 8.522222
## 128 8.521959 8.521960
## 129 8.532599 8.532600
## 130 8.552839 8.552839
## 131 8.549428 8.549428
## 132 8.556202 8.556202
## 133 8.571724 8.571722
## 134 8.551795 8.551796
## 135 8.542243 8.542244
## 136 8.545986 8.545986
## 137 8.525974 8.525975
Podemos representar las observaciones y el valor suavizado del estado junto con su intervalo de confianza 95%.
plot(paro[,1], xlab="Fecha", ylab="Paro (log)", type="p",
cex=0.5, col="green",
main="Paro registrado y EPA")
legend(x="bottomright",legend=c("Registrado","EPA",expression(alpha[t])),
col=c("green","blue","black"), pch=c(1,1,NA), pt.cex=0.5,
seg.len=1, lty=c(NA,NA,1), cex=0.9, lwd=c(NA,NA,2))
points(paro[,2], col="blue", cex=0.5)
lines(zoo(mod1.out$alphahat, order.by=index(paro)), lwd=2)
<- 1.96*sqrt(mod1.out$V[1,1,])
ciw lines(zoo(mod1.out$alphahat + ciw, order.by=index(paro)), lty=3)
lines(zoo(mod1.out$alphahat - ciw, order.by=index(paro)), lty=3)
Lectura datos PIB (del Banco de España):
<- read.table(file="PIB.txt",skip=2,header=FALSE,dec=",")
temp head(temp)
## V1 V2
## 1 1995:1 157.257
## 2 1995:2 158.022
## 3 1995:3 158.572
## 4 1995:4 159.538
## 5 1996:1 160.417
## 6 1996:2 161.615
tail(temp)
## V1 V2
## 71 2012:3 232.995
## 72 2012:4 231.201
## 73 2013:1 230.480
## 74 2013:2 230.169
## 75 2013:3 230.345
## 76 2013:4 230.747
<- zoo(temp[,2],order.by=as.yearqtr(1995 + seq(0,75)/4))
PIB head(PIB)
## 1995 Q1 1995 Q2 1995 Q3 1995 Q4 1996 Q1 1996 Q2
## 157.257 158.022 158.572 159.538 160.417 161.615
tail(PIB)
## 2012 Q3 2012 Q4 2013 Q1 2013 Q2 2013 Q3 2013 Q4
## 232.995 231.201 230.480 230.169 230.345 230.747
A continuación, datos de producción de energía eléctrica mensuales (del Banco de España):
<- read.table(file="elec.txt",skip=2,header=FALSE,dec=",")
temp head(temp)
## V1 V2
## 1 1977:01 7863
## 2 1977:02 7147
## 3 1977:03 7452
## 4 1977:04 6726
## 5 1977:05 6857
## 6 1977:06 6541
tail(temp)
## V1 V2
## 440 2013:08 22046.6
## 441 2013:09 20937.2
## 442 2013:10 21120.9
## 443 2013:11 21651.0
## 444 2013:12 22886.6
## 445 2014:01 23118.4
<- zoo(temp[,2],order.by=as.yearmon(1977 + seq(0,444)/12))
ELEC head(ELEC)
## ene 1977 feb 1977 mar 1977 abr 1977 may 1977 jun 1977
## 7863 7147 7452 6726 6857 6541
tail(ELEC)
## ago 2013 sep 2013 oct 2013 nov 2013 dic 2013 ene 2014
## 22046.6 20937.2 21120.9 21651.0 22886.6 23118.4
Tenemos ahora que alinear las series. Podemos fecharlas el último día de cada periodo:
index(PIB) <- as.Date(index(PIB), frac=1)
index(ELEC) <- as.Date(index(ELEC), frac=1)
head(PIB)
## 1995-03-31 1995-06-30 1995-09-30 1995-12-31 1996-03-31 1996-06-30
## 157.257 158.022 158.572 159.538 160.417 161.615
head(ELEC)
## 1977-01-31 1977-02-28 1977-03-31 1977-04-30 1977-05-31 1977-06-30
## 7863 7147 7452 6726 6857 6541
Al tener un fechado homogéneo, podemos emparejarlas y se alinearán automáticamente:
<- cbind(100*PIB,ELEC)
datos tail(datos)
## 100 * PIB ELEC
## 2013-08-31 NA 22046.6
## 2013-09-30 23034.5 20937.2
## 2013-10-31 NA 21120.9
## 2013-11-30 NA 21651.0
## 2013-12-31 23074.7 22886.6
## 2014-01-31 NA 23118.4
plot(datos, log="y")
plot(na.approx(datos), col=1:2, main="Interpolando datos")
plot(na.approx(datos),screen=1,col=1:2, main="PIB y energía bruta",ylab="Escala log",xlab="Fecha")
<- window(datos, start=as.Date("1995-01-31"), end=as.Date("2014-01-31"))
datos plot(na.approx(datos),screen=1,col=1:2, main="PIB y energía bruta",xlab="Fecha")
Podríamos ahora proceder como en el caso del paro registrado y EPA. ¡Observad! El nivel de las series no es (ni tiene porque ser) homogéneo. El multiplicar por 100 una permite representarlas en una escala numérica común, pero todavía tenemos que dar cuenta de su diferente nivel (en media, la serie “negra” por encima de la “roja”). Una posibilidad podría ser incluir un parámetro en la ecuación de observación que ajustara los niveles respectivos:
Leamos primero los datos ya preparados:
load("Pseudomona.rda") # Contiene una dataframe de nombre 'datos'.
datos
## N IPM Tiempo DDDe DDS
## 1998 Q1 27 25 1 2.5035765 0
## 1998 Q2 27 25 2 14.9647887 0
## 1998 Q3 27 25 3 20.5081001 0
## 1998 Q4 27 25 4 19.0707351 0
## 1999 Q1 47 42 5 13.9973082 0
## 1999 Q2 47 42 6 13.5743520 0
## 1999 Q3 47 42 7 3.9556962 0
## 1999 Q4 47 42 8 10.2005533 0
## 2000 Q1 7 6 9 14.8351648 0
## 2000 Q2 5 4 10 1.6430412 0
## 2000 Q3 2 1 11 28.3512064 0
## 2000 Q4 7 7 12 9.5737483 0
## 2001 Q1 7 6 13 23.0658199 0
## 2001 Q2 12 9 14 7.6243455 0
## 2001 Q3 11 9 15 10.7898010 0
## 2001 Q4 15 9 16 9.7634763 0
## 2002 Q1 6 5 17 5.8291771 0
## 2002 Q2 4 2 18 6.2586926 1
## 2002 Q3 8 5 19 3.7323944 1
## 2002 Q4 6 5 20 2.8236915 1
## 2003 Q1 8 6 21 1.8839713 1
## 2003 Q2 7 7 22 0.0000000 1
## 2003 Q3 3 3 23 2.1084337 1
## 2003 Q4 6 6 24 7.0175439 1
## 2004 Q1 6 5 25 6.4862104 1
## 2004 Q2 7 5 26 7.6923077 1
## 2004 Q3 6 4 27 9.3608597 1
## 2004 Q4 7 5 28 6.6192560 1
## 2005 Q1 5 4 29 6.8983402 1
## 2005 Q2 10 8 30 2.2522523 1
## 2005 Q3 5 5 31 18.6969697 1
## 2005 Q4 3 3 32 5.2356021 1
## 2006 Q1 5 3 33 42.7612994 1
## 2006 Q2 6 5 34 7.4417010 1
## 2006 Q3 7 1 35 12.5282167 1
## 2006 Q4 8 3 36 5.2492047 1
## 2007 Q1 5 1 37 3.9043584 1
## 2007 Q2 4 1 38 3.9874082 1
## 2007 Q3 6 4 39 12.1268657 1
## 2007 Q4 10 3 40 4.1832669 1
## 2008 Q1 8 4 41 7.0106908 1
## 2008 Q2 10 3 42 5.7588076 1
## 2008 Q3 16 5 43 1.2012012 1
## 2008 Q4 9 4 44 1.4065817 1
## 2009 Q1 4 2 45 1.4317181 1
## 2009 Q2 4 2 46 1.8936170 1
## 2009 Q3 16 7 47 1.2486127 1
## 2009 Q4 11 6 48 1.8671193 1
## 2010 Q1 4 3 49 0.8970727 1
## 2010 Q2 6 4 50 11.0146252 1
## 2010 Q3 10 5 51 2.5945471 1
## 2010 Q4 7 5 52 1.5903308 1
## 2011 Q1 7 3 53 0.8354756 1
## 2011 Q2 8 5 54 0.0000000 1
## 2011 Q3 13 8 55 0.1062925 1
## 2011 Q4 12 8 56 1.3605442 1
## 2012 Q1 4 2 57 0.7207578 1
## 2012 Q2 11 2 58 0.9647651 1
## 2012 Q3 18 11 59 1.8041237 1
## 2012 Q4 9 4 60 0.5859375 1
## 2013 Q1 4 2 61 0.0000000 1
## 2013 Q2 6 3 62 1.1585994 1
## 2013 Q3 1 1 63 0.0000000 1
## 2013 Q4 6 4 64 2.0360825 1
A continuación especificamos el modelo deseado, con ayuda de funciones constructoras disponibles en el paquete KFAS. En los lugares en que han de aparecer algunos parámetros ponemos valores arbitrarios (ceros); luego serán rellenados por los valores estimados.
En u
tenemos sujetos en riesgo. El modelo que sigue especifica que la sensibilidad evoluciona como un nivel aleatorio mas partes explicadas por la DDS y DDDe –que supondremos fijas–. La ecuación de observación ya no es la conocida función lineal a que estamos habituados, sino que lo que tenemos es que la observación se distribuye como Poisson con \(\lambda\) igual a la sensibilidad multiplicada por \(N\) (número de sujetos en riesgo).
<- formula(~ -1 + DDDe + DDS)
f <- SSModel(IPM ~ 1
mod2+ SSMregression(rformula = f,
Q = matrix(0, 2, 2),
P1inf=diag(2),
data=datos)
+ SSMtrend(degree = 1,
Q = list(matrix(0, 1, 1)),
P1inf = matrix(1, 1, 1)) ,
distribution="poisson", u = datos[,"N"], data=datos
)str(mod2)
## List of 14
## $ y : Time-Series [1:64, 1] from 1 to 64: 25 25 25 25 42 42 42 42 6 4 ...
## $ Z : num [1, 1:3, 1:64] 2.5 0 1 15 0 ...
## ..- attr(*, "dimnames")=List of 3
## .. ..$ : NULL
## .. ..$ : chr [1:3] "DDDe" "DDS" "level"
## .. ..$ : NULL
## $ H : chr "Omitted"
## $ T : num [1:3, 1:3, 1] 1 0 0 0 1 0 0 0 1
## ..- attr(*, "dimnames")=List of 3
## .. ..$ : chr [1:3] "DDDe" "DDS" "level"
## .. ..$ : chr [1:3] "DDDe" "DDS" "level"
## .. ..$ : NULL
## $ R : num [1:3, 1:3, 1] 1 0 0 0 1 0 0 0 1
## ..- attr(*, "dimnames")=List of 3
## .. ..$ : chr [1:3] "DDDe" "DDS" "level"
## .. ..$ : NULL
## .. ..$ : NULL
## $ Q : num [1:3, 1:3, 1] 0 0 0 0 0 0 0 0 0
## $ a1 : num [1:3, 1] 0 0 0
## ..- attr(*, "dimnames")=List of 2
## .. ..$ : chr [1:3] "DDDe" "DDS" "level"
## .. ..$ : NULL
## $ P1 : num [1:3, 1:3] 0 0 0 0 0 0 0 0 0
## ..- attr(*, "dimnames")=List of 2
## .. ..$ : chr [1:3] "DDDe" "DDS" "level"
## .. ..$ : chr [1:3] "DDDe" "DDS" "level"
## $ P1inf : num [1:3, 1:3] 1 0 0 0 1 0 0 0 1
## ..- attr(*, "dimnames")=List of 2
## .. ..$ : chr [1:3] "DDDe" "DDS" "level"
## .. ..$ : chr [1:3] "DDDe" "DDS" "level"
## $ u : Time-Series [1:64, 1] from 1 to 64: 27 27 27 27 47 47 47 47 7 5 ...
## $ distribution: chr "poisson"
## $ tol : num 1.49e-08
## $ call : language SSModel(formula = IPM ~ 1 + SSMregression(rformula = f, Q = matrix(0, 2, 2), P1inf = diag(2), data = datos) | __truncated__ ...
## $ terms :Classes 'terms', 'formula' language IPM ~ 1 + SSMregression(rformula = f, Q = matrix(0, 2, 2), P1inf = diag(2), data = datos) + SSMtrend(degree | __truncated__ ...
## .. ..- attr(*, "variables")= language list(IPM, SSMregression(rformula = f, Q = matrix(0, 2, 2), P1inf = diag(2), data = datos), SSMtrend(degree =| __truncated__ ...
## .. ..- attr(*, "factors")= int [1:3, 1:2] 0 1 0 0 0 1
## .. .. ..- attr(*, "dimnames")=List of 2
## .. .. .. ..$ : chr [1:3] "IPM" "SSMregression(rformula = f, Q = matrix(0, 2, 2), P1inf = diag(2), data = datos)" "SSMtrend(degree = 1, Q = list(matrix(0, 1, 1)), P1inf = matrix(1, 1, 1))"
## .. .. .. ..$ : chr [1:2] "SSMregression(rformula = f, Q = matrix(0, 2, 2), P1inf = diag(2), data = datos)" "SSMtrend(degree = 1, Q = list(matrix(0, 1, 1)), P1inf = matrix(1, 1, 1))"
## .. ..- attr(*, "term.labels")= chr [1:2] "SSMregression(rformula = f, Q = matrix(0, 2, 2), P1inf = diag(2), data = datos)" "SSMtrend(degree = 1, Q = list(matrix(0, 1, 1)), P1inf = matrix(1, 1, 1))"
## .. ..- attr(*, "specials")=Dotted pair list of 6
## .. .. ..$ SSMregression: int 2
## .. .. ..$ SSMtrend : int 3
## .. .. ..$ SSMseasonal : NULL
## .. .. ..$ SSMcycle : NULL
## .. .. ..$ SSMarima : NULL
## .. .. ..$ SSMcustom : NULL
## .. ..- attr(*, "order")= int [1:2] 1 1
## .. ..- attr(*, "intercept")= int 1
## .. ..- attr(*, "response")= int 1
## .. ..- attr(*, ".Environment")=<environment: R_GlobalEnv>
## - attr(*, "class")= chr "SSModel"
## - attr(*, "p")= int 1
## - attr(*, "m")= int 3
## - attr(*, "k")= int 3
## - attr(*, "n")= int 64
## - attr(*, "tv")= int [1:5] 1 1 0 0 0
## - attr(*, "state_types")= chr [1:3] "regression" "regression" "level"
## - attr(*, "eta_types")= chr [1:3] "regression" "regression" "level"
Esta es la matriz de observación (temporalmente variable):
$Z[,,1:5] mod2
## [,1] [,2] [,3] [,4] [,5]
## DDDe 2.503577 14.96479 20.5081 19.07074 13.99731
## DDS 0.000000 0.00000 0.0000 0.00000 0.00000
## level 1.000000 1.00000 1.0000 1.00000 1.00000
Matriz de covarianzas de las observaciones, no hay.
$H mod2
## [1] "Omitted"
Matriz de covarianzas de los elementos del vector de estado:
$Q mod2
## , , 1
##
## [,1] [,2] [,3]
## [1,] 0 0 0
## [2,] 0 0 0
## [3,] 0 0 0
Matriz de transición:
$T mod2
## , , 1
##
## DDDe DDS level
## DDDe 1 0 0
## DDS 0 1 0
## level 0 0 1
Definimos una función updatefn
para reemplazar los valores estimados de los parámetros en sus ubicaciones.
<- function(pars, model, ...) {
updatefn <- diag(c(0,0,exp(pars[1])))
Q "Q"] <- Q
model[return(model)
}
Proporcionamos valores iniciales al único parámetro del modelo y llevamos a cabo la estimación. Es mucho más onerosa que en el caso lineal gaussiano, que el filtro de Kalman maneja directamente; ahora la estimación se hace por simulación de modelos que aproximan el que deseamos (detalles en Durbin-Koopman, cap. 11).
<- c(-6)
init <- fitSSM(model=mod2, inits=init, updatefn=updatefn, method="BFGS", tol=10^-4, nsim=100) fit
El resultado del ajuste puede verse en fit
; contiene los valores de los parámetros (en $optim.out$par
), el valor optimizado de la verosimilitud (en $optim.out$value
), un diagnóstico sobre la convergencia (optim.out$convergence
: valor cero significa que todo fue bien) e información acerca del modelo.
fit
## $optim.out
## $optim.out$par
## [1] -5.913961
##
## $optim.out$value
## [1] 134.4912
##
## $optim.out$counts
## function gradient
## 13 3
##
## $optim.out$convergence
## [1] 0
##
## $optim.out$message
## NULL
##
##
## $model
## Call:
## SSModel(formula = IPM ~ 1 + SSMregression(rformula = f, Q = matrix(0,
## 2, 2), P1inf = diag(2), data = datos) + SSMtrend(degree = 1,
## Q = list(matrix(0, 1, 1)), P1inf = matrix(1, 1, 1)), data = datos,
## u = datos[, "N"], distribution = "poisson")
##
## State space model object of class SSModel
##
## Dimensions:
## [1] Number of time points: 64
## [1] Number of time series: 1
## [1] Number of disturbances: 3
## [1] Number of states: 3
## Names of the states:
## [1] DDDe DDS level
## Distributions of the time series:
## [1] poisson
##
## Object is a valid object of class SSModel.
Podemos ahora reemplazar los valores estimados de los parámetros en el modelo y correr el filtro de Kalman:
<- updatefn(pars=fit$optim.out$par, model=mod2)
mod2 <- KFS(mod2) mod2.out
En mod2.out$a
tenemos los valores filtrados, en mod2.out$alphahat
valores suavizados y en mod2.out$muhat
valores esperados de la variable respuesta (\(N_t\lambda_t\), valor esperado de sensibles en el periodo \(t\)). Se imprimen los primeros valores de las respectivas series.
head(mod2.out$a)
## NULL
head(mod2.out$alphahat)
## DDDe DDS level
## [1,] 0.001250509 -0.1343691 -0.1117110
## [2,] 0.001250509 -0.1343691 -0.1138130
## [3,] 0.001250509 -0.1343691 -0.1171291
## [4,] 0.001250509 -0.1343691 -0.1214188
## [5,] 0.001250509 -0.1343691 -0.1270862
## [6,] 0.001250509 -0.1343691 -0.1324245
head(cbind(mod2.out$model$y,mod2.out$muhat))
## mod2.out$model$y mod2.out$muhat
## [1,] 25 24.22189
## [2,] 25 24.55063
## [3,] 25 24.63956
## [4,] 25 24.49003
## [5,] 42 42.12179
## [6,] 42 41.87537
Para decidir sobre el posible efecto de la DDS, examinaremos su valor suavizado (estimado con todas las observaciones, pasadas, presentes y futuras).
$alphahat[,2][1] mod2.out
## [1] -0.1343691
y compararemos con su desviación típica
sqrt(mod2.out$V[2,2,])[1]
## [1] 0.2117715
El efecto sería aproximadamente significativo al nivel 5% si tuviera una magnitud de al menos dos desviaciones típicas, lo que aquí no sucede.
Mostramos a continuación dos gráficos, comparando los sensibles observados y esperados de acuerdo con el modelo y la estimación de la sensibilidad \(\lambda_t\) a lo largo del tiempo, con un intervalo de confianza 95% para la misma.
<- zoo(mod2.out$model$y, order.by=index(datos)) # Observados
o <- zoo(mod2.out$muhat, order.by=index(datos)) # Esperados
e plot(o, xlab="Fecha", ylab="Sensibles", type="p",
cex=0.6, col="green",
main="Sensibles esperados y observados (PseudoAer vs. IPM)")
points(e, cex=0.6, col="black")
legend(x="topright",
legend=c("Sensibles obs.", expression(N[t]*lambda[t])),
col=c("green","black"), pch=c(1,1), pt.cex=0.5)
<- zoo(datos[,"N"], order.by=index(datos))
a <- 100 * (e / a)
sensibilidad plot(sensibilidad, xlab="Fecha", ylab="Sensibles", type="l",
cex=0.6, col="black", lwd=2, ylim=c(0,100),
main="Sensibilidad ajustada y observada (PseudoAer vs. IPM)")
<- 1.96 * sqrt(mod2.out$V_mu[1,1,]) * (100 / a)
ciw lines(sensibilidad + ciw, lty=2)
lines(sensibilidad - ciw, lty=2)
points(100*o/a,col="green",cex=0.5)
abline(v=2002.25,col="red")
text(2002.25,0,"DDS",pos=4,col="red")