fulem
fulem

Reputation: 85

R: Plot multiple functions with ggplot2

I need to plot multiple explicit functions definitions to compare them visually.

Consider the family of functions:

fun.1 <- function(x) { 1 / ( 0.01 + x) }
fun.2 <- function(x) { 1 / ( 0.1 + x) }
fun.3 <- function(x) { 1 / ( 0.3 + x) }

The idea is to plot the result of evaluating these functions on the following inputs:

xs = c(1, 2, 4, 8, 16, 32, 64, 128, 256, 512, 1024, 2048)

Currently, my code is:

library(ggplot2)
library(dplyr)

xs = c(1, 2, 4, 8, 16, 32, 64, 128, 256, 512, 1024, 2048)

fun.1 <- function(x) { 1 / ( 0.01 + x) }
fun.2 <- function(x) { 1 / ( 0.1 + x) }
fun.3 <- function(x) { 1 / ( 0.3 + x) }

base <- ggplot(data.frame(x = xs), aes(x=as.factor(x)))

base + theme_classic() +
       theme(
          legend.position = c(.95, .95),
          legend.justification = c("right", "top"),
          legend.box.just = "right",
          legend.margin = margin(6, 6, 6, 6)
          ) +
       geom_function(fun = fun.1, colour = "red") + 
       geom_function(fun = fun.2, colour = "blue") +
       geom_function(fun = fun.3, colour = "yellow") +
       scale_x_discrete(name = "Input",
                        breaks = xs) +
       scale_y_discrete(name = "Growth")

The resulting plot is this

Now this looks ugly among other things, i can't make the legend to appear and there are warnings:

Multiple drawing groups in `geom_function()`. Did you use the correct `group`, `colour`, or `fill` aesthetics?
Multiple drawing groups in `geom_function()`. Did you use the correct `group`, `colour`, or `fill` aesthetics?
Multiple drawing groups in `geom_function()`. Did you use the correct `group`, `colour`, or `fill` aesthetics?

The style i'm trying to get is like this. Ideally, the points should be visible and connected by a continuous curve.

The question is, what is the best way to create a plot with multiple functions with a style like this ?

Upvotes: 1

Views: 2770

Answers (2)

Duck
Duck

Reputation: 39585

You can also try:

library(ggplot2)
#Plot
base <- ggplot(data.frame(x = xs), aes(x=as.factor(x)))
#Plot 2
base + theme_classic() +
  theme(
    legend.position = c(.95, .95),
    legend.justification = c("right", "top"),
    legend.box.just = "right",
    legend.margin = margin(6, 6, 6, 6)
  ) +
  stat_function(fun = fun.1, aes(colour = "fun.1"),geom = 'point',size=1) + 
  stat_function(fun = fun.1, aes(colour = "fun.1"),geom = 'line') + 
  stat_function(fun = fun.2, aes(colour = "fun.2"),geom='point',size=1) +
  stat_function(fun = fun.2, aes(colour = "fun.2"),geom = 'line') +
  stat_function(fun = fun.3, aes(colour = "fun.3"),geom = 'point',size=1) +
  stat_function(fun = fun.3, aes(colour = "fun.3"),geom = 'line') +
  scale_x_discrete(name = "Input",
                   breaks = xs) +
  scale_y_discrete(name = "Growth")+
  scale_color_manual(values = c('red','blue','yellow'))

Output:

enter image description here

Upvotes: 0

teunbrand
teunbrand

Reputation: 37903

You can just lapply() over something and if it automatically gets added to the ggplot because it is a list. This is only useful though if the output of the lapply loop are things ggplot understands, such as geoms.

The example below assumes that the relevant function can be found using the naming structure you provided.

library(ggplot2)
library(dplyr)
#> 
#> Attaching package: 'dplyr'
#> The following objects are masked from 'package:stats':
#> 
#>     filter, lag
#> The following objects are masked from 'package:base':
#> 
#>     intersect, setdiff, setequal, union

xs = c(1, 2, 4, 8, 16, 32, 64, 128, 256, 512, 1024, 2048)

fun.1 <- function(x) { 1 / ( 0.01 + x) }
fun.2 <- function(x) { 1 / ( 0.1 + x) }
fun.3 <- function(x) { 1 / ( 0.3 + x) }

ggplot(data.frame(x = xs), aes(x=x)) + 
  lapply(1:3, function(i) {
    fun_name <- paste0("fun.", i)
    fun <- get(fun_name)
    line <- geom_function(fun = fun,
                          aes(colour = fun_name))
    points <- geom_point(aes(y = fun(xs), colour = fun_name))
    list(line, points)
  }) +
  scale_x_log10(breaks = xs, name = "Input") +
  scale_y_continuous(name = "Growth") +
  theme_classic() +
  theme(
    legend.position = c(.95, .95),
    legend.justification = c("right", "top"),
    legend.box.just = "right",
    legend.margin = margin(6, 6, 6, 6)
  )

Created on 2020-12-07 by the reprex package (v0.3.0)

Point of note, whereas your plot seems continuous in almost every way, you add the _discrete() variant of position scales which I don't understand. I converted everything to numeric instead.

Upvotes: 3

Related Questions