#' Fill in missing values with previous or next value
#'
#' Fills missing values in selected columns using the next or previous entry.
#' This is useful in the common output format where values are not repeated,
#' and are only recorded when they change.
#'
#' Missing values are replaced in atomic vectors; `NULL`s are replaced in lists.
#'
#' @section Grouped data frames:
#' With grouped data frames created by [dplyr::group_by()], `fill()` will be
#' applied _within_ each group, meaning that it won't fill across group
#' boundaries. This can also be accomplished using the `.by` argument to
#' `fill()`, which creates a temporary grouping for just this operation.
#'
#' @inheritParams dplyr::mutate
#'
#' @param data A data frame.
#' @param ... <[`tidy-select`][tidyr_tidy_select]> Columns to fill.
#' @param .direction Direction in which to fill missing values. Currently
#' either "down" (the default), "up", "downup" (i.e. first down and then up)
#' or "updown" (first up and then down).
#'
#' @export
#' @examples
#' # direction = "down" --------------------------------------------------------
#' # Value (year) is recorded only when it changes
#' sales <- tibble::tribble(
#' ~quarter, ~year, ~sales,
#' "Q1", 2000, 66013,
#' "Q2", NA, 69182,
#' "Q3", NA, 53175,
#' "Q4", NA, 21001,
#' "Q1", 2001, 46036,
#' "Q2", NA, 58842,
#' "Q3", NA, 44568,
#' "Q4", NA, 50197,
#' "Q1", 2002, 39113,
#' "Q2", NA, 41668,
#' "Q3", NA, 30144,
#' "Q4", NA, 52897,
#' "Q1", 2004, 32129,
#' "Q2", NA, 67686,
#' "Q3", NA, 31768,
#' "Q4", NA, 49094
#' )
#' # `fill()` defaults to replacing missing data from top to bottom
#' sales %>% fill(year)
#'
#' # direction = "up" ----------------------------------------------------------
#' # Value (pet_type) is missing above
#' tidy_pets <- tibble::tribble(
#' ~rank, ~pet_type, ~breed,
#' 1L, NA, "Boston Terrier",
#' 2L, NA, "Retrievers (Labrador)",
#' 3L, NA, "Retrievers (Golden)",
#' 4L, NA, "French Bulldogs",
#' 5L, NA, "Bulldogs",
#' 6L, "Dog", "Beagles",
#' 1L, NA, "Persian",
#' 2L, NA, "Maine Coon",
#' 3L, NA, "Ragdoll",
#' 4L, NA, "Exotic",
#' 5L, NA, "Siamese",
#' 6L, "Cat", "American Short"
#' )
#'
#' # For values that are missing above you can use `.direction = "up"`
#' tidy_pets %>%
#' fill(pet_type, .direction = "up")
#'
#' # direction = "downup" ------------------------------------------------------
#' # Value (n_squirrels) is missing above and below within a group
#' squirrels <- tibble::tribble(
#' ~group, ~name, ~role, ~n_squirrels,
#' 1, "Sam", "Observer", NA,
#' 1, "Mara", "Scorekeeper", 8,
#' 1, "Jesse", "Observer", NA,
#' 1, "Tom", "Observer", NA,
#' 2, "Mike", "Observer", NA,
#' 2, "Rachael", "Observer", NA,
#' 2, "Sydekea", "Scorekeeper", 14,
#' 2, "Gabriela", "Observer", NA,
#' 3, "Derrick", "Observer", NA,
#' 3, "Kara", "Scorekeeper", 9,
#' 3, "Emily", "Observer", NA,
#' 3, "Danielle", "Observer", NA
#' )
#'
#' # The values are inconsistently missing by position within the `group`.
#' # Use `.direction = "downup"` to fill missing values in both directions
#' # and `.by = group` to apply the fill per group.
#' squirrels %>%
#' fill(n_squirrels, .direction = "downup", .by = group)
#'
#' # If you want, you can also supply a data frame grouped with `group_by()`,
#' # but don't forget to `ungroup()`!
#' squirrels %>%
#' dplyr::group_by(group) %>%
#' fill(n_squirrels, .direction = "downup") %>%
#' dplyr::ungroup()
fill <- function(data,
...,
.by = NULL,
.direction = c("down", "up", "downup", "updown")) {
check_dots_unnamed()
UseMethod("fill")
}
#' @export
fill.data.frame <- function(data,
...,
.by = NULL,
.direction = c("down", "up", "downup", "updown")) {
vars <- names(tidyselect::eval_select(
expr = expr(c(...)),
data = data,
allow_rename = FALSE
))
.direction <- arg_match0(
arg = .direction,
values = c("down", "up", "downup", "updown"),
)
fn <- function(col) {
vec_fill_missing(col, direction = .direction)
}
dplyr::mutate(
data,
dplyr::across(any_of(vars), .fns = fn),
.by = {{ .by }}
)
}
Add the following code to your website.
For more information on customizing the embed code, read Embedding Snippets.