Pratyush T.
Pratyush T.

Reputation: 53

Using map (purrr) to iterate over two vectors in R

I am trying to plot maps for three different dependent variables (each plotted separately), over multiple metro areas. To do this, I am trying to do 2 nested maps. I start by doing it just over the 3 different variables foe one metro area - defining the function and running it through map and it works. The code for this part is below:

plot_fun <- function(x){
  base_plot_2015$`14460` +
  data_2015 %>% subset(met2013 == 14460) %>% subset(onetsoccode == "151021") %>% 
    geom_sf(mapping = aes_string(fill = x)) +
    scale_fill_viridis_c(option = "plasma") 
}

expl <- names(data_2015)[17:19]
expl <- set_names(expl)

plot_Boston <- map(expl, ~plot_fun(.x))

This gives me a list of 3 maps and so far so good. (Please note that the base_plot is a list containing maps of each of the metro areas saved as the metro area id, which is stored as double).

Next, I want to do the same, but also iterate it over different metro codes. I try doing this by defining the following function:

plot_fun <- function(x,y){
  base_plot_2015$`y` +
    data_2015 %>% subset(met2013 == y) %>% subset(onetsoccode == "151021") %>% 
    geom_sf(mapping = aes_string(fill = x)) +
    scale_fill_viridis_c(option = "plasma") 
}

The idea is to map it over a vector of metro areas (y) for each dependent variable to be plotted(x). However, before I do that, I can't get this function to work. When I run this function using plot_fun("work_home", 14460), I get the following error:

Error in base_plot_2015$y + data_2015 %>% subset(met2013 == y) %>% subset(onetsoccode == : non-numeric argument to binary operator

Would someone be kind enough to point out what I am doing wrong here?

Also, to do the nested map, does the code map(met, ~map(expl, ~plot_fun, y=.x)) (where met is a vector of metro area codes) make sense?

Upvotes: 5

Views: 1061

Answers (1)

Marcus
Marcus

Reputation: 3636

TLDR: When you subsetting your base_plot list, you need to use the [[ operator instead of $

In your second version, the function sees it as "y" a string, not the variable y. Since, presumably, there is no metro code "y", NULL is returned. The binary operator is + and NULL is non-numeric. Here's a simplified example of what's happening:

base_data <- list(
  a = letters,
  b = LETTERS,
  c = rnorm(100)
)

getMyData <- function(y){
  base_data$`x`
} 

getMyData("a")
#> NULL

base_data$x <- "QSBjbHVlIHBlcmhhcHM="

val <- getMyData("a") %>% print()
#> [1] "QSBjbHVlIHBlcmhhcHM="

base64enc::base64decode(val) %>% rawToChar()
#> [1] "A clue perhaps"

If you change your function to use the [[ operator, you can use the string value in the variable y to subset:

actuallyGetMyData <- function(x){
  base_data[[x]]
}

actuallyGetMyData("a")[1:10]
#> [1] "a" "b" "c" "d" "e" "f" "g" "h" "i" "j"

EDIT: Regarding the mapping over the permutations (because it's hard to read code in comments)

the call map(expl, ~plot_fun) creates an anonymous function that returns the function plot_fun. You either need to drop the anonymous function

map(met, ~map(expl, plot_fun, y=.x))

or call the function

map(met, ~map(expl, ~plot_fun(.x, .y), .y=.x))

I find the nested maps a little hard to read (.x becoming .y and what not) so alternatively you could just first enumerate all the combinations and then map over those

plotVars <- expand.grid(expl, met)
plot_final <- map2(plotVars[[1]], plotsVars[[2]],  plot_fun) %>% 
  set_names(paste(plotVars[[1]], plotVars[[2]], sep = "_"))

Note that your plot_final list will no longer be nested though.

Upvotes: 4

Related Questions