-
Notifications
You must be signed in to change notification settings - Fork 70
Open
Description
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