-
Notifications
You must be signed in to change notification settings - Fork 66
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
Fix rbind() for vctrs_vctr with underlying type character #1186
Comments
I independently ran across this and also pretty much convinced myself to implement FWIW I am explicitly inheriting base type, i.e. the constructor uses # I'm working on the drive_id class in a googledrive branch,
# but will soon merge this
devtools::load_all("~/rrr/googledrive")
#> ℹ Loading googledrive
x <- data.frame(a = as_id(month.name[1:3]))
x
#> a
#> 1 January
#> 2 February
#> 3 March
x$a
#> <drive_id[3]>
#> [1] January February March
class(x$a)
#> [1] "drive_id" "vctrs_vctr" "character"
rbind(x, x)
#> Error: `levels.drive_id()` not supported. Created on 2021-07-06 by the reprex package (v2.0.0.9000) |
But the more I look into it, I'm not so sure I want Maybe, all things considered, it's just as well that one needs to use So far my main conclusion is just that this is a super weird error to encounter and that is what leads to the sense of disorientation.
|
On the other hand, concatenating objects of the same class with I'm just wondering what other consequences implementing |
Locally, I've added and exported But, if we make Is this surprising? Or does this suggest that I've done something odd in my implementation of vctrs methods for devtools::load_all("~/rrr/googledrive")
#> ℹ Loading googledrive
a_drive_id <- as_id(month.name[1:3])
could_be_a_drive_id <- month.name[4:6]
invalid_drive_id <- c("April", "%*@", "June")
x <- data.frame(a = a_drive_id)
class(x$a)
#> [1] "drive_id" "vctrs_vctr" "character"
# combine drive_id with character (that can be coerced to drive_id)
# inside a data.frame,
# with rbind: get a drive_id or character, depending on input order
# with vec_rbind: get character
# with c: get character
# with vec_c: get character
y <- data.frame(a = could_be_a_drive_id)
class(y$a)
#> [1] "character"
x_and_y_rbind <- rbind(x, y)
class(x_and_y_rbind$a)
#> [1] "drive_id" "vctrs_vctr" "character"
y_and_x_rbind <- rbind(y, x)
class(y_and_x_rbind$a)
#> [1] "character"
x_and_y_vec_rbind <- vec_rbind(x, y)
class(x_and_y_vec_rbind$a)
#> [1] "character"
y_and_x_vec_rbind <- vec_rbind(y, x)
class(y_and_x_vec_rbind$a)
#> [1] "character"
c(a_drive_id, could_be_a_drive_id)
#> [1] "January" "February" "March" "April" "May" "June"
class(c(a_drive_id, could_be_a_drive_id))
#> [1] "character"
vctrs::vec_c(a_drive_id, could_be_a_drive_id)
#> [1] "January" "February" "March" "April" "May" "June"
class(vctrs::vec_c(a_drive_id, could_be_a_drive_id))
#> [1] "character"
# combine drive_id with character (that can NOT be coerced to drive_id)
# inside a data.frame,
# with rbind: get an error or character, depending on input order
# with vec_rbind: get character
# with c: get character
# with vec_c: get character
z <- data.frame(a = invalid_drive_id)
class(z$a)
#> [1] "character"
x_and_z_rbind <- rbind(x, z)
#> Error: A <drive_id> must match this regular expression: `^[a-zA-Z0-9_-]+$`
#> Invalid input:
#> x '%*@'
z_and_x_rbind <- rbind(z, x)
class(z_and_x_rbind$a)
#> [1] "character"
x_and_z_vec_rbind <- vec_rbind(x, z)
class(x_and_z_vec_rbind$a)
#> [1] "character"
z_and_x_vec_rbind <- vec_rbind(z, x)
class(z_and_x_vec_rbind$a)
#> [1] "character"
c(a_drive_id, invalid_drive_id)
#> [1] "January" "February" "March" "April" "%*@" "June"
class(c(a_drive_id, invalid_drive_id))
#> [1] "character"
vctrs::vec_c(a_drive_id, invalid_drive_id)
#> [1] "January" "February" "March" "April" "%*@" "June"
class(vctrs::vec_c(a_drive_id, invalid_drive_id))
#> [1] "character" Created on 2021-07-07 by the reprex package (v2.0.0.9000) |
I don't believe that this is specific to character in any way. You can't use library(vctrs)
data <- data.frame(foo = new_vctr(1:5, class = "foo"))
rbind(data, data)
#> Error: `levels.foo()` not supported. Looking into this, here is where I think I would be ok with supplying a I agree with Jenny that data <- data.frame(foo = new_vctr(1:5, class = "foo"))
data2 <- data.frame(foo = 1:5)
levels.vctrs_vctr <- function(x) {
NULL
}
rbind(data, data)$foo
#> <foo[10]>
#> [1] 1 2 3 4 5 1 2 3 4 5
rbind(data, data2)$foo
#> <foo[10]>
#> [1] 1 2 3 4 5 1 2 3 4 5
# integer first, lost class
rbind(data2, data)$foo
#> [1] 1 2 3 4 5 1 2 3 4 5 |
But in this case the base R results are wrong (or, rather, "can be and often are wrong"). So I'm not sure "compatibility" really describes what we'd be enabling. I know my reprex is very long, but it shows |
Taking another look at this, I think we should just remove the Or return |
I think I'm fine with that too. I vote for returning |
Implement levels.vctrs_vctr() if underlying type is character?
Perhaps only with base type fallback, #1135.
With this,
rbind()
works well enough in this case.Created on 2020-07-10 by the reprex package (v0.3.0)
The text was updated successfully, but these errors were encountered: