Freakonometrics

To content | To menu | To search

Tag - rstats

Entries feed - Comments feed

Wednesday, November 14 2012

Série temporelles

Pour la section sur les séries temporelles, je mets des notes de cours en ligne. Les slides seront en ligne d'ici la semaine prochaine. Sinon, quelques séries dans le fichier ci-dessous

> source(
+ "http://freakonometrics.blog.free.fr/public/data/sourcets.R")

Les données sont les suivantes, avec l'utilisation du mot clé épilation sur Google,

> ts.plot(epilation,col="red")

..

ou l'utilisation du mot clé gym,

> ts.plot(gym,col="blue")

On a aussi la série des décès suite à des accidents de la route en Ontario,

> ts.plot(ontario,col="purple")

ou la série du nombre de vente de voitures au Québec

> ts.plot(quebec,col="purple")

La série suivante est la température minimale journalière au parc Montsouris, à Paris,

> ts.plot(temperature,col="green")

Pour finir, deux séries de transport, avec le nombre de voitures qui ont emprunté l'autoroute A7 en France

> ts.plot(autoroute7,col="red")

ou le nombre de voyageur dans un aéroport,

> ts.plot(airline,col="blue")

Tuesday, October 16 2012

Régresion multiple

Un peu de code pour le cours de demain,

US=read.table("http://freakonometrics.free.fr/US.txt",
header=TRUE,sep=";")
abreviation=read.table(
"http://freakonometrics.blog.free.fr/public/data/etatus.csv",
header=TRUE,sep=",")
US$USPS=rownames(US)
US=merge(US,abreviation)
US$state=tolower(US$NOM)
GV=read.table(
"http://freakonometrics.blog.free.fr/public/data/governor.csv",
header=TRUE,sep=";")
etat=strsplit(as.character(GV$State),"-")
listeetat=rep(NA,nrow(GV))
for(i in 1:nrow(GV)){
listeetat[i]=etat[[i]][1]
}
indice=which(is.na(listeetat)==FALSE)
basegv=data.frame(state=tolower(listeetat[indice]),
party=GV$Party[indice])
base=merge(US,basegv)

Je mets aussi une petite fonction pour faire des graphiques,

library(maps)
VL0=strsplit(map("state")$names,":")
VL=VL0[[1]]
for(i in 2:length(VL0)){VL=c(VL,VL0[[i]][1])}
ETAT=match(VL,US$state)
library(RColorBrewer)
carte=function(V=US$Murder,titre=
"Taux d'homicides aux Etats-Unis"){
variable=as.numeric(as.character(cut(V,
quantile(V,seq(0,1,by=1/6)),labels=1:6)))
niveau=variable[ETAT]
couleur=rev(brewer.pal(6, "RdBu"))
noml=levels(cut(V,quantile(V,seq(0,1,by=1/6))))
map("state", fill = TRUE, col=couleur[niveau]);
legend(-78,34,legend=noml,fill=couleur,
cex=1,bty="n");
title(titre)}
carte(US$Murder,titre=
"Taux d'homicides aux Etats-Unis")

Tuesday, September 25 2012

Prévision des vente de voitures au Québec

Après discussion, la date limite pour me renvoyer le second devoir est fixée à vendredi 5 octobre midi, par courriel. Je veux un fichier pdf par équipe, avec une page de garde indiquant le nom des membres du groupe, et le mot clé retenu.

Maintenant, je vais en profiter pour mettre en ligne le code tapé de matin, en cours. On avait travaillé sur la modélisation de la série des ventes de voiture, au Québec. La série est en ligne,

X=read.table(
"http://freakonometrics.blog.free.fr/public/
data/car-sales-quebec.csv"
, header=TRUE,sep=";",nrows=108) Xt=ts(X[,2],start=c(1960,1),frequency=12)

On peut regarder l'évolution de cette série temporelles (car les dessins, et la visualisation, c'est important),

plot(Xt)

On note une tendance linéaire (ou qu'on pourrait supposer linéaire), que l'on va estimer,

X=as.numeric(Xt)
temps=1:length(X)
base=data.frame(temps,X)
reg=lm(X~temps)

et que l'on pourra représenter

plot(temps,X,type="l",xlim=c(0,120))
abline(reg,col="red")

Si on veut une série stationnaire (et c'est effectivement ce que l'on cherche), on va retrancher cette tendance à notre série, et travailler sur la série résiduelle.

La série résiduelle est ici

Y=X-predict(reg)
plot(temps,Y,type="l")

que l'on devrait pouvoir supposer stationnaire. Classiquement, on regarde les autocorrélations,

acf(Y,36,lwd=4)

où on repère un beau cycle annuel, mais on va surtout utiliser les autocorrélations partielles pour identifier l'ordre de la composante autorégressive.

pacf(Y,36,lwd=4)

La 12ème est non-nulle, et tous les autres ensuite sont significativement nulles (ou presque). On va donc tenter un http://latex.codecogs.com/gif.latex?AR(12).

> fit.ar12=arima(Y,order=c(12,0,0))
> fit.ar12
Series: Y
ARIMA(12,0,0) with non-zero mean
 
Coefficients:
ar1     ar2      ar3      ar4     ar5      ar6     ar7
      0.1975  0.0832  -0.1062  -0.1212  0.1437  -0.1051  0.0319
s.e.  0.0838  0.0809   0.0826   0.0843  0.0850   0.0833  0.0854
ar9     ar10    ar11    ar12  intercept
      -0.0332  -0.0616  0.2635  0.4913  -148.3180
s.e.   0.0853   0.0840  0.0840  0.0841   384.5095
 
sigma^2 estimated as 2177974:  log likelihood=-946.75
AIC=1921.51   AICc=1926.03   BIC=1959.06

La douzième composante http://latex.codecogs.com/gif.latex?\phi_{12} semble significative, si on repense au test de Student,

u=seq(-6,6,by=.1)
plot(u,dnorm(u),type="l")
points(0.4913/0.0841,0,pch=19,col="red")

En revanche, la huitième http://latex.codecogs.com/gif.latex?\phi_{8} ne l'est pas

points(-0.1018/0.0847,0,pch=19,col="blue")

(voilà pour le petit retour sur la lecture des tests). Vérifions maintenant que le bruit résiduel est bien blanc... Si on regarde les autocorrélations

acf(residuals(fit.ar12),36,lwd=4)

Mais ce n'est pas un test de bruit blanc. En particulier, si une autocorrélation semblerait significative, on pourrait accepter que - globalement - les autocorrélations soient très proches de 0. On va alors faire des tests de Box-Pierce

> Box.test(residuals(fit.ar12),lag=12,
+  type='Box-Pierce')
 
Box-Pierce test
 
data:  residuals(fit.ar12)
X-squared = 7.7883, df = 12, p-value = 0.8014

Pour l'interpétation du test, c'est toujours pareil: on a une statistique de l'ordre de 7, et la loi sous-jacente (la somme des carrés des 12 premières autocorrélations du bruit) est une loi du chi-deux, à 12 degrés de liberté,

u=seq(0,30,by=.1)
plot(u,dchisq(u,df=12),type="l")
points(7.7883,0,pch=19,
col="red")

On accepte l'hypothèse que les 12 premières autocorrélations soient nulles. Si on va un peu plus loin,

> Box.test(residuals(fit.ar12),lag=18,
+  type='Box-Pierce')
 
Box-Pierce test
 
data:  residuals(fit.ar12)
X-squared = 20.3861, df = 18, p-value = 0.3115

on a la même conclusion,

Pour rappels, on peut retrouver à la main la p-value,

> 1-pchisq(20.3861,df=18)
[1] 0.3115071

Faisons notre petite dessin de toutes les p-value, pour s'assurer que le bruit est bien un bruit blanc,

 BP=function(h) Box.test(residuals(fit.ar12),lag=h,
type='Box-Pierce')$p.value
plot(1:24,Vectorize(BP)(1:24),type='b',
ylim=c(0,1),col="red")
abline(h=.05,lty=2,col="blue")

Cette fois c'est bon, on tient notre premier modèle, un http://latex.codecogs.com/gif.latex?AR(12). Si on veut aller plus loin, on peut regarder ce que donnerait de la sélection automatique,

> library(caschrono)
> armaselect(Y,nbmod=5)
p q      sbc
[1,] 14 1 1635.214
[2,] 12 1 1635.645
[3,] 15 1 1638.178
[4,] 12 3 1638.297
[5,] 12 4 1639.232

On peut être tenté d'aller voir ce qui se passerait en rajoutant une composante moyenne mobile à notre modèle précédant,

> fit.arma12.1=arima(Y,order=c(12,0,1))
> fit.arma12.1
Series: Y
ARIMA(12,0,1) with non-zero mean
 
Coefficients:
ar1     ar2      ar3      ar4     ar5      ar6     ar7
      0.0301  0.1558  -0.0941  -0.1461  0.1063  -0.0688  -0.002
s.e.  0.1235  0.0854   0.0757   0.0784  0.0807   0.0774   0.080
ar9     ar10    ar11    ar12     ma1  intercept
      -0.0646  -0.0798  0.2538  0.5786  0.2231  -131.3495
s.e.   0.0802   0.0766  0.0751  0.0861  0.1393   368.8156
 
sigma^2 estimated as 2127759:  log likelihood=-945.65
AIC=1921.31   AICc=1926.52   BIC=1961.54

Le nouveau coefficient, http://latex.codecogs.com/gif.latex?\theta_1 est à peine significatif. Éventuellement avec un seuil à 10%... Pourquoi pas? Si on regarde les résidus, sans grande surprise, on a toujours un bruit blanc, encore plus blanc qu'auparavant (en violet sur le dessin ci-dessous)

BP=function(h) Box.test(
residuals(fit.arma12.1),lag=h,
type='Box-Pierce')$p.value
plot(1:24,Vectorize(BP)(1:24),type='b',
ylim=c(0,1),col="red")
abline(h=.05,lty=2,col="blue")
 
BP=function(h) Box.test(residuals(fit.ar12),lag=h,
type='Box-Pierce')$p.value
lines(1:24,Vectorize(BP)(1:24),col="purple",
type="b")

On peut aussi aller voir parmi les modèles proposés, en particulier le modèle http://latex.codecogs.com/gif.latex?AR(14).

> fit.ar14=
+ arima(Y,order=c(14,0,0),method="CSS")
> fit.ar14
Series: Y
ARIMA(14,0,0) with non-zero mean
 
Coefficients:
ar1     ar2      ar3      ar4     ar5      ar6     ar7
      0.2495  0.2105  -0.0584  -0.1569  0.1282  -0.1152  0.0268
s.e.  0.0956  0.0972   0.0854   0.0830  0.0838   0.0840  0.0847
ar9     ar10    ar11    ar12     ar13     ar14  intercept
      -0.0327  -0.1116  0.2649  0.5887  -0.1575  -0.1572    80.5
s.e.   0.0855   0.0851  0.0853  0.0886   0.1031   0.0999   338.9
 
sigma^2 estimated as 2218612:  part log likelihood=-942.31

C'est un peu tiré par les cheveux, mais on pourrait accepter l'hypothèse que http://latex.codecogs.com/gif.latex?\phi_{14} soit significativement non-nuls. Mais on est encore limite...  Allez, on l'accepte aussi dans notre gang de modèle.

On a finalement trois modèles. Si on fait un peu de backtesting, sur les 12 derniers mois,

T=length(Y)
backtest=12
subY=Y[-((T-backtest+1):T)]
subtemps=1:(T-backtest)
plot(temps,Y,type="l")
lines(subtemps,subY,lwd=4)
fit.ar12.s=arima(subY,
order=c(p=12,d=0,q=0),method="CSS")
fit.arma12.1.s=arima(subY,
order=c(p=12,d=0,q=1),method="CSS")
fit.ar14.s=arima(subY,
order=c(p=14,d=0,q=0),method="CSS")
p.ar12=predict(fit.ar12.s,12)
pred.ar12=as.numeric(p.ar12$pred)
p.arma12.1=predict(fit.arma12.1.s,12)
pred.arma12.1=as.numeric(p.arma12.1$pred)
p.ar14=predict(fit.ar14.s,12)
pred.ar14=as.numeric(p.ar14$pred)

on obtient les prévisions suivantes (avec les valeurs observées dans la première colonne)

> (M=cbind(observé=Y[((T-backtest+1):T)],
+  modèle1=pred.ar12,
+  modèle2=pred.arma12.1,
+  modèle3=pred.ar14))
observé    modèle1    modèle2    modèle3
97  -4836.2174 -5689.3331 -5885.4486 -6364.2471
98  -3876.4199 -4274.0391 -4287.2193 -4773.8116
99   1930.3776  1817.8411  2127.9915  2290.1460
100  3435.1751  4089.3598  3736.1110  4039.4150
101  7727.9726  6998.9829  7391.6694  7281.4797
102  2631.7701  3456.8819  3397.5478  4230.5324
103  -509.4324 -2128.6315 -2268.9672 -2258.7216
104 -1892.6349 -3877.7609 -3694.9409 -3620.4798
105 -4310.8374 -3384.0905 -3430.4090 -2881.4942
106  2564.9600  -504.6883  -242.5018   183.2891
107 -1678.2425 -1540.9904 -1607.5996  -855.7677
108 -4362.4450 -3927.4772 -3928.0626 -3718.3922
>  sum((M[,1]-M[,2])^2)
[1] 19590931
>  sum((M[,1]-M[,3])^2)
[1] 17293716
>  sum((M[,1]-M[,4])^2)
[1] 21242230

I.e. on aurait envie de retenir le second modèle, http://latex.codecogs.com/gif.latex?ARMA(12,1). On va maintenant l'utiliser pour faire un peu de prévision,

library(forecast)
fit.arma12.1=
arima(Y,order=c(12,0,1))
fit.arma12.1
PREDARMA=forecast(fit.arma12.1,12)
plot(Y,xlim=c(1,120),type="l")
temps=T+1:12
polygon(c(temps,rev(temps)),c(PREDARMA$lower[,2],
rev(PREDARMA$upper[,2])),col="yellow",border=NA)
polygon(c(temps,rev(temps)),c(PREDARMA$lower[,1],
rev(PREDARMA$upper[,1])),col="orange",border=NA)
lines(temps,PREDARMA$mean,col="red")

Cela dit, on peut aussi aller beaucoup plus loin dans la prévision,

PREDARMA=forecast(fit.arma12.1,120)
plot(Y,xlim=c(1,210),type="l")
temps=T+1:120
polygon(c(temps,rev(temps)),c(PREDARMA$lower[,2],
rev(PREDARMA$upper[,2])),col="yellow",border=NA)
polygon(c(temps,rev(temps)),c(PREDARMA$lower[,1],
rev(PREDARMA$upper[,1])),col="orange",border=NA)
lines(temps,PREDARMA$mean,col="red")

Bon, on y est presque, car on a modélisé http://latex.codecogs.com/gif.latex?(Y_t), la série obtenue en enlevant la tendance linéaire. Pour remonter sur http://latex.codecogs.com/gif.latex?(X_t), on va rajouter la tendance à la prévision faite auparavant,

X=as.numeric(Xt)
temps=1:length(X)
plot(temps,X,type="l",xlim=c(0,210),
ylim=c(5000,30000))
base=data.frame(temps,X)
reg=lm(X~temps)
abline(reg,col="red")
PREDTENDANCE=predict(reg,newdata=
data.frame(temps=T+1:120))
temps=T+1:120
polygon(c(temps,rev(temps)),c(PREDTENDANCE+
PREDARMA$lower[,2],rev(PREDTENDANCE+PREDARMA$upper[,2])),
col="yellow",border=NA)
polygon(c(temps,rev(temps)),c(PREDTENDANCE+
PREDARMA$lower[,1],rev(PREDTENDANCE+PREDARMA$upper[,1])),
col="orange",border=NA)
lines(temps,PREDTENDANCE+PREDARMA$mean,col="red")

Friday, September 21 2012

Transformation logarithmique de séries temporelles

Pour poursuivre une discussion amorcée en fin de cours, dans certains cas, on peut avoir l'impression que modéliser une série pourrait être compliqué,

plot(X,xlim=c(1,length(X)+20))

Mais on peut avoir l'intuition que modéliser le logarithme de la série pourrait être plus simple,

> X=log(Y)
> plot(X,xlim=c(1,length(X)+20))

On va alors tenter une modélisation par un processus ARMA de cette dernière série,

> md=arima(X,c(12,0,1))
> P=predict(md,24)
> E=P$pred
> V=P$se^2

On peut alors faire une prévision sur cette série plus simple à modéliser, et visualiser cette prévision.

> temps=length(X)+1:24
> ciu=(E+2*sqrt(V))
> cil=(E-2*sqrt(V))
> polygon(c(temps,rev(temps)),c(ciu,
+ rev(cil)),col="yellow",border=NA)
> lines(temps,E,col="red",lwd=2)
> lines(temps,ciu,col="red",lty=2)
> lines(temps,cil,col="red",lty=2)

Maintenant, on va devoir remonter. On va utiliser un résultat que l'on a vu sur la transformation logarithmique dans une régression: si après avoir pris le logarithme, on a un modèle simple, Gaussien, c'est que le modèle initial était log-normal. On peut alors utiliser les propriétés de la loi lognormale, dont on connait les moments à partir de ceux de la loi Gaussienne sous-jacente. Pour la prévision, on n'a pas trop le choix,

> mu=exp(E+.5*V)

Par contre, pour construire un intervalle de confiance, soit on utilise la variance de notre loi lognormale pour avoir la variance de notre processus, et on oublie cette histoire de loi lognormale pour construire un intervalle Gaussien,

> sig2=(exp(V)-1)*exp(2*E+V)
> ci1u=mu+2*sqrt(sig2)
> ci1l=mu-2*sqrt(sig2)

ou alors on utilise le fait que comme la transformation est monotone, l'intervalle de confiance peut etre vu comme une transformation du précédant intervalle de confiance,

> ci2u=exp(E+2*sqrt(V))
> ci2l=exp(E-2*sqrt(V))

Si on compare visuellement les deux, on a dans le premier cas,

> plot(Y,xlim=c(1,length(X)+20))
> temps=length(X)+1:24
> polygon(c(temps,rev(temps)),c(ci1u,
> rev(ci1l)),col="yellow",border=NA)
> lines(temps,mu,col="red",lwd=2)
> lines(temps,ci1u,col="red",lty=2)
> lines(temps,ci1l,col="red",lty=2)

(qui est symétrique et centré sur notre prévision) et dans le second

> plot(Y,xlim=c(1,length(X)+20))
> temps=length(X)+1:24
> ci1u=mu+2*sqrt(sig2)
> ci1l=mu-2*sqrt(sig2)
> polygon(c(temps,rev(temps)),c(ci2u,
> rev(ci2l)),col="yellow",border=NA)
> lines(temps,mu,col="red",lwd=2)
> lines(temps,ci2u,col="red",lty=2)
> lines(temps,ci2l,col="red",lty=2)

Thursday, September 20 2012

FAQ prévision et ARIMA avec R

Suite aux (nombreuses) questions de ce matin, quelques éléments de réponse...

  • l'estimation ne "converge pas"

quelques exemples d'erreurs qui peuvent apparaître lors de l'estimation...  Car quand on travaille sur une série non-stationnaire (ou presque) et que l'on ajuste un ARIMA, on peut avoir des soucis...

> arima(cumsum(rnorm(100)),c(1,0,0))
 
Call:
arima(x = cumsum(rnorm(100)), order = c(1, 0, 0))
 
Coefficients:
ar1  intercept
        1     0.5096
s.e.  NaN  1302.3084
 
sigma^2 estimated as 0.8942:
log likelihood = -136.81,  aic = 279.61
Message d'avis :
In sqrt(diag(x$var.coef)) : production de NaN

cette erreur n'est pas trop gênante: il n'arrive pas à calculer l'écart-type du coefficient d'autorégression, qui vaut 1. Bref, on a une racine unité, et le mieux (après avoir fait un test de racine unité pour confirmer) est alors de différencier. Maintenant, on peut avoir une erreur un peu plus vicieuse,

> arima(cumsum(rnorm(100)),c(5,0,0))
Erreur dans arima(cumsum(rnorm(100)), c(5, 0, 0)) :
partie AR non stationnaire dans CSS

Il semble dire que l'on a une racine unité, mais on ne voit rien... Une stratégie peut être de changer la méthode d'estimation (qui, encore une fois, est une méthode numérique d'optimisation), par exemple, on peut se contenter des moindres carrés conditionnels (par défaut, on commence par les moindres carrés, puis une routine de maximisation de la vraisemblance est lancée),

> arima(cumsum(rnorm(100)),c(5,0,0),method="CSS")
 
Call:
arima(x=cumsum(rnorm(100)), order=c(5,0,0), method="CSS")
 
Coefficients:
ar1     ar2     ar3     ar4      ar5  intercept
      0.7468  0.1532  0.0244  0.1836  -0.1255    -8.4584
s.e.  0.0997  0.1231  0.1247  0.1219   0.1003     8.3181
 
sigma^2 estimated as 1.153:  part log likelihood = -149

Normalement, cette méthode permet d'avoir des estimateurs même si on nous encourage à aller faire un test de racine unité...

  • la fonction H2M ne marche pas
Il semble que la fonction que j'avais mise en ligne dans un précédant billet, pour transformer les données hebdomadaire ne fonctionne pas... sous windows. En fait, la fonction marche, il semble que l'on récupère une série temporelle, mais on récupère un message d'erreur
> plot(X)
Erreur dn[[2]]
N'ayant pas windows, je vais avancer à tâtons... La fonction suivante devrait faire la job
H2M=function(BASE){
X=BASE[,2]
Y=BASE[,1]
date1=substr(as.character(Y),1,10)
date2=substr(as.character(Y),14,23)
D1=as.Date(date1,"%Y-%m-%d")
D2=as.Date(date2,"%Y-%m-%d")
vm=vy=N=NA
for(t in 1:length(D1)){
mois=seq(D1[t],D2[t],length=7)
vm=c(vm,as.POSIXlt(mois)$mon+1)
vy=c(vy,as.POSIXlt(mois)$year+1900)
N=c(N,rep(X[t],7))}
N=N[-1]; vm=vm[-1]; vy=vy[-1]
YM=vy*100+vm
Z=tapply(N,as.factor(YM),mean)
Zts=as.ts(as.numeric(Z),start=c(2004,1),frequency=12)
return(Zts)}
Dans le pire des cas, il est possible de récupérer une série sur laquelle travailler en utilisant
> X=as.numeric(H2M(base))
> temps=seq(2004,length=length(X),by=1/12)
> plot(temps,X)
Le vecteur contient toutes les informations pour faire la modélisation, c'est juste que quand on fera un graphique, en abscisse on aura l'indice du vecteur, et pas l'année (je renvoie à la discussion en cours sur ce sujet).

  • le fichier de Google est en anglais...
Là c'est un peu plus vicieux... mais on va y arriver, il suffit de changer un peu la fonction que l'on va utiliser... Mais tout d'abord, je vais me contredire un peu: on commencer par ouvrir la base sous excel, et on enregistre en csv (avec un séparateur de colonne en point virgule),
> base=read.table("trends2.csv",skip=5,
+ header=FALSE,nrows=458,sep=";")
> head(base)
V1   V2   V3
1  Jan 4 2004 0.00 >10%
2 Jan 11 2004 1.20 >10%
3 Jan 18 2004 1.40 >10%
4 Jan 25 2004 1.64 >10%
5  Feb 1 2004 1.40 >10%
6  Feb 8 2004 1.27 >10%
Et cette fois, on modifie un peu la fonction, car la date a un format différent,
H2M=function(BASE){
Mois = c("Jan","Fév", "Mar", "Avr",
"Mai", "Jui", "Jul", "Aoû", "Sep", "Oct",
"Nov", "Déc")
Months = c("Jan", "Feb", "Mar", "Apr",
"May", "Jun", "Jul", "Aug",
"Sep", "Oct", "Nov", "Dec")
X=BASE[,2]
jour=BASE[,1]
D1=as.Date(as.character(jour),"%b %d %Y")
if(sum(is.na(D1)>0)){
moisGB=substr(as.character(jour),1,3)
V=1:12; names(V)=Months
NoMois=as.numeric(V[moisGB])
moisFR=Mois[NoMois]
jourFR=paste(moisFR,substr(as.character(
jour),4,nchar(as.character(jour))),sep="")
D1=as.Date(jourFR,"%b %d %Y")
}
D2=D1+6
vm=vy=N=NA
for(t in 1:length(D1)){
mois=seq(D1[t],D2[t],length=7)
vm=c(vm,as.POSIXlt(mois)$mon+1)
vy=c(vy,as.POSIXlt(mois)$year+1900)
N=c(N,rep(X[t],7))}
N=N[-1]; vm=vm[-1]; vy=vy[-1]
YM=vy*100+vm
Z=tapply(N,as.factor(YM),mean)
Zts=ts(as.numeric(Z),start=c(2004,1),frequency=12)
return(Zts)}
Normalement, ce code marche...

  • un peu de lecture pour finir...
Suite au cours de mardi dernier, un petit complément sur les suites définies par une récurrence (linéaire) à l'ordre 2, ou je peux renvoyer vers quelques notes de Djamal Rebaïne.

(nonparametric) Copula density estimation

Today, we will go further on the inference of copula functions. Some codes (and references) can be found on a previous post, on nonparametric estimators of copula densities (among other related things).  Consider (as before) the loss-ALAE dataset (since we've been working a lot on that dataset)

> library(MASS)
> library(evd)
> X=lossalae
> U=cbind(rank(X[,1])/(nrow(X)+1),rank(X[,2])/(nrow(X)+1))

The standard tool to plot nonparametric estimators of densities is to use multivariate kernels. We can look at the density using

> mat1=kde2d(U[,1],U[,2],n=35)
> persp(mat1$x,mat1$y,mat1$z,col="green",
+ shade=TRUE,theta=s*5,
+ xlab="",ylab="",zlab="",zlim=c(0,7))

or level curves (isodensity curves) with more detailed estimators (on grids with shorter steps)

> mat1=kde2d(U[,1],U[,2],n=101)
> image(mat1$x,mat1$y,mat1$z,col=
+ rev(heat.colors(100)),xlab="",ylab="")
> contour(mat1$x,mat1$y,mat1$z,add=
+ TRUE,levels = pretty(c(0,4), 11))

http://freakonometrics.blog.free.fr/public/perso6/3dcop-est1.gif

Kernels are nice, but we clearly observe some border bias, extremely strong in corners (the estimator is 1/4th of what it should be, see another post for more details). Instead of working on sample http://latex.codecogs.com/gif.latex?(U_i,V_i) on the unit square, consider some transformed sample http://latex.codecogs.com/gif.latex?(Q(U_i),Q(V_i)), where http://latex.codecogs.com/gif.latex?Q:(0,1)\rightarrow\mathbb{R} is a given function. E.g. a quantile function of an unbounded distribution, for instance the quantile function of the http://latex.codecogs.com/gif.latex?\mathcal{N}(0,1) distribution. Then, we can estimate the density of the transformed sample, and using the inversion technique, derive an estimator of the density of the initial sample. Since the inverse of a (general) function is not that simple to compute, the code might be a bit slow. But it does work,

> gaussian.kernel.copula.surface <- function (u,v,n) {
+   s=seq(1/(n+1), length=n, by=1/(n+1))
+   mat=matrix(NA,nrow = n, ncol = n)
+ sur=kde2d(qnorm(u),qnorm(v),n=1000,
+ lims = c(-4, 4, -4, 4))
+ su<-sur$z
+ for (i in 1:n) {
+     for (j in 1:n) {
+ 	Xi<-round((qnorm(s[i])+4)*1000/8)+1;
+ 	Yj<-round((qnorm(s[j])+4)*1000/8)+1
+ 	mat[i,j]<-su[Xi,Yj]/(dnorm(qnorm(s[i]))*
+ 	dnorm(qnorm(s[j])))
+     }
+ }
+ return(list(x=s,y=s,z=data.matrix(mat)))
+ }

Here, we get

http://freakonometrics.blog.free.fr/public/perso6/3dcop-est2.gif

Note that it is possible to consider another transformation, e.g. the quantile function of a Student-t distribution.

> student.kernel.copula.surface =
+  function (u,v,n,d=4) {
+  s <- seq(1/(n+1), length=n, by=1/(n+1))
+  mat <- matrix(NA,nrow = n, ncol = n)
+ sur<-kde2d(qt(u,df=d),qt(v,df=d),n=5000,
+ lims = c(-8, 8, -8, 8))
+ su<-sur$z
+ for (i in 1:n) {
+     for (j in 1:n) {
+ 	Xi<-round((qt(s[i],df=d)+8)*5000/16)+1;
+ 	Yj<-round((qt(s[j],df=d)+8)*5000/16)+1
+ 	mat[i,j]<-su[Xi,Yj]/(dt(qt(s[i],df=d),df=d)*
+ 	dt(qt(s[j],df=d),df=d))
+     }
+ }
+ return(list(x=s,y=s,z=data.matrix(mat)))
+ }

Another strategy is to consider kernel that have precisely the unit interval as support. The idea is here to consider the product of Beta kernels, where parameters depend on the location

> beta.kernel.copula.surface=
+  function (u,v,bx=.025,by=.025,n) {
+  s <- seq(1/(n+1), length=n, by=1/(n+1))
+  mat <- matrix(0,nrow = n, ncol = n)
+ for (i in 1:n) {
+     a <- s[i]
+     for (j in 1:n) {
+     b <- s[j]
+ 	mat[i,j] <- sum(dbeta(a,u/bx,(1-u)/bx) *
+     dbeta(b,v/by,(1-v)/by)) / length(u)
+     }
+ }
+ return(list(x=s,y=s,z=data.matrix(mat)))
+ }

http://freakonometrics.blog.free.fr/public/perso6/3dcop-est3.gif

On those two graphs, we can clearly observe strong tail dependence in the upper (right) corner, that cannot be intuited using a standard kernel estimator...

Wednesday, September 19 2012

Prévision, choix de modèle(s) et backtesting

Un billet pour reprendre (et compléter) le code vu hier, en cours de modèles de prévision. On a toujours pour but de prévoir l'évolution du trafic autoroutier, en France, sur l'autoroute A7.

> autoroute=read.table(
+ "http://freakonometrics.blog.free.fr/public/data/autoroute.csv",
+ header=TRUE,sep=";")
> a7=autoroute$a007
> A7=ts(a7,start = c(1989, 9), frequency = 12)

Comme je l'ai dit en cours, les constantes dans les modèles ARIMA, a complexifie inutilement les notations, donc pour faire des choses proches de ce qu'ai pu faire dans le cours, on va centrer la série, et considérer http://latex.codecogs.com/gif.latex?X_t-\overline%20X

> A7d=A7-mean(A7)
> plot(A7d,xlim=c(1989,2000))

On avait vu qu'un modèle http://latex.codecogs.com/gif.latex?AR(12) pouvait convenir, donc on va l'utiliser pour faire de la prévision.

>  fit=arima(A7d,order=c(p=12,d=0,q=0),
+      include.mean =FALSE)
>  summary(fit)
Series: A7d
ARIMA(12,0,0) with zero mean
 
Coefficients:
ar1      ar2      ar3     ar4      ar5     ar6
      0.0981  -0.0353  -0.0375  0.0530  -0.0934  0.0598
s.e.  0.0630   0.0623   0.0600  0.0606   0.0618  0.0568
ar7     ar8     ar9     ar10    ar11    ar12
      -0.0839  0.0278  0.0072  -0.1080  0.1578  0.7887
s.e.   0.0619  0.0629  0.0627   0.0629  0.0637  0.0623
 
sigma^2 estimated as 12823951:  log likelihood=-827.24
AIC=1678.48   AICc=1683.6   BIC=1710.23
 
In-sample error measures:
ME         RMSE          MAE          MPE
257.8363135 3581.0544202 2335.0686684    1.7258390
MAPE         MASE
34.7942409    0.2603139 

La fonction permettant de faire de la prévision est alors

> horizon=48
>
library(forecast) > A7hat=forecast(fit,h=horizon)

On a alors un ensemble de séries temporelles, la première étant la prévision proprement dite http://latex.codecogs.com/gif.latex?{}_T\widehat{X}_{T+h}, i.e. http://latex.codecogs.com/gif.latex?EL(X_{T+h}|\underline{X}_T), où http://latex.codecogs.com/gif.latex?EL(\cdot) désigne une espérance linéaire (ou en terme technique, une projection sur l'adhérence de l'ensemble des des combinaisons linéaires du passé). On a ensuite les intervalles de confiance estimés, à partir d'une hypothèse de normalité, en utilise un estimateur de http://latex.codecogs.com/gif.latex?{}_T\text{mse}_{T+h}=\mathbb{E}([{}_T\widehat{X}_{T+h}-X_{T+h}]^2|\underline{X}_T),

> A7hat
Point    Lo 80   Hi 80    Lo 95    Hi 95
Oct 1996    -7276.44 -11865.7 -2687.1 -14295.1  -257.70
Nov 1996   -10453.79 -15065.1 -5842.4 -17506.1 -3401.39
Dec 1996    -6970.67 -11583.5 -2357.8 -14025.3    84.02
Jan 1997   -13404.09 -18021.2 -8786.9 -20465.4 -6342.78

On peut alors représenter ces valeurs graphiquement, comme on l'a fait en cours

>  (TPS=tsp(A7hat$mean))
[1] 1996.750 2000.667   12.000
>  lines(A7hat$mean,col="red",lwd=2)
>  temps=seq(from=TPS[1],to=TPS[2],by=1/TPS[3])
>  lines(temps,A7hat$lower[,1],col="blue",lwd=2)
>  lines(temps,A7hat$lower[,2],col="purple",lwd=2)
>  lines(temps,A7hat$upper[,1],col="blue",lwd=2)
>  lines(temps,A7hat$upper[,2],col="purple",lwd=2)
>  abline(v=TPS[1]-.5/TPS[3],col="green")

En allant un peu plus loin, on peut faire plus joli, en utilisant des aires colorées afin d'illustrer une région de confiance

> plot(A7d,xlim=c(1989,2000))
> polygon(c(temps,rev(temps)),c(A7hat$lower[,2],
+ rev(A7hat$upper[,2])),col="yellow",border=NA)
> polygon(c(temps,rev(temps)),c(A7hat$lower[,1],
+ rev(A7hat$upper[,1])),col="orange",border=NA)
> lines(temps,A7hat$mean,col="red")
> abline(v=TPS[1]-.5/TPS[3],col="green")

Maintenant, si on revient sur notre série initiale, l'autocorrélogramme pourrait laisser penser qu'il y a une racine unité saisonnière,

> plot(acf(A7d,lag=36))

Donc on peut tenter de différencier à l'ordre 12,

> plot(acf(diff(A7d,12),lag=36))

On pourrait avoir l'impression que la série ainsi différenciée est un bruit blanc. On peut faire un test de Box-Pierce pour tester cette hypothèse,

> BP=function(h) Box.test(diff(A7d,12),lag=h,
+ type='Box-Pierce')$p.value
> plot(1:24,Vectorize(BP)(1:24),type='b',
+ ylim=c(0,1),col="red")
> abline(h=.05,lty=2,col="blue")

On pourrait valider ici l'hypothèse de bruit blanc. On va donc ajuster un (pur) modèle ARIMA saisonnier,

> fit2=arima(A7d, order = c(p=0, d=0, q=0),
+ seasonal = list(order = c(0, 1, 0), period = 12))

Si on compare les deux modèles,

> AIC(fit1)
[1] 1511.815
> AIC(fit2)
[1] 1245.374

Le second modèle ayant un critère d'Akaike plus faible, on aurait tendance  à préférer ce modèle au premier. Mais cette méthode de comparaison n'est pas forcément la plus pertinent, si l'objet est de faire une prévision à court terme (disons quelques mois). Pour cela, on va plutôt enlever les dernières observations, ajuster un modèle sur la série restreinte, et comparer les prévisions, avec ce qui a été réellement observé. Disons qu'on va faire du backtesting sur les 9 derniers mois,

> T=length(A7d)
> backtest=9
> subA7d=ts(A7d[-((T-backtest+1):T)],
+ start = c(1989, 9), frequency = 12) > fit1s=arima(subA7d,order=c(p=12,d=0,q=0), + include.mean =FALSE)

pour le premier modèle, et

> fit2s=arima(subA7d, order = c(p=0, d=0, q=0),
+ seasonal = list(order = c(0, 1, 0), period = 12))

pour le second. Ensuite, on peut faire de la prévision, et comparer les prévisions avec les données écartées pour l'estimation du modèle.,

> XA7p1=forecast(fit1s,h=backtest)
> XA7p2=forecast(fit2s,h=backtest)
> A7dobs=A7d[((T-backtest+1):T)]
> (M=cbind(observé=A7dobs,modèle1=as.numeric(XA7p1$mean),
+ modèle2=as.numeric(XA7p2$mean))) observé modèle1 modèle2 [1,] -13610.071 -12264.7138 -13558.071 [2,] -10108.071 -12236.6227 -10410.071 [3,] -8034.071 -9333.8365 -9504.071 [4,] 3899.929 4071.9313 4108.929 [5,] 2009.929 901.3745 2661.929 [6,] 5253.929 7610.9954 5866.929 [7,] 25357.929 29232.2317 30317.929 [8,] 35229.929 29641.5172 31737.929 [9,] 4341.929 5603.8008 4940.929

On peut alors retenir un critère classique, comme l'erreur quadratique, et donc comparer la somme des carrées des erreurs,

> sum((M[,1]-M[,2])^2)
[1] 62677238
> sum((M[,1]-M[,3])^2)
[1] 40253827

Le second modèle semble ici avoir été meilleur, sur les 9 derniers mois. On peut aussi visualiser les erreurs de prévision,

> (TPS=tsp(subA7d))
[1] 1989.667 1995.917   12.000
> temps=seq(TPS[2]+1/TPS[3],by=1/TPS[3],length=backtest)
> rangex=c(TPS[1],TPS[2]+backtest/TPS[3])
> plot(subA7d,type="l",xlim=rangex)
> abline(v=T-11.5,lty=2)
> polygon(c(temps,rev(temps)),c(A7dobs,rev(XA7p1$mean)),
+ col="yellow",border=NA) > lines(temps,A7dobs) > lines(temps,XA7p1$mean,col="red")

pour le premier modèle,

et

> polygon(c(temps,rev(temps)),c(A7dobs,rev(XA7p2$mean)),
+ col="yellow",border=NA) > lines(temps,XA7p2$mean,col="red")

pour le second,

Monday, September 17 2012

Copulas and tail dependence, part 1

As mentioned in the course last week Venter (2003) suggested nice functions to illustrate tail dependence (see also some slides used in Berlin a few years ago).
  • Joe (1990)'s lambda
Joe (1990) suggested a (strong) tail dependence index. For lower tails, for instance, consider
http://blogperso.univ-rennes1.fr/arthur.charpentier/public/perso3/toc3latex2png.2.php.png
i.e

http://blogperso.univ-rennes1.fr/arthur.charpentier/public/perso3/toc3latex2png.3.php.png
  • Upper and lower strong tail (empirical) dependence functions

The idea is to plot the function above, in order to visualize limiting behavior. Define

http://blogperso.univ-rennes1.fr/arthur.charpentier/public/perso3/Llatex2png.2.php.png
for the lower tail, and
http://blogperso.univ-rennes1.fr/arthur.charpentier/public/perso3/Clatex2png.2.php.png
for the upper tail, where http://perso.univ-rennes1.fr/arthur.charpentier/latex/toclatex2png-12.2.php.png is the survival copula associated with http://perso.univ-rennes1.fr/arthur.charpentier/latex/toclatex2png-13.2.php.png, in the sense that
http://perso.univ-rennes1.fr/arthur.charpentier/latex/toclatex2png-14.2.php.png
while
http://perso.univ-rennes1.fr/arthur.charpentier/latex/toclatex2png-15.2.php.png

Now, one can easily derive empirical conterparts of those function, i.e.

http://perso.univ-rennes1.fr/arthur.charpentier/latex/toclatex2png-18.2.php.png
and
http://perso.univ-rennes1.fr/arthur.charpentier/latex/toclatex2png-19.2.php.png
Thus, for upper tail, on the right, we have the following graph

http://freakonometrics.blog.free.fr/public/perso6/upper-lambda.gif

and for the lower tail, on the left, we have

http://freakonometrics.blog.free.fr/public/perso6/lower-lambda.gif

For the code, consider some real data, like the loss-ALAE dataset.

> library(evd)
> X=lossalae

The idea is to plot, on the left, the lower tail concentration function, and on the right, the upper tail function.

> U=rank(X[,1])/(nrow(X)+1)
> V=rank(X[,2])/(nrow(X)+1)
> Lemp=function(z) sum((U<=z)&(V<=z))/sum(U<=z)
> Remp=function(z) sum((U>=1-z)&(V>=1-z))/sum(U>=1-z)
> u=seq(.001,.5,by=.001)
> L=Vectorize(Lemp)(u)
> R=Vectorize(Remp)(rev(u))
> plot(c(u,u+.5-u[1]),c(L,R),type="l",ylim=0:1,
+ xlab="LOWER TAIL          UPPER TAIL")
> abline(v=.5,col="grey")

Now, we can compare this graph, with what should be obtained for some parametric copulas that have the same Kendall's tau (e.g.). For instance, if we consider a Gaussian copula,

> tau=cor(lossalae,method="kendall")[1,2]
> library(copula)
> paramgauss=sin(tau*pi/2)
> copgauss=normalCopula(paramgauss)
> Lgaussian=function(z) pCopula(c(z,z),copgauss)/z
> Rgaussian=function(z) (1-2*z+pCopula(c(z,z),copgauss))/(1-z)
> u=seq(.001,.5,by=.001)
> Lgs=Vectorize(Lgaussian)(u)
> Rgs=Vectorize(Rgaussian)(1-rev(u))
> lines(c(u,u+.5-u[1]),c(Lgs,Rgs),col="red")

or Gumbel's copula,

> paramgumbel=1/(1-tau)
> copgumbel=gumbelCopula(paramgumbel, dim = 2)
> Lgumbel=function(z) pCopula(c(z,z),copgumbel)/z
> Rgumbel=function(z) (1-2*z+pCopula(c(z,z),copgumbel))/(1-z)
> u=seq(.001,.5,by=.001)
> Lgl=Vectorize(Lgumbel)(u)
> Rgl=Vectorize(Rgumbel)(1-rev(u))
> lines(c(u,u+.5-u[1]),c(Lgl,Rgl),col="blue")

That's nice (isn't it?), but since we do not have any confidence interval, it is still hard to conclude (even if it looks like Gumbel copula has a much better fit than the Gaussian one). A strategy can be to generate samples from those copulas, and to visualize what we had. With a Gaussian copula, the graph looks like
> u=seq(.0025,.5,by=.0025); nu=length(u)
> nsimul=500
> MGS=matrix(NA,nsimul,2*nu)
> for(s in 1:nsimul){
+ Xs=rCopula(nrow(X),copgauss)
+ Us=rank(Xs[,1])/(nrow(Xs)+1)
+ Vs=rank(Xs[,2])/(nrow(Xs)+1)
+ Lemp=function(z) sum((Us<=z)&(Vs<=z))/sum(Us<=z)
+ Remp=function(z) sum((Us>=1-z)&(Vs>=1-z))/sum(Us>=1-z)
+ MGS[s,1:nu]=Vectorize(Lemp)(u)
+ MGS[s,(nu+1):(2*nu)]=Vectorize(Remp)(rev(u))
+ lines(c(u,u+.5-u[1]),MGS[s,],col="red")
+ }

(including - pointwise - 90% confidence bands)

> Q95=function(x) quantile(x,.95)
> V95=apply(MGS,2,Q95)
> lines(c(u,u+.5-u[1]),V95,col="red",lwd=2)
> Q05=function(x) quantile(x,.05)
> V05=apply(MGS,2,Q05)
> lines(c(u,u+.5-u[1]),V05,col="red",lwd=2)

while it is

with Gumbel copula. Isn't it a nice (graphical) tool ?

But as mentioned in the course, the statistical convergence can be slow. Extremely slow. So assessing if the underlying copula has tail dependence, or not, it now that simple. Especially if the copula exhibits tail independence. Like the Gaussian copula. Consider a sample of size 1,000. This is what we obtain if we generate random scenarios,

or we look at the left tail (with a log-scale)

Now, consider a 10,000 sample,

or with a log-scale

We can even consider a 100,000 sample,

or with a log-scale

On those graphs, it is rather difficult to conclude if the limit is 0, or some strictly positive value (again, it is a classical statistical problem when the value of interest is at the border of the support of the parameter). So, a natural idea is to consider a weaker tail dependence index. Unless you have something like 100,000 observations...

Les tests de bruit-blanc

Histoire de presque finir la modélisation des processus ARMA, un mot sur les tests de bruit blanc, basé sur ce que nous avons pu voire en cours la semaine passée. Poursuivons à partir du billet précédant, sur la série du trafic autoroutier, ou plus précisément la série

> a7=as.numeric(A7)

qui est un simple vecteur, et non plus une série temporel, au sens défini par R. On peut tenter une modélisation http://freakonometrics.blog.free.fr/public/perso6/ar12.gif

> fit=arima(a7,order=c(12,0,0))
> summary(fit)
Series: a7
ARIMA(12,0,0) with non-zero mean
 
Coefficients:
ar1      ar2      ar3     ar4      ar5
      0.0979  -0.0355  -0.0377  0.0528  -0.0935
s.e.  0.0630   0.0623   0.0600  0.0606   0.0618
ar6      ar7     ar8     ar9     ar10
      0.0596  -0.0839  0.0276  0.0072  -0.1081
s.e.  0.0568   0.0618  0.0629  0.0627   0.0629
ar11    ar12  intercept
      0.1577  0.7885  38653.234
s.e.  0.0637  0.0623   1507.591
 
sigma^2 estimated as 12820052:  log likelihood=-827.22
AIC=1680.44   AICc=1686.44   BIC=1714.64
 
In-sample error measures:
ME         RMSE          MAE
3.152304e+02 3.580510e+03 2.334799e+03
MPE         MAPE
3.321677e-02 6.232293e+00 

et on peut s'intéresser un peu à la série des autocorrélations de la série des résidus empiriques http://freakonometrics.blog.free.fr/public/perso6/epsilonhat.gif, obtenus par partir de l'estimation précédente,

http://freakonometrics.blog.free.fr/public/perso6/hatepsilon.gif
> plot(acf(residuals(fit)))

Les lignes horizontales sont les valeurs critiques d'un test http://freakonometrics.blog.free.fr/public/perso6/rhotest1.gif contre http://freakonometrics.blog.free.fr/public/perso6/rhotest2.gif, à http://freakonometrics.blog.free.fr/public/perso6/rhotest3.gif donné. Ce n'est pas un test de bruit blanc, qui cherche à tester si toutes les autocorrélations sont nulles,

http://freakonometrics.blog.free.fr/public/perso6/rhotest4.gif

Naturellement, on a envie d'utiliser la distance euclidienne, pour voir si la distance est petit, i.e. quelque chose du genre

http://freakonometrics.blog.free.fr/public/perso6/rhotest6.gif

C'est la statistique proposée par Box-Pierce,

http://freakonometrics.blog.free.fr/public/perso6/box.gif
> rho=as.vector(acf(residuals(fit))$acf)
> (Q=length(residuals(fit))*sum(rho[1:6]^2))
[1] 5.27944

On peut l'avoir automatiquement à l'aide de la fonction

> Box.test(residuals(fit),lag=6,
+ type='Box-Pierce')
data:  residuals(fit)
X-squared = 5.2794, df = 6, p-value = 0.5085

Sous l'hypothèse nulle, la statistique doit suivre une loi du chi-deux, d'où la p-value renvoyée ci-dessus,

> 1-pchisq(Q,df=6)
[1] 0.5085045

Une autre statistique possible est celle de Ljung-Box, qui tient compte du fait que les autocorrélations n'ont pas été calculées sur le même nombre d'observations (d'où la petite correction,  qui devient importante si on a peu de données),

http://freakonometrics.blog.free.fr/public/perso6/box2.gif
> Box.test(residuals(fit),lag=6,
+ type='Ljung-Box')
data:  residuals(fit)
X-squared = 5.5278, df = 6, p-value = 0.4781

Mais le test est fait ici pour un nombre de retard fixé. Il convient de le faire pour plusieurs retards possible. Ou mieux, de faire un petit dessin, permettant de visualiser la p-value pour différents retards,

> BP=function(h) Box.test(residuals(fit),lag=h,
+ type='Box-Pierce')$p.value
> plot(1:24,Vectorize(BP)(1:24),type='b',
+ ylim=c(0,1),col="red")
> abline(h=.05,lty=2,col="blue")

On valide ici l'hypothèse de bruit blanc pour notre série. Si on avait tenté sur un modèle http://latex.codecogs.com/gif.latex?AR(7),

> fit=arima(a7,order=c(7,0,0))

cette fois, en revanche, on ne peut accepter l'hypothèse de bruit blanc sur la série des résidus induits,

Ordres d'un processus ARMA

Dans la méthodologie de Box & Jenkins, une étape qui arrive très rapidement est le choix des ordres du processus http://freakonometrics.blog.free.fr/public/perso6/ARMA__latex.gif, une fois que l'on validé l'hypothèse de stationnarité de la série, comme on l'a vu en cours la semaine passée. Considérons la série du trafic autoroutier,

> autoroute=read.table(
+"http://freakonometrics.blog.free.fr/public/data/autoroute.csv",
+ header=TRUE,sep=";")
> a7=autoroute$a007
> A7=ts(a7,start = c(1989, 9), frequency = 12)
Un outils pratique de sélections des ordres http://freakonometrics.blog.free.fr/public/perso6/platex.gif et http://freakonometrics.blog.free.fr/public/perso6/qlatex.gif dans un modèle http://freakonometrics.blog.free.fr/public/perso6/ARMAlatex.gif est la fonction d'autocorrélation étendue. La définition est donnée dans les notes de cours (Def. 223) à partir des statistiques proposées par Tsay & Tiao (1984),
>  EACF=eacf(A7)
AR/MA
  0 1 2 3 4 5 6 7 8 9 10 11 12 13
0 x o o x x x x x o o x  x  x  o
1 x x o o x x x o o o x  x  x  x
2 o x o o o o x o o o o  x  x  o
3 o x o x o o o o o o o  x  x  x
4 x x x x o o o o o o o  x  o  o
5 x x o o o x o o o o o  x  o  x
6 x x o o o x o o o o o  x  o  o
7 o x o o o x o o o o o  x  o  o
>  EACF
$eacf
           [,1]       [,2]        [,3]        [,4]
[1,]  0.6476234  0.2124105 -0.02413173 -0.24234535
[2,]  0.4889076  0.2797152 -0.02494135 -0.06094037
[3,]  0.1006541 -0.2285771  0.03514148  0.08000588
[4,]  0.1390240 -0.2788742  0.04386746  0.28307260
[5,] -0.5091680  0.3144899 -0.34572269  0.31450865
[6,] -0.4224571 -0.4877505  0.16054232 -0.09130728
[7,] -0.4731353 -0.4324857 -0.04847184 -0.10500350
[8,] -0.2129591 -0.4072901  0.09487899 -0.06493243
             [,5]        [,6]        [,7]        [,8]
[1,] -0.514330187 -0.61634046 -0.52314403 -0.28008661
[2,] -0.254912957 -0.28966664 -0.33963243 -0.21863077
[3,] -0.156624357 -0.01199786 -0.25116738  0.13079231
[4,] -0.183283544  0.03651508 -0.08711829  0.11626377
[5,] -0.190885091 -0.09786463 -0.09182557  0.08818875
[6,] -0.072904071  0.29271777 -0.09334712  0.01972648
[7,] -0.009873289  0.36909726  0.01698660 -0.03317456
[8,]  0.020485930  0.38342158  0.16981715 -0.02592442
             [,9]       [,10]       [,11]     [,12]
[1,] -0.082607058  0.15178887  0.56583179 0.8368975
[2,] -0.085026400  0.02731460  0.26158357 0.7844748
[3,]  0.001866994 -0.11658312  0.03038621 0.7026361
[4,]  0.025183793 -0.21608692  0.05660781 0.6674301
[5,] -0.006831894 -0.02514440 -0.07390257 0.4762563
[6,]  0.010058718 -0.03888613 -0.04382043 0.5338091
[7,]  0.032124905 -0.07022090 -0.04427400 0.4674165
[8,]  0.024189179 -0.20818201  0.01459933 0.4830369
          [,13]       [,14]
[1,]  0.5637439  0.17862571
[2,]  0.4530716  0.24413569
[3,]  0.2534178 -0.20160890
[4,]  0.2409861 -0.29462510
[5,] -0.1517324 -0.14763294
[6,] -0.1701182 -0.28771495
[7,] -0.1651515  0.05466457
[8,] -0.1403198 -0.04095030
 
$ar.max
[1] 8
 
$ma.ma
[1] 14
Les ronds dans la matrice désignent des valeurs non-significatives. Par défaut, le nombre de retards, pris en compte pour la composante autorégressive est faible, mais on peut l'augmenter.
> EACF=eacf(A7,13,13)
AR/MA
   0 1 2 3 4 5 6 7 8 9 10 11 12 13
0  x o o x x x x x o o x  x  x  o
1  x x o o x x x o o o x  x  x  x
2  o x o o o o x o o o o  x  x  o
3  o x o x o o o o o o o  x  x  x
4  x x x x o o o o o o o  x  o  o
5  x x o o o x o o o o o  x  o  x
6  x x o o o x o o o o o  x  o  o
7  o x o o o x o o o o o  x  o  o
8  o x o o o x o o o o o  x  o  o
9  x x o o x o x o o o o  x  o  o
10 x x o o x o x o o o o  x  o  o
11 x x o o x x x o o o x  x  o  o
12 x x o o o o o o o o o  o  o  o
13 x x o o o o o o o o o  o  o  o
> EACF$eacf
      [,1]  [,2]  [,3]  [,4]  [,5]  [,6]  [,7]  [,8]  [,9] [,10]
[1,]  0.64  0.21 -0.02 -0.24 -0.51 -0.61 -0.52 -0.28 -0.08  0.15
[2,]  0.48  0.27 -0.02 -0.06 -0.25 -0.28 -0.33 -0.21 -0.08  0.02
[3,]  0.10 -0.22  0.03  0.08 -0.15 -0.01 -0.25  0.13  0.00 -0.11
[4,]  0.13 -0.27  0.04  0.28 -0.18  0.03 -0.08  0.11  0.02 -0.21
[5,] -0.50  0.31 -0.34  0.31 -0.19 -0.09 -0.09  0.08  0.00 -0.02
[6,] -0.42 -0.48  0.16 -0.09 -0.07  0.29 -0.09  0.01  0.01 -0.03
[7,] -0.47 -0.43 -0.04 -0.10  0.00  0.36  0.01 -0.03  0.03 -0.07
[8,] -0.21 -0.40  0.09 -0.06  0.02  0.38  0.16 -0.02  0.02 -0.20
[9,] -0.14 -0.50 -0.10 -0.06  0.11  0.38 -0.01 -0.02 -0.08 -0.20
[10,] -0.29  0.48  0.00  0.18  0.27 -0.12  0.41  0.00  0.16  0.15
[11,]  0.24  0.48 -0.04  0.02  0.46  0.10  0.37 -0.10  0.05  0.16
[12,] -0.59  0.49 -0.18  0.05  0.31 -0.32  0.34 -0.16  0.07  0.16
[13,] -0.31  0.31 -0.12  0.16 -0.11  0.04 -0.04  0.12 -0.09  0.00
[14,]  0.47  0.26  0.11  0.13 -0.01 -0.01  0.00  0.07 -0.03  0.02
On peut aussi visualiser graphiquement les différentes valeurs, avec en ordonnée les ordres autoréregressifs (http://freakonometrics.blog.free.fr/public/perso6/platex.gif) et en abscisse les ordres moyenne mobile (http://freakonometrics.blog.free.fr/public/perso6/qlatex.gif). 
> library(RColorBrewer)
> CL=brewer.pal(6, "RdBu")
> ceacf=matrix(as.numeric(cut(EACF$eacf,
+ ((-3):3)/3,labels=1:6)),nrow(EACF$eacf),
+ ncol(EACF$eacf))
> for(i in 1:ncol(EACF$eacf)){
+ for(j in 1:nrow(EACF$eacf)){
+ polygon(c(i-1,i-1,i,i)-.5,c(j-1,j,j,j-1)-.5,
+ col=CL[ceacf[j,i]])
+ }}

Un bruit blanc est en bas à gauche (http://freakonometrics.blog.free.fr/public/perso6/platex.gif et http://freakonometrics.blog.free.fr/public/perso6/qlatex.gif nuls). Dans cette méthode, dite méthode des coins, on cherche un coin telle que dans le quadrant supérieur, les valeurs soient non-significatives (ternes sur le dessin ci-dessus). La figure ci-desssous correspond à l'analyse d'un modèle http://freakonometrics.blog.free.fr/public/perso6/ARMA126latex.gif

Les fortes valeurs positives sont en bleu foncé, les fortes valeurs négatives sont en rouge foncé. On peut aussi regarder la fonction suivante, qui utilise une identification de modèle par MINIC (Minimum Information Criterion) à l'aide du critère de Shcwarz (BIC, ou SBC Schwarz's Bayesian Criterion)
> armaselect(A7,nbmod=5)
p q      sbc
[1,] 12 1 1441.798
[2,] 13 0 1442.628
[3,] 12 0 1443.188
[4,] 12 2 1443.362
[5,] 14 0 1445.069
Enfin, une dernière fonction possible est évoquée dans la section 6.5. du livre de Cryer & Chan (2008),
> ARMA.SELECTION=
+ armasubsets(A7,nar=14,nma=14,ar.method='ols')
> plot(ARMA.SELECTION)

basé sur le critère de Schwarz.
Je vais me répéter, mais ces méthodes ne sont que des outils, histoire d'avoir des pistes si on ne sait trop dans quelle direction partir. Et compte tenu de la saisonnalité de la série, je serais pour ma part parti sur un modèle avec http://freakonometrics.blog.free.fr/public/perso6/platex.gif=12, histoire de voir si on ne pourrait pas avoir un modèle simple, et facilement interprétable en plus...

Thursday, September 6 2012

Simulation de séries temporelles

Un billet rapide pour reprendre le code tapé en cours, la semaine passée. Considérons  un processus autorégressif d'ordre 1,  où  est un bruit blanc, stationnaire, i.e.  appartient à l'intervalle . Le code pour simuler un tel processus est

n=1000
bruit=rnorm(n)
phi1= .85
X=rep(NA,n)
X[1]=0
for(t in 2:n){X[t]=phi1*X[t-1]+bruit[t]}
plot(acf(X),lwd=5,col='blue')
plot(pacf(X),lwd=5,col='blue')

ou avec un autocorrélation au premier ordre négative,

phi1= -0.7

On peut aussi regarder un processus autorégressif au second ordre, 

sur la figure ci-dessous (avec en haut à gauche le triangle de stationnarité du couple de paramètres).

phi1=  0.3
phi2=  0.5
X=rep(NA,n)
X[1:2]=0
for(t in 3:n){
X[t]=phi1*X[t-1]+phi2*X[t-2]+bruit[t]}

Histoire de changer un peu, on peut regarder un processus moyenne mobile au premier ordre,  où  est un paramètre dans .

theta1=  .8
X=rep(NA,n)
X[1]=0
for(t in 2:n){
X[t]=bruit[t]+theta1*bruit[t-1]}

ou une moyenne mobile du second ordre,

theta1= -.6
theta2=  .5
X=rep(NA,n)
X[1:2]=0
for(t in 3:n){
X[t]=bruit[t]+theta1*bruit[t-1]+
theta2*bruit[t-2]}


Sunday, July 1 2012

Visualizing uncertainty using Jackknife

Once again, I (re)discovered last week at the Rmetrics conference that old tools can be extremely interesting to illustrate complex ideas, like uncertainty in fnancial markets, and stock prices. For instance a 99.5% quantile: we look for the scenario that occur with a probability of 1 out of 200. Are there nice ways to illustrate that quantity ?

Consider the monthly evolution of the SP500 index over the last 22 years,

> library(quantmod)
> getSymbols('^GSPC', from='1990-01-01')
[1] "GSPC"
> GSPC = adjustOHLC(GSPC,
+ symbol.name='^GSPC') > MGSPC = to.monthly(GSPC) > CLOSE = MGSPC$GSPC.Close > plot(CLOSE)

It is possible to use Jackknife technique to illustrate uncertainty. The idea, in Jackknife, it to remove one of the observations, and to do that for all observations. More formally, from a sample , we define a (sub)sample where observation  as been removed, i.e. . Then, we can study all samples when one observation was removed.

Here, in the context of financial time series, over 270 months, we can wonder what might have been the final value of the index if one observation (i.e. one month) had been removed. It is actually the idea of Jackknife,

> R=diff(log(CLOSE)); R=R[-1]
> n=length(R)
> X=rnorm(n,mean(R),sd(R))
> X=R
> MX=t(matrix(X,n,n))
> MX=exp(MX)
> diag(MX)=1
> SMX=MX
> for(k in 2:n){SMX[,k]=SMX[,k-1]*(MX[,k])}

We can plot the different trajectories of the index, when we remove one month,

> init=as.numeric(CLOSE[1])
> plot(1:n,init*cumprod(exp(X)),type="l",
+ xlab="",ylab="",col="white")
> for(k in 1:n){lines(0:n,init*c(1,SMX[k,]),
+ col="light blue")}
> lines(0:n,init*c(1,cumprod(exp(X))),lwd=2,
+ col="blue")

This can be used to understand sensitivity, or unccertainty, of financial time series,

We can then look closer at the final value of the index, over those 270 scenarios,

or we also use a Box-Plot,

Here we can clearly see the impact: if we remove one good month, the index ends around 1250, while it reaches 1650 if we remove a bad month. The difference is huge. So instead of talking about volatility (which is actually a complex concept), that Jackknife idea of remove observations might be more intuitive, and much easier to get a first understanding of uncertainty. But those ideas of resampling are great. I will post a nice application soon (but first, I will discuss with some colleagues in Lyon).

Friday, June 29 2012

Provisionnement et modélisation individuelle des sinistres

Il y a quelques années, Guillaume Beneteau avait fait son mémoire d'actuariat, en France, sur l'utilisation de données individuelles pour faire du provisionnement (au lieu de travailler sur des donnees aggregées dans des triangles). Dans le même genre d'idées, Ngoc An Dinh et Gilles Chau viennent de soutenir leur mémoire cette semaine à l'ENSAE, encadré par Frédéric Planchet. L'idée était de modéliser les processus de règlements avec deux approches : la première intitulée dynamiques markoviennes qui considère les processus de règlements (avec une certaine dynamique sous-jacente), et une basée sur des modèles linéaires généralisés qui suppose que les règlements appartiennent à une famille de distribution paramétrique et les paramètres dépendent des facteurs liés aux règlements. Le mémoire est en ligne sur le blog, ainsi que le code R.

Wednesday, June 27 2012

Qui se ressemble se suit (sur Twitter au moins)

Un nouveau billet pour reprendre une analyse marrante faite par @3wen  (Ewen Gallic, qui travaille à Montréal alors que je profite de la Suisse). Suite à l'analyse amusante des trolls de Twitter, j'avais lancé l'idée qu'il pourrait être amusant de regarder parmi les députés français (que j'avais un peu suivis l'autre jour), qui tweete avec qui. Ewen a eu la bonne idée de regarder sur  http://lelab.europe1.fr/ ce qui lui a permis de récupérer la liste des comptes Twitter des députés, en France. L'idée est simple: parmi les députés français, on regarde qui suit qui. Quelqu'un de très suivi sera au centre du nuage, alors que quelqu'un qui se contente de suivre sera sur le bord (intensément connecté au reste du nuage). Pour les personnes peu familières, Twitter n'est pas Facebook: on n'a pas des "amis", il y a des gens que l'on suit parce qu'ils racontent des choses qui peuvent nous intéresser.

En utilisant gephi Ewen a ensuite pu visualiser le graphique des interconnexions entre les députés.

Sans grande surprise, les députés de gauche suivent essentiellement les députés de gauche, et inversement. Quelques gros comptes font la passerelle entre les deux groupes parlementaires.

En fait, si on regarde en détail (voire sur le fichier image complet), on peut observer un peu mieux ce qui se passe,

Bon, la grande difficulté est de lire ces interactions correctement. En particulier, on ne peut pas conclure (à la vue seule du graphique) que Cécile Duflot est de gauche ! Ce que cela nous dit est que ce qu'elle raconte intéresse les gens de gauche (ou en tous cas les députés du Parti Socialiste), et pas du tout les gens de droite (les députés de l'UMP) ! On notera aussi que le centre n'existe pas. Sur Twitter en tous cas. Et si on regarde tout en bas, à droite, on retrouve le Front National, et on a la confirmation que ce que raconte le Front National n'intéresse personne !

Continue reading...

Saturday, June 23 2012

Actuarial models with R, Meielisalp

I will be giving a short course in Switzerland next week, at the 6th R/Rmetrics Meielisalp Workshop & Summer School on Computational Finance and Financial Engineering organized by ETH Zürich, https://www.rmetrics.org/. The long version of the slides for the course on Actuarial models with R can be found online with the #Rmetrics tag, and the short version will be uploaded soon. There will be some practicals, based on Swiss mortality table for the part on demography. The datasets can be uploaded using the following code,

DEATH=read.table(
"http://freakonometrics.free.fr/DeathsSwitzerland.txt",
header=TRUE,skip=2)
EXPOSURE=read.table(
"http://freakonometrics.free.fr/ExposuresSwitzerland.txt",
header=TRUE,skip=2)
DEATH$Age=as.numeric(as.character(DEATH$Age))
DEATH=DEATH[-which(is.na(DEATH$Age)),]
EXPOSURE$Age=as.numeric(as.character(EXPOSURE$Age))
EXPOSURE=EXPOSURE[-which(is.na(EXPOSURE$Age)),]
  • based on those datasets, plot the log mortality rates for male and female,

  • for those two datasets, plot the log mortality rates in 1900 and 1950, respectively
  • for those two datasets, plot the log mortality rates for the cohort born on 1900 and 1950, respectively
  • on the total dataset (male and female), fit a Lee-Carter model. Plot the age coefficients

  • plot the time coefficients and propose a forecast for that series of estimators.

  • plot the residuals obtained from the regression

  • using those estimates, and the forecasts, project the log-mortality rates

  • extrapolate the survival function of an insured aged 40 in 2000, and compare it with the one obtained on the longitudinal dataset.

  • based on those survival functions, compute actuarial present values for several quantities, e.g. whole life annuities for some insured aged 40, and whole life insurances, and compare those values from 1900 till 2040 (on the graphs below, titles were inverted).

Then, we will briefly mention payment triangles. We will work on the triangle used on http://rworkingparty.wikidot.com/ that can be downloaded below,
OthLiabData=read.csv(
"http://www.casact.org/research/reserve_data/othliab_pos.csv",
header=TRUE, sep=",")
library(plyr)
OL=SumData=ddply(OthLiabData,.(AccidentYear,DevelopmentYear,
DevelopmentLag),summarise,IncurLoss=sum(IncurLoss_H1-BulkLoss_H1),
CumPaidLoss=sum(CumPaidLoss_H1), EarnedPremDIR=
sum(EarnedPremDIR_H1))
LossTri <- as.triangle(OL, origin="AccidentYear",
dev="DevelopmentLag", value="IncurLoss")
Year=as.triangle(OL, origin="AccidentYear",
dev="DevelopmentLag", value="DevelopmentYear")
TRIANGLE=LossTri
TRIANGLE[Year>1997]=NA
Here, the triangle looks like that
> TRIANGLE
dev
origin      1      2      3      4      5      6      7      8      9     10
1988 128747 195938 241180 283447 297402 308815 314126 317027 319135 319559
1989 135147 208767 270979 304488 330066 339871 344742 347800 353245     NA
1990 152400 238665 297495 348826 359413 364865 372436 372163     NA     NA
1991 151812 266245 357430 400405 423172 442329 460713     NA     NA     NA
1992 163737 269170 347469 381251 424810 451221     NA     NA     NA     NA
1993 187756 358573 431410 476674 504667     NA     NA     NA     NA     NA
1994 210590 351270 486947 581599     NA     NA     NA     NA     NA     NA
1995 213141 351363 444272     NA     NA     NA     NA     NA     NA     NA
1996 237162 378987     NA     NA     NA     NA     NA     NA     NA     NA
1997 220509     NA     NA     NA     NA     NA     NA     NA     NA     NA
  • suggest an estimation for the amount of reserves, all years.
  • using a Poisson regression, propose a VaR with level 99.5% for future payments, for all claims that already occurred.

- page 1 of 10