Skip to content

vec_get() #141

@DavisVaughan

Description

@DavisVaughan

As a complement to vec_slice() @romainfrancois and I have been discussing the utility of a function that would extract 1 observation, but would be more of an analog to [[ than [.

Possible implementation:

vec_rip <- function (x, i) {
  
  if (is.logical(i)) {
    i <- which(i)
  }
  
  stopifnot(is.integer(i) || is.character(i))
  
  # this is new 
  stopifnot(length(i) == 1L)
  
  if (is.null(x)) {
    NULL
  }
  else if (is.data.frame(x)) {
    out <- lapply(x, `[`, i)
    vec_restore(out, x)
  }
  else if (is_vector(x)) {
    d <- vec_dims(x)
    if (d == 1) {
      # this is all that changed?
      if (is.object(x)) {
        # x[i]
        x[[i]]
      }
      else {
        .subset2(x, i)
        # x[[i, drop = FALSE]]
      }
    }
    else if (d == 2) {
      x[i, , drop = FALSE]
    }
    else {
      miss_args <- rep(list(missing_arg()), d - 1)
      eval_bare(expr(x[i, !!!miss_args, drop = FALSE]))
    }
  }
  else {
    stop("`x` must be a vector", call. = FALSE)
  }
}

It would function somewhat like:

vec_rip(list(x = 1, y = 2), 1L)
#> [1] 1
vec_slice(list(x = 1, y = 2), 1L)
#> $x
#> [1] 1

# Not sure about what these should do:
vec_rip(mtcars, 1L)
#>   mpg cyl disp  hp drat   wt  qsec vs am gear carb
#> 1  21   6  160 110  3.9 2.62 16.46  0  1    4    4
vec_rip(matrix(1:12, nrow = 3), 1L)
#>      [,1] [,2] [,3] [,4]
#> [1,]    1    4    7   10

vec_slice(starwars$films, 1L)
#> [[1]]
#> [1] "Revenge of the Sith"     "Return of the Jedi"     
#> [3] "The Empire Strikes Back" "A New Hope"             
#> [5] "The Force Awakens"

vec_rip(starwars$films, 1L)
#> [1] "Revenge of the Sith"     "Return of the Jedi"     
#> [3] "The Empire Strikes Back" "A New Hope"             
#> [5] "The Force Awakens"

# vec_rip() can only extract 1 observation at a time
vec_slice(starwars$films, 1:2)
#> [[1]]
#> [1] "Revenge of the Sith"     "Return of the Jedi"     
#> [3] "The Empire Strikes Back" "A New Hope"             
#> [5] "The Force Awakens"      
#> 
#> [[2]]
#> [1] "Attack of the Clones"    "The Phantom Menace"     
#> [3] "Revenge of the Sith"     "Return of the Jedi"     
#> [5] "The Empire Strikes Back" "A New Hope"

vec_rip(starwars$films, 1:2)
#> Error in vec_rip(starwars$films, 1:2): length(i) == 1L is not TRUE

We are currently undecided on what it "should" do for data frames and matrices. A couple ideas:

1) Return the 1 row observation as is (so the same as vec_slice(), this is shown above). This doesn't feel right.
2) Extract the 1 row observation, then coerce it to some lower level type. For data.frames, a list and for matrices, a vector.

If 2) is chosen, one question that came up is "what should it do for list columns"? Two possibilities:

data("starwars", package = "dplyr")
starwars <- starwars[,c("species", "films")]
one_ob <- vctrs::vec_slice(starwars, 1L)

# don't drop a dimension
as.list(one_ob)
#> $species
#> [1] "Human"
#> 
#> $films
#> $films[[1]]
#> [1] "Revenge of the Sith"     "Return of the Jedi"     
#> [3] "The Empire Strikes Back" "A New Hope"             
#> [5] "The Force Awakens"

# drop 1 dimension
lapply(one_ob, .subset2, 1)
#> $species
#> [1] "Human"
#> 
#> $films
#> [1] "Revenge of the Sith"     "Return of the Jedi"     
#> [3] "The Empire Strikes Back" "A New Hope"             
#> [5] "The Force Awakens"

# but with better support for this
# so it doesn't unravel (i.e. better support 
# for vctr rcrd types)
.subset2(as.POSIXlt(Sys.time()), 1)
#> [1] 26.99161

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions