Plot a discontinuous function in R without connecting a "jump"

Viewed 48

I'd like to plot a discontinuous function without connecting a jump. For example, in the following plot, I'd like to delete the line connecting (0.5, 0.5) and (0.5, 1.5).

f <- function(x){
  (x < .5) * (x) + (x >= .5) * (x + 1)
}
ggplot()+ 
  geom_function(fun = f)

Edit: I'm looking for a solution that works even if the discountinuous point is not a round number, say pi/10.

3 Answers

You could write a little wrapper function which finds discontinuities in the given function and plots them as separate groups:

plot_fun <- function(fun, from = 0, to = 1, by = 0.001) {
  
  x <- seq(from, to, by)
  groups <- cut(x, c(-Inf, x[which(abs(diff(fun(x))) > 0.1)], Inf))
  df <- data.frame(x, groups, y = fun(x))
  
  ggplot(df, aes(x, y, group = groups)) +
    geom_line()
}

This allows

plot_fun(f)

enter image description here

plot_fun(floor, 0, 10)

enter image description here

This answer is based on Allan Cameron's answer, but depicts the jump using open and closed circles. Whether the function is right or left continuous is controlled by an argument.

library("ggplot2")
plot_fun <- function(fun, from = 0, to = 1, by = 0.001, right_continuous = TRUE) {

  x <- seq(from, to, by)
  tol_vertical <- 0.1
  y <- fun(x)
  idx_break <- which(abs(diff(y)) > tol_vertical)
  x_break <- x[idx_break]
  y_break_l <- y[idx_break]
  y_break_r <- y[idx_break + 1]

  groups <- cut(x, c(-Inf, x_break, Inf))
  df <- data.frame(x, groups, y = fun(x))

  plot_ <- ggplot(df, aes(x, y, group = groups)) +
            geom_line()

  # add open and closed points showing jump
  dataf_l <- data.frame(x = x_break, y = y_break_l)
  dataf_r <- data.frame(x = x_break, y = y_break_r)
  shape_open_circle <- 1
  # this is the default of shape, but might as well specify.
  shape_closed_circle <- 19
  shape_size <- 4
  if (right_continuous) {
    shape_l <- shape_open_circle
    shape_r <- shape_closed_circle
  } else {
    shape_l <- shape_closed_circle
    shape_r <- shape_open_circle
  }

  plot_ <- plot_ +
    geom_point(data = dataf_l, aes(x = x, y = y), group = NA, shape = shape_l, size = shape_size) +
    geom_point(data = dataf_r, aes(x = x, y = y), group = NA, shape = shape_r, size = shape_size)
  return(plot_)
}

Here's the OP's original example:

f <- function(x){
  (x < .5) * (x) + (x >= .5) * (x + 1)
}
plot_fun(f)

Plot of "f"

Here's Allan's additional example using floor, which shows multiple discontinuities:

plot_fun(floor, from = 0, to = 10)

Plot of "floor"

And here's an example showing that the function does not need to be piecewise linear:

f_curved <- function(x) ifelse(x > 0, yes = 0.5*(2-exp(-x)), no = 0)
plot_fun(f_curved, from = -1, to = 5)

Plot of "f_curved"

You can insert everything inside an ifelse:

f <- function(x){
  ifelse(x==0.5, 
         NA, 
         (x < .5) * (x) + (x >= .5) * (x + 1))
}
ggplot()+ 
  geom_function(fun = f)

enter image description here

Related