0

I have a tricky problem to access the string values of a list as argument to purr functions.

My goal is to concatenate all permutations of the string elements of two vectors (to use in an output filename) which I put into one input list:

target.labels <- c("Prefix_A", "Prefix_B")
features.sets <- c("Suffix_X", "Suffix_Y")

input.list <- expand.grid(x=target.labels, y=features.sets)

The expected result should look like:

"Prefix_A-Suffix_X" "Prefix_B-Suffix_X" "Prefix_A-Suffix_Y" "Prefix_B-Suffix_Y"

Here's what I tried:

library(dplyr)
library(purrr)

fun1 <- function(x,y) { paste0(c(x, y), collapse = "-") }
fun2 <- function(x,y) { paste(x, y, sep = "-") }
fun3 <- function(x,y) { glue::glue("x = {x}, y = {y}") }


input.list %>% pmap_chr(fun1)
## [1] "1-1" "2-1" "1-2" "2-2"

input.list %>% pmap_chr(fun2)
## [1] "1-1" "2-1" "1-2" "2-2

input.list %>% pmap_chr(fun3)
## [1] "x = 1, y = 1" "x = 2, y = 1" "x = 1, y = 2" "x = 2, y = 2"

input.list %>% pmap_chr(~paste(.x, .y, sep = "-"))
## [1] "1-1" "2-1" "1-2" "2-2"

As you can see, the purr::pmap functions only retrieve the elements' index values instead of the string values. On the other side, it may not be specific to purr as the apply functions show the same problem:

mapply(fun1, input.list$x, input.list$y)
## [1] "1-1" "2-1" "1-2" "2-2"

One hunch is that somehow, the hidden c() function in paste0() or paste() prevents the access of the string values - but only in combination with purr:pmap, not with purr:map2!

So this works:

map2_chr(.x = input.list$x, .y = input.list$y, ~paste(.x, .y, sep = "-"))

## [1] "Prefix_A-Suffix_X" "Prefix_B-Suffix_X" "Prefix_A-Suffix_Y"
## [4] "Prefix_B-Suffix_Y"

My hunch is that this issue may have something to do with NSE (non-standard evaluation) but I just can't figure it out because the purr:map2 works as expected.

I would be grateful for a good explanation why this happens - and how to make it work with purr:pmap.

Agile Bean
  • 6,437
  • 1
  • 45
  • 53

2 Answers2

1

The base function expand.grid is turning your columns into factors. Since you are already using tidyverse functions, use the tidy equivalent crossing instead

input.list <- crossing(x=target.labels, y=features.sets)

Then fun1 or fun1 should work fine. The problem with factors is that they are basically stored as integers in R so they are more likey to be converted to numbers than characters.

MrFlick
  • 195,160
  • 17
  • 277
  • 295
  • Fantastic! Yes, all the functions work now. Wow - I would never have imagined it is the datatype of the columns that matter... – Agile Bean Feb 06 '19 at 16:24
  • So now I understand why the `map2_chr(.x = input.list$x, .y = input.list$y, ~paste(.x, .y, sep = "-"))` worked - the input arguments are passed as columns directly subset from the dataframe, which avoids the conversion to factors. – Agile Bean Feb 06 '19 at 16:32
0

Here the expand.grid columns can be changed to character class if we use stringsAsFactors = FALSE, and then with pmap can paste the elements in each row

library(purrr)
input.list <- expand.grid(x=target.labels, y=features.sets, stringsAsFactors = FALSE)
pmap_chr(input.list, paste, collapse="-")
#[1] "Prefix_A Suffix_X" "Prefix_B Suffix_X" "Prefix_A Suffix_Y" "Prefix_B Suffix_Y"
akrun
  • 874,273
  • 37
  • 540
  • 662
  • Yes, this works thank you! I prefer the solution by @MrFlick as it is shorter but your solution is equivalent as it treats the same underlying problem just from another side. – Agile Bean Feb 06 '19 at 16:29
  • No problem. Just wanted to show the issue in your post – akrun Feb 06 '19 at 16:30