2

I have a solution to format nested facet labels that is working really well. The solution was provided here .

I needed to raise another question as I need to be able to subset a list, but apparently I cant figure it out without help!

What I need to do: I want to be able to exclude Fac2 from the list. So the graph will only show Fac1 and Fac3. I think I need to subset this list List <- split(data,data$Fac_Map).

  • In the real data it is a long list of Facs and a wide graph so I want to exclude rather than list all of the inclusions.
  • I will be putting Fac2 at the end of the graph joined by ggarrange (there are two reasons for this but no need to go into the complexity of the real data). So I need to be able to filter the list within the first function G.

Data:

data <- structure(
  list(
    Fac_Map = structure(
      c(1L, 1L, 1L, 1L, 2L, 2L,
        2L, 2L, 3L, 3L, 3L, 3L),
      .Label = c("Fac1", "Fac2", "Fac3"),
      class = "factor"
    ),
    S_Residency = structure(
      c(1L, 1L, 2L, 2L, 1L, 1L, 2L, 2L,
        1L, 1L, 2L, 2L),
      .Label = c("Intl", "Local"),
      class = "factor"
    ),
    Period = structure(
      c(1L, 2L, 1L, 2L, 1L, 2L, 1L, 2L, 1L,
        2L, 1L, 2L),
      .Label = c("2019 P2", "2020 P2"),
      class = "factor"
    ),
    Result = c(92.9, 91.1, 85.8, 87.9, 94.1, 91.7, 87.5, 88.6,
               90, 90.1, 87.4, 88.9)
  ),
  class = "data.frame",
  row.names = c(NA,-12L)
)

Working solution to format facet labels provided by @Duck here

#Create list
List <- split(data,data$Fac_Map)
#Now function to plot
myplotfun <- function(x)
{
  G <- ggplot(x,aes(x=Period,y=Result))+
    geom_point() +
    facet_grid(. ~ S_Residency,
               scales = "free", space = "free") +
    ylim(range(x$Result)) +
    xlab("")+ylab("")+ggtitle(unique(x$Fac_Map))
  return(G)
}
#Apply for plots
List2 <- lapply(List,myplotfun)
#Wrap final plot
wrap_plots(List2,nrow = 1)

Is it possible to filter the list so I can just have Fac1 and Fac3 without having to subset the whole dataset which would prevent a ggarrange?

The output of the code:

enter image description here

Keelin
  • 367
  • 1
  • 10
  • 2
    Maybe I'm not fully understanding the question, but what would be wrong with `wrap_plots(List2[-2],nrow = 1)`? – teunbrand Aug 26 '20 at 10:42
  • Thanks @teunbrand this could help. I cant quite figure out how to pass the string "Fac2" into this, can this be done somehow? I need to pass in a string as the graph can dynamically change based on user selections. – Keelin Aug 26 '20 at 13:13
  • If you want to rely on a character match instead of a known position you could do the following: `rm_me <- match("Fac2", names(List2)); wrap_plots(List2[-rm_me],nrow = 1)` – teunbrand Aug 26 '20 at 13:17

2 Answers2

1

I would suggest two approaches in order to solve your issue. You could use $ to remove the element or defining a vector with names to be removed. Here the code using your dataframe data:

We have two ways to remove the object. First we can use $ and invoke the element in order to remove:

library(ggplot2)
library(dplyr)
library(patchwork)
#Create list
List <- split(data,data$Fac_Map)
#Remove
List$Fac2 <- NULL
#Plot
#Now function to plot
myplotfun <- function(x)
{
  G <- ggplot(x,aes(x=Period,y=Result))+
    geom_point() +
    facet_grid(. ~ S_Residency,
               scales = "free", space = "free") +
    ylim(range(x$Result)) +
    xlab("")+ylab("")+ggtitle(unique(x$Fac_Map))
  return(G)
}
#Apply for plots
List2 <- lapply(List,myplotfun)
#Wrap final plot
wrap_plots(List2,nrow = 1)

Output:

enter image description here

Second method we can set a vector of names to be removed and use indexing:

List <- split(data,data$Fac_Map)
#Define vector with names
vecrem <- c("Fac2")
#Remove
List[[vecrem]] <- NULL
#Now function to plot
myplotfun <- function(x)
{
  G <- ggplot(x,aes(x=Period,y=Result))+
    geom_point() +
    facet_grid(. ~ S_Residency,
               scales = "free", space = "free") +
    ylim(range(x$Result)) +
    xlab("")+ylab("")+ggtitle(unique(x$Fac_Map))
  return(G)
}
#Apply for plots
List2 <- lapply(List,myplotfun)
#Wrap final plot
wrap_plots(List2,nrow = 1)

Output:

enter image description here

Update:

You can have individual plots by isolating the desired element in a new list and removing from the big one to continue with the scheme of plotting. Then you apply the function in a normal way and in the end you can save in a new objects G1 and G2. Finally you can merge or arrange as you want:

#Create list
List <- split(data,data$Fac_Map)
#Define vector with names
vecrem <- c("Fac2")
#Isolate in a new list
List.single <- List[vecrem]
#Remove from big list
List[[vecrem]] <- NULL
#Now function to plot
myplotfun <- function(x)
{
  G <- ggplot(x,aes(x=Period,y=Result))+
    geom_point() +
    facet_grid(. ~ S_Residency,
               scales = "free", space = "free") +
    ylim(range(x$Result)) +
    xlab("")+ylab("")+ggtitle(unique(x$Fac_Map))
  return(G)
}
#Apply for plots
List2 <- lapply(List,myplotfun)
#Apply the plot for single element
List.single2 <- lapply(List.single,myplotfun)
#Wrap final plot
G1 <- wrap_plots(List2,nrow = 1)
G2 <- wrap_plots(List.single2,nrow = 1)
#Merge plots
G1+G2

Output G1:

enter image description here

Output G2:

enter image description here

Combined output G1+G2:

enter image description here

Duck
  • 39,058
  • 13
  • 42
  • 84
  • Thats exactly what I need @Duck thanks so very very much. I'll accept this shortly. Now that I know how to filter something out of a list (both of your solutions work perfectly) - Is there also a way to do the opposite as in filter a list to show only Fac2? Cant figure out how to do that either :( I need to be able to add the Fac2 graph at the end. – Keelin Aug 26 '20 at 12:51
  • @Keelin Do you mean only have a plot for Fac2? – Duck Aug 26 '20 at 12:53
  • Yes thats what I mean, I'll be adding it as a second plot using ggarrange (it has different periods in the real data) – Keelin Aug 26 '20 at 13:05
  • @Keelin I have added an update for you that does what you want :) – Duck Aug 26 '20 at 13:16
0

You can filter data and drop levels that don't appear in data before splitting:

data <- data %>% 
  filter(Fac_Map != "Fac2")  %>% 
  mutate(Fac_Map = fct_drop(Fac_Map))
det
  • 5,013
  • 1
  • 8
  • 16