--- title: "Missing Data (Part 2)" description: | A deeper look at missing data, imputation, and characterization. author: - name: Kris Sankaran affiliation: UW Madison date: 02-22-2021 output: distill::distill_article: self_contained: false --- _[Reading](https://naniar.njtierney.com/articles/getting-started-w-naniar.html), [Recording](https://mediaspace.wisc.edu/media/Week%205%20%5B2%5D%20Missing%20Data%20(Part%202)/1_0fwe9l8o), [Rmarkdown](https://raw.githubusercontent.com/krisrs1128/stat479/master/_posts/2021-01-30-week5-2/week5-2.Rmd)_ ```{r setup, include=FALSE} knitr::opts_chunk$set(cache = FALSE, message = FALSE, warning = FALSE, echo = TRUE) ``` ```{r} library("MASS") library("dplyr") library("ggplot2") library("naniar") library("rpart") library("simputation") ``` 1. The previous notes described how visualization can help quantify how much missingness if present, and where it occurs. Here, we will explore how visualization is also helpful in efforts to impute and characterize missing values. 2. For example, visualization is helpful for understanding the results of imputation algorithms. Before describing this, it will be helpful to have a crash course on missing data imputation. 3. Imputation algorithms try to replace all the missing values with plausible values. The reason we might do this is that we don’t want to discard all the observations with the missing values, but our usual models might throw errors if we provide data with missing values. 4. Median imputation replaces each missing values in any given column by the median of the observed values in that field. It works one column at a time. ```{r} x <- data.frame(value = c(rnorm(450), rep(NA, 50))) %>% sample_frac(1) %>% # randomly reorder bind_shadow() %>% # create column checking if missing mutate(imputed = naniar::impute_median(value)) x ``` ```{r} ggplot(x) + geom_histogram(aes(x = imputed, fill = value_NA)) + scale_fill_brewer(palette = "Set2") ``` 5. We can be cleverer though, if we imagine that multiple columns are related in some way. Consider the scatterplot below. When both observations are present, a point is drawn along the middle. When there is a missing value in one of the columns, we plot the one dimension that we do have along the appropriate edge. How would you impute the second column if you knew its value in the first column was low? ```{r} Sigma <- matrix(c(1, 0.9, 0.9, 1), 2) x <- mvrnorm(500, c(0, 0), Sigma = Sigma) %>% as.data.frame() for (j in seq_len(2)) { x[sample(nrow(x), 50), j] <- NA } ggplot(x) + geom_miss_point(aes(x = V1, y = V2), jitter = 0) + scale_color_brewer(palette = "Set2") + coord_fixed() ``` You would probably project the missing values onto the line that goes through the bulk of the fully observed data. ```{r, echo = FALSE} x0 <- nabular(x) %>% filter(is.na(V1) | is.na(V2)) x0[is.na(x0$V1), "V1"] <- x0[is.na(x0$V1), "V2"] x0[is.na(x0$V2), "V2"] <- x0[is.na(x0$V2), "V1"] ggplot(x) + geom_miss_point(aes(x = V1, y = V2), jitter = 0) + geom_abline(slope = 1, color = "#0c0c0c") + geom_point(data = x0, aes(x = V1, y = V2), col = "#66c2a5") + geom_segment( data = x0 %>% filter(V2_NA == "NA"), aes(x = V1, xend = V1, y = -3.4, yend = V2), col = "#66c2a5", size = 0.2, alpha = 0.8 ) + geom_segment( data = x0 %>% filter(V1_NA == "NA"), aes(x = -3.2, xend = V1, y = V2, yend = V2), col = "#66c2a5", size = 0.2, alpha = 0.8 ) + scale_color_brewer(palette = "Set2") + coord_fixed() ``` 6. Multiple imputation methods formalize this intuition. They make use of multivariate relationships between columns to guess plausible values for missing data. This is an example of multiple imputation on the `airquality` dataset -- the multivariate relationship between ozone, temperature, and wind is used to fill in missing values for ozone. ```{r} aq_imputed <- airquality %>% bind_shadow() %>% as.data.frame() %>% impute_lm(Ozone ~ Temp + Wind) ggplot(aq_imputed) + geom_point(aes(x = Temp, y = Ozone)) + scale_color_brewer(palette = "Set2") ``` 7. But how can we tell if a multiple imputation method is effective? We can plot the data, making sure to distinguish between true and imputed observations. ```{r} ggplot(aq_imputed) + geom_point(aes(x = Temp, y = Ozone, col = Ozone_NA)) + scale_color_brewer(palette = "Set2") ``` 8. Finally, let’s ask, why are the data missing in the first place? A natural idea is to try to find characteristics of observations that are predictive of their having missing values for some particular field. For example, maybe respondents within a given age group always leave a question blank. To this end, a well chosen plot can be very suggestive. 9. We can illustrate this idea with the `airquality` data. It seems that the sensor might have broken down in June. When there are very many possible variables to compare against, a model can be especially helpful for guiding the search for informative plots. We'll develop this idea further two lectures from now, when we look at how mutual information is used to guide the visualization search in Profiler. ```{r} rpart_model <- airquality %>% add_prop_miss() %>% rpart(prop_miss_all ~ ., data = .) rpart_model$variable.importance ``` ```{r} ggplot(airquality) + geom_miss_point(aes(x = Ozone, y = Solar.R)) + scale_color_brewer(palette = "Set2") + facet_wrap(~ Month) ``` 10. If we can predict missingness well, then the data are *not* missing at random. The bad news is that more specialized imputation strategies may be necessary. The good news is that we may be able to actually characterize the mechanism behind the missing data.