I am trying to run the same R code using computers that different versions of packages, but in one of them the code works and in the other it produces an error.
Specifically, I downloaded files from this link and combined stringr
's str_remove
function with dplyr
's rename_with
function to rename variables in a data frame of class sf
(so I can have variable names standardized according to names that I have in other files). The code that I ran was the following:
library(tidyverse)
library(sf)
library(stringr)
dft <- st_read(file.path('D:', 'Downloads', 'tl_rd22_01_cd118.shp'), quiet = TRUE) %>%
rename_with(., .fn = str_remove, pattern = '\\d{2}$')
In one of the computers the code ran as expected (removing digits at the end of the name of some variables), but in the other computer it produced the following error message (when translated to English):
Error in .fn(names(agr)) : argument "pattern" missing, no pattern
However, in the computer that displayed an error, the following code works.
library(tidyverse)
library(sf)
library(stringr)
dft <- st_read(file.path('D:', 'Downloads', 'tl_rd22_01_cd118.shp'), quiet = TRUE) %>%
st_drop_geometry(.) %>%
rename_with(., .fn = str_remove, pattern = '\\d{2}$')
The problem with this solution is that I loose the geometry column.
This error message does not seem to make sense to me. The problem seems to be with the versions that I have in the two computers. In the one that worked, the versions of dplyr
, stringr
, and sf
are 1.0.7, 1.4.0, and 1.0.3, respectively; in the one that did not work, they are 1.1.4, 1.5.1, and 1.0.14. For me, it seems that there is some incompatibility between replace_with
and objects of class sf
.
sf
has a new method: rename_with.sf
as of version 1.0-13:
sf:::rename_with.sf
#> function (.data, .fn, .cols, ...)
#> {
#> if (!requireNamespace("rlang", quietly = TRUE))
#> stop("rlang required: install that first")
#> .fn = rlang::as_function(.fn)
#> agr = st_agr(.data)
#> ret = NextMethod()
#> names(agr) = .fn(names(agr))
#> st_agr(ret) = agr
#> ret
#> }
#> <bytecode: 0x00000235ee991c48>
#> <environment: namespace:sf>
This means that the ...
isn't passed to the function in this case, as it is with rename_with.data.frame
:
dplyr:::rename_with.data.frame
#> function (.data, .fn, .cols = everything(), ...)
#> {
#> .fn <- as_function(.fn)
#> cols <- tidyselect::eval_select(enquo(.cols), .data, allow_rename = FALSE)
#> names <- names(.data)
#> sel <- vec_slice(names, cols)
#> new <- .fn(sel, ...)
#> if (!is_character(new)) {
#> cli::cli_abort("{.arg .fn} must return a character vector, not {.obj_type_friendly {new}}.")
#> }
#> if (length(new) != length(sel)) {
#> cli::cli_abort("{.arg .fn} must return a vector of length {length(sel)}, not {length(new)}.")
#> }
#> names <- vec_assign(names, cols, new)
#> names <- vec_as_names(names, repair = "check_unique")
#> set_names(.data, names)
#> }
#> <bytecode: 0x00000235ee381aa0>
#> <environment: namespace:dplyr>
For a working approach, you could try using a purrr
function to explicitly pass the arguments:
library(tidyverse)
library(sf)
download.file("https://www2.census.gov/geo/tiger/TIGER_RD18/LAYER/CD/tl_rd22_01_cd118.zip", "tl_rd22_01_cd118.zip")
unzip("tl_rd22_01_cd118.zip")
st_read('tl_rd22_01_cd118.shp', quiet = TRUE) |>
rename_with(~ str_remove(.x, pattern = '\\d{2}$'))
#> Simple feature collection with 7 features and 12 fields
#> Geometry type: MULTIPOLYGON
#> Dimension: XY
#> Bounding box: xmin: -88.47323 ymin: 30.14442 xmax: -84.88825 ymax: 35.00803
#> Geodetic CRS: NAD83
#> STATEFP GEOID CD118FP NAMELSAD LSAD CDSESSN MTFCC FUNCSTAT
#> 1 01 0101 01 Congressional District 1 C2 118 G5200 N
#> 2 01 0102 02 Congressional District 2 C2 118 G5200 N
#> 3 01 0103 03 Congressional District 3 C2 118 G5200 N
#> 4 01 0104 04 Congressional District 4 C2 118 G5200 N
#> 5 01 0105 05 Congressional District 5 C2 118 G5200 N
#> 6 01 0106 06 Congressional District 6 C2 118 G5200 N
#> 7 01 0107 07 Congressional District 7 C2 118 G5200 N
...
Or explicitly use the dplyr:::rename_with.data.frame
method in your function call:
st_read('tl_rd22_01_cd118.shp', quiet = TRUE) %>%
dplyr:::rename_with.data.frame(., .fn = str_remove, pattern = '\\d{2}$')
I'd suggest a potential GitHub fix as I think the easiest tweak would be to change this line from:
names(agr) = .fn(names(agr))
to:
names(agr) = .fn(names(agr), ...)