├── .Rbuildignore ├── .gitignore ├── .travis.yml ├── DESCRIPTION ├── LICENSE ├── LICENSE.md ├── NAMESPACE ├── R ├── glances.R ├── mputr-package.R ├── nobs.R ├── tidiers.R └── utils.R ├── README.Rmd ├── README.md ├── appveyor.yml ├── inst └── explore.Rmd ├── man ├── glance.melded.Rd ├── mputr.Rd ├── nobs.melded.Rd ├── pipe.Rd ├── reexports.Rd └── tidy.melded.Rd └── mputr.Rproj /.Rbuildignore: -------------------------------------------------------------------------------- 1 | ^.*\.Rproj$ 2 | ^\.Rproj\.user$ 3 | ^README\.Rmd$ 4 | ^LICENSE\.md$ 5 | ^\.travis\.yml$ 6 | ^appveyor\.yml$ 7 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | inst/doc 2 | .Rproj.user 3 | .Rhistory 4 | .RData 5 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | # R for travis: see documentation at https://docs.travis-ci.com/user/languages/r 2 | 3 | language: R 4 | cache: packages 5 | -------------------------------------------------------------------------------- /DESCRIPTION: -------------------------------------------------------------------------------- 1 | Package: mputr 2 | Title: Tidy Approaches for Multiple Imputation 3 | Version: 0.0.0.9000 4 | Authors@R: c( 5 | person(given = "Nicholas", 6 | family = "Tierney", 7 | email = "nicholas.tierney@gmail.com", 8 | role = c("aut", "cre"), 9 | comment = c(ORCID = "https://orcid.org/0000-0003-1460-8722")), 10 | person(given = "Andrew", 11 | family = "Heiss", 12 | role = c("aut"), 13 | email = "andrew@andrewheiss.com", 14 | comment = c(ORCID = "0000-0002-3948-3914")) 15 | ) 16 | Description: An experimental package to provide broom tidier functions for 17 | multiply imputed data. 18 | Depends: R (>= 3.5) 19 | License: MIT + file LICENSE 20 | Encoding: UTF-8 21 | LazyData: true 22 | Suggests: 23 | knitr, 24 | rmarkdown 25 | VignetteBuilder: knitr 26 | Imports: 27 | purrr, 28 | dplyr, 29 | tidyr, 30 | Amelia, 31 | broom, 32 | generics, 33 | magrittr, 34 | stats, 35 | tibble, 36 | forcats 37 | RoxygenNote: 6.1.1 38 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | YEAR: 2019 2 | COPYRIGHT HOLDER: Nicholas Tierney 3 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | # MIT License 2 | 3 | Copyright (c) 2019 Nicholas Tierney 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /NAMESPACE: -------------------------------------------------------------------------------- 1 | # Generated by roxygen2: do not edit by hand 2 | 3 | S3method(glance,melded) 4 | S3method(nobs,melded) 5 | S3method(tidy,melded) 6 | export("%>%") 7 | export(augment) 8 | export(glance) 9 | export(tidy) 10 | importFrom(generics,augment) 11 | importFrom(generics,glance) 12 | importFrom(generics,tidy) 13 | importFrom(magrittr,"%>%") 14 | importFrom(stats,nobs) 15 | -------------------------------------------------------------------------------- /R/glances.R: -------------------------------------------------------------------------------- 1 | #' Summarise Amelia's multiple imputations 2 | #' 3 | #' A broom glance summary for multiple imputations from Andrew's blog post: 4 | #' https://www.andrewheiss.com/blog/2018/03/07/amelia-tidy-melding/ 5 | #' Andrews note: This means properly melded parameters and the simple average 6 | #' of the parameters of these models are roughly the same sake of simplicty 7 | #' we just take the average here. 8 | #' 9 | #' @param x Amelia melded imputed data 10 | #' @param ... other arguments passed to methods 11 | #' 12 | #' @return one line dataframe summary of the multiply imputed data 13 | #' @export 14 | glance.melded <- function(x, ...) { 15 | output <- tibble::tibble(models = x) %>% 16 | dplyr::mutate(glance = models %>% purrr::map(broom::glance)) %>% 17 | tidyr::unnest(glance) %>% 18 | dplyr::summarise_at(.vars = dplyr::vars(r.squared, 19 | adj.r.squared, 20 | sigma, 21 | statistic, 22 | p.value, 23 | df, 24 | logLik, 25 | AIC, 26 | BIC, 27 | deviance, 28 | df.residual), 29 | .funs = list(mean = mean)) %>% 30 | dplyr::mutate(m = as.integer(length(x))) 31 | 32 | # glance objects only have a data.frame class, not tbl_df or anything else 33 | class(output) <- "data.frame" 34 | output 35 | } 36 | -------------------------------------------------------------------------------- /R/mputr-package.R: -------------------------------------------------------------------------------- 1 | #' mputr 2 | #' 3 | #' @name mputr 4 | #' @docType package 5 | NULL 6 | 7 | if(getRversion() >= "2.15.1") utils::globalVariables(c(".")) 8 | globalVariables(c("AIC", 9 | "BIC", 10 | "key", 11 | "r.squared", 12 | "adj.r.squared", 13 | "tidied", 14 | "statistic", 15 | "std.error", 16 | "value", 17 | "m", 18 | "term", 19 | "estimate", 20 | "models", 21 | "p.value", 22 | "deviance", 23 | "df", 24 | "df.residual", 25 | "logLik", 26 | "sigma")) 27 | -------------------------------------------------------------------------------- /R/nobs.R: -------------------------------------------------------------------------------- 1 | #' Take the number of observations from the first Amelia melded model 2 | #' 3 | #' @param object Amelia melded model 4 | #' @param ... other options to pass to `nobs` 5 | #' 6 | #' @return integer 7 | #' @importFrom stats nobs 8 | #' @method nobs melded 9 | #' @export 10 | nobs.melded <- function(object, ...) { 11 | stats::nobs(object[[1]]) 12 | } 13 | -------------------------------------------------------------------------------- /R/tidiers.R: -------------------------------------------------------------------------------- 1 | #' Tidy summaries for melded data 2 | #' 3 | #' This is an approach for tidying Amelia imputed dataframes from Andrew Heiss 4 | #' 5 | #' 6 | #' @param x melded object from Amelia 7 | #' @param ... other arguments passed to methods 8 | #' @param conf.int Return confidence intervals. Default is FALSE 9 | #' @param conf.level Confidence level (if confidence intervals are use). 10 | #' Default is 0.95. 11 | #' 12 | #' @return tidy dataframe with pooled estimates from imputed data 13 | #' @export 14 | tidy.melded <- function(x, 15 | ..., 16 | conf.int = FALSE, 17 | conf.level = 0.95) { 18 | # Get the df from one of the models 19 | model_degrees_freedom <- broom::glance(x[[1]])$df.residual 20 | 21 | # Create matrices of the estimates and standard errors 22 | params <- tibble::tibble(models = x) %>% 23 | dplyr::mutate(m = 1:dplyr::n(), 24 | tidied = models %>% purrr::map(tidy)) %>% 25 | tidyr::unnest(tidied) %>% 26 | dplyr::select(m, 27 | term, 28 | estimate, 29 | std.error) %>% 30 | tidyr::gather(key, 31 | value, 32 | estimate, 33 | std.error) %>% 34 | # Order the terms so that spread() keeps them in order 35 | dplyr::mutate(term = forcats::fct_inorder(term)) %>% 36 | tidyr::spread(term, value) 37 | 38 | just_coefs <- params %>% 39 | dplyr::filter(key == "estimate") %>% 40 | dplyr::select(-m, -key) 41 | 42 | just_ses <- params %>% 43 | dplyr::filter(key == "std.error") %>% 44 | dplyr::select(-m, -key) 45 | 46 | # Meld the coefficients with Rubin's rules 47 | coefs_melded <- Amelia::mi.meld(just_coefs, just_ses) 48 | 49 | # Create tidy output 50 | output <- as.data.frame(cbind(t(coefs_melded$q.mi), 51 | t(coefs_melded$se.mi))) %>% 52 | purrr::set_names(nm = c("estimate", "std.error")) %>% 53 | tibble::rownames_to_column(var = "term") %>% 54 | # dplyr::mutate(term = rownames(.)) %>% 55 | dplyr::select(term, dplyr::everything()) %>% 56 | dplyr::mutate(statistic = estimate / std.error, 57 | p.value = 2 * stats::pt(abs(statistic), 58 | model_degrees_freedom, 59 | lower.tail = FALSE)) 60 | 61 | # Add confidence intervals if needed 62 | if (conf.int & conf.level) { 63 | # Convert conf.level to tail values (0.025 when it's 0.95) 64 | a <- (1 - conf.level) / 2 65 | 66 | output <- output %>% 67 | dplyr::mutate( 68 | conf.low = estimate + std.error * stats::qt(a, model_degrees_freedom), 69 | conf.high = estimate + std.error * stats::qt((1 - a), model_degrees_freedom) 70 | ) 71 | } 72 | 73 | # tidy objects only have a data.frame class, not tbl_df or anything else 74 | class(output) <- "data.frame" 75 | output 76 | } 77 | -------------------------------------------------------------------------------- /R/utils.R: -------------------------------------------------------------------------------- 1 | #' Pipe operator 2 | #' 3 | #' See \code{magrittr::\link[magrittr]{\%>\%}} for details. 4 | #' 5 | #' @name %>% 6 | #' @rdname pipe 7 | #' @keywords internal 8 | #' @export 9 | #' @importFrom magrittr %>% 10 | #' @usage lhs \%>\% rhs 11 | NULL 12 | 13 | #' @importFrom generics tidy 14 | #' @export 15 | generics::tidy 16 | 17 | #' @importFrom generics glance 18 | #' @export 19 | generics::glance 20 | 21 | #' @importFrom generics augment 22 | #' @export 23 | generics::augment 24 | -------------------------------------------------------------------------------- /README.Rmd: -------------------------------------------------------------------------------- 1 | --- 2 | output: github_document 3 | --- 4 | 5 | 6 | 7 | ```{r, include = FALSE} 8 | knitr::opts_chunk$set( 9 | collapse = TRUE, 10 | comment = "#>", 11 | fig.path = "man/figures/README-", 12 | out.width = "100%" 13 | ) 14 | ``` 15 | # mputr 16 | 17 | 18 | [![Lifecycle: experimental](https://img.shields.io/badge/lifecycle-experimental-orange.svg)](https://www.tidyverse.org/lifecycle/#experimental) 19 | [![Travis build status](https://travis-ci.org/njtierney/mputr.svg?branch=master)](https://travis-ci.org/njtierney/mputr) 20 | [![AppVeyor build status](https://ci.appveyor.com/api/projects/status/github/njtierney/mputr?branch=master&svg=true)](https://ci.appveyor.com/project/njtierney/mputr) 21 | 22 | 23 | The goal of mputr is to make it easier to deal with multiple imputation. 24 | 25 | It is an **experimental package** that may end up being worked back into `naniar`. 26 | 27 | It's goals are to: 28 | 29 | * Provide broom tidiers for multiple imputation objects (based on Andrew Heiss's work [here](https://www.andrewheiss.com/blog/2018/03/07/amelia-tidy-melding/), and [here](https://www.andrewheiss.com/blog/2018/03/08/amelia-broom-huxtable/)) 30 | * Provide helpers for exploring multiple imputations 31 | * Potentially provide an interface similar to `simputation`, but for multiple imputation. 32 | 33 | 34 | ## Installation 35 | 36 | ``` r 37 | # install.packages("remotes") 38 | remotes::install_github("njtierney/mputr") 39 | ``` 40 | ] 41 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | # mputr 5 | 6 | 7 | 8 | [![Lifecycle: 9 | experimental](https://img.shields.io/badge/lifecycle-experimental-orange.svg)](https://www.tidyverse.org/lifecycle/#experimental) 10 | [![Travis build 11 | status](https://travis-ci.org/njtierney/mputr.svg?branch=master)](https://travis-ci.org/njtierney/mputr) 12 | [![AppVeyor build 13 | status](https://ci.appveyor.com/api/projects/status/github/njtierney/mputr?branch=master&svg=true)](https://ci.appveyor.com/project/njtierney/mputr) 14 | 15 | 16 | The goal of mputr is to make it easier to deal with multiple imputation. 17 | 18 | It is an **experimental package** that may end up being worked back into 19 | `naniar`. 20 | 21 | It’s goals are to: 22 | 23 | - Provide broom tidiers for multiple imputation objects (based on 24 | Andrew Heiss’s work 25 | [here](https://www.andrewheiss.com/blog/2018/03/07/amelia-tidy-melding/), 26 | and 27 | [here](https://www.andrewheiss.com/blog/2018/03/08/amelia-broom-huxtable/)) 28 | - Provide helpers for exploring multiple imputations 29 | - Potentially provide an interface similar to `simputation`, but for 30 | multiple imputation. 31 | 32 | ## Installation 33 | 34 | ``` r 35 | # install.packages("remotes") 36 | remotes::install_github("njtierney/mputr") 37 | ``` 38 | 39 | \] 40 | -------------------------------------------------------------------------------- /appveyor.yml: -------------------------------------------------------------------------------- 1 | # DO NOT CHANGE the "init" and "install" sections below 2 | 3 | # Download script file from GitHub 4 | init: 5 | ps: | 6 | $ErrorActionPreference = "Stop" 7 | Invoke-WebRequest http://raw.github.com/krlmlr/r-appveyor/master/scripts/appveyor-tool.ps1 -OutFile "..\appveyor-tool.ps1" 8 | Import-Module '..\appveyor-tool.ps1' 9 | 10 | install: 11 | ps: Bootstrap 12 | 13 | cache: 14 | - C:\RLibrary 15 | 16 | environment: 17 | NOT_CRAN: true 18 | # env vars that may need to be set, at least temporarily, from time to time 19 | # see https://github.com/krlmlr/r-appveyor#readme for details 20 | # USE_RTOOLS: true 21 | # R_REMOTES_STANDALONE: true 22 | 23 | # Adapt as necessary starting from here 24 | 25 | build_script: 26 | - travis-tool.sh install_deps 27 | 28 | test_script: 29 | - travis-tool.sh run_tests 30 | 31 | on_failure: 32 | - 7z a failure.zip *.Rcheck\* 33 | - appveyor PushArtifact failure.zip 34 | 35 | artifacts: 36 | - path: '*.Rcheck\**\*.log' 37 | name: Logs 38 | 39 | - path: '*.Rcheck\**\*.out' 40 | name: Logs 41 | 42 | - path: '*.Rcheck\**\*.fail' 43 | name: Logs 44 | 45 | - path: '*.Rcheck\**\*.Rout' 46 | name: Logs 47 | 48 | - path: '\*_*.tar.gz' 49 | name: Bits 50 | 51 | - path: '\*_*.zip' 52 | name: Bits 53 | -------------------------------------------------------------------------------- /inst/explore.Rmd: -------------------------------------------------------------------------------- 1 | --- 2 | title: "explore" 3 | output: rmarkdown::html_vignette 4 | vignette: > 5 | %\VignetteIndexEntry{explore} 6 | %\VignetteEngine{knitr::rmarkdown} 7 | %\VignetteEncoding{UTF-8} 8 | --- 9 | 10 | ```{r, include = FALSE} 11 | knitr::opts_chunk$set( 12 | collapse = TRUE, 13 | comment = "#>" 14 | ) 15 | ``` 16 | 17 | ```{r setup} 18 | library(tidyverse) 19 | library(Amelia) 20 | library(broom) 21 | library(naniar) 22 | 23 | # Use the africa dataset from Ameila 24 | data(africa) 25 | 26 | # Build some example models 27 | model_original1 <- lm(gdp_pc ~ trade + civlib, data = africa) 28 | model_original2 <- lm(gdp_pc ~ trade + civlib + infl, data = africa) 29 | 30 | set.seed(1234) 31 | 32 | imp_amelia <- 33 | amelia( 34 | x = africa, 35 | m = 5, 36 | cs = "country", 37 | ts = "year", 38 | logs = "gdp_pc", 39 | p2s = 0 40 | ) 41 | 42 | shadow_africa <- as_shadow(africa) 43 | 44 | bind_row_n <- function(data, n){ 45 | 46 | tibble::as_tibble(data[rep(seq_len(nrow(data)), n), ]) 47 | 48 | } 49 | 50 | bind_row_n(africa, 2) 51 | 52 | # helper to bind the original shadow data `m` times 53 | # adds .m option to as_shadow 54 | africa %>% 55 | as_shadow() %>% 56 | bind_row_n(10) %>% 57 | mutate(m = rep("imp{1}")) 58 | 59 | models_imputed_df <- 60 | bind_rows(unclass(imp_amelia$imputations), .id = "m") %>% 61 | group_by(m) %>% 62 | bind_cols(shadow_africa) 63 | nest() %>% 64 | mutate(model = data %>% map( ~ lm(gdp_pc ~ trade + civlib, data = .))) 65 | 66 | models_imputed_df 67 | ``` 68 | -------------------------------------------------------------------------------- /man/glance.melded.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/glances.R 3 | \name{glance.melded} 4 | \alias{glance.melded} 5 | \title{Summarise Amelia's multiple imputations} 6 | \usage{ 7 | \method{glance}{melded}(x, ...) 8 | } 9 | \arguments{ 10 | \item{x}{Amelia melded imputed data} 11 | 12 | \item{...}{other arguments passed to methods} 13 | } 14 | \value{ 15 | one line dataframe summary of the multiply imputed data 16 | } 17 | \description{ 18 | A broom glance summary for multiple imputations from Andrew's blog post: 19 | https://www.andrewheiss.com/blog/2018/03/07/amelia-tidy-melding/ 20 | Andrews note: This means properly melded parameters and the simple average 21 | of the parameters of these models are roughly the same sake of simplicty 22 | we just take the average here. 23 | } 24 | -------------------------------------------------------------------------------- /man/mputr.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/mputr-package.R 3 | \docType{package} 4 | \name{mputr} 5 | \alias{mputr} 6 | \alias{mputr-package} 7 | \title{mputr} 8 | \description{ 9 | mputr 10 | } 11 | -------------------------------------------------------------------------------- /man/nobs.melded.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/nobs.R 3 | \name{nobs.melded} 4 | \alias{nobs.melded} 5 | \title{Take the number of observations from the first Amelia melded model} 6 | \usage{ 7 | \method{nobs}{melded}(object, ...) 8 | } 9 | \arguments{ 10 | \item{object}{Amelia melded model} 11 | 12 | \item{...}{other options to pass to `nobs`} 13 | } 14 | \value{ 15 | integer 16 | } 17 | \description{ 18 | Take the number of observations from the first Amelia melded model 19 | } 20 | -------------------------------------------------------------------------------- /man/pipe.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/utils.R 3 | \name{\%>\%} 4 | \alias{\%>\%} 5 | \title{Pipe operator} 6 | \usage{ 7 | lhs \%>\% rhs 8 | } 9 | \description{ 10 | See \code{magrittr::\link[magrittr]{\%>\%}} for details. 11 | } 12 | \keyword{internal} 13 | -------------------------------------------------------------------------------- /man/reexports.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/utils.R 3 | \docType{import} 4 | \name{reexports} 5 | \alias{reexports} 6 | \alias{tidy} 7 | \alias{glance} 8 | \alias{augment} 9 | \title{Objects exported from other packages} 10 | \keyword{internal} 11 | \description{ 12 | These objects are imported from other packages. Follow the links 13 | below to see their documentation. 14 | 15 | \describe{ 16 | \item{generics}{\code{\link[generics]{tidy}}, \code{\link[generics]{glance}}, \code{\link[generics]{augment}}} 17 | }} 18 | 19 | -------------------------------------------------------------------------------- /man/tidy.melded.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/tidiers.R 3 | \name{tidy.melded} 4 | \alias{tidy.melded} 5 | \title{Tidy summaries for melded data} 6 | \usage{ 7 | \method{tidy}{melded}(x, ..., conf.int = FALSE, conf.level = 0.95) 8 | } 9 | \arguments{ 10 | \item{x}{melded object from Amelia} 11 | 12 | \item{...}{other arguments passed to methods} 13 | 14 | \item{conf.int}{Return confidence intervals. Default is FALSE} 15 | 16 | \item{conf.level}{Confidence level (if confidence intervals are use). 17 | Default is 0.95.} 18 | } 19 | \value{ 20 | tidy dataframe with pooled estimates from imputed data 21 | } 22 | \description{ 23 | This is an approach for tidying Amelia imputed dataframes from Andrew Heiss 24 | 25 | } 26 | -------------------------------------------------------------------------------- /mputr.Rproj: -------------------------------------------------------------------------------- 1 | Version: 1.0 2 | 3 | RestoreWorkspace: No 4 | SaveWorkspace: No 5 | AlwaysSaveHistory: Default 6 | 7 | EnableCodeIndexing: Yes 8 | UseSpacesForTab: Yes 9 | NumSpacesForTab: 2 10 | Encoding: UTF-8 11 | 12 | RnwWeave: knitr 13 | LaTeX: pdfLaTeX 14 | 15 | AutoAppendNewline: Yes 16 | StripTrailingWhitespace: Yes 17 | 18 | BuildType: Package 19 | PackageUseDevtools: Yes 20 | PackageInstallArgs: --no-multiarch --with-keep.source 21 | PackageRoxygenize: rd,collate,namespace 22 | --------------------------------------------------------------------------------