Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

R: How to slice a window of elements in named vector

Tags:

r

vector

Given the following named vector:

x <- c(54, 36, 67, 25, 76)
names(x) <- c('a', 'b', 'c', 'd', 'e')

How one can extract the elements between 'b' and 'd'? I can do that for data tables with the dplyr::select(dt, b:d) but for some reason, I cannot find a solution for named vectors (all the examples I find are for extracting element(s) by giving all the names not a range of names)...

like image 784
Marc Avatar asked Nov 17 '25 23:11

Marc


1 Answers

You could do

x[which(names(x) == "b"):which(names(x) == "d")]
#>  b  c  d 
#> 36 67 25 

The problem being that there is no guarantee in a named vector that names are unique, and if there are duplicate names the entire concept becomes meaningless.

If you wanted a complete solution that allows for tidyverse-style non-standard evaluation and sensible error messages you could have

subset_named <- function(data, exp)
{
  if(missing(exp)) return(data)
  exp <- as.list(match.call())$exp
  if(is.numeric(exp)) return(data[exp])
  if(is.character(exp)) return(data[exp])
  
  tryCatch({
    ss <- suppressWarnings(eval(exp))
    return(data[ss])},
    error = function(e)
    {
      if(as.character(exp[[1]]) != ":")
        stop("`exp` must be a sequence created by ':'")
      n <- names(data)
      first <- as.character(exp[[2]])
      second <- as.character(exp[[3]])
      first_match <- which(n == first)
      second_match <- which(n == second)
      if(length(first_match) == 0)
        stop("\"", first, "\" not found in names(", 
             deparse(substitute(data)), ")")
      if(length(second_match) == 0)
        stop("\"", second, "\" not found in names(", 
             deparse(substitute(data)), ")")
      if(length(first_match) > 1) {
        warning("\"", first, 
                "\" found more than once. Using first occurence only")
        first_match <- first_match[1]
      }
      if(length(second_match) > 1) {
        warning("\"", second, 
                "\" found more than once. Using first occurence only")
        second_match <- second_match[1]
      }
      return(data[first_match:second_match])
    })
}

That allows the following behaviour:

subset_named(x, "b":"d")
#>  b  c  d 
#> 36 67 25

subset_named(x, b:d)
#>  b  c  d 
#> 36 67 25

subset_named(x, 1:3)
#>  a  b  c 
#> 54 36 67

subset_named(x, "e")
#>  e 
#> 76

subset_named(x)
#>  a  b  c  d  e 
#> 54 36 67 25 76
like image 81
Allan Cameron Avatar answered Nov 20 '25 19:11

Allan Cameron