Geografische analyse van risico’s met INLA

Paula Moraga laat zien hoe je geografische gegevens kunt analyseren met het programma INLA

Paula Moraga/Harrie Jonkman true
2022-08-15
Paula Moraga’s boek

1. Introductie

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 …

2. Hoe werkt INLA

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.

Lineaire voorspeller

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")\]

De inla() functie

De inla() functie wordt gebruikt om het model te fitten. De belangrijkste argumenten van inla() zijn de volgende:

Prior specificatie

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:

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.

Voorbeeld

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.

Data

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, namelijkhospitalvoor het ziekenhuis,nvoor het aantal operaties dat in elk ziekenhuis in een periode van een jaar is uitgevoerd, enr` 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

Model

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.

Resultaten

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:

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

3. Gezondheid in gebieden

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.

Data en kaart

We laden het pakket SpatialEpien 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"

2 Data voorbereiding

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:

2.1 Geobserveerde casussen

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:

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.

Verwachte gevallen

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,

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

Verhouding rokers

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.

SMRs

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.

Data

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

Gegevens aan de kaart toevoegen

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

SMR in kaart brengen

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:

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.

Modeleren

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.

Model

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)\).

Buurtmatrix

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 spdeppakket. 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.

Inferentie met INLA

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.

Resultaten

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 resultaten toevoegen aan de kaart

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

Ziekterisico in kaart brengen

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.