STAT545-UBC / Discussion

Public discussion
38 stars 20 forks source link

Faceting a graph in a functioin #465

Open Adam-Ziada opened 7 years ago

Adam-Ziada commented 7 years ago

I'm working on adapting a script I've used to make a function. My function body is below, but when I call it with:

facet2D(mtcars, mtcars$wt, mtcars$mpg, mtcars$am, mtcars$cyl) I get the following error:

Error in layout_base(data, vars, drop = drop) : At least one layer must contain all variables used for facetting

facet2D <- function(data, c1, c2, f1, f2) {

ggplot(data, aes(c1, c2)) +
    geom_point(alpha=1) +
    facet_wrap(f1 ~ f2) 

}

Does anyone know what could be causing this? Also note that I've used mutate on mtcars to turn cyl and am from numerica data into factors.

Note: running the below code works fine.

facet2D <- function(data, c1, c2, f1, f2) {

ggplot(data, aes(c1, c2)) +
    geom_point(alpha=1)

}

facet2D(mtcars, mtcars$wt, mtcars$mpg, mtcars$am, mtcars$cyl)

samhinshaw commented 7 years ago

Could you repost this with code formatting? For example, this (screenshot):


code formatting


...gives you this:


<code chunks here>

little bits of code like this


It makes it a lot easier to copy into R on my end 😃

oganm commented 7 years ago

@samhinshaw I often see you trying to show this syntax to people without it being rendered itself. If you leave 4 spaces before all your code it won't be rendered so you can do

```
hede
```

without resorting to screenshots

samhinshaw commented 7 years ago

augh, thank you, that's been driving me nuts.

Adam-Ziada commented 7 years ago

kk how is this?

my_cars <- dplyr::mutate(mtcars, cyl = as.factor(mtcars$cyl), am = as.factor(mtcars$am))
facet2D <- function(data, c1, c2, f1, f2) {
    ggplot(data, aes(c1, c2)) +
        geom_point(alpha=1) +
        facet_wrap(f1 ~ f2) 
}
facet2D(my_cars, my_cars$wt, my_cars$mpg, my_cars$am, my_cars$cyl)

produces the following error and no graph: Error in layout_base(data, vars, drop = drop) : At least one layer must contain all variables used for facetting


The following works perfectly fine tho

my_cars <- dplyr::mutate(mtcars, cyl = as.factor(mtcars$cyl), am = as.factor(mtcars$am))
ggplot(my_cars, aes(wt, mpg)) +
    geom_point(alpha=1) +
    facet_wrap(am ~ cyl) 
samhinshaw commented 7 years ago

Because of the error message and your troubleshooting, we can see the error message is coming from facet_wrap(). If you check out the documentation for facet_wrap(), we can see that there is an additional method you can supply your facets with, passing them as a character vector.

Unrelated, you don't need to provide the data.frame:

facet2D <- function(data, c1, c2, f1, f2) {
  ggplot(data = data, aes(x = c1, y = c2)) +
    geom_point(alpha = 1) +
    facet_wrap(c(paste(f1), paste(f2))) 
}

facet2D(data = mtcars, wt, mpg, am, cyl)
Adam-Ziada commented 7 years ago

Thank you! Just to confirm the following code seems to work on my computer!

facet2D <- function(data, c1, c2, f1, f2) {
    ggplot(data = data, aes(x = c1, y = c2)) +
        geom_point(alpha = 1) +
        facet_wrap(c(paste(f1), paste(f2))) 
}

facet2D(data = my_cars, my_cars$wt, my_cars$mpg, "am", "cyl")
Adam-Ziada commented 7 years ago

Alright, so there is still an issue.

facet2D <- function(data, c1, c2, f1, f2) {
    ggplot(data = data, aes(x = c1, y = c2)) +
        geom_point(alpha = 1) +
        facet_wrap(c(paste(f1), paste(f2))) 
}

facet2D(data = my_cars, my_cars$wt, my_cars$mpg, "am", "cyl")

image

gives a distinctly different graph than

ggplot2::ggplot(my_cars, ggplot2::aes(wt, mpg)) +
  ggplot2::geom_point(alpha = 1) +
  ggplot2::facet_wrap(c(paste("am"), paste("cyl")))

image

The issue seems to be with facet_wrap again, as if you remove it both the graphs look identical, but when you include it thing seem to go wrong. I use the following definition for my_cars.

my_cars <- dplyr::mutate(mtcars, cyl = as.factor(mtcars$cyl),
                         gear = as.factor(mtcars$gear),
                         carb = as.factor(mtcars$carb),
                         am = as.factor(mtcars$am),
                         vs = as.factor(mtcars$vs))

Any ideas? @samhinshaw

samhinshaw commented 7 years ago

Looking at the problem again, I think you're better off just using aes_string() and passing your arguments as character vectors. ggplot just isn't happy otherwise! Just make sure you document the function so its users will know how to input each argument. Sorry for not recommending this from the get-go :no_mouth:

facet2D <- function(data, c1, c2, f1, f2) {
  ggplot(data, aes_string(x = c1, y = c2)) +
    geom_point(alpha = 1) +
    facet_wrap(c(f1, f2))
}

facet2D(data = mtcars, c1 = "wt", c2 = "mpg", f1 = "am", f2 = "cyl")

For an example that's less abstract, we can use a DF with 2+ categorical variables:

HairDF <- data.frame(HairEyeColor) # HairEyeColor is a built in data table

# Function ------

facet2D <- function(InputDF, Xaxis, Yaxis, WrapOne, WrapTwo) {
  ggplot(InputDF, aes_string(x = Xaxis, y = Yaxis)) +
    geom_bar(stat = "identity") + 
    facet_wrap(c(WrapOne, WrapTwo))
}

facet2D(InputDF = HairDF, Xaxis = "Eye", Yaxis = "Freq", WrapOne = "Hair", WrapTwo = "Sex")

Also, I just want to make sure you know, when you're calling functions that operate exclusively on one data.frame (such as mutate() and ggplot(), you don't need to specify the data.frame. I only say this because specifying the DF where it's not necessary has the potential to cause hard-to-troubleshoot issues with code.

my_cars <- dplyr::mutate(mtcars, cyl = as.factor(cyl),
                         gear = as.factor(gear),
                         carb = as.factor(carb),
                         am = as.factor(am),
                         vs = as.factor(vs))
Adam-Ziada commented 7 years ago

Thank you!