Paula Moraga laat zien hoe je geografische gegevens kunt analyseren met het programma INLA
Geospatial Health Data: Modeling and Visualization with R-INLA and Shiny beschrijft hoe je geografische gezondheidsgegevens in R analyseert en visualiseert. Het is een goede introductie op hoe je ziekten en ziektelast in kaart kunt brengen en hoe nieuwe computertechnieken als de integrated nested Laplace approximation (INLA) werken om plaatselijke en geostatistische gegevens te analyseren en te visualiseren. Deze nieuwe benadering maakt het mogelijk de ziektelast te kwantificeren, geografische patronen en veranderingen in de tijd te begrijpen, risicofactoren te identificeren en ongelijkheden tussen bevolkingsgroepen te meten. Het boek van Paula Moraga laat ook zien hoe interactieve en statische visualisaties kunnen worden gemaakt, zoals ziektekaarten en tijdplots en beschrijft verschillende R-pakketten die kunnen worden gebruikt om analyses eenvoudig om te zetten in informatieve en interactieve rapporten, dashboards, en Shiny webapplicaties. Dit alles vergemakkelijkt de communicatie van inzichten naar medewerkers en beleidsmakers.
Het boek bevat gedetailleerde voorbeelden van verschillende ziekte- en milieutoepassingen die gebruik maken van gegevens uit de praktijk, zoals malaria in Gambia, kanker in Schotland en de VS en luchtvervuiling in Spanje. De voorbeelden in het boek zijn toegespitst op gezondheidstoepassingen, maar de behandelde benaderingen zijn ook toepasbaar op andere gebieden die gebruik maken van geografische gegevens zoals epidemiologie, ecologie, demografie of criminologie. Het boek behandelt de volgende onderwerpen:
Het boek maakt gebruik van publiek beschikbare gegevens en beschrijft de R-code voor het importeren, manipuleren, modelleren en visualiseren van de gegevens, evenals de interpretatie van de resultaten. Hierdoor is de inhoud volledig reproduceerbaar en toegankelijk voor studenten, onderzoekers en praktijkmensen (hoewel dat in mijn geval nietg altijd even makkelijk lukte en uiteindelijk is het jammer dat ze niet deze gegevens op Github heeft gezet). In deze tutorial laat ik via Paula Moraga zien hoe INLA werkt. In het tweede deel volg ik een geografische analyse van …
De “integrated nested Laplace approximation” (INLA) benadering is
geïmplementeerd in het R pakket R-INLA
(Havard Rue et
al. 2021). Instructies hoe het pakket installeert vind je op de INLA
website (http://www.r-inla.org). Daar vind je ook documentatie
over het pakket, voorbeeld, een discussieforum en andere bronnen over de
theorie en toepassingen van INLA. Het pakket R-INLA
staat
niet op CRAN (the Comprehensive R Archive Network) omdat het externe
C-bibliotheken gebruikt en dat zorgt voor bepaalde problemen. Dus als je
het pakket wilt installeren gebruik dan install.packages()
en voeg de URL van de R-INLA repository toe. Bijvoorbeeld om een
stabiele versie van het pakket te installeren, moeten we de volgende
instructie toevoegen:
install.packages("INLA",
repos = "https://inla.r-inla-download.org/R/stable", dep = TRUE)
Om dan het pakket en twee andere pakketten te laden, is het nodig het volgende te typen:
Om een model met INLA te fitten moeten we twee stappen nemen. - Eerst
schrijven we de lineaire voorspeller van het model als een
formule-object in R.
- Dan voeren we het model uit door de inla() functie aan te roepen
waarin we de formule, de familie, de data en andere opties specificeren.
De uitvoering van inla()
geeft een object terug dat de
informatie van het gepaste model bevat, met inbegrip van verscheidene
samenvattingen en de posterior marginals van de parameters, de lineaire
voorspellers, en de gepaste waarden.
- Deze posteriors kunnen vervolgens worden nabewerkt met behulp van een
reeks functies die door R-INLA worden geleverd. - Het
pakket biedt ook schattingen van verschillende criteria om Bayesiaanse
modellen te beoordelen en te vergelijken. Deze omvatten het
informatiecriterium voor modelafwijking (DIC) (Spiegelhalter et
al. 2002), het Watanabe-Akaike-informatiecriterium (WAIC) (Watanabe
2010), de marginale waarschijnlijkheid en de conditionele voorspellende
ordinaten (CPO) (Held, Schrödle, and Rue 2010). Meer details over het
gebruik van R-INLA worden hieronder gegeven.
De syntaxis van de lineaire voorspeller in R-INLA is
vergelijkbaar met de syntaxis die wordt gebruikt om lineaire modellen te
fitten met de (bekende) functie lm()
. We moeten de
uitkomstvariabele schrijven, dan het ~ symbool, en tenslotte de vaste en
willekeurige effecten gescheiden door + operatoren. Willekeurige
effecten worden gespecificeerd door gebruik te maken van de f() functie.
Het eerste argument van f() is een indexvector die het element van het
willekeurige effect specificeert dat van toepassing is op elke
observatie, en het tweede argument is de naam van het model (b.v. “iid”,
“ar1”). Bijkomende parameters van f() kunnen bekeken worden door ?f in
te typen. Bijvoorbeeld, als we het model
\[Y \sim N(\eta_i, \sigma^2),i=1,...,\eta\] \[\eta_i=\beta_0 + \beta_1x_1 + \beta_2x_2 + u_1,\]
waar \(Y_i\) de uitkomstvariable is, \(\eta_1\) is de lineaire voorspeller, met \(x_1, x_2\) als twee verklarende variabelen en met \(u_1 \sim N(0,\sigma^2)\) wordt de formule geschreven als
\[y \sim x_1 + x_2 + f(i, model="iid")\]
Merk op dat de formule standaard een intercept bevat. Als we \(beta_0\) expliciet in de formule willen opnemen, zouden we het intercept moeten verwijderen (0 toevoegen) en het als een covariate term moeten opnemen (b0 toevoegen).
\[y \sim 0 +\beta_0 + x_1 + x_2 + f(i, model= "iid")\]
inla()
functieDe inla()
functie wordt gebruikt om het model te fitten.
De belangrijkste argumenten van inla()
zijn de
volgende:
formula
: formuleobject dat de lineaire voorspeller
specificeert,data
: dataframe met de data. Als we de responsvariabele
voor enkele observaties willen voorspellen, moeten we de
responsvariabele van deze observaties specificeren als
NA
;family
: string of vector van tekenreeksen die de
waarschijnlijkheidsfamilie aangeven, zoals gaussian
,
poisson
of binomial
. Standaard is de familie
gaussian
. Een lijst van mogelijke alternatieven kan worden
bekeken door names(inla.models()$likelihood
) in te typen,
en details voor individuele families kunnen worden bekeken met
inla.doc
(“familyname”);control.compute
: lijst met de specificatie van
verschillende rekenvariabelen, zoals dic dat een Booleaanse variabele is
die aangeeft of de DIC van het model moet worden berekend;control.predictor
: lijst met de specificatie van
verschillende variabelen voor de voorspellers, zoals link, de
linkfunctie van het model, en compute, een Booleaanse variabele die
aangeeft of de marginale dichtheden voor de lineaire voorspeller moeten
worden berekend.De namen van de priors die beschikbaar zijn in R-INLA kunnen worden
bekeken door names(inla.models()$prior)
in te typen, en een
lijst met de opties van elk van de priors kan worden bekeken met
inla.models()$prior
. De documentatie over een specifieke
prior kan worden bekeken met inla.doc(“priorname”).
Standaard wordt aan het intercept van het model een Gaussische prior
toegekend met gemiddelde en precisie gelijk aan 0. Aan de rest van de
vaste effecten worden Gaussische priors toegekend met gemiddelde gelijk
aan 0 en precisie gelijk aan 0.001. Deze waarden kunnen worden bekeken
met
inla.set.control.fixed.default()[c("mean.intercept", "prec.intercept", "mean", "prec")]
.
De waarden van deze priors kunnen worden veranderd in het control.fixed
argument van inla()
door een lijst toe te wijzen met het
gemiddelde en de precisie van de Gaussische verdelingen. Specifiek bevat
de lijst mean.intercept en prec.intercept die het prior gemiddelde en de
precisie voor het intercept vertegenwoordigen, en mean en prec die het
prior gemiddelde en de precisie vertegenwoordigen voor alle vaste
effecten behalve het intercept.
The names of the priors available in R-INLA can be seen by typing names(inla.models()\(prior), and a list with the options of each of the priors can be seen with inla.models()\)prior. The documentation regarding a specific prior can be seen with inla.doc(“priorname”).
prior.fixed <- list(mean.intercept = <>, prec.intercept = <>,
mean = <>, prec = <>)
res <- inla(formula,
data = d,
control.fixed = prior.fixed
)
De priors van de hyperparameters \(\theta\) worden toegewezen in de argument hyper van f().
formula <- y ~ 1 + f(<>, model = <>, hyper = prior.f)
De priors van de parameters van de likelihood worden toegewezen in de
parameter control.family
van inla()
.
res <- inla(formula,
data = d,
control.fixed = prior.fixed,
control.family = list(..., hyper = prior.l)
)
hyper
accepteert een benoemde lijst met namen gelijk aan
elk van de hyperparameters en waarden gelijk aan een lijst met de
specificatie van de priors. Specifiek bevat de lijst de volgende
waarden:
initial
: initiële waarde van de hyperparameter (goede
initiële waarden kunnen het inferentieproces sneller maken),prior
: naam van de prior verdeling (bijv. “iid”,
“bym2”),param
: vector met de waarden van de parameters van de prior
verdeling,vaste
: Booleaanse variabele die aangeeft of de
hyperparameter een vaste waarde is.prior.prec <- list(initial = <>, prior = <>,
param = <>, fixed = <>)
prior <- list(prec = prior.prec)
Priors moeten worden ingesteld in de interne schaal van de
hyperparameters. Bijvoorbeeld, het iid
model definieert een
vector van onafhankelijke en Gaussisch verdeelde willekeurige variabelen
met precisie \(\Gamma\). We kunnen de
documentatie van dit model bekijken door inla.doc(“iid”) in te typen en
zien dat de precisie \(\Gamma\) in de
log-schaal wordt weergegeven als \(log(\Gamma)\). Daarom moet de prioriteit
gedefinieerd worden op de log-precisie \(log(\Gamma)\).
R-INLA biedt ook een nuttig kader voor het bouwen van priors genaamd Penalized Complexity of PC priors (Fuglstad et al. 2019). PC priors worden gedefinieerd op individuele modelcomponenten die kunnen worden beschouwd als een flexibele uitbreiding van een eenvoudig, interpreteerbaar, basismodel. PC priors bestraffen afwijkingen van het basismodel. Zo controleren ze de flexibiliteit, verminderen ze over-fitting, en verbeteren ze de voorspellende prestaties. PC-prioriteiten hebben één parameter die de in het model toegestane mate van flexibiliteit bepaalt. Deze priors worden gespecificeerd door de waarden \(U,\alpha\) zo in te stellen dat
\[P(T(\epsilon)>U)=alpha,\]
waarin \(T(\epsilon)\) een interpreteerbare transformatie is van de flexibiliteitsparameter \(\epsilon\) \(U\) een bovengrens is die een staartgebeurtenis specificeert, en \(alpha\) de waarschijnlijkheid van deze gebeurtenis is.
Hier laten we een voorbeeld zien dat demonstreert hoe een model te specificeren en te fitten en de resultaten te inspecteren met behulp van een echte dataset en R-INLA. Meer specifiek modelleren we gegevens over sterftecijfers na operaties in 12 ziekenhuizen. Het doel van deze analyse is om aan de hand van sterftecijfers de prestaties van elk ziekenhuis te beoordelen en te bepalen of een ziekenhuis ongewoon goed of slecht presteert.
We gebruiken de data Surg
die het aantal operaties en
het aantal sterfgevallen bevat in 12 ziekenhuizen waar hartchirurgie op
baby’s wordt uitgevoerd.
Surgis een dataframe met drie kolommen, namelijk
hospitalvoor het ziekenhuis,
nvoor het aantal operaties dat in elk ziekenhuis in een periode van een jaar is uitgevoerd, en
r`
voor het aantal sterfgevallen binnen 30 dagen na de operatie in elk
ziekenhuis.
n r hospital
1 47 0 A
2 148 18 B
3 119 8 C
4 810 46 D
5 211 8 E
6 196 13 F
7 148 9 G
8 215 31 H
9 207 14 I
10 97 8 J
11 256 29 K
12 360 24 L
Wij specificeren een model om de sterftecijfers in elk van de ziekenhuizen te verkrijgen. We gaan uit van een binomiale waarschijnlijkheid voor het aantal sterfgevallen in elk ziekenhuis, \(Y_i\), met sterftecijfer \(p_i\)
\[Y_i \sim Binomial(n_1,p_1), i=1, ...,12\]
We nemen ook aan dat de sterftecijfers in de verschillende ziekenhuizen op één of andere manier gelijkaardig zijn en specificeren een random effects-model voor de werkelijke sterftecijfers \(p_i\)
\[logit(p_1)=alpha + u_1, u_1 \sim N(0,sigma^2)\].
Standaard wordt een niet-informatieve prior gespecificeerd voor \(\alpha\), die het populatielogit-sterftecijfer is
\[\alpha \sim N(0, 1/\Gamma), \Gamma=0\]
In R-INLA is de standaard prior voor de precisie van de willekeurige effecten \(U_i\) \(1/sigma^2 \sim Gamma(1,5x10^-5)\)$. We kunnen deze prioriteit veranderen door een Penalized Complexity (PC) prioriteit in te stellen op de standaardafwijking \(\sigma\). We kunnen bijvoorbeeld specificeren dat de waarschijnlijkheid dat \(\sigma\) groter is dan 1 klein is, gelijk aan 0,01: \(P(\sigma>1)=0,01\). In R-INLA wordt deze prioriteit gespecificeerd als
en het model wordt vertaald in R-code met behulp van de volgende formule:
Informatie over het model genaamd “iid” kan worden gevonden door inla.doc(“iid”) in te typen, en documentatie over de PC-voorafgaande “pc.prec” kan worden gezien met inla.doc(“pc.prec”).
Vervolgens roepen we inla()
aan met vermelding van de
formule, de data, de familie en het aantal trials. We voegen
control.predictor = list(compute = TRUE)
toe om de
posterior marginals van de parameters te berekenen, en
control.compute = list(dic = TRUE)
om aan te geven dat de
DIC berekend moet worden.
Wanneer inla()
wordt uitgevoerd, krijgen we een object
van de klasse inla dat de informatie van het gepaste model bevat,
inclusief samenvattingen en posterior marginale dichtheden van de vaste
effecten, de willekeurige effecten, de hyperparameters, de lineaire
voorspellers en de gepaste waarden. Een samenvatting van het
geretourneerde object res kan worden bekeken met
summary(res)
.
Call:
c("inla.core(formula = formula, family = family, contrasts =
contrasts, ", " data = data, quantiles = quantiles, E = E,
offset = offset, ", " scale = scale, weights = weights,
Ntrials = Ntrials, strata = strata, ", " lp.scale = lp.scale,
link.covariates = link.covariates, verbose = verbose, ", "
lincomb = lincomb, selection = selection, control.compute =
control.compute, ", " control.predictor = control.predictor,
control.family = control.family, ", " control.inla =
control.inla, control.fixed = control.fixed, ", " control.mode
= control.mode, control.expert = control.expert, ", "
control.hazard = control.hazard, control.lincomb =
control.lincomb, ", " control.update = control.update,
control.lp.scale = control.lp.scale, ", " control.pardiso =
control.pardiso, only.hyperparam = only.hyperparam, ", "
inla.call = inla.call, inla.arg = inla.arg, num.threads =
num.threads, ", " blas.num.threads = blas.num.threads, keep =
keep, working.directory = working.directory, ", " silent =
silent, inla.mode = inla.mode, safe = FALSE, debug = debug, ",
" .parent.frame = .parent.frame)")
Time used:
Pre = 0.363, Running = 0.18, Post = 0.0282, Total = 0.571
Fixed effects:
mean sd 0.025quant 0.5quant 0.975quant mode kld
(Intercept) -2.545 0.14 -2.838 -2.54 -2.281 NA 0
Random effects:
Name Model
hospital IID model
Model hyperparameters:
mean sd 0.025quant 0.5quant 0.975quant
Precision for hospital 12.06 18.54 2.39 8.30 41.52
mode
Precision for hospital NA
Deviance Information Criterion (DIC) ...............: 74.92
Deviance Information Criterion (DIC, saturated) ....: -10.09
Effective number of parameters .....................: 8.24
Marginal log-Likelihood: -41.17
is computed
Posterior summaries for the linear predictor and the fitted values are computed
(Posterior marginals needs also 'control.compute=list(return.marginals.predictor=TRUE)')
We kunnen de resultaten plotten met plot(res)
of ook met
plot(res, plot.prior = TRUE)
als we de prior en posterior
verdelingen in dezelfde plot willen plotten. Bij het uitvoeren van
inla()
, stellen we
control.compute = list(dic = TRUE)
in; het resultaat bevat
dus de DIC van het model. De DIC is gebaseerd op een afweging tussen de
fit van de data met het model en de complexiteit van het model, waarbij
kleinere waarden van de DIC wijzen op een beter model.
[1] 74.91528
Samenvattingen van de vaste effecten kunnen worden verkregen door
res$summary.fixed
te typen. Dit geeft een dataframe met het
gemiddelde, de standaardafwijking, de 2.5, 50 en 97.5 percentielen, en
de modus van de posterior. De kolom kld
vertegenwoordigt de
symmetrische Kullback-Leibler divergentie (Kullback en Leibler 1951) die
het verschil beschrijft tussen de Gaussische en de vereenvoudigde of
volledige Laplace benaderingen voor elke posterior.
mean sd 0.025quant 0.5quant 0.975quant mode
(Intercept) -2.544854 0.1397699 -2.83792 -2.53984 -2.280816 NA
kld
(Intercept) 9.027659e-07
We kunnen ook de samenvattingen van de willekeurige effecten en de
hyperparameters verkrijgen door respectievelijk
res$summary.random
(dat is een lijst) en
res$summary.hyperpar
(dat is een dataframe) in te
typen.
$hospital
ID mean sd 0.025quant 0.5quant 0.975quant mode
1 A -0.33389451 0.3603077 -1.16307122 -0.29192464 0.2665814 NA
2 B 0.35080284 0.2505081 -0.10880181 0.33919168 0.8720602 NA
3 C -0.04133592 0.2606810 -0.57618694 -0.03642691 0.4657133 NA
4 D -0.21850800 0.1806740 -0.58244022 -0.21590716 0.1335002 NA
5 E -0.35533380 0.2629646 -0.92479019 -0.33602535 0.1090034 NA
6 F -0.05944544 0.2350541 -0.53879448 -0.05528049 0.3974950 NA
7 G -0.09889857 0.2527838 -0.62418946 -0.09087808 0.3837764 NA
8 H 0.55120472 0.2367548 0.11491956 0.54265233 1.0390553 NA
9 I -0.04842532 0.2316528 -0.51874159 -0.04509161 0.4037989 NA
10 J 0.06195432 0.2676280 -0.46856895 0.05941080 0.6006390 NA
11 K 0.35065083 0.2194084 -0.05555099 0.34182971 0.8051082 NA
12 L -0.06820972 0.2060300 -0.48243026 -0.06622356 0.3362472 NA
kld
1 2.002965e-05
2 2.441988e-07
3 6.073521e-08
4 5.309114e-07
5 7.111982e-07
6 1.313149e-08
7 9.431419e-08
8 5.547491e-07
9 1.036271e-08
10 7.893393e-09
11 4.752694e-07
12 1.356876e-07
mean sd 0.025quant 0.5quant
Precision for hospital 12.06245 18.54074 2.38571 8.305076
0.975quant mode
Precision for hospital 41.52403 NA
Bij het uitvoeren van inla()
, als we in
control.predictor
compute = TRUE
instellen,
bevat het geretourneerde object ook de volgende objecten:
summary.linear.predictor
: dataframe met het gemiddelde,
de standaardafwijking, en de kwantielen van de lineaire
voorspellers,summary.fitted.values
: dataframe met het gemiddelde, de
standaardafwijking, en de kwantielen van de gepaste waarden, verkregen
door de lineaire voorspellers te transformeren met de inverse van de
link-functie,marginals.linear.predictor
: lijst met de posterior
marginals van de lineaire voorspellers,marginals.fitted.values
: lijst met de posterior
marginals van de gepaste waarden, verkregen door de lineaire
voorspellers te transformeren met de inverse van de link functie.Merk op dat als een observatie NA
is, de gebruikte link
functie de identiteit is. Als we willen dat
summary.fitted.values
en
marginals.fitted.values
de waarden in de getransformeerde
schaal bevatten, moeten we de juiste link in
control.predictor
instellen. Als alternatief kunnen we
handmatig de marginale in het inla object transformeren met de
inla.tmarginal()
functie.
De voorspelde sterftecijfers in ons voorbeeld kunnen worden verkregen
met res$summary.fitted.values
.
mean sd 0.025quant 0.5quant
fitted.Predictor.01 0.05644048 0.01867703 0.02292710 0.05562314
fitted.Predictor.02 0.10253828 0.02126297 0.06699312 0.10042687
fitted.Predictor.03 0.07216207 0.01702174 0.04222902 0.07094756
fitted.Predictor.04 0.05999673 0.00783653 0.04536530 0.05974917
fitted.Predictor.05 0.05387150 0.01288259 0.03041322 0.05332887
fitted.Predictor.06 0.07051697 0.01443560 0.04458770 0.06968698
fitted.Predictor.07 0.06829273 0.01549981 0.04056203 0.06738163
fitted.Predictor.08 0.12186957 0.02176714 0.08365632 0.12045830
fitted.Predictor.09 0.07117792 0.01424848 0.04557239 0.07035957
fitted.Predictor.10 0.07945389 0.01927646 0.04666029 0.07763782
fitted.Predictor.11 0.10185627 0.01715688 0.07181163 0.10064079
fitted.Predictor.12 0.06945235 0.01154999 0.04830017 0.06893376
0.975quant mode
fitted.Predictor.01 0.09581101 NA
fitted.Predictor.02 0.14978961 NA
fitted.Predictor.03 0.10935445 NA
fitted.Predictor.04 0.07606539 NA
fitted.Predictor.05 0.08059969 NA
fitted.Predictor.06 0.10137331 NA
fitted.Predictor.07 0.10155982 NA
fitted.Predictor.08 0.16827664 NA
fitted.Predictor.09 0.10161711 NA
fitted.Predictor.10 0.12271521 NA
fitted.Predictor.11 0.13874397 NA
fitted.Predictor.12 0.09364497 NA
De kolom mean
laat zien dat de ziekenhuizen 2, 8 en 11
de hoogste posterior gemiddelden van de sterftecijfers hebben. De
kolommen 0.025quant
en 0.975quant
bevatten de
onder- en bovengrenzen van 95% geloofwaardige intervallen van de
sterftecijfers en geven maatstaven voor de onzekerheid.
We kunnen ook een lijst verkrijgen met de posterior marginals van de
vaste effecten door res$marginals.fixed
te typen en lijsten
met de posterior marginals van de random effecten en de hyperparameters
door respectievelijk marginals.random
en
marginals.hyperpar
te typen. Marginals zijn benoemde
lijsten die matrices met 2 kolommen bevatten. Kolom x vertegenwoordigt
de waarde van de parameter, en kolom y is de dichtheid.
R-INLA bevat verschillende functies om de posterior
marginals te manipuleren. Bijvoorbeeld, inla.emarginal()
en
inla.qmarginal()
berekenen de verwachting en kwantielen,
respectievelijk, van de posterior marginalen. inla.smarginal() kan
worden gebruikt om een spline afvlakking te verkrijgen,
inla.tmarginal()
kan worden gebruikt om de marginalen te
transformeren, en inla.zmarginal()
geeft samenvattende
statistieken.
In ons voorbeeld bevat het eerste element van de posterior marginalen
van de vaste effecten, res$marginals.fixed[[1]]
, de
posterior elementen van het intercept $ We kunnen
inla.smarginal()
toepassen om een spline afvlakking van de
marginale dichtheid te verkrijgen en deze vervolgens plotten met de
ggplot() functie van het ggplot2
pakket.
Het kwantiel en de verdelingsfuncties worden gegeven door
inla.qmarginal()
en inla.pmarginal()
,
respectievelijk. We kunnen het kwantiel 0.05 van \(\alpha\) verkrijgen, en de kans dat \(\alpha\) lager is dan dit kwantiel als
volgt uitzetten:
[1] -2.781735
[1] 0.05
Een grafiek van de waarschijnlijkheid dat \(\alpha\) lager is dan het 0,05 kwantiel kan als volgt worden gemaakt
De functie inla.dmarginal()
berekent de dichtheid bij
bepaalde waarden. Bijvoorbeeld, de dichtheid bij waarde -2.5 kan als
volgt berekend worden:
[1] 2.973883
Indien we een transformatie van de marginale willen verkrijgen,
kunnen we inla.tmarginal()
gebruiken. Indien we
bijvoorbeeld de variantie van het willekeurige effect \(u_i\) willen verkrijgen, kunnen we de
marginale van de precisie \(gamma\)
verkrijgen en dan de inverse functie toepassen.
Een plot van de posterior van de variantie van het willekeurige effect \(u_i\) is weergegeven in
Als we nu de gemiddelde posterior van de variantie willen verkrijgen, kunnen we `inla.emarginal() gebruiken.
[1] 0.1459612
De standaardafwijking kan worden berekend met de uitdrukking
\[Var[X]=E[X^2} - E[X]^2\]
[1] 0.1033927
Kwantielen worden berekend met de inla.qmarginal()
functie.
[1] 0.02377769 0.12018989 0.41926470
Onderstaande is gebaseerd op Paula Moraga’s Geospatial Health Data bookGeospatial Health Data, 2019, Chapman&Hall/CRC en haar tutorial en specieke het derde hoofdstuk waarin ze ingaat op het analyseren van gezondheid in gebieden.
In deze tutorial worden methodes om ziekte in kaarten te zetten
gepresenteerd, specifiek om het risico op longkanker in Pennsylvania,
Verenigde Staten, in het jaar 2002 in te schatten. We gebruiken gegevens
van het R-pakket SpatialEpi
dat de bevolking, de gevallen
van longkanker en de verhouding rokers in de districten van Pennsylvania
bevat. De bevolkingsgegevens zijn afkomstig van de 2000 decennium
census. De ziektegevallen en het percentage rokers zijn afkomstig van de
website van het Pennsylvania Departement van Gezondheid.
Wij laten zien hoe de waargenomen en verwachte ziektegevallen en de
gestandaardiseerde sterfteratio’s (SMR’s) in elk van de districten van
Pennsylvania kunnen worden berekend. We verkrijgen ook schattingen van
het ziekterisico en kwantificeren risicofactoren met behulp van INLA.
Tenslotte laten we zien hoe we interactieve kaarten kunnen maken van de
ziekterisicoschattingen met behulp van leaflet
.
Een uitgebreide versie van dit voorbeeld verscheen in Moraga, P. Small Area Disease Risk Estimation and Visualization Using R. The R Journal, 10(1):495-506, 2018, en het boek Moraga, P. Geospatial Health Data: Modeling and Visualization with R-INLA and Shiny. Chapman & Hall/CRC, 2019.
We laden het pakket SpatialEpi
en halen de data
binnen.
Vervolgens inspecteren we de data:
[1] "list"
[1] "geo" "data" "smoking"
[4] "spatial.polygon"
We zien dat pennLC een listobject is met de volgende elementen:
We inspecteren de eerste rijen van pennLC\(data en pennLC\)smoking. pennLC$data bevat het aantal gevallen van longkanker en de bevolking op districtsniveau, gestratificeerd naar ras (blank en niet-blank), geslacht (vrouw en man) en leeftijd (jonger dan 40, 40-59, 60-69 en 70+).
county cases population race gender age
1 adams 0 1492 o f Under.40
2 adams 0 365 o f 40.59
3 adams 1 68 o f 60.69
4 adams 0 73 o f 70+
5 adams 0 23351 w f Under.40
6 adams 5 12136 w f 40.59
De geo-gegevens zien er zo uit:
county x y
1 adams -77.21550 39.87776
2 allegheny -79.97980 40.46986
3 armstrong -79.46558 40.81654
4 beaver -80.34774 40.68327
5 bedford -78.48906 40.01033
6 berks -75.92717 40.41920
pennLC$smoking bevat county-specifieke proportie rokers.
county smoking
1 adams 0.234
2 allegheny 0.245
3 armstrong 0.250
4 beaver 0.276
5 bedford 0.228
6 berks 0.249
De kaart van de graafschappen van Pennsylvania wordt gegeven door het
object SpatialPolygons
met de naam
pennLC$spatial.polygon
. Wij kunnen de kaart als volgt
plotten:
[1] "SpatialPolygons"
attr(,"package")
[1] "sp"
We maken nu een dataframe met de naam d met kolommen die de id’s van de provincies, het waargenomen en het verwachte aantal gevallen, de rookverhoudingen en de SMR’s bevatten. Specifiek zal d de volgende kolommen bevatten:
pennLC$data
bevat de gevallen in elke county
gestratificeerd naar ras, geslacht en leeftijd. We kunnen het aantal
gevallen in elke county, Y, verkrijgen door de rijen van pennLC$data per
county te aggregeren en het waargenomen aantal gevallen op te tellen met
het pakket dplyr.
# A tibble: 6 x 2
county Y
<fct> <int>
1 adams 55
2 allegheny 1275
3 armstrong 49
4 beaver 172
5 bedford 37
6 berks 308
Een alternatief voor dplyr om het aantal gevallen in elke provincie
te berekenen is om de aggregate()
functie te gebruiken.
aggregate()
accepteert de volgende drie argumenten:
x
: dataframe met de gegevens,by
: lijst van groeperingselementen (elk element moet
even lang zijn als de variabelen in dataframe x),FUN
: functie om de samenvattingsstatistieken te
berekenen die op alle gegevensverzamelingen moeten worden
toegepast.We kunnen aggregate()
gebruiken met x gelijk aan de
vector van de gevallen, by gelijk aan een lijst met de districten, en
FUN gelijk aan sum
. Vervolgens stellen we de namen van de
kolommen van het verkregen dataframe gelijk aan county
en
Y
.
Nu berekenen we het verwachte aantal gevallen in elke county, dat het totale aantal ziektegevallen vertegenwoordigt dat men zou verwachten als de bevolking in het county zich gedroeg zoals de bevolking van Pennsylvania zich gedraagt. Het verwachte aantal gevallen \(E_i\) in elke county i kan met indirecte standaardisering als volgt worden berekend
Ei=∑j=1mr(s)jn(i)j,
waarbij r(s)j het percentage is (aantal gevallen gedeeld door bevolking) in stratum j van de bevolking van Pennsylvania, en \(n_j^(i)\) de bevolking in stratum \(j\) van county \(i\).
We kunnen de verwachte tellingen hiervoor berekenen met de
expected()
functie van het SpatialEpi pakket. Deze functie
heeft drie argumenten, te weten,
populatie
: een vector van bevolkingstellingen voor elk
strata in elk gebied,gevallen
: een vector met het aantal gevallen voor elk
strata in elk gebied,n.strata
: het aantal beschouwde strata.De vectoren populatie
en gevallen
moeten
eerst per gebied worden gesorteerd en daarna moeten binnen elk gebied de
tellingen voor alle strata in dezelfde volgorde worden opgesomd. Alle
strata moeten in de vectoren worden opgenomen, ook strata met 0
gevallen.
Om de verwachte tellingen te krijgen sorteren we de gegevens eerst
met de order()
functie waarbij we de volgorde aangeven als
county, ras, geslacht en tenslotte leeftijd.
Vervolgens krijgen we de verwachte tellingen E in elke county door de
expected()
functie aan te roepen waarbij we
population
gelijk stellen aan
pennLC$data$population
en cases
gelijk stellen
aan pennLC$data$cases
. Er zijn 2 rassen, 2 geslachten en 4
leeftijdsgroepen voor elke county, dus het aantal strata is ingesteld op
2 x 2 x 4 = 16.
Nu voegen we de vector E toe aan het dataframe d, dat de ids van de provincies (county) en de waargenomen tellingen (Y) bevat.
county Y E
1 adams 55 69.62730
2 allegheny 1275 1182.42804
3 armstrong 49 67.61012
4 beaver 172 172.55806
5 bedford 37 44.19013
6 berks 308 300.70598
We voegen ook aan d
de variabele roken toe, die het
percentage rokers in elk county weergeeft. We voegen deze variabele toe
met de merge()
functie waarbij we county
specificeren als de kolom voor het samenvoegen.
Voor elke county \(i\) wordt de SMR gedefinieerd als de verhouding tussen de waargenomen tellingen en de verwachte tellingen.
\[SMR_i=Y_i/E_i\] Wij berekenen de
vector van SMR’s als de verhouding tussen de waargenomen en de verwachte
tellingen, en voegen deze toe aan het gegevenskader d
.
De definitieve dataset ziet er zo uit:
county Y E smoking SMR
1 adams 55 69.62730 0.234 0.7899200
2 allegheny 1275 1182.42804 0.245 1.0782897
3 armstrong 49 67.61012 0.250 0.7247435
4 beaver 172 172.55806 0.276 0.9967660
5 bedford 37 44.19013 0.228 0.8372910
6 berks 308 300.70598 0.249 1.0242563
Wij hebben een dataframe d
geconstrueerd met de
waargenomen en verwachte ziektetellingen, de rokerspercentages en de SMR
voor elk van de counties. De kaart van de Pennsylvania counties wordt
gegeven door het object SpatialPolygons dat kaart heet. Met behulp van
dit object en het data frame d
kunnen we een
SpatialPolygonsDataFrame maken waarmee we kaarten kunnen maken van de
variabelen in d
. Om dat te doen stellen we eerst de
rijnamen van het data frame d
gelijk aan
d$county
. Dan voegen we de SpatialPolygons
kaart samen met het data frame d
dat overeenkomt met de
SpatialPolygons member Polygons ID slot waarden met de data frame rij
namen (match.ID = TRUE
).
county Y E smoking SMR
adams adams 55 69.62730 0.234 0.7899200
allegheny allegheny 1275 1182.42804 0.245 1.0782897
armstrong armstrong 49 67.61012 0.250 0.7247435
beaver beaver 172 172.55806 0.276 0.9967660
bedford bedford 37 44.19013 0.228 0.8372910
berks berks 308 300.70598 0.249 1.0242563
We kunnen de waargenomen en verwachte ziektetellingen, de SMR’s, en
de rokersverhoudingen visualiseren in een interactieve chroplethkaart.
We maken deze kaart met behulp van het leaflet
pakket.
We maken de kaart door eerst leaflet()
aan te roepen en
de standaard OpenStreetMap kaarttegels aan de kaart toe te voegen met
addTiles()
. Daarna voegen we de Pennsylvania counties toe
met addPolygons()
waarbij we de kleur van de grenzen van de
gebieden opgeven (color
) en de lijndikte
(weight
). We vullen de gebieden met de kleuren gegeven door
de kleurenpalet-functie gegenereerd met colorNumeric()
, en
stellen fillOpacity in op een waarde kleiner dan 1 om de
achtergrondkaart te kunnen zien. We gebruiken
colorNumeric()
om een kleurenpalet functie te maken die
data waarden in kaart brengt in kleuren volgens een gegeven
kleurenpalet. We maken de functie met de volgende twee parameters:
palette
: kleurfunctie waarnaar waarden zullen worden
gemapt, endomein
: mogelijke waarden die kunnen worden
toegewezen.Tenslotte voegen we de legenda toe door de kleurenpalette-functie
(pal
) op te geven en de waarden die gebruikt worden om
kleuren te genereren uit de palette-functie (values
). We
stellen opacity
in op dezelfde waarde als de opacity in de
gebieden, en specificeren een titel en een positie voor de legenda.
We kunnen de kaart verbeteren door de provincies te markeren als de
muis er met de muis overheen gaat en informatie te tonen over de
waargenomen en verwachte tellingen, SMR’s en rookverhoudingen. We doen
dit door de argumenten highlightOptions
, label
en labelOptions
toe te voegen aan
addPolygons()
. We kiezen ervoor om de gebieden te
highlighten met een grotere lijndikte
(highlightOptions(weight = 4)
). We maken de labels met HTML
syntaxis. Eerst maken we de tekst die moet worden weergegeven met de
functie sprintf()
die een tekenvector teruggeeft die een
opgemaakte combinatie van tekst en variabele waarden bevat. Vervolgens
passen we htmltools::HTML()
toe die de tekst als HTML
markeert. In labelOptions specificeren we de labelstijl, de tekstgrootte
en de richting. Mogelijke waarden voor richting zijn links, rechts en
auto en dit specificeert de richting waarin het label wordt weergegeven
ten opzichte van de marker. Wij kiezen auto zodat de optimale richting
wordt gekozen afhankelijk van de positie van de marker.
Nu kunnen we de kaart bekijken en zien welke gebieden
Deze kaart geeft een idee van het ziekterisico in Pennsylvania. SMR’s
kunnen echter misleidend en onvoldoende betrouwbaar zijn in districten
met kleine bevolkingsaantallen. Modelmatige benaderingen daarentegen
maken het mogelijk covariaten op te nemen en informatie te lenen van
naburige counties om de lokale schattingen te verbeteren, wat resulteert
in het afvlakken van extreme waarden op basis van kleine
steekproefgroottes. In de volgende sectie zullen we laten zien hoe we
ziekterisicoschattingen kunnen verkrijgen met behulp van een model dat
INLA
gebruikt.
In this Section we specify the model for the observed data, and
detail the required steps to fit the model and obtain the disease risk
estimates using INLA
.
Stel \(Y_i\) en \(E_i\) respectievelijk het waargenomen en het verwachte aantal ziektegevallen zijn en stel \(\theta_i\) is het relatieve risico voor district \(i=1,...,n\). Het model wordt als volgt gespecificeerd:
\[Y_i|Poisson(E_i \times \theta_i),
i=1,...,n,\] \[log(\theta_i)=\beta_0+\beta_1 \times
smoking_i+u_i+v_i\]. - \(\beta_0\) is het intercept,
- \(\beta_1\) is de coëfficiënt van het
rokersaandeel covariaat,
- \(u_i\) een gestructureerd ruimtelijk
effect is, \(u_i|u-i∼N(u¯δi,1τunδi\))
(conditioneel autoregressief model (CAR)),
- \(v_i\) een ongestructureerd
ruimtelijk effect is, \(v_i∼N(0,1/τv)\).
We maken de buurtmatrix die nodig is om het ruimtelijk willekeurig
effect te definiëren met behulp van de poly2nb()
en de
nb2INLA()
functies van het spdep
pakket. Eerst
gebruiken we poly2nb()
om een burenlijst te maken op basis
van gebieden met aangrenzende grenzen. Elk element van de lijst nb
vertegenwoordigt een gebied en bevat de indices van zijn buren.
Bijvoorbeeld, nb[[2]]
bevat de buren van gebied 2.
[[1]]
[1] 21 28 67
[[2]]
[1] 3 4 10 63 65
[[3]]
[1] 2 10 16 32 33 65
[[4]]
[1] 2 10 37 63
[[5]]
[1] 7 11 29 31 56
[[6]]
[1] 15 36 38 39 46 54
Daarna gebruiken we de nb2INLA()
functie om de nb lijst
om te zetten in een bestand genaamd map.adj met de representatie van de
buurt matrix zoals vereist door INLA
. Het
map.adj
bestand wordt opgeslagen in de werkdirectory die
kan worden verkregen met getwd()
. Vervolgens lezen we het
map.adj
bestand met behulp van de
inla.read.graph()
functie van INLA
, en slaan
het op in het object g
dat we later zullen gebruiken om het
ruimtelijke ziektemodel met INLA
te specificeren.
Het model omvat twee willekeurige effecten, namelijk \(u_i\) voor het modelleren van ruimtelijke
restvariatie, en \(v_i\) voor het
modelleren van ongestructureerde ruis. Wij moeten twee vectoren in de
gegevens opnemen die de indices van deze willekeurige effecten
aanduiden. Wij noemen \(re_u\) de
indexvector voor \(u_i\), en \(re_v\) de indexvector voor \(v_i\). We stellen zowel \(re_u\) als \(re_v\) gelijk aan 1,…,n, waarbij n het
aantal counties is. In ons voorbeeld, n=67 en dit kan worden verkregen
met het aantal rijen in de data (nrow(map@data)
).
Wij specificeren de modelformule door de respons in het linkerdeel op
te nemen en de vaste en toevallige effecten in het rechterdeel.
Willekeurige effecten worden ingesteld met f() met parameters gelijk aan
de naam van de variabele en het gekozen model. Voor \(u_i\) gebruiken we
model = "besag"
met een buurtmatrix gegeven door
g
. Voor \(v_i\) kiezen we
model = "iid"
.
We fitten het model door de inla()
functie aan te roepen
en de standaard priors in INLA
te gebruiken. We
specificeren de formule, familie, data en de verwachte tellingen en
stellen control.predictor
gelijk aan
list(compute = TRUE)
om de posterieure gemiddelden van de
voorspellers te berekenen.
We kunnen het resultaatobject res inspecteren met
summary()
.
Call:
c("inla.core(formula = formula, family = family, contrasts =
contrasts, ", " data = data, quantiles = quantiles, E = E,
offset = offset, ", " scale = scale, weights = weights,
Ntrials = Ntrials, strata = strata, ", " lp.scale = lp.scale,
link.covariates = link.covariates, verbose = verbose, ", "
lincomb = lincomb, selection = selection, control.compute =
control.compute, ", " control.predictor = control.predictor,
control.family = control.family, ", " control.inla =
control.inla, control.fixed = control.fixed, ", " control.mode
= control.mode, control.expert = control.expert, ", "
control.hazard = control.hazard, control.lincomb =
control.lincomb, ", " control.update = control.update,
control.lp.scale = control.lp.scale, ", " control.pardiso =
control.pardiso, only.hyperparam = only.hyperparam, ", "
inla.call = inla.call, inla.arg = inla.arg, num.threads =
num.threads, ", " blas.num.threads = blas.num.threads, keep =
keep, working.directory = working.directory, ", " silent =
silent, inla.mode = inla.mode, safe = FALSE, debug = debug, ",
" .parent.frame = .parent.frame)")
Time used:
Pre = 0.386, Running = 0.36, Post = 0.0252, Total = 0.771
Fixed effects:
mean sd 0.025quant 0.5quant 0.975quant mode kld
(Intercept) -0.323 0.150 -0.620 -0.323 -0.028 NA 0
smoking 1.155 0.624 -0.078 1.156 2.382 NA 0
Random effects:
Name Model
re_u Besags ICAR model
re_v IID model
Model hyperparameters:
mean sd 0.025quant 0.5quant 0.975quant
Precision for re_u 231.19 124.15 76.99 203.24 550.11
Precision for re_v 18051.81 18041.22 1142.95 12555.36 66158.02
mode
Precision for re_u NA
Precision for re_v NA
Marginal log-Likelihood: -289.44
is computed
Posterior summaries for the linear predictor and the fitted values are computed
(Posterior marginals needs also 'control.compute=list(return.marginals.predictor=TRUE)')
We zien dat de intercept \(\beta_0\)= -0.323 met een 95% geloofwaardig
interval gelijk aan (-0.621, -0.028) en de rookcoëfficiënt is \(\beta_1\)= 1.156 met een 95% geloofwaardig
interval gelijk aan (-0.081, 2.384). We kunnen de posterior verdeling
van de rookcoëfficiënt plotten. We doen dit door een afvlakking van de
marginale verdeling van de coëfficiënt te berekenen met
inla.smarginal()
en deze vervolgens te plotten met
ggplot()
van het ggplot2
pakket.
De schattingen van het ziekterisico en de onzekerheid voor elk van de
counties worden gegeven door de gemiddelde posterior en de 95%
geloofwaardige intervallen van \(\theta_i\), \(i=1,...,n\) die in
res$summary.fitted.values
staan. Kolom gemiddelde is de
gemiddelde posterior en 0.025quant en 0.975quant zijn de 2.5 en 97.5
percentielen, respectievelijk. We voegen deze gegevens toe aan map om
kaarten van deze variabelen te kunnen maken. We kennen het gemiddelde
toe aan de schatting van het relatieve risico, en 0,025quant en
0,975quant aan de onder- en bovengrens van 95%
geloofwaardigheidsintervallen van de risico’s.
mean sd 0.025quant 0.5quant
fitted.Predictor.01 0.8789838 0.05834751 0.7637139 0.8791642
fitted.Predictor.02 1.0599090 0.02761044 1.0070178 1.0594682
fitted.Predictor.03 0.9637739 0.05138019 0.8580258 0.9652580
fitted.Predictor.04 1.0272360 0.05119153 0.9274735 1.0268761
fitted.Predictor.05 0.9074353 0.05474297 0.7985128 0.9077688
fitted.Predictor.06 0.9949928 0.04020334 0.9182593 0.9941317
0.975quant mode
fitted.Predictor.01 0.9939813 NA
fitted.Predictor.02 1.1152839 NA
fitted.Predictor.03 1.0616492 NA
fitted.Predictor.04 1.1292698 NA
fitted.Predictor.05 1.0152424 NA
fitted.Predictor.06 1.0767078 NA
We tonen het geschatte ziekterisico in een interactieve kaart met
leaflet
. In de kaart voegen we labels toe die verschijnen
als de muis over de counties beweegt, met informatie over waargenomen en
verwachte tellingen, SMRs, rokerspercentages, RRs, en onder- en
bovengrenzen van 95% geloofwaardige intervallen.
We zien dat districten met een hoger ziekterisico in het westen en zuidoosten van Pennsylvania liggen en districten met een lager risico in het centrum. De 95% geloofwaardige intervallen geven de onzekerheid in de risicoschattingen aan.