Skip to content
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
Closed

Modifying names #276

jennybc opened this issue Dec 14, 2016 · 5 comments
Labels
feature a feature request or enhancement

Comments

@jennybc
Copy link
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
Copy link

+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
Copy link
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
Copy link
Member

hadley commented Mar 3, 2017

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

@hadley hadley added the feature a feature request or enhancement label Mar 3, 2017
@jrnold
Copy link
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
Copy link
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 as completed in 241e09c Mar 4, 2017
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
feature a feature request or enhancement
Projects
None yet
Development

No branches or pull requests

4 participants