Reputation: 1165
I would like to create a set of custom functions in ggplot using multiple variables in a dataframe to calculate the function output. Here's a toy example with slopes and y-intersects stored as 'a' and 'b' in two columns of a data frame. One simple solution I was able to figure out is to manually cycle through the values. output1
library(ggplot2)
# define slope (a) and y intersect (b) of lines
df <- data.frame(a=c(2, 1), b=c(0, -5))
# plot lines with slopes (a) and y intersect (b)
plot <- ggplot() + xlim(-50, 50) +
geom_function(fun = function(x) df$a[1]*x+df$b[1]) +
geom_function(fun = function(x) df$a[2]*x+df$b[2])
print(plot)
If I use a for loop ggplot seems to replace the first function with the second function and I'm not really sure why? output2
library(ggplot2)
# define slope (a) and y intersect (b) of lines
df <- data.frame(a=c(2, 1), b=c(0, -5))
# plot lines with slopes (a) and y intersect (b)
plot <- ggplot() + xlim(-50, 50)
for (i in 1:nrow(df)) {plot <- plot + geom_function(fun = function(x) df$a[i]*x+df$b[i])}
print(plot)
However, neither option is ideal in my eyes, but my intuitive solution clearly gives incorrect results. output3
library(ggplot2)
# define slope (a) and y intersect (b) of lines
df <- data.frame(a=c(2, 1), b=c(0, -5))
# plot lines with slopes (a) and y intersect (b)
plot <- ggplot() + xlim(-50, 50) +
geom_function(fun = function(x) df$a*x+df$b)
print(plot)
Is there no easy way to solve this within ggplot? I don't really want to create a new data frame for discrete x values to retain the option to plot functions instead of points connected by a line.
@Allan and @Magnus, I only chose the linear plots as the simplest example, but I would like to plot a custom function f(x) <- s/sqrt(x)/(2*c) for many s and c values. My current work-around has drawbacks (need to enumerate every pair of factors; only plots line connecting discrete data points not smooth function; ...). output4
library(ggplot2)
# define s and c
df <- data.frame(s=c(0.2, 0.5, 0.5), c=c(3, 5, 7))
# plot lines with function f(x) <- s/sqrt(x)/(2*c)
plot <- ggplot() + xlim(0, 50) +
geom_line(data=data.frame(x=1:50, y=df$s[1]/sqrt(1:50/(2*df$c[1]))), aes(x=x, y=y)) +
geom_line(data=data.frame(x=1:50, y=df$s[2]/sqrt(1:50/(2*df$c[2]))), aes(x=x, y=y)) +
geom_line(data=data.frame(x=1:50, y=df$s[3]/sqrt(1:50/(2*df$c[3]))), aes(x=x, y=y))
print(plot)
My 'intuitive' solution of calling the plot without specifying the row item geom_line(data=data.frame(x=1:50, y=df$s/sqrt(1:50/(2*df$c))), aes(x=x, y=y)
doesn't work and would only address part of the problem anyway.
Upvotes: 1
Views: 1804
Reputation: 1165
After a couple of weeks with R the answer to my previous question is kinda obvious. I was simply not creating the layers the right way and the for-loop is inefficient at doing so anyway. Without going into the details of the data, I was performing a 'power analysis of some of our studies.
I was able to show the average behavior across all studies, but I wanted to overlay a graph of the power of the study against sample number based on the variance of all individual studies as well.
I simply added a function called by mapply to just add a bunch of additional layers that I can then append to the basic plot. `my_funct describes the relationship between n, cv, and power of the study.
#// function to plot layers of dashed line for each study
geom_layer <- function (study, cv) { geom_function(aes(color=study), fun = my_funct, args = (cv), alpha=.7, linetype="dashed", size = 1) }
#// set up geom_lines for all other studies and store in layers
layers <- mapply(geom_layer, data_stats$study, data_stats$cv, SIMPLIFY = F )
#// combine plot and layers
print(plot + layers)
Upvotes: 3
Reputation: 173803
If you want all your lines on a single plot and you are planning to use simple straight lines with a slope and intercept, you could use geom_abline
. To give a slightly expanded example:
library(ggplot2)
# define slope (a) and y intersect (b) of lines
df <- data.frame(a = c(2, 1, -0.5, 1.5), b = c(0, -5, 5, 3))
# plot lines with slopes (a) and y intersect (b)
ggplot(df) +
xlim(-50, 50) +
ylim(-50, 50) +
geom_abline(aes(slope = a, intercept = b, color = factor(seq(nrow(df))))) +
labs(color = "equation")
Upvotes: 0
Reputation: 951
I'm not sure what you are looking for. If you want to loop through a dataframe which consist of slopes and intercept values, then here is a way. The thing to keep in mind here is evaluation of expressions. See more info in this post. If this is not what you are looking for, please provide an example of the preferred output.
library(ggplot2)
# Either
df <- data.frame(a=c(10, 5), b=c(50, -5))
plot <- ggplot()
for (i in 1:nrow(df)) {print(plot + geom_function(fun = function(x) df$a[i]*x+df$b[i]))}
# Or the list way to save each plot
df <- data.frame(a=c(10, 5), b=c(50, -5))
plot_list <- vector("list", nrow(df))
for (i in 1:nrow(df)) {
plot_list[[i]] <- local({
i <- i
p <- ggplot() + geom_function(fun = function(x) df$a[i]*x+df$b[i])
print(p)
})
}
Created on 2020-12-01 by the reprex package (v0.3.0)
Upvotes: 0