Reputation: 1
I am working in R version 4.3.2 (2023-10-31).
I am plotting a contour line for a function of 2 variables at a particular level of the function. I am doing so for two different functions on the same plot. I would like to include a small arrow at some point close to each contour line that shows a direction of increase of the function. Any direction will do though the direction of the gradient (at that point) will be best.
Here is some example code that creates contour lines like I want. How do I now include one arrow for each line at some point close to the contour line which shows a direction of increase of the function?
library(tidyverse)
x <- seq(1,2,length.out=100)
y <- seq(1,2,length.out=100)
myf <- function(x,y) {x*y}
myg <- function(x,y) {x^2 + y^2}
d1 <- expand.grid(X1 = x, X2 = y) %>%
mutate(Z = myf(X1,X2)) %>%
as.data.frame()
d2 <- expand.grid(X1 = x, X2 = y) %>%
mutate(Z = myg(X1,X2)) %>%
as.data.frame()
ggplot(data = d1, aes(x=X1,y=X2,z=Z))+
stat_contour(breaks = c(2)) +
stat_contour(data=d2, aes(x=X1,y=X2,z=Z), breaks=c(6))
Upvotes: 0
Views: 57
Reputation: 173803
Effectively Z
is the value of a scalar field over X1
and X2
. To show the gradient of Z
over X1
, X2
at particular points, you can calculate the rate of change of Z
in the y direction at each point, and the rate of change in the x direction at each point. The arctangent of their ratio (theta) will be the direction of the gradient. You can therefore draw a line of length L from any given point a in the direction of the gradient by drawing a segment between [X1a, X2a] and [X1a + L * cos(thetaa), X2a + L * sin(thetaa)]:
d1 <- d1 %>%
group_by(X1) %>%
arrange(X2) %>%
mutate(dzdy = c(NA, diff(Z)/diff(X2))) %>%
group_by(X2) %>%
arrange(X1) %>%
mutate(dzdx = c(NA, diff(Z)/diff(X1))) %>%
mutate(theta = atan2(dzdy, dzdx)) %>%
mutate(xend = X1 + 0.1 * cos(theta), yend = X2 + 0.1 * sin(theta))
d2 <- d2 %>%
group_by(X1) %>%
arrange(X2) %>%
mutate(dzdy = c(NA, diff(Z)/diff(X2))) %>%
group_by(X2) %>%
arrange(X1) %>%
mutate(dzdx = c(NA, diff(Z)/diff(X1))) %>%
mutate(theta = atan2(dzdy, dzdx)) %>%
mutate(xend = X1 + 0.1 * cos(theta), yend = X2 + 0.1 * sin(theta))
ggplot(data = d1, aes(X1, X2, z = Z)) +
geom_segment(aes(xend = xend, yend = yend),
data = d1 %>% filter(abs(Z - 2) < 0.0001),
arrow = arrow(length = unit(2, 'mm'), type = 'closed')) +
stat_contour(breaks = 2) +
stat_contour(data = d2, breaks = 6) +
geom_segment(aes(xend = xend, yend = yend),
data = d2 %>% filter(abs(Z - 6.01) < 0.001),
arrow = arrow(length = unit(2, 'mm'), type = 'closed')) +
coord_fixed()
We can see that these lines point in the direction of increasing Z
by showing them over contour maps of d1
ggplot(data = d1, aes(X1, X2, z = Z)) +
geom_contour_filled() +
geom_segment(aes(xend = xend, yend = yend),
data = d1 %>% filter(abs(Z - 2) < 0.0001),
arrow = arrow(length = unit(2, 'mm'), type = 'closed')) +
coord_fixed()
and d2
:
ggplot(data = d2, aes(X1, X2, z = Z)) +
geom_contour_filled() +
geom_segment(aes(xend = xend, yend = yend),
data = d2 %>% filter(abs(Z - 6.01) < 0.001),
arrow = arrow(length = unit(2, 'mm'), type = 'closed')) +
coord_fixed()
Upvotes: 1
Reputation: 41285
You could use geom_segment
to create custom arrow lines in your plot. You could specify the coordinates like this:
library(tidyverse)
ggplot(data = d1, aes(x=X1,y=X2,z=Z))+
stat_contour(breaks = c(2)) +
stat_contour(data=d2, aes(x=X1,y=X2,z=Z), breaks=c(6)) +
geom_segment(data = data.frame(x = c(1.25, 1.75),
xend = c(1.5, 1.5),
y = c(1.5, 1.75),
yend = c(1.25, 2)),
aes(x = x, y = y, xend = xend, yend = yend),
inherit.aes = FALSE,
arrow = arrow(length = unit(0.5, "cm")))
Created on 2024-01-11 with reprex v2.0.2
Upvotes: 0