I have a basic plot using stat.funciton:
x<-c(-5,5)
fun.1 <- function(x) x^2 + x
p<-ggplot(data.frame(x=x),aes(x)) +stat_function(fun = fun.1,geom="line")
print(p)
Is there any way to change the linetype of the plot conditional on the y-value? In other words, how can I change this plot so that values on the curve that lie above, say, y=10, are plotted as dotted instead of solid line? Note I get that I could make separate curves for the x-values, and define the function piecewise, but I'm looking for a simpler solution.
EDIT: The followibng code (piecewise-defined x) doesnt seem to work either:
x1<-c(-5,4)
fun.1 <- function(x1) x1^2 + x1
x2<-c(4,5)
fun.2 <- function(x2) x2^2 + x2
p1<-ggplot(data.frame(x=x1),aes(x1)) +stat_function(fun = fun.1,geom="line")
p1<-p1+ggplot(data=data.frame(x2),aes(x2))+stat_function(fun = fun.2,geom="line",lty=2)
print(p1)
>Error: Don't know how to add o to a plot
However the second plot works when done separately:
x2<-c(4,5)
fun.2 <- function(x2) x2^2 + x2
p2<-ggplot(data=data.frame(x2),aes(x2))+stat_function(fun = fun.2,geom="line",lty=2)
print(p2)
Please help?
You need to create a separate group for each segment of the curve that is above/below 10.
ggplot(data.frame(x=x),aes(x)) +
stat_function(fun = fun.1,geom="line", n=400,
aes(group=factor(c(0,cumsum(diff(..y.. >= 10) != 0))),
linetype=factor(c(0,cumsum(diff(..y.. >= 10) != 0))))) +
scale_linetype_manual(values=c(2,1,2)) +
guides(linetype=FALSE)
Below is more detail on how the groups are created. First we start with a simple y
vector for illustration:
> y = c(7:13,12:7)
> y
[1] 7 8 9 10 11 12 13 12 11 10 9 8 7
Now let's find where the curve is above or below 10:
> y >= 10
[1] FALSE FALSE FALSE TRUE TRUE TRUE TRUE TRUE TRUE TRUE FALSE FALSE FALSE
Mathematical functions treat logical TRUE
and FALSE
as having numeric values of, respectively, 1 and 0, so we can now find the specific values at which the curve crosses y==10
:
> diff(y >=10)
[1] 0 0 1 0 0 0 0 0 0 -1 0 0
We want to increment to a new group each time the curve crosses y==10
:
> diff(y >=10) != 0
[1] FALSE FALSE TRUE FALSE FALSE FALSE FALSE FALSE FALSE TRUE FALSE FALSE
> cumsum(diff(y >=10) != 0)
[1] 0 0 1 1 1 1 1 1 1 2 2 2
So now we have three different groups. But diff
returns a vector that is one shorter than the original vector. So we add 0
onto the beginning of the grouping vector so that it will be the same length as the data vectors.
> c(0, cumsum(diff(y >=10) != 0))
[1] 0 0 0 1 1 1 1 1 1 1 2 2 2
Finally, we convert it to a factor, otherwise linetype will give us an error for mapping a continuous variable to linetype
:
> factor(c(0, cumsum(diff(y >=10) != 0)))
[1] 0 0 0 1 1 1 1 1 1 1 2 2 2
Levels: 0 1 2
Also, ..y..
is the vector of y
values internally calculated by ggplot to plot your function, so that's why we use ..y..
inside ggplot.