Kapitel 64 Fehlende Daten und Multiple Imputation

Fehlende Daten ersetzen ist oft besser, als sie nicht zu ersetzen - auch wenn dies auf den ersten Blick nicht ganz intuitiv erscheint und für manche sogar nach Betrug tönt.

Wichtige Punkte:

64.0.1 Wir benötigen folgende Pakete in diesem Kapitel

library(tidyverse)
library(missMethods)
library(VIM)
library(naniar)
library(rio)
library(rpart)
library(rpart.plot)
library(mice)

64.1 Beispiel 1

Für das erste Beispiel benutzen wir Daten eines veröffentlichten Artikels (Estévez-Pedraza, Á. G., Parra-Rodríguez, L., Martínez-Méndez, R., Portillo-Rodríguez, O., & Ronzón-Hernández, Z. (2021). A novel model to quantify balance alterations in older adults based on the center of pressure (CoP) measurements with a cross-sectional study. PloS one, 16(8), e0256129.

Zuerst erstellen wir mit den kompletten Daten ein einfaches logistisches Regressionsmodell mit der abhängigen Variable falls (0/1) und den unabhängigen Variablen age, sex, bmi, gait_speed, squats und balance_alteration (0/1).

data<-rio::import("https://doi.org/10.1371/journal.pone.0256129.s002", format="csv")

data<-janitor::clean_names(data, case='snake')
data<-data[,1:10]
names(data)
##  [1] "folio_num"          "age"                "sex"               
##  [4] "multimorbidity"     "polipharmacy"       "falls"             
##  [7] "bmi"                "gait_speed"         "squats"            
## [10] "balance_alteration"
Zu Übungszwecken fügen wir noch die Variablen kg und height_in_m an, die wir aus der bmi Variable berechnen. Da wir in dieser Formel für kg und height zwei unbekannte Grössen haben, müssen wir mindestens eine frei erfinden. So erfinden wir die Variable height.

Danach können wir das Körpergewicht berechnen.

data$height<-rnorm(nrow(data), 1.74, 0.12)
data$kg<-data$bmi*data$height^2
# 
# # just to check: 
# data$bmi2<-data$kg/data$height^2
# plot(data$bmi, data$bmi2)
glm.fit.true<-glm(falls~age+sex+bmi+multimorbidity+polipharmacy+gait_speed+squats+balance_alteration, family=binomial(link='logit'), data=data)
jtools::summ(glm.fit.true, exp=TRUE, confint=TRUE)
Observations 414
Dependent variable falls
Type Generalized linear model
Family binomial
Link logit
χ²(8) 15.14
Pseudo-R² (Cragg-Uhler) 0.05
Pseudo-R² (McFadden) 0.03
AIC 557.93
BIC 594.17
exp(Est.) 2.5% 97.5% z val. p
(Intercept) 0.02 0.00 0.54 -2.30 0.02
age 1.05 1.01 1.09 2.51 0.01
sex 0.61 0.38 0.98 -2.06 0.04
bmi 1.02 0.97 1.07 0.59 0.56
multimorbidity 1.38 0.87 2.19 1.35 0.18
polipharmacy 1.10 0.72 1.68 0.44 0.66
gait_speed 1.00 0.99 1.01 0.85 0.40
squats 0.97 0.89 1.05 -0.84 0.40
balance_alteration 0.74 0.44 1.25 -1.12 0.26
Standard errors: MLE

64.1.1 Missing At Random

Erstellen wir einen Datensatz mit Missing At Random fehlenden Werten mit dem Befehl delete_MAR_1_to_x des Pakets missMethods. Wir haben die fehlenden Werte so erstellt, dass sie von der Variable age abhängen.

set.seed(666)
df_mar <- missMethods::delete_MAR_1_to_x(data, p=c(0.02,0.1,0.075, 0.1,0.2, 0.2,0.4,0.3,0.2,0.2), cols_mis=c("falls", "sex", "bmi", "gait_speed", "squats", "balance_alteration","multimorbidity","polipharmacy", "kg","height"), cols_ctrl=rep("age",10), x_stochastic=TRUE, n.mis_stochastic=TRUE, x=4)

Visualisieren wir die fehlenden Werte.

naniar::vis_miss(df_mar[,1:12])
## Warning: `gather_()` was deprecated in tidyr 1.2.0.
## ℹ Please use `gather()` instead.
## ℹ The deprecated feature was likely used in the visdat package.
##   Please report the issue at <]8;;https://github.com/ropensci/visdat/issueshttps://github.com/ropensci/visdat/issues]8;;>.
Visualisierung fehlender Werte. Prozente per Variable.

Abbildung 16.1: Visualisierung fehlender Werte. Prozente per Variable.

In der Realität wüssten wir ja nicht direkt, dass die fehlenden Werte vom Alter abhängen, wir könnten dies herausfinden, wenn wir alle Kombination mit folgender Graphik anschauen.

VIM::marginplot(df_mar[,c('age', 'polipharmacy')])
Boxplots des Alters, einmal für diejenigen Teilnehmenden, die fehlende Werte in der Variable Polipharmacy haben (rot) und einmall für diejenigen Teilnehmenden ohne fehlende Werte in der Variable Polipharmacy.Fe

Abbildung 64.1: Boxplots des Alters, einmal für diejenigen Teilnehmenden, die fehlende Werte in der Variable Polipharmacy haben (rot) und einmall für diejenigen Teilnehmenden ohne fehlende Werte in der Variable Polipharmacy.Fe

mean(df_mar$age[is.na(df_mar$polipharmacy)])
## [1] 73.64516
mean(df_mar$age[!is.na(df_mar$polipharmacy)])
## [1] 68.76552

Wir sehen, dass die Personen mit fehlenden Werten in der Variable polipharmacy älter sind, als diejenigen ohne fehlende Werte. In der Graphik sehen wir dies mit dem vergleich der beiden horizontalen Boxplots, der rote ist für diejnigen mit fehlenden polipharmacy, der blaue für diejenigen ohne fehlende Werte in der Variable polipharmacy.

Eine andere Möglichkeit, das missingness-pattern zu untersuchen ist folgender Baum. Auch hier sehen wir, dass bei älteren Personen die Wahrscheinlichkeit für fehlende Werte höher ist als bei jüngeren Personen.

df_mar %>%
  naniar::add_prop_miss() %>% # Adds Column Containing Proportion Of Missing Data Values
  rpart::rpart(prop_miss_all ~ ., data = ., model=TRUE) %>% # the rpart function does the regression tree 
  rpart.plot::prp(type = 4, extra = 101, prefix = "Prop. Miss = ") # plots the tree
Baum mit Proportionen den fehlenden Daten pro dichotomisierte Variablen.

Abbildung 64.2: Baum mit Proportionen den fehlenden Daten pro dichotomisierte Variablen.

Wenn wir unsere Analyse mit den fehlenden Daten laufen lassen, so sehen wir, dass die Resultate anders sind als beim kompletten Datensatz.

glm.fit_mar<-glm(falls~age+sex+bmi+multimorbidity+polipharmacy+gait_speed+squats+balance_alteration, data=df_mar,family=binomial(link='logit'))
jtools::summ(glm.fit_mar, exp=TRUE, confint=TRUE)
Observations 109 (305 missing obs. deleted)
Dependent variable falls
Type Generalized linear model
Family binomial
Link logit
χ²(8) 11.38
Pseudo-R² (Cragg-Uhler) 0.14
Pseudo-R² (McFadden) 0.08
AIC 147.58
BIC 171.80
exp(Est.) 2.5% 97.5% z val. p
(Intercept) 0.00 0.00 6.75 -1.49 0.14
age 1.05 0.95 1.15 0.97 0.33
sex 0.36 0.12 1.08 -1.82 0.07
bmi 1.04 0.93 1.17 0.68 0.50
multimorbidity 2.75 1.06 7.13 2.09 0.04
polipharmacy 0.65 0.24 1.78 -0.84 0.40
gait_speed 1.01 0.98 1.03 0.48 0.63
squats 1.08 0.89 1.30 0.79 0.43
balance_alteration 0.71 0.17 2.95 -0.47 0.64
Standard errors: MLE
Die Analyse mit den fehlenden Werten ist eine sogenannte complete case analysis, da wir nur die Fälle in die Analyse einschliessen, die komplette Daten haben. Da die Daten nun nicht Missing Completely At Random sind, sollten wir nicht einfach dieser complete case analysis vertrauen. Wir sollten versuchen, die fehlenden Daten sinnvoll zu ersetzen.

Da wir in unserem Beispiel den Datensatz ohne fehlende Werte auch haben, können wir danach die Analysen mit den ersetzten Daten vergleichen (was wir leider in der Realität nie tun können).

64.1.2 Multiple Imputation mit mice

Mice bedeutet: Multivariate Imputation by Chained Equations.

Für jede Variable mit fehlenden Werten erstellen wir nun ein Modell, um die fehlenden Werte möglichst sinnvoll zu ersetzen. Der Befehl flux aus dem mice Paket, zeigt uns pro Variable die Proportion der vorhandenen Werte, sowie den Influx und den Outflux Wert. Eine vertiefte Beschreibung dieser Statistiken finden Sie hier (hier klicken). Kurz gesagt: Bei Variablen mit einem hohen Influx Wert können fehlende Werte einfacher ersetzt werden. Variablen mit einem hohen Outflux Wert eignen sich besser um fehlende Werte zu schätzen. Je weiter unten eine Variable in der Tabelle ist, desto eher könnte es beim Ersetzen der Werte Probleme geben. Variablen die höher in der Tabelle eingeordnet sind, haben weniger fehlende Werte und eignen sich eher besser zum Schätzen (d.h. zum imputieren) der fehlenden Wert. Variablen mit zu vielen fehlenden Werten sollten eventuell als Prädktoren für die Imputation ausgeschlossen werden. Dies zeigen wir später.

mice::flux(df_mar[,-1])
##                         pobs     influx   outflux      ainb      aout      fico
## age                1.0000000 0.00000000 1.0000000 0.0000000 0.1794686 0.7946860
## sex                0.9009662 0.08501706 0.8290713 0.7902439 0.1651475 0.7721180
## multimorbidity     0.5990338 0.36132249 0.3956931 0.8295181 0.1185484 0.6572581
## polipharmacy       0.7004831 0.26318552 0.5141319 0.8088710 0.1317241 0.7068966
## falls              0.9806763 0.01731829 0.9703903 0.8250000 0.1775862 0.7906404
## bmi                0.9251208 0.06743637 0.8869448 0.8290323 0.1720627 0.7780679
## gait_speed         0.9009662 0.08344267 0.8209960 0.7756098 0.1635389 0.7721180
## squats             0.7995169 0.17213330 0.6541050 0.7903614 0.1468278 0.7432024
## balance_alteration 0.7995169 0.17816846 0.6850606 0.8180723 0.1537764 0.7432024
## height             0.7995169 0.17633167 0.6756393 0.8096386 0.1516616 0.7432024
## kg                 0.7995169 0.17055891 0.6460296 0.7831325 0.1450151 0.7432024

Nun zur eigentlichen Multiple Imputation der Daten. Eine Übersicht über den Prozess finden Sie hier.

Wir starten mit einem einfachen Ansatz, bei dem alle Variablen benutzt werden, um die fehlenden Werte zu ersetzen. Wir lassen die Funktion mice auch auswählen, welche Regression sie benutzt, um die fehlenden Werte zu schätzen und ersetzen.

df_imputed<-mice(df_mar, seed = 123, m=10,print = FALSE)

Eventuelle Probleme können wir mit der folgenden Funktion analysieren. In unserem Fall gibt es jedoch keine Probleme, deswegen ist diese Tabelle leer. Siehe dazu mehr hier

df_problems<-df_imputed$loggedEvents
head(df_problems)
## NULL

Wir können auch anschauen, welche Methoden (Regressionen) für die einzelnen Variablen benutzt wurden.

DT::datatable(data.frame(df_imputed$method), filter='top')

Wir können jetzt mit der Funktion with die Analysen über die 10 Datensätze durchführen, und danach mit dem Befehl pool die Rubin’s Rule anwenden um das endgültige Resultat zu erhalten.
Wenn wir uns das Objekt fit anschauen, sehen wir, dass die Analysen in zehn Datensätzen durchgeführt wurde.

fit <- with(df_imputed, glm(falls~age+sex+bmi+multimorbidity+polipharmacy+gait_speed+squats+balance_alteration, family=binomial(link='logit')))
fit
## call :
## with.mids(data = df_imputed, expr = glm(falls ~ age + sex + bmi + 
##     multimorbidity + polipharmacy + gait_speed + squats + balance_alteration, 
##     family = binomial(link = "logit")))
## 
## call1 :
## mice(data = df_mar, m = 10, printFlag = FALSE, seed = 123)
## 
## nmis :
##          folio_num                age                sex     multimorbidity 
##                  0                  0                 41                166 
##       polipharmacy              falls                bmi         gait_speed 
##                124                  8                 31                 41 
##             squats balance_alteration             height                 kg 
##                 83                 83                 83                 83 
## 
## analyses :
## [[1]]
## 
## Call:  glm(formula = falls ~ age + sex + bmi + multimorbidity + polipharmacy + 
##     gait_speed + squats + balance_alteration, family = binomial(link = "logit"))
## 
## Coefficients:
##        (Intercept)                 age                 sex                 bmi  
##          -3.785766            0.039977           -0.726288            0.013702  
##     multimorbidity        polipharmacy          gait_speed              squats  
##           0.826579           -0.639937            0.004613           -0.012368  
## balance_alteration  
##          -0.064539  
## 
## Degrees of Freedom: 413 Total (i.e. Null);  405 Residual
## Null Deviance:       554.2 
## Residual Deviance: 523   AIC: 541
## 
## [[2]]
## 
## Call:  glm(formula = falls ~ age + sex + bmi + multimorbidity + polipharmacy + 
##     gait_speed + squats + balance_alteration, family = binomial(link = "logit"))
## 
## Coefficients:
##        (Intercept)                 age                 sex                 bmi  
##          -4.138216            0.041841           -0.545170            0.010572  
##     multimorbidity        polipharmacy          gait_speed              squats  
##           0.668470           -0.177426            0.006497           -0.016006  
## balance_alteration  
##          -0.137928  
## 
## Degrees of Freedom: 413 Total (i.e. Null);  405 Residual
## Null Deviance:       556.8 
## Residual Deviance: 535.5     AIC: 553.5
## 
## [[3]]
## 
## Call:  glm(formula = falls ~ age + sex + bmi + multimorbidity + polipharmacy + 
##     gait_speed + squats + balance_alteration, family = binomial(link = "logit"))
## 
## Coefficients:
##        (Intercept)                 age                 sex                 bmi  
##          -4.133251            0.047391           -0.641532            0.022666  
##     multimorbidity        polipharmacy          gait_speed              squats  
##           0.544273           -0.666359            0.003995           -0.038949  
## balance_alteration  
##          -0.184141  
## 
## Degrees of Freedom: 413 Total (i.e. Null);  405 Residual
## Null Deviance:       553.3 
## Residual Deviance: 528.9     AIC: 546.9
## 
## [[4]]
## 
## Call:  glm(formula = falls ~ age + sex + bmi + multimorbidity + polipharmacy + 
##     gait_speed + squats + balance_alteration, family = binomial(link = "logit"))
## 
## Coefficients:
##        (Intercept)                 age                 sex                 bmi  
##          -3.765091            0.044575           -0.652391            0.003752  
##     multimorbidity        polipharmacy          gait_speed              squats  
##           0.654738           -0.640950            0.003512           -0.004461  
## balance_alteration  
##           0.077900  
## 
## Degrees of Freedom: 413 Total (i.e. Null);  405 Residual
## Null Deviance:       555.9 
## Residual Deviance: 531.7     AIC: 549.7
## 
## [[5]]
## 
## Call:  glm(formula = falls ~ age + sex + bmi + multimorbidity + polipharmacy + 
##     gait_speed + squats + balance_alteration, family = binomial(link = "logit"))
## 
## Coefficients:
##        (Intercept)                 age                 sex                 bmi  
##          -4.061137            0.044571           -0.720282            0.008182  
##     multimorbidity        polipharmacy          gait_speed              squats  
##           0.506417           -0.120965            0.006373           -0.031066  
## balance_alteration  
##          -0.115978  
## 
## Degrees of Freedom: 413 Total (i.e. Null);  405 Residual
## Null Deviance:       553.3 
## Residual Deviance: 533   AIC: 551
## 
## [[6]]
## 
## Call:  glm(formula = falls ~ age + sex + bmi + multimorbidity + polipharmacy + 
##     gait_speed + squats + balance_alteration, family = binomial(link = "logit"))
## 
## Coefficients:
##        (Intercept)                 age                 sex                 bmi  
##          -4.403420            0.052771           -0.508234            0.013164  
##     multimorbidity        polipharmacy          gait_speed              squats  
##           0.520078           -0.379283            0.005483           -0.045783  
## balance_alteration  
##          -0.354434  
## 
## Degrees of Freedom: 413 Total (i.e. Null);  405 Residual
## Null Deviance:       554.2 
## Residual Deviance: 534.5     AIC: 552.5
## 
## [[7]]
## 
## Call:  glm(formula = falls ~ age + sex + bmi + multimorbidity + polipharmacy + 
##     gait_speed + squats + balance_alteration, family = binomial(link = "logit"))
## 
## Coefficients:
##        (Intercept)                 age                 sex                 bmi  
##           -4.26037             0.04669            -0.61072             0.01975  
##     multimorbidity        polipharmacy          gait_speed              squats  
##            0.63057            -0.51063             0.01053            -0.09262  
## balance_alteration  
##           -0.14208  
## 
## Degrees of Freedom: 413 Total (i.e. Null);  405 Residual
## Null Deviance:       554.2 
## Residual Deviance: 526.9     AIC: 544.9
## 
## [[8]]
## 
## Call:  glm(formula = falls ~ age + sex + bmi + multimorbidity + polipharmacy + 
##     gait_speed + squats + balance_alteration, family = binomial(link = "logit"))
## 
## Coefficients:
##        (Intercept)                 age                 sex                 bmi  
##          -4.095661            0.044825           -0.760440            0.010772  
##     multimorbidity        polipharmacy          gait_speed              squats  
##           0.675776           -0.309476            0.004273           -0.007910  
## balance_alteration  
##          -0.215617  
## 
## Degrees of Freedom: 413 Total (i.e. Null);  405 Residual
## Null Deviance:       554.2 
## Residual Deviance: 528.5     AIC: 546.5
## 
## [[9]]
## 
## Call:  glm(formula = falls ~ age + sex + bmi + multimorbidity + polipharmacy + 
##     gait_speed + squats + balance_alteration, family = binomial(link = "logit"))
## 
## Coefficients:
##        (Intercept)                 age                 sex                 bmi  
##          -3.722868            0.036855           -0.607606            0.007831  
##     multimorbidity        polipharmacy          gait_speed              squats  
##           0.848297           -0.186488            0.006143           -0.016953  
## balance_alteration  
##          -0.110817  
## 
## Degrees of Freedom: 413 Total (i.e. Null);  405 Residual
## Null Deviance:       555.1 
## Residual Deviance: 529   AIC: 547
## 
## [[10]]
## 
## Call:  glm(formula = falls ~ age + sex + bmi + multimorbidity + polipharmacy + 
##     gait_speed + squats + balance_alteration, family = binomial(link = "logit"))
## 
## Coefficients:
##        (Intercept)                 age                 sex                 bmi  
##          -4.161573            0.045826           -0.644825            0.014752  
##     multimorbidity        polipharmacy          gait_speed              squats  
##           1.328598           -0.578001            0.001097            0.016022  
## balance_alteration  
##          -0.585445  
## 
## Degrees of Freedom: 413 Total (i.e. Null);  405 Residual
## Null Deviance:       555.9 
## Residual Deviance: 511.7     AIC: 529.7

Jetzt benutzen wir den Befehl pool um die Resultate zusammenzufassen. Wir können jetzt die Analysen mit den ursprünglichen Analysen vergleichen, da wir ja in unserem Beispiel einen Datensatz haben, bei dem es noch keine fehlenden Werte gab.

est1 <- pool(fit)
summary(est1)
##                 term     estimate   std.error  statistic        df    p.value
## 1        (Intercept) -4.052735039 1.803940951 -2.2466007 391.04691 0.02522287
## 2                age  0.044531813 0.019274371  2.3104159 336.37996 0.02146974
## 3                sex -0.641749008 0.262812784 -2.4418485 257.64459 0.01528543
## 4                bmi  0.012514597 0.026289225  0.4760352 344.92909 0.63435066
## 5     multimorbidity  0.720379674 0.351392427  2.0500717  27.78156 0.04990216
## 6       polipharmacy -0.420951705 0.322300904 -1.3060829  32.96054 0.20056621
## 7         gait_speed  0.005251193 0.005635265  0.9318448 123.93964 0.35322839
## 8             squats -0.025008899 0.052055041 -0.4804318  55.23088 0.63281788
## 9 balance_alteration -0.183307390 0.336080958 -0.5454263  69.49355 0.58720681

Wir könnten auch alle 10 Datensätze mit den imputierten Werten anschauen:

DT::datatable(m_imputed_datasets<-df_imputed %>% 
  mice::complete("long"), filter='top')

64.2 Zweiter Ansatz

64.2.1 Schnelle Variablenauswahl zur Schätzung der fehlenden Werte

Wir können die mice Funktion quickpred benutzen, um schnell eine Auswahl von unabhängigen Variablen in den Modellen zur Schätzung der fehlenden Werte zu bekommen.

  • Zuerst definieren wir Variablen, die für jede Variable mit fehlenden Werten benutzt werden soll. In unserem Fall ist dies das Alter
inlist <- c("age")
  • Nun können wir die predictor-matrix erstellen. In dieser Matrix sind die Zeilen die zu imputierenden Variablen und die Spalten geben die Variablen an, die zum imputieren benutzt werden. Eine 0 in der Zelle bedeutet, dass für die Zeilenvariable die Spaltenvariable nicht benutzt wird, eine 1 bedeutet, dass die Spaltenvariable für die Zeilenvariable benutzt wird.

    Die Option minpuc gibt an, was die minimale Vollständigkeit sein muss, damit eine Variable benutzt wird.

pred <- quickpred(df_mar, minpuc = 0.5, include = inlist)
pred
##                    folio_num age sex multimorbidity polipharmacy falls bmi
## folio_num                  0   0   0              0            0     0   0
## age                        0   0   0              0            0     0   0
## sex                        0   1   0              0            1     1   0
## multimorbidity             0   1   0              0            1     1   1
## polipharmacy               0   1   1              0            0     0   0
## falls                      0   1   1              0            0     0   0
## bmi                        0   1   0              1            0     0   0
## gait_speed                 0   1   1              1            1     0   1
## squats                     1   1   1              0            1     0   0
## balance_alteration         0   1   0              1            1     0   1
## height                     0   1   0              1            1     0   1
## kg                         0   1   0              0            0     0   1
##                    gait_speed squats balance_alteration height kg
## folio_num                   0      0                  0      0  0
## age                         0      0                  0      0  0
## sex                         1      1                  1      0  0
## multimorbidity              1      0                  1      0  0
## polipharmacy                1      1                  1      0  0
## falls                       0      0                  0      0  0
## bmi                         1      0                  1      1  1
## gait_speed                  0      1                  1      0  1
## squats                      1      0                  1      0  1
## balance_alteration          1      1                  0      0  1
## height                      0      0                  0      0  1
## kg                          1      0                  1      1  0

Wir sehen jetzt, dass die ID-Variable folio_num nie imputiert wird (da sie ja nie fehlende Werte hat), aber einmal zur Imputation benutzt wird, nämlich für die Variable squats. Wir könnten jetzt in die entsprechende Zelle eine 0 setzen, da es ja in den meisten Fällen keinen Sinn macht, die ID-Nummer zur Imputation zu benutzen.

pred[9,1]<-0

pred
##                    folio_num age sex multimorbidity polipharmacy falls bmi
## folio_num                  0   0   0              0            0     0   0
## age                        0   0   0              0            0     0   0
## sex                        0   1   0              0            1     1   0
## multimorbidity             0   1   0              0            1     1   1
## polipharmacy               0   1   1              0            0     0   0
## falls                      0   1   1              0            0     0   0
## bmi                        0   1   0              1            0     0   0
## gait_speed                 0   1   1              1            1     0   1
## squats                     0   1   1              0            1     0   0
## balance_alteration         0   1   0              1            1     0   1
## height                     0   1   0              1            1     0   1
## kg                         0   1   0              0            0     0   1
##                    gait_speed squats balance_alteration height kg
## folio_num                   0      0                  0      0  0
## age                         0      0                  0      0  0
## sex                         1      1                  1      0  0
## multimorbidity              1      0                  1      0  0
## polipharmacy                1      1                  1      0  0
## falls                       0      0                  0      0  0
## bmi                         1      0                  1      1  1
## gait_speed                  0      1                  1      0  1
## squats                      1      0                  1      0  1
## balance_alteration          1      1                  0      0  1
## height                      0      0                  0      0  1
## kg                          1      0                  1      1  0

Weiter sehen wir, dass age für jede Variable benutzt wird (ausser natürlich für die vollständige Variable folio_num). Mit dieser predictor matrix können wir jetzt die unabhänigen Variablen der Modelle für jede Variable bestimmen.

64.3 Auswahl der Imputationsmodelle

Wir haben gesehen, wie wir die unäbhängigen Variablen in den Modellen bestimmen können (predictor-matrix), nun müssen wir noch lernen, wie wir die Art der Modelle bestimmen können. Geben wir die Methode im mice Befehl nicht explizit an, wählt mice eine Methode aus -was ja oft sehr gut funktioniert. Wir können jedoch mit der Option method für jede zu imputierende Variable ein spezifisches Modell angeben.

Eine längere Einführung zu diesem Thema finden sie hier.

The body mass index (BMI) can be cal- culated within mice by specifying the string ‘~I(weight/height^2)’ as the univariate imputation method for the target column data$bmi.

Wenn wir eine berechnete Variable haben, wie hier zum Beispiel die BMI Variable, dann müssen wir bei der Imputation darauf achten, dass der Zusammenhang zwischen den Variablen (hier: bmi, height und kg) auch in den imputierten Variablen stimmt. Dafür gibt es die passive Imputation. Mehr dazu hier und auch hier.

names(df_mar)
##  [1] "folio_num"          "age"                "sex"               
##  [4] "multimorbidity"     "polipharmacy"       "falls"             
##  [7] "bmi"                "gait_speed"         "squats"            
## [10] "balance_alteration" "height"             "kg"
df_imputed2<-mice(df_mar, meth = c("","", "logreg", "logreg","logreg", "logreg", "~I(kg/height^2)","pmm", "pmm", "logreg","pmm", "pmm"))
## Warning: Type mismatch for variable(s): sex
## Imputation method logreg is for categorical data.
## Warning: Type mismatch for variable(s): multimorbidity
## Imputation method logreg is for categorical data.
## Warning: Type mismatch for variable(s): polipharmacy
## Imputation method logreg is for categorical data.
## Warning: Type mismatch for variable(s): falls
## Imputation method logreg is for categorical data.
## Warning: Type mismatch for variable(s): balance_alteration
## Imputation method logreg is for categorical data.
## 
##  iter imp variable
##   1   1  sex  multimorbidity  polipharmacy  falls  bmi  gait_speed  squats  balance_alteration  height  kg
##   1   2  sex  multimorbidity  polipharmacy  falls  bmi  gait_speed  squats  balance_alteration  height  kg
##   1   3  sex  multimorbidity  polipharmacy  falls  bmi  gait_speed  squats  balance_alteration  height  kg
##   1   4  sex  multimorbidity  polipharmacy  falls  bmi  gait_speed  squats  balance_alteration  height  kg
##   1   5  sex  multimorbidity  polipharmacy  falls  bmi  gait_speed  squats  balance_alteration  height  kg
##   2   1  sex  multimorbidity  polipharmacy  falls  bmi  gait_speed  squats  balance_alteration  height  kg
##   2   2  sex  multimorbidity  polipharmacy  falls  bmi  gait_speed  squats  balance_alteration  height  kg
##   2   3  sex  multimorbidity  polipharmacy  falls  bmi  gait_speed  squats  balance_alteration  height  kg
##   2   4  sex  multimorbidity  polipharmacy  falls  bmi  gait_speed  squats  balance_alteration  height  kg
##   2   5  sex  multimorbidity  polipharmacy  falls  bmi  gait_speed  squats  balance_alteration  height  kg
##   3   1  sex  multimorbidity  polipharmacy  falls  bmi  gait_speed  squats  balance_alteration  height  kg
##   3   2  sex  multimorbidity  polipharmacy  falls  bmi  gait_speed  squats  balance_alteration  height  kg
##   3   3  sex  multimorbidity  polipharmacy  falls  bmi  gait_speed  squats  balance_alteration  height  kg
##   3   4  sex  multimorbidity  polipharmacy  falls  bmi  gait_speed  squats  balance_alteration  height  kg
##   3   5  sex  multimorbidity  polipharmacy  falls  bmi  gait_speed  squats  balance_alteration  height  kg
##   4   1  sex  multimorbidity  polipharmacy  falls  bmi  gait_speed  squats  balance_alteration  height  kg
##   4   2  sex  multimorbidity  polipharmacy  falls  bmi  gait_speed  squats  balance_alteration  height  kg
##   4   3  sex  multimorbidity  polipharmacy  falls  bmi  gait_speed  squats  balance_alteration  height  kg
##   4   4  sex  multimorbidity  polipharmacy  falls  bmi  gait_speed  squats  balance_alteration  height  kg
##   4   5  sex  multimorbidity  polipharmacy  falls  bmi  gait_speed  squats  balance_alteration  height  kg
##   5   1  sex  multimorbidity  polipharmacy  falls  bmi  gait_speed  squats  balance_alteration  height  kg
##   5   2  sex  multimorbidity  polipharmacy  falls  bmi  gait_speed  squats  balance_alteration  height  kg
##   5   3  sex  multimorbidity  polipharmacy  falls  bmi  gait_speed  squats  balance_alteration  height  kg
##   5   4  sex  multimorbidity  polipharmacy  falls  bmi  gait_speed  squats  balance_alteration  height  kg
##   5   5  sex  multimorbidity  polipharmacy  falls  bmi  gait_speed  squats  balance_alteration  height  kg

Jetzt haben wir noch einen Fehler gemacht: wir haben ein paar 0/1 Variablen, die wir mit einer logistischen Regression imputieren wollten. Dazu müssen wir jedoch die 0/1 Variablen als Faktor Variablen deklarieren. Die Imputation hat zwar auch so funktioniert, aber wir nehmen die Warnung ernst und tansformieren zuerst die Variablen und lassen die Imputation noch einmal laufen.

df_imputed2$method
##          folio_num                age                sex     multimorbidity 
##                 ""                 ""           "logreg"           "logreg" 
##       polipharmacy              falls                bmi         gait_speed 
##           "logreg"           "logreg"  "~I(kg/height^2)"              "pmm" 
##             squats balance_alteration             height                 kg 
##              "pmm"           "logreg"              "pmm"              "pmm"

Hier transformieren wir die entsprechenden Variablen zu Faktor-Variablen.

df_mar<-df_mar %>% 
  mutate(across(c(sex, multimorbidity, polipharmacy, falls, balance_alteration), factor))

Nun wollen wir auch noch die prediction matrix ändern. Wir möchten nicht, dass die Variable folio_num benutzt wird um andere Variablen (hier die Squat-Variable) zu imputieren. Wir könnten auch noch andere Variablen ändern.

pred <- quickpred(df_mar, minpuc = 0.5, include = inlist)
pred
##                    folio_num age sex multimorbidity polipharmacy falls bmi
## folio_num                  0   0   0              0            0     0   0
## age                        0   0   0              0            0     0   0
## sex                        0   1   0              0            1     1   0
## multimorbidity             0   1   0              0            1     1   1
## polipharmacy               0   1   1              0            0     0   0
## falls                      0   1   1              0            0     0   0
## bmi                        0   1   0              1            0     0   0
## gait_speed                 0   1   1              1            1     0   1
## squats                     1   1   1              0            1     0   0
## balance_alteration         0   1   0              1            1     0   1
## height                     0   1   0              1            1     0   1
## kg                         0   1   0              0            0     0   1
##                    gait_speed squats balance_alteration height kg
## folio_num                   0      0                  0      0  0
## age                         0      0                  0      0  0
## sex                         1      1                  1      0  0
## multimorbidity              1      0                  1      0  0
## polipharmacy                1      1                  1      0  0
## falls                       0      0                  0      0  0
## bmi                         1      0                  1      1  1
## gait_speed                  0      1                  1      0  1
## squats                      1      0                  1      0  1
## balance_alteration          1      1                  0      0  1
## height                      0      0                  0      0  1
## kg                          1      0                  1      1  0
pred[9,1]<-0
pred
##                    folio_num age sex multimorbidity polipharmacy falls bmi
## folio_num                  0   0   0              0            0     0   0
## age                        0   0   0              0            0     0   0
## sex                        0   1   0              0            1     1   0
## multimorbidity             0   1   0              0            1     1   1
## polipharmacy               0   1   1              0            0     0   0
## falls                      0   1   1              0            0     0   0
## bmi                        0   1   0              1            0     0   0
## gait_speed                 0   1   1              1            1     0   1
## squats                     0   1   1              0            1     0   0
## balance_alteration         0   1   0              1            1     0   1
## height                     0   1   0              1            1     0   1
## kg                         0   1   0              0            0     0   1
##                    gait_speed squats balance_alteration height kg
## folio_num                   0      0                  0      0  0
## age                         0      0                  0      0  0
## sex                         1      1                  1      0  0
## multimorbidity              1      0                  1      0  0
## polipharmacy                1      1                  1      0  0
## falls                       0      0                  0      0  0
## bmi                         1      0                  1      1  1
## gait_speed                  0      1                  1      0  1
## squats                      1      0                  1      0  1
## balance_alteration          1      1                  0      0  1
## height                      0      0                  0      0  1
## kg                          1      0                  1      1  0
names(df_mar)
df_imputed<-mice(df_mar, m=10, meth = c("","", "logreg", "logreg","logreg", "logreg", "~I(kg/height^2)","pmm", "pmm", "logreg","pmm", "pmm"), predictorMatrix = pred)

Schauen wir uns die imputierten Daten an.

DT::datatable(m_imputed_datasets<-df_imputed %>% 
  mice::complete("long"), filter='top')
densityplot(df_imputed)

Jetzt berechnen wir die Regression noch einmal und vergleichen wieder mit dem originalen Datensatz - der ja keine fehlenden Werte aufwies.

Wir sehen, dass die Odds Ratios des imputierten Modells näher beim wahren Modell sind als die Odds Ratios der Analyse mit den fehlenden Werten. Leider können wir das imputierte Modell nicht gleich darstellen, wie die beiden anderen Modelle.

  • Im rosaroten Kasten: Modell mit den imputierten Werten
  • glm.fit.true: Das ursprüngliche Modell, bei dem keine Werte fehlten (was wir für diese Übung als wahres Resultat bezeichnen)
  • glm.fit_mar: Modell mit den Missing At Random Daten
fit <- with(df_imputed, glm(falls~age+sex+bmi+multimorbidity+polipharmacy+gait_speed+squats+balance_alteration, family=binomial(link='logit')))
est.imputed<-pool(fit)
summary(est.imputed, exp=TRUE)
##                  term   estimate   std.error  statistic        df    p.value
## 1         (Intercept) 0.01777602 1.797517398 -2.2419283 348.72826 0.02559425
## 2                 age 1.04632031 0.018902253  2.3954573 339.22350 0.01714125
## 3                sex1 0.53782610 0.256763357 -2.4155316 280.67825 0.01635116
## 4                 bmi 1.01157205 0.024867350  0.4626791 323.54240 0.64390544
## 5     multimorbidity1 1.72429317 0.361651463  1.5064704  23.46381 0.14529211
## 6       polipharmacy1 0.76761220 0.257303537 -1.0278546 113.68029 0.30620029
## 7          gait_speed 1.00304416 0.005282272  0.5754213 267.28436 0.56549050
## 8              squats 0.99465054 0.045311062 -0.1183777 198.17026 0.90588827
## 9 balance_alteration1 0.81477130 0.314137040 -0.6520970 106.84925 0.51573894
jtools::summ(glm.fit.true, exp=TRUE, confint=TRUE)
Observations 414
Dependent variable falls
Type Generalized linear model
Family binomial
Link logit
χ²(8) 15.14
Pseudo-R² (Cragg-Uhler) 0.05
Pseudo-R² (McFadden) 0.03
AIC 557.93
BIC 594.17
exp(Est.) 2.5% 97.5% z val. p
(Intercept) 0.02 0.00 0.54 -2.30 0.02
age 1.05 1.01 1.09 2.51 0.01
sex 0.61 0.38 0.98 -2.06 0.04
bmi 1.02 0.97 1.07 0.59 0.56
multimorbidity 1.38 0.87 2.19 1.35 0.18
polipharmacy 1.10 0.72 1.68 0.44 0.66
gait_speed 1.00 0.99 1.01 0.85 0.40
squats 0.97 0.89 1.05 -0.84 0.40
balance_alteration 0.74 0.44 1.25 -1.12 0.26
Standard errors: MLE
jtools::summ(glm.fit_mar, exp=TRUE, confint=TRUE)
Observations 109 (305 missing obs. deleted)
Dependent variable falls
Type Generalized linear model
Family binomial
Link logit
χ²(8) 11.38
Pseudo-R² (Cragg-Uhler) 0.14
Pseudo-R² (McFadden) 0.08
AIC 147.58
BIC 171.80
exp(Est.) 2.5% 97.5% z val. p
(Intercept) 0.00 0.00 6.75 -1.49 0.14
age 1.05 0.95 1.15 0.97 0.33
sex 0.36 0.12 1.08 -1.82 0.07
bmi 1.04 0.93 1.17 0.68 0.50
multimorbidity 2.75 1.06 7.13 2.09 0.04
polipharmacy 0.65 0.24 1.78 -0.84 0.40
gait_speed 1.01 0.98 1.03 0.48 0.63
squats 1.08 0.89 1.30 0.79 0.43
balance_alteration 0.71 0.17 2.95 -0.47 0.64
Standard errors: MLE