I have a function draw_plot() that takes in a data frame and other parameters(through the ellipsis) and simply returns a line plot.
draw_plot <- function(data, ...){
plot <- ggplot(data = data, mapping = aes(x = x, y = y))
plot <- plot + do.call("geom_line", c(list(...)))
return(plot)}
data <- tibble::tibble(x = c(1,2,3,4,5,6), y = c(5,6,7,8,9,10))
draw_plot(data, test = 2, wrong = "")
If I call the function as above, with parameters with key values which the geom_line() function doesn't recognize, it returns a warning saying 'Ignoring unknown parameters: test, wrong'.
I am hoping to improve the function to facilitate more parameters, and other ggplot geoms. (ex: draw_plot(data, stat, ...)) The stat parameter will not be valid for geom_line(). Therefore I want to capture the original warning message and append 'stat' to it. ex: 'Ignoring unknown parameters: test, wrong, stat'
To do this, is there any way that I can capture this particular warning message into a variable? p.s. I am trying to stick to base R to do this.
Here is how you can capture the warning message -
draw_plot <- function(data, ...){
plot <- ggplot(data = data, mapping = aes(x = x, y = y))
plot <- tryCatch(plot + do.call("geom_line", c(list(...))),
warning = function(w) {
if(grepl('Ignoring unknown parameters', w$message))
wmsg <<- as.character(w$message)
return(NULL)
})
if(exists('wmsg')) print(wmsg)
return(plot)
}
draw_plot(data, test = 2, wrong = "")
draw_plot(data)
Related
While trying to pass the function inside the memosie i am getting an error of
Error in FUN(X[[i]], ...) : object 'condition' not found but if run alone its working fine
This is the sample function actually in my shiny app i am also getting the same error
library(ggplot2)
meansdf <- data.frame(means = c(13.8, 14.8), condition = 1:2)
testplot <- function(df, x, y) {
arg <- match.call()
scale <- 0.5
p <- ggplot(df, aes(x = eval(arg$x),
y = eval(arg$y) * scale,
fill = eval(arg$x)))
p + geom_bar(position = "dodge", stat = "identity")
}
a = memoise::memoise(testplot)
a(meansdf, condition, means)
Use,
a(meansdf, meansdf$condition, meansdf$means)
Sorry if my question has been answered somewhere already, but all the other posts about "Aesthetics must be either length 1 or the same as the data" did not help me.
I'm trying to run the following code, but it gives this error: Error: Aesthetics must be either length 1 or the same as the data (1): x
library(tidyverse)
demand <- function(q) (q - 10)^2
supply <- function(q) q^2 + 2*q + 8
x <- 0:5
chart <- ggplot() +
stat_function(aes(x), color = "Demand", fun = demand) +
stat_function(aes(x), color = "Supply", fun = supply)
chart
What's wrong here?
The output should look like this:
According to ?ggplot2::stat_function(), this geom does not require data = .... This because "stat_function() computes the following variables: x values along a grid [and] y value of the function evaluated at corresponding x". The problem is related to the way you use x inside aes(). Please, look at the code below:
EDIT: I have added the possibility to use the x object to set the x-axis.
library(ggplot2)
demand <- function(q) (q - 10)^2
supply <- function(q) q^2 + 2*q + 8
x <- 0:5
chart <- ggplot() +
stat_function(aes(color = "Demand"), fun = demand) +
stat_function(aes(color = "Supply"), fun = supply) +
xlim(min(x), max(x)) +
scale_color_manual(name = "Legend",
values = c("Demand" = "red", "Supply" = "#24C6CA"))
chart
The output:
Is it what you are looking for? Note the use of xlim() to set the range of the x-axis.
Context
Reading the vignette Programming with dplyr I tried to use the ... and !!! operators to implement a function that would wrap around ggplot functions and would accept an arbitrary number of arguments that would define which variables in a dataframe were to be mapped to each aesthetic.
My goal
I wanted to define a function plot_points2() such that
plot_points2(df, x = x, y = y, color = z) would be equivalent to df %>% ggplot( mapping = aes(x = x, y = y, color = z) ) + geom_point(alpha = 0.1)
plot_points2(df, x = x, y = z, color = y) would be equivalent to df %>% ggplot( mapping = aes(x = x, y = z, color = y) ) + geom_point(alpha = 0.1)
plot_points2(df, x = x, y = z) would be equivalent to df %>% ggplot( mapping = aes(x = x, y = z) ) + geom_point(alpha = 0.1)
What failed
packages
require(tidyverse)
require(rlang)
reduced example dataset
df <- tibble(g1= sample(x = c(1,2,3), replace = T, size = 10000),
g2= sample(x = c("a","b","c"), replace = T, size = 10000),
x = rnorm(10000, 50, 10),
y = rnorm(10000, 0, 20) + x*2,
z = rnorm(10000, 10, 5))
df
my attempt
plot_points2 <- function(d, ...){
args <- quos(...)
print(args)
ggplot(data = d, mapping = aes(!!!args)) + geom_point(alpha = 0.1)
}
plot_points2(df, x = x, y = y, color = z)
the error
Error: Can't use `!!!` at top level
Call `rlang::last_error()` to see a backtrace
Why I think it should work
I figure what I wanted to acomplish isn't much different from an example in the vignette that uses these operators to make a function that wraps around mutate(), and passes multiple arguments that defined the grouping variables (in deed I was able to implement a function that does that to the example dataset above I'm posting as an example), but somehow the latter works and the former doesn't:
this works
add_dif_to_group_mean <- function(df, ...) {
groups <- quos(...)
df %>% group_by(!!!groups) %>% mutate(x_dif = x-mean(x),
y_dif = y-mean(y),
z_dif = z-mean(z))
}
df %>% add_dif_to_group_mean(g1)
df %>% add_dif_to_group_mean(g1, g2)
this doesn't
plot_points2 <- function(d, ...){
args <- quos(...)
print(args)
ggplot(data = d, mapping = aes(!!!args)) + geom_point(alpha = 0.1)
}
plot_points2(df, x = x, y = y, color = z)
I also read that the problem could be related with aes() being evaluated only when the plot is printed, but in that case I think using !! and unpacking manually should raise the same error but it doesn't:
plot_points2b <- function(d, ...){
args <- quos(...)
print(args)
ggplot(data = d, mapping = aes(x = !!args[[1]],
y = !!args[[2]],
color = !!args[[3]])) +
geom_point(alpha = 0.1)
}
plot_points2b(df, x = x, y = y, color = z)
In deed this last example works fine if you plot 3 variables, but it doesn't allow you to plot a number of variables different from 3
eg: plot_points2b(df, x = x, y = z) is not equivalent to
df %>% ggplot( mapping = aes(x = x, y = z) ) + geom_point(alpha = 0.1)
In stead it raises the error:
Error in args[[3]] : subscript out of bounds
Anyone knows what concept am I missing here? Thank you in advance!
Your specific use case is an example in ?aes. aes automatically quotes its arguments. One can simply directly pass the dots. Try:
plot_points3 <- function(d, ...){
print(aes(...))
ggplot(d, aes(...)) + geom_point(alpha = 0.1)
}
plot_points3(df, x = x, y = y, color = z)
This nicely prints:
Aesthetic mapping:
* `x` -> `x`
* `y` -> `y`
* `colour` -> `z`
And yields the required plot.
As mentioned in my comment, I think you may already have x and y in your environment and that is why some of your code is working. I'm not totally sure what you are trying to achieve but I think you are doing too much rlang for getting your code to run without error.
For example:
plot_points <- function(d, ...){
ggplot(data = d, mapping = aes(x = x, y = y)) +
geom_point(alpha = 0.1)
}
plot_points (df, x, y)
will make your plot without any reason to add the overhead and complexity of !!! or enquo().
You were on this path here too, where this much simpler code works fine:
add_dif_to_group_mean <- function(., ...) {
df %>% group_by(g1) %>% mutate(x_dif = x-mean(x),
y_dif = y-mean(y),
z_dif = z-mean(z))
}
df %>% add_dif_to_group_mean(g1)
Likewise:
plot_points2 <- function(d, ...){
ggplot(data = d, mapping = aes(x=x, y=y, color=z)) +
geom_point(alpha = 0.1)
}
plot_points2(df, x = x, y = y, color = z)
works fine from what I can tell.
So I understand that maybe you are working through the examples in the book, which is great. But I think there is a missing issue somewhere that would make it so you have to do all the extra stuff in a real world function. For example, maybe you want to pass in strings like "x" and "y" instead of x and y?
I have tried to fix this problem a number of ways, but I am new to R, so I don't know the tips and tricks. I am trying to graph a polynomial function using code for a quadratic function, provided by my teacher, however, I keep running into the "unused arguments error". the issue is that I have given three arguments, which is what the function expects. this is the code I entered:
> quadratic <- function(x, u.values){
+ X <- cbind(1, x, x^2)
+ return(X %*% u.values)
+ }
> dev.new()
> ggplot() +
+ geom_point(aes(x = t,y = y),data= GData)+
+ stat_function(fun=quadratic(args=c(1.9604816, -0.1201548, -4.9768049)))
Error in quadratic(args = c(1.9604816, -0.1201548, -4.9768049)) :
unused argument(s) (args = c(1.9604816, -0.1201548, -4.9768049))
As you've defined it, quadratic doesn't have an args parameter. That's what's causing your error.
I'm guessing -- but I can't test, since your example isn't reproducible -- that you should change args in your stat_function call to something like args = list(u.values = c(1.9604816, -0.1201548, -4.9768049)).
Edit:
To clarify, args parameter of stat_function takes additional arguments to the function you specify (quadratic in this case) as a list. What you're doing is passing a named argument args to quadratic, when you want to be passing a named argument args to stat_function.
Here is a reproducible example that works:
g <- data.frame(t = seq(0.1, 1, by = 0.1), y = seq(2.1, 3, by = 0.1))
quadratic <- function(x, u.values){
X <- cbind(1, x, x^2)
return(X %*% u.values)
}
ggplot(data = g, aes(x = t, y = y)) +
geom_point() +
stat_function(fun = quadratic,
args = list(u.values = c(1.9604816, -0.1201548, -4.9768049)))
Obviously this data has nothing to do with yours, but the quadratic function is applied correctly to the data and the results are plotted.
Please note that this is a simplified version (and therefore duplicate of my earlier post):
https://stackoverflow.com/questions/18358694/xyplot-2-separate-data-frame-lengths
It may well be that it contained too much information, however quite basic was asked.
So here again:
I would like to plot 2 columns of different length with xyplot (only xyplot please).
The data:
Data <- data.frame(var1=rnorm(10,0,1),prob=seq(0.023,0.365,length=10))
Long <- data.frame(var2=rnorm(20,2,3))
How I would plot the Long (var2) vector of length 20 onto the plot of "Data" where (prob~var1) is plotted first.
You can use approx() inside your call to xyplot to interpolate the values of Data$prob after passing Long$var2 as a separate variable in the call. Notice the custom prepanel plot to adjust the limits.
lattice::xyplot(prob ~ var1, data = Data, z = Long$var2,
xlab = "Var1, Var2",
prepanel = function(x, y, z, ...) {
list(xlim = range(x, z),
ylim = range(y))
},
panel = function(x, y, z, ...) {
b <- approx(y, n = length(z))$y
panel.xyplot(x, y, ...)
panel.xyplot(z, b, col = "orange", ...)
})
EDIT: Actually, it is much cleaner to just reshape the data first.
dd <- rbind(data.frame(var = "var1",
val = Data$var1,
prob = Data$prob),
data.frame(var = "var2",
val = Long$var2,
prob = approx(Data$prob, n = 20)$y))
xyplot(prob ~ val, data = dd, groups = var, auto.key = TRUE)