googledrive icon indicating copy to clipboard operation
googledrive copied to clipboard

"Error: 'levels.drive_id()' not supported." when using apply on drive_ls() output

Open 16EAGLE opened this issue 3 years ago • 2 comments

Thank you very much for this great project.

When using apply on the output of drive_ls() with googledrive 2.0.0 from CRAN or this repo (e.g. to row-by-row create sub-folders and download the respective files) the error Error: 'levels.drive_id()' not supported. is thrown. With version 1.0.1, it is possible to run apply on the output of drive_ls() without this error and without converting IDs to character.

See the reprex below orientated on the workflow that I have been using:

library(googledrive)
df <- drive_examples_remote() #or for me a `drive_ls()` call

# a download function using apply (here I usually create id-based subfolders etc.)
download_files <- function(df){
  catch <- apply(df, MARGIN = 1, function(x){
    drive_download(file = as_id(x[["id"]]), path = x[["name"]])
  })
}

Calling download_files() with googledrive 2.0.0 fails:

# fails:
download_files(df[1:3,])
#> Error: `levels.drive_id()` not supported.

rlang::last_error()
#> <error/vctrs_error_unsupported>
#> `levels.drive_id()` not supported.
#> Backtrace:
#>  1. global::download_files(df[1:3, ])
#>  2. base::apply(...)
#>  4. base::as.matrix.data.frame(X)
#>  6. vctrs:::levels.vctrs_vctr(xj)
#>  7. vctrs:::stop_unsupported(x, "levels")
#>  8. vctrs:::stop_vctrs(...)
#> Run `rlang::last_trace()` to see the full context.

Calling apply on a drive_ls() output works with googledrive 1.0.1 or by converting the IDs to character and removing the drive_resource column:

df$drive_resource <- NULL
df$id <- as.character(df$id)
download_files(df[1:2,])
#> File downloaded:
#> • 'chicken_doc' <id: 1X9pd4nOjl33zDFfTjw-_eFL7Qb9_g6VfVFDp1PPae94>
#> Saved locally as:
#> • 'chicken_doc.docx'
#> File downloaded:
#> • 'chicken_sheet' <id: 1SeFXkr3XdzPSuWauzPdN-XnaryOYmZ7sFiUF5t-wSVU>
#> Saved locally as:
#> • 'chicken_sheet.xlsx'

Created on 2021-07-26 by the reprex package (v2.0.0)

Could it be that there is a method missing for class "drive_id" "vctrs_vctr" "character"?

Its not so problematic for me since I simply can convert to character but maybe it would be convenient if apply would work directly.

Thanks!

sessionInfo
R version 4.1.0 (2021-05-18)
Platform: x86_64-pc-linux-gnu (64-bit)
Running under: Ubuntu 20.04.2 LTS

Matrix products: default
BLAS:   /usr/lib/x86_64-linux-gnu/blas/libblas.so.3.9.0
LAPACK: /usr/lib/x86_64-linux-gnu/lapack/liblapack.so.3.9.0

locale:
  [1] LC_CTYPE=en_US.UTF-8       LC_NUMERIC=C               LC_TIME=de_DE.UTF-8        LC_COLLATE=en_US.UTF-8    
[5] LC_MONETARY=de_DE.UTF-8    LC_MESSAGES=en_US.UTF-8    LC_PAPER=de_DE.UTF-8       LC_NAME=C                 
[9] LC_ADDRESS=C               LC_TELEPHONE=C             LC_MEASUREMENT=de_DE.UTF-8 LC_IDENTIFICATION=C       

attached base packages:
  [1] stats     graphics  grDevices utils     datasets  methods   base     

other attached packages:
  [1] googledrive_2.0.0.9000

loaded via a namespace (and not attached):
  [1] rstudioapi_0.13  magrittr_2.0.1   rappdirs_0.3.3   tidyselect_1.1.1 R6_2.5.0         rlang_0.4.11     fansi_0.5.0     
[8] httr_1.4.2       dplyr_1.0.7      tools_4.1.0      utf8_1.2.2       cli_3.0.1        withr_2.4.2      askpass_1.1     
[15] ellipsis_0.3.2   openssl_1.4.4    tibble_3.1.3     gargle_1.2.0     lifecycle_1.0.0  crayon_1.4.1     purrr_0.3.4     
[22] vctrs_0.3.8      fs_1.5.0         curl_4.3.2       glue_1.4.2       compiler_4.1.0   pillar_1.6.1     generics_0.1.0  
[29] jsonlite_1.7.2   pkgconfig_2.0.3 

EDIT: reprex with the remote examples

16EAGLE avatar Jul 26 '21 11:07 16EAGLE

I believe you've come across another variation of this issue: https://github.com/r-lib/vctrs/issues/1416.

It turns out base R, in some of the apply functions, calls levels() on columns or objects, as part of figuring out what the column is. But instead of taking the lack of a levels() method as info, it just fails if there's no levels() method. It strikes me as an odd thing to do and that issue is where we're thinking whether to do anything about it and if so, what.

I'm not convinced adding a levels() method for every vctrs class is a good idea, as it allows some things in base R to appear to "work", but produce an incorrect result. I show a case of that in https://github.com/r-lib/vctrs/issues/1416, where rbind()'s idiosyncratic logic creates an invalid drive_id column. This would undermine the whole point of creating the drive_id class and implementing methods that codify what it is and how to combine it with other things.

My preferred solution is to use functions within the tidyverse to work with such a vector, e.g. to use purrr::map_*() instead of [sl]apply(), because the map_*() functions do what I want and don't require a levels() method to exist.

I think your choices, certainly near-term, are to either switch to something like purrr::pmap() or purrr::pwalk() or dplyr::rowwise() %>% dplyr::summarize() for your row-by-row work or to do the class-stripping you describe above.

jennybc avatar Jul 26 '21 15:07 jennybc

Note to self: Update on where this went in vctrs: https://github.com/r-lib/vctrs/issues/1186

jennybc avatar Oct 29 '21 15:10 jennybc

vctrs has since made a change that prevents this weird error about levels.drive_id().

jennybc avatar Mar 20 '23 04:03 jennybc