New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Modifying names #276

Closed
jennybc opened this Issue Dec 14, 2016 · 5 comments

Comments

Projects
None yet
4 participants
@jennybc
Member

jennybc commented Dec 14, 2016

set_names() is mostly setNames() with the tweak that the names default to the input (instead of vice versa in base).

set_names(x, nm = x)

A related case that comes up as (or more?) often is to transform existing names. It feels like there are various purrr-ish ways to make that nicer. What if it felt like this? modify_names(x, ~ toupper(.nms)).

This seems very related to #240. Maybe it turns out to be nicely handled as a special case there.

@alistaire47

This comment has been minimized.

alistaire47 commented Feb 10, 2017

+1; I use setNames with gsub and tolower a lot. A version that can pick out one name (or a pattern?) à la dplyr::rename could be handy to avoid excess ifelse, too.

@jrnold

This comment has been minimized.

Contributor

jrnold commented Feb 22, 2017

The set of functions I've been using in my rubbish package are:

  • recode_names(x, .f, ...): set names using either a named vector of replacements, or a function
  • replace_names(x, pattern, replacment, all = TRUE): replaces names with a regular expression
  • set_names_idx(x, .f, ...): set names from their index integer value using either a function or a sprintf pattern
#' Recode names of a vector
#'
#' @param .f A named character vector or something that
#'   can be coerced into a function. If it is a function
#'   it must return a character vector the same length as
#'   \code{names(x)}.
#' @param ... Arguments passed to \code{.f} if it is a function.
#' @param x A vector
#' @return The vector \code{x} with new names.
#' @export
recode_names <- function(x, .f, ...) {
  old_names <- names(.f)
  if (is_character(.f) & is.null(names(.f))) {
    if (is.null(names(.f))) {
      stop("If a character vector, .f must be named.",
           call. = FALSE)
    }
    bad_names <- setdiff(.f, old_names)
    if (length(bad_names)) {
        stop("All names in .f must be in names(x): ",
             paste0(unname(bad_names), collapse = ", "),
             call. = FALSE)
    }
    new_names <- set_names(old_names, old_names)
    new_names[old_names] <- .f
  } else if (is_function(.f)) {
    new_names <- as_function(.f)(old_names, ...)
  }
  set_names(x, unname(new_names))
}

#' Set names in a vector with sequential numbering
#'
#' Set the names in vector with a pattern based on sequential numbering,
#' e.g. \code{Var1}, \code{Var2}, ...
#'
#' @param x An object to be named
#' @param .f A character vector or function. If \code{.f} is a function,
#'  then its first argument will be the index numbers of the names.
#'  If \code{.f} is a character, then it should be a format string
#'  for \code{sprintf} pattern.
#' @param ... Arguments passed to \code{.f}.
#' @return The object \code{x} with its names (colnames, rownames) set.
#' @seealso \code{\link[stats]{setNames}}, \code{\link[purrr]{set_names}},
#'   \code{\link[magrittr]{set_colnames}}, \code{\link[magrittr]{set_colnames}}.
#' @export
#' @examples
#' set_names_idx(1:5)
#' set_names_idx(1:5, "X%d")
#' set_names_idx(1:5, function(i) stringr::str_c("Var_", i))
set_names_idx <- function(x, .f = "%d", ...) {
  set_names(x, make_seq_names(seq_along(x), .f, ...))
}


#' Set names in a vector by regular expression
#'
#' @param x Vector to name
#' @param pattern,replacement Pattern and replacement strings. See \code{\link[stringr]{str_replace}}
#' @param all Use \code{\link[stringr]{str_replace_all}},
#'   else use \code{\link[stringr]{str_replace}}.
#' @return Return table with renamed variables
#' @export
#' @examples
#' replace_names(c(a = 1, b = 2), c("beta" = "b"))
#' fruits <- c("one apple" = 1, "two pears" = 2, "three bananas" = 3)
#' replace_names(fruits, "[aeiou]", "-")
#' replace_names(fruits, "[aeiou]", "-", all = TRUE)
replace_names <- function(x, pattern, replacement, all = TRUE) {
  assert_that(is.flag(all))
  .f <- if (all) str_replace_all else str_replace
  set_names(x, .f(names(x), pattern, replacement))
}
@hadley

This comment has been minimized.

Member

hadley commented Mar 3, 2017

Seems reasonable for the second argument to set_names() to be a function.

@hadley hadley added the feature label Mar 3, 2017

@jrnold

This comment has been minimized.

Contributor

jrnold commented Mar 3, 2017

what about partial name replacement in set_names(), possibly with an argument that indicates whether all names are required.

@hadley

This comment has been minimized.

Member

hadley commented Mar 4, 2017

That feels out of scope to me, and more in the realm of lplyr which provides a dplyr backend for lists.

@hadley hadley closed this in 241e09c Mar 4, 2017

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment