Creating many plots with ggplot and map functions

I am struggling to understand how to implement pmap as part of a mutate. It seems like this combo would be perfect for what I need but I am fuzzy on using pmap especially on using the data.

I am working on a project where we are comparing the effectiveness of two systems to identify potentially non-systematic data. We want to manually check the cases when the systems do not provide a convergent answer. So we need to look at isolated figures for these. Thus, we are making a lot of plots. The plots need to include raw data, predicted values, details about the different systems, and some of the other details of the plot change based on the data (such as subtitles, limits, axis labels) and include information.

Below includes code the gets the job done, with simplified data, using successive map2()s to create a ggplot object and then add each feature that needs to use data from the tibble. Please note, that I had to comment out the code to print an example figure because imgur is blocked at my work and it was causing reprex to fail. The example data includes several "unimportant" data columns because my data has extra columns that I don't need for plotting and I don't know if my pmap failure is because I am not accounting for that extra data.

library(tidyverse)
library(glue)
#> 
#> Attaching package: 'glue'
#> The following object is masked from 'package:dplyr':
#> 
#>     collapse

xval = 0:3

figure_data = 
  tibble(
    subject = c(2, 3, 3),
    model = c("ModA", "ModA", "ModB"),
    exp = c(3, 2, 1),
    group = c("GrpX", "GrpY", "GrpZ"),
    task = c("TaskA", "TaskB", "TaskC"),
    unimportant_data = letters[7:9],
    data = list(
      tibble(xval, yval = 0:3, unimp_data = 5:8),
      tibble(xval, yval = 3:0, unimp_data = 5:8),
      tibble(xval, yval = rep(1, 4), unimp_data = 5:8)
    ),
    predicted = list(
      tibble(xval, pred = 1:4),
      tibble(xval, pred = 4:1),
      tibble(xval, pred = 2:2)
    )
  )

#Works but is clunky
figure_data_map2 = figure_data %>%
  mutate(
    plot = map2(
      data,
      predicted,
      ~ ggplot() +
        geom_point(data = .x, aes(x = xval, y = yval)) +
        geom_line(data = .y, aes(x = xval, y = pred)) +
        ylim(0, 5) +
        xlab("X Descriptor") +
        ylab("Y Descriptor")
    ),
    plot_title = glue("Subject: {subject}, Model: {model}"),
    plot = map2(plot, plot_title,
                ~ .x + ggtitle(.y)),
    plot_sub = glue("Exp: {exp}, Task: {task}, Group: {group}"),
    plot = map2(plot, plot_sub,
                ~ .x + labs(subtitle = .y))
  )

#For example, only showing 1 figure.
#figure_data_map2 %>% pluck("plot",1)
#Had to comment out figure because imgur is blocked at work

Created on 2020-03-10 by the reprex package (v0.3.0)

I started down the path after giving up on using pmap. I don't love it because it is clunky and hard to read. But,... it works.

I would still like to know how to get pmap to work. What is wrong with the code below? I found some suggestions to reference the variables using the position in the list (e.g., ..1), but that didn't seem to work.

#Unclear how pmap should work
figure_data_pmap = 
  figure_data %>%
  mutate(plot = pmap(
    list(data, predicted, subject, model, exp, task, group),
    ~ ggplot() +
      geom_point(data = data , aes(x = xval, y = yval)) +
      geom_line(data = predicted, aes(x = xval, y = pred)) +
      ylim(0, 5) +
      xlab("X Descriptor") +
      ylab("Y Descriptor") ++labs(
        title = glue("Subject: {subject}, Model: {model}"),
        subtitle = glue("Exp: {exp}, Task: {task}, Group: {group}")
      )
  ))
#> Error: `data` must be a data frame, or other object coercible by `fortify()`, not a list

Created on 2020-03-10 by the reprex package (v0.3.0)

Hi @jefriedel,

Yes, with pmap() you can refer to items in list(...) by their position and prefixed by ... So this works for me:

figure_data_pmap =
  figure_data %>%
  mutate(plot = pmap(
    list(data, predicted, subject, model, exp, task, group),
    ~ ggplot() +
      geom_point(data = ..1, aes(x = xval, y = yval)) +
      geom_line(data = ..2, aes(x = xval, y = pred)) +
      ylim(0, 5) +
      xlab("X Descriptor") +
      ylab("Y Descriptor") +
      labs(
        title = glue("Subject: {..3}, Model: {..4}"),
        subtitle = glue("Exp: {..5}, Task: {..6}, Group: {..7}")
      )
  ))
1 Like

This definitely works for my example data and the solution builds out for my actual use case.

I feel like I tried this several times and was still getting the same error about "data must be a data frame,..." So I don't know what I messed up and maybe I will figure it out some day. I appreciate your help!

1 Like

This topic was automatically closed 7 days after the last reply. New replies are no longer allowed.