--- date: "`r Sys.Date()`" title: "Recoding to and from NA with mde" output: html_document vignette: > %\VignetteIndexEntry{recoding-to-from-na} %\VignetteEngine{knitr::rmarkdown} %\VignetteEncoding{UTF-8} resource_files: - man/figures/mde_icon_2.png --- ```{r, include = FALSE} knitr::opts_chunk$set( collapse = TRUE, comment = "#>" ) ``` The goal of `mde` is to ease exploration of missingness. **Loading the package** ```{r} library(mde) ``` # Recoding as NA * `recode_as_na` As the name might imply, this converts any value or vector of values to `NA` i.e. we take a value such as "missing" or "NA" (not a real `NA` according to `R`) and convert it to R's known handler for missing values (`NA`). To use the function out of the box (with default arguments), one simply does something like: ```{r} dummy_test <- data.frame(ID = c("A","B","B","A"), values = c("n/a",NA,"Yes","No")) # Convert n/a and no to NA head(recode_as_na(dummy_test, value = c("n/a","No"))) ``` Great, but I want to do so for specific columns not the entire dataset. You can do this by providing column names to `subset_cols`. ```{r} another_dummy <- data.frame(ID = 1:5, Subject = 7:11, Change = c("missing","n/a",2:4 )) # Only change values at the column Change head(recode_as_na(another_dummy, subset_cols = "Change", value = c("n/a","missing"))) ``` To recode columns using [RegEx](https://en.wikipedia.org/wiki/Regular_expression),one can provide `pattern_type` and a target `pattern`. Currently supported `pattern_types` are `starts_with`, `ends_with`, `contains` and `regex`. See docs for more details.: ```{r} # only change at columns that start with Solar head(recode_as_na(airquality,value=190,pattern_type="starts_with",pattern="Solar")) ``` ```{r} # recode at columns that start with O or S(case sensitive) head(recode_as_na(airquality,value=c(67,118),pattern_type="starts_with",pattern="S|O")) ``` ```{r} # use my own RegEx head(recode_as_na(airquality,value=c(67,118),pattern_type="regex",pattern="(?i)^(s|o)")) ``` * `recode_as_na_if` This function allows one to deliberately introduce missing values if a column meets a certain threshold of missing values. This is similar to `amputation` but is much more basic. It is only provided here because it is hoped it may be useful to someone for whatever reason. ```{r} head(recode_as_na_if(airquality,sign="gt", percent_na=20)) ``` * `recode_as_na_str` This allows recoding as `NA` based on a string match. ```{r} partial_match <- data.frame(A=c("Hi","match_me","nope"), B=c(NA, "not_me","nah")) recode_as_na_str(partial_match,"ends_with","ME", case_sensitive=FALSE) ``` * `recode_as_na_for` For all values greater/less/less or equal/greater or equal than some value, can I convert them to `NA`?! **Yes You Can!** All we have to do is use `recode_as_na_for`: ```{r} head(recode_as_na_for(airquality,criteria="gt",value=25)) ``` To do so at specific columns, pass an optional `subset_cols` character vector: ```{r} head(recode_as_na_for(airquality, value=40,subset_cols=c("Solar.R","Ozone"), criteria="gt")) ``` # Recoding NA as * `recode_na_as` Sometimes, for whatever reason, one would like to replace `NA`s with whatever value they would like. `recode_na_as` provides a very simple way to do just that. ```{r} head(recode_na_as(airquality)) # use NaN head(recode_na_as(airquality, value=NaN)) ``` As a "bonus", you can manipulate the data only at specific columns as shown here: ```{r} head(recode_na_as(airquality, value=0, subset_cols="Ozone")) ``` The above also supports custom recoding similar to `recode_na_as`: ```{r} head(mde::recode_na_as(airquality, value=0, pattern_type="starts_with",pattern="Solar")) ``` * `column_based_recode` Ever needed to change values in a given column based on the proportions of `NA`s in other columns(row-wise)?!. The goal of `column_based_recode` is to achieve just that. Let's see how we could do this with a simple example: ```{r} head(column_based_recode(airquality, values_from = "Wind", values_to="Wind", pattern_type = "regex", pattern = "Solar|Ozone")) ``` * `custom_na_recode` This allows recoding `NA` values with common stats functions such as `mean`,`max`,`min`,`sd`. To use default values: ```{r} head(custom_na_recode(airquality)) ``` To use select columns: ```{r} head(custom_na_recode(airquality,func="mean",across_columns=c("Solar.R","Ozone"))) ``` To use a function from another package to perform replacements: To perform a forward fill with `dplyr`'s `lead`: ```{r} # use lag for a backfill head(custom_na_recode(airquality,func=dplyr::lead )) ``` To perform replacement by group: ```{r} some_data <- data.frame(ID=c("A1","A1","A1","A2","A2", "A2"),A=c(5,NA,0,8,3,4),B=c(10,0,0,NA,5,6),C=c(1,NA,NA,25,7,8)) head(custom_na_recode(some_data,func = "mean", grouping_cols = "ID")) ``` Across specific columns: ```{r} head(custom_na_recode(some_data,func = "mean", grouping_cols = "ID", across_columns = c("C", "A"))) ``` * `recode_na_if` Given a `data.frame` object, one can recode `NA`s as another value based on a grouping variable. In the example below, we replace all `NA`s in all columns with 0s if the ID is `A2` or `A3` ```{r} some_data <- data.frame(ID=c("A1","A2","A3", "A4"), A=c(5,NA,0,8), B=c(10,0,0,1), C=c(1,NA,NA,25)) head(recode_na_if(some_data,grouping_col="ID", target_groups=c("A2","A3"), replacement= 0)) ``` # Dropping Missing Values * `drop_na_if` Suppose you wanted to drop any column that has a percentage of `NA`s greater than or equal to a certain value? `drop_na_if` does just that. We can drop any columns that have greater than or equal(gteq) to 24% of the values missing from `airquality`: ```{r} head(drop_na_if(airquality, sign="gteq",percent_na = 24)) ``` The above also supports less than or equal to(`lteq`), equal to(`eq`), greater than(`gt`) and less than(`lt`). To keep certain columns despite fitting the target `percent_na` criteria, one can provide an optional `keep_columns` character vector. ```{r} head(drop_na_if(airquality, percent_na = 24, keep_columns = "Ozone")) ``` Compare the above result to the following: ```{r} head(drop_na_if(airquality, percent_na = 24)) ``` To drop groups that meet a set missingness criterion, we proceed as follows. ```{r} grouped_drop <- structure(list(ID = c("A", "A", "B", "A", "B"), Vals = c(4, NA, NA, NA, NA), Values = c(5, 6, 7, 8, NA)), row.names = c(NA, -5L), class = "data.frame") # Drop all columns for groups that meet a percent missingness of greater than or # equal to 67 drop_na_if(grouped_drop,percent_na = 67,sign="gteq", grouping_cols = "ID") ``` * `drop_row_if` This is similar to `drop_na_if` but does operations rowwise not columnwise. Compare to the example above: ```{r} # Drop rows with at least two NAs head(drop_row_if(airquality, sign="gteq", type="count" , value = 2)) ``` To drop based on percentages: ```{r} # Drops 42 rows head(drop_row_if(airquality, type="percent", value=16, sign="gteq", as_percent=TRUE)) ``` For more details, please see the documentation of `drop_row_if`. * `drop_na_at` This provides a simple way to drop missing values only at specific columns. It currently only returns those columns with their missing values removed. See usage below. Further details are given in the documentation. It is currently case sensitive. ```{r} head(drop_na_at(airquality,pattern_type = "starts_with","O")) ``` * `drop_all_na` This drops columns where all values are missing. ```{r} test2 <- data.frame(ID= c("A","A","B","A","B"), Vals = c(4,rep(NA, 4))) drop_all_na(test2, grouping_cols="ID") ``` Alternatively, we can drop groups where all variables are all NA. ```{r} test2 <- data.frame(ID= c("A","A","B","A","B"), Vals = rep(NA, 5)) head(drop_all_na(test2, grouping_cols = "ID")) ``` * `dict_recode` If one would like to recode column values using a "dictionary", `dict_recode` provides a simple way to do that. For example, if one would like to convert `NA` values in `Solar.R` to 520 and those in `Ozone` to 42, one simply calls the following: ```{r} head(dict_recode(airquality, use_func="recode_na_as", patterns = c("solar", "ozone"), pattern_type="starts_with", values = c(520,42))) ``` # General Recoding * For general recoding of values, one can use `recode_as_value` for example as shown below ```{r} head(recode_as_value(airquality, value=c(67,118),replacement=NA, pattern_type="starts_with",pattern="S|O")) ``` The above is a more general function that can do what the other functions do and may be more useful for development purposes. --- Please note that the `mde` project is released with a [Contributor Code of Conduct](https://github.com/Nelson-Gon/mde/blob/master/.github/CODE_OF_CONDUCT.md). By contributing to this project, you agree to abide by its terms. For further exploration, please `browseVignettes("mde")`. To raise an issue, please do so [here](https://github.com/Nelson-Gon/mde/issues) Thank you, feedback is always welcome :)