Skip to content

ggplot with arrows

10 messages · Ebert,Timothy Aaron, @vi@e@gross m@iii@g oii gm@ii@com, Rui Barradas +3 more

#
Consider:

x <- seq(-5,5,length.out = 100)
y <- x^2
data <- data.frame(x,y)
library(ggplot2)
ggplot(data,aes(x,y))+
? stat_function(
? ? fun = function(x) x^2,
? ? color = "blue", linewidth = 1.25
? ) +
? theme_linedraw()
I'd like to add an arrow to the ends of curve to illustrate the?curve continues indefinitely in that direction,
ChatGPT suggests using geom_segment or geom_link but there has an easier way to do this.

Any suggestions would be appreciated.
#
I would save the graph as a PowerPoint object and then edit it in PowerPoint.
library(ggplot2)
library(grid)
library(officer)
library(rvg)
x <- seq(-5, 5, length.out = 100)
y <- x^2
data <- data.frame(x, y)
plot <- ggplot(data, aes(x, y)) +
  geom_path(color = "blue", linewidth = 1.25) +
  geom_segment(
    aes(x = x[1], y = y[1], xend = x[100], yend = y[100]),
    arrow = arrow(angle = 20, type = "closed", ends = "both", length = unit(0.2, "inches")),
    color = "red"
  ) +
  theme_linedraw()
doc <- read_pptx()
doc <- add_slide(doc, layout = "Title and Content", master = "Office Theme")
doc <- ph_with(doc, dml(ggobj = plot), location = ph_location_fullsize())
print(doc, target = "quadratic_with_arrows.pptx")

If I remember I think you have to ungroup it in PowerPoint and then all elements become editable. The general approach can be done with other file formats/programs, not just PowerPoint.
Tim

-----Original Message-----
From: R-help <r-help-bounces at r-project.org> On Behalf Of Thomas Subia via R-help
Sent: Friday, August 1, 2025 10:31 AM
To: r-help at r-project.org
Subject: [R] ggplot with arrows

[External Email]

Consider:

x <- seq(-5,5,length.out = 100)
y <- x^2
data <- data.frame(x,y)
library(ggplot2)
ggplot(data,aes(x,y))+
  stat_function(
    fun = function(x) x^2,
    color = "blue", linewidth = 1.25
  ) +
  theme_linedraw()
I'd like to add an arrow to the ends of curve to illustrate the curve continues indefinitely in that direction, ChatGPT suggests using geom_segment or geom_link but there has an easier way to do this.

Any suggestions would be appreciated.



______________________________________________
R-help at r-project.org mailing list -- To UNSUBSCRIBE and more, see
https://stat.ethz.ch/mailman/listinfo/r-help
PLEASE do read the posting guide https://www.r-project.org/posting-guide.html
and provide commented, minimal, self-contained, reproducible code.
#
For some, the purist perspective is to automate a process using a single
tool. Some see it as pure to use a series of (often smaller and more focused
set of) tools that together do the job.

In a sense, ggplot is closer to the latter with endless extensions you can
add in to tweak a result and sometimes people stand on their heads to do
something that could be done with less effort by a technique like switching
mid-way to another tool, perhaps with a manual component. 

The suggestion by Tim is reasonable and, as he notes, common enough
especially for one-time projects or people who are not as great at R
programming as using other tools like PowerPoint or EXCEL. And, sometimes
you have to hand your work to a client who wants to adjust things just so
using the tool of their choice, including playing with fonts or where to
wrap text or position things more precisely.

Of course, if you do things repeatedly such as make a new graph every day
with more data, it gets annoying to have to step out each time and take a
saved file and open it anew and ...


-----Original Message-----
From: R-help <r-help-bounces at r-project.org> On Behalf Of Ebert,Timothy Aaron
Sent: Friday, August 1, 2025 10:44 AM
To: Thomas Subia <tgs77m at yahoo.com>; r-help at r-project.org
Subject: Re: [R] ggplot with arrows

I would save the graph as a PowerPoint object and then edit it in
PowerPoint.
library(ggplot2)
library(grid)
library(officer)
library(rvg)
x <- seq(-5, 5, length.out = 100)
y <- x^2
data <- data.frame(x, y)
plot <- ggplot(data, aes(x, y)) +
  geom_path(color = "blue", linewidth = 1.25) +
  geom_segment(
    aes(x = x[1], y = y[1], xend = x[100], yend = y[100]),
    arrow = arrow(angle = 20, type = "closed", ends = "both", length =
unit(0.2, "inches")),
    color = "red"
  ) +
  theme_linedraw()
doc <- read_pptx()
doc <- add_slide(doc, layout = "Title and Content", master = "Office Theme")
doc <- ph_with(doc, dml(ggobj = plot), location = ph_location_fullsize())
print(doc, target = "quadratic_with_arrows.pptx")

If I remember I think you have to ungroup it in PowerPoint and then all
elements become editable. The general approach can be done with other file
formats/programs, not just PowerPoint.
Tim

-----Original Message-----
From: R-help <r-help-bounces at r-project.org> On Behalf Of Thomas Subia via
R-help
Sent: Friday, August 1, 2025 10:31 AM
To: r-help at r-project.org
Subject: [R] ggplot with arrows

[External Email]

Consider:

x <- seq(-5,5,length.out = 100)
y <- x^2
data <- data.frame(x,y)
library(ggplot2)
ggplot(data,aes(x,y))+
  stat_function(
    fun = function(x) x^2,
    color = "blue", linewidth = 1.25
  ) +
  theme_linedraw()
I'd like to add an arrow to the ends of curve to illustrate the curve
continues indefinitely in that direction, ChatGPT suggests using
geom_segment or geom_link but there has an easier way to do this.

Any suggestions would be appreciated.



______________________________________________
R-help at r-project.org mailing list -- To UNSUBSCRIBE and more, see
https://stat.ethz.ch/mailman/listinfo/r-help
PLEASE do read the posting guide
https://www.r-project.org/posting-guide.html
and provide commented, minimal, self-contained, reproducible code.

______________________________________________
R-help at r-project.org mailing list -- To UNSUBSCRIBE and more, see
https://stat.ethz.ch/mailman/listinfo/r-help
PLEASE do read the posting guide
https://www.R-project.org/posting-guide.html
and provide commented, minimal, self-contained, reproducible code.
#
On 8/1/2025 3:43 PM, Ebert,Timothy Aaron wrote:
Hello,

First of all, according to the documentation ?stat_function, section 
Arguments,


data	
Ignored by stat_function(), do not use.


As for the arrows, first get the equations of the lines tangent to the 
end points of the parabola,

y = -10*x - 25
y = 10*x - 25

then compute segments' end points and plot.


f <- function(x, b = -25, m) m*x + b
arrow_data <- data.frame(
   id = c("n", "p"),
   x = c(-5, 5),
   y = f(c(-5, 5), m = c(-10, 10)),
   xend = c(-6, 6),
   yend = f(c(-6, 6), m = c(-10, 10))
)

ggplot() +
   stat_function(
     fun = function(x) x^2,
     color = "blue", linewidth = 1.25,
     xlim = c(-5, 5)
   ) +
   geom_segment(
     data = arrow_data,
     mapping = aes(x = x, y = y, xend = xend, yend = yend, group = id),
     arrow = arrow(length = unit(0.5, "cm")),
     linewidth = 1.25,
     linetype = "dashed",
     inherit.aes = FALSE
   ) +
   theme_linedraw()



Remove linetype and add color if you want.

Hope this helps,

Rui Barradas
#
On 8/1/2025 4:15 PM, Rui Barradas wrote:
Hello,

Sorry, my previous code is a bit messy.

You don't need to group, so I removed 'id' from the arrow_data.
It's better to define the arrows' end points x coordinates as variables, 
it makes it easier to change the plot at will.

And the function is now defined with the intercept as last argument.

f <- function(x, m, b = -25) m*x + b

# here plot with linetype = "dashed"
x0 <- c(-5, 5)
x1 <- c(-6, 6)
arrow_data <- data.frame(
   x = x0,
   y = f(x = x0, m = c(-10, 10)),
   xend = x1,
   yend = f(x = x1, m = c(-10, 10))
)

ggplot() +
   stat_function(
     fun = function(x) x^2,
     color = "blue", linewidth = 1.25,
     xlim = c(-5, 5)
   ) +
   geom_segment(
     data = arrow_data,
     mapping = aes(x = x, y = y, xend = xend, yend = yend),
     arrow = arrow(length = unit(0.5, "cm")),
     linewidth = 1.25,
     linetype = "dashed",
     inherit.aes = FALSE
   ) +
   theme_linedraw()


#---

# here the line type is the default solid, so
# start the arrows a bit away from the parabola, 0.1 away.
x0 <- c(-5.1, 5.1)
x1 <- c(-6, 6)
arrow_data <- data.frame(
   x = x0,
   y = f(x = x0, m = c(-10, 10)),
   xend = x1,
   yend = f(x = x1, m = c(-10, 10))
)

ggplot() +
   stat_function(
     fun = function(x) x^2,
     color = "blue", linewidth = 1.25,
     xlim = c(-5, 5)
   ) +
   geom_segment(
     data = arrow_data,
     mapping = aes(x = x, y = y, xend = xend, yend = yend),
     arrow = arrow(length = unit(0.5, "cm")),
     linewidth = 1.25,
     inherit.aes = FALSE
   ) +
   theme_linedraw()


Hope this helps,

Rui Barradas
2 days later
#
Hi

At the risk of being labelled a "purist", you do not have to leave R to 
gain access to the underlying components and modify them.

The following code combines Rui's tidy up of the ggplot() call with an 
example of directly modifying the underlying 'grid' grob to add arrows 
to the ends.  There are more arguments in arrow() that you can explore 
if you want to.


library(ggplot2)
library(grid)

ggplot() +
   stat_function(
     fun = function(x) x^2,
     color = "blue", linewidth = 1.25,
     xlim = c(-5, 5)
   ) +
   theme_linedraw()

grid.force()
## Check the name of the line grob
## grid.ls()
grid.edit("GRID.polyline", grep=TRUE,
           arrow=arrow(ends="both"))


Hope that helps.

Paul
On 2/08/2025 3:24 am, Rui Barradas wrote:

  
    
#
That was good in that the arrows look like part of the figure.
In this problem I have two choices. The easy one is that this is a one-off task. The easy solution (for me) is to to offload the figure to another program and add the arrows manually. They will look like I want and I will be done in 5 to 10 minutes. It might take a few minutes longer if I find that PowerPoint is not quite working out and I need a program like Photoshop. I can copy-paste, or change the output file type. The difficult problem is where this is one case in a broader application where I need to graph a function within the specified range and add arrows to the ends of the function if it continues beyond the plotted limits as expected from the plotted portion. If this is for a classroom it may use methods far beyond the abilities of the students present (programming, and mathematically).

Arrows for sin(x) or tan(x) do not make as much sense as for the x^2 function provided. The program does not catch cases where the function may be undefined or change directions outside the printed range. The arrow gives the impression that the figure will continue in the same direction for all x outside of the printed range.
Calculus gives us a way to check function performance. Something like this, though I suspect this is a start rather than a final solution:
library(ggplot2)
library(grid)
library(numDeriv)

Min_x <- -10
Max_x <- 1
my_function <- "x^2 / (1 - x)"

f_expr <- parse(text = my_function)
f <- function(x) eval(f_expr[[1]], envir = list(x = x))
detect_discontinuity <- function(f, Min_x, Max_x, buffer = 2, n = 1000, jump_tol = 100) {
  x_seq <- seq(Min_x - buffer, Max_x + buffer, length.out = n)
  y_seq <- tryCatch(f(x_seq), error = function(e) rep(NA, length(x_seq)))
  bad_vals <- !is.finite(y_seq)
  jumps <- abs(diff(y_seq))
  big_jump <- which(jumps > jump_tol)
  discont_locs <- sort(unique(c(
    x_seq[bad_vals],
    x_seq[big_jump]
  )))

  list(
    discontinuities = discont_locs,
    has_left = any(discont_locs < Min_x),
    has_right = any(discont_locs > Max_x)
  )
}
plot_with_discontinuity_check <- function(f, Min_x, Max_x) {
  check <- detect_discontinuity(f, Min_x, Max_x)

  p <- ggplot() +
    stat_function(
      fun = f,
      color = "blue", linewidth = 1.25,
      xlim = c(Min_x, Max_x)
    ) +
    theme_linedraw()
  print(p)
  grid.force()
  if (!check$has_left && !check$has_right) {
    grid.edit("GRID.polyline", grep = TRUE, arrow = arrow(ends = "both"))
  } else if (!check$has_left) {
    grid.edit("GRID.polyline", grep = TRUE, arrow = arrow(ends = "first"))
  } else if (!check$has_right) {
    grid.edit("GRID.polyline", grep = TRUE, arrow = arrow(ends = "last"))
  }
  if (check$has_left) {
    grid.text("Left: Discontinuity prevents extension",
              x = unit(0.05, "npc"), y = unit(0.95, "npc"),
              just = "left", gp = gpar(col = "black", fontsize = 8))
  }
  if (check$has_right) {
    grid.text("Right: Discontinuity prevents extension",
              x = unit(0.95, "npc"), y = unit(0.95, "npc"),
              just = "right", gp = gpar(col = "black", fontsize = 8))
  }
}
plot_with_discontinuity_check(f, Min_x, Max_x)

I suspect there are better approaches, but some form of error checking is essential to ensure that arrows are appropriate at both ends of the function as graphed. Graphs should not lie, but that is a difficult task in the generic problem. Prove that the function does not change direction or have a discontinuity outside the printed range from -infinity to +infinity. It is also possible that the graph is well behaved on one side but not the other.

Is there a better solution? Are there other ways to check for conditions that might make arrows inappropriate?
One could check and see if the graph ends at a discontinuity and add a bar at the end of the line. One might check to see if a discontinuity occurs within the figure and add a bar at the end of each line at the point of the discontinuity. I think the current graph glosses over the problem. How broadly does one define a function? Is this a function with respect to this problem: Ifelse(x>4,x^2,3x*x^0.5)?

Tim

-----Original Message-----
From: Paul Murrell <paul at stat.auckland.ac.nz>
Sent: Sunday, August 3, 2025 5:01 PM
To: Rui Barradas <ruipbarradas at sapo.pt>; Ebert,Timothy Aaron <tebert at ufl.edu>; Thomas Subia <tgs77m at yahoo.com>; r-help at r-project.org
Subject: Re: [R] ggplot with arrows

[External Email]

Hi

At the risk of being labelled a "purist", you do not have to leave R to gain access to the underlying components and modify them.

The following code combines Rui's tidy up of the ggplot() call with an example of directly modifying the underlying 'grid' grob to add arrows to the ends.  There are more arguments in arrow() that you can explore if you want to.


library(ggplot2)
library(grid)

ggplot() +
   stat_function(
     fun = function(x) x^2,
     color = "blue", linewidth = 1.25,
     xlim = c(-5, 5)
   ) +
   theme_linedraw()

grid.force()
## Check the name of the line grob
## grid.ls()
grid.edit("GRID.polyline", grep=TRUE,
           arrow=arrow(ends="both"))


Hope that helps.

Paul
On 2/08/2025 3:24 am, Rui Barradas wrote:
--
Dr Paul Murrell (he/him)
Te Kura Tatauranga | Department of Statistics Waipapa Taumata Rau | The University of Auckland Private Bag 92019, Auckland 1142, New Zealand
64 9 3737599 x85392
paul at stat.auckland.ac.nz
http://www.stat.auckland.ac.nz/~paul/
#
Thanks all for the suggestions!?
Your suggestions will help my students better visualize how functions behave. and help me to write more efficient ggplot scripts!
On Sunday, August 3, 2025 at 04:37:40 PM PDT, Ebert,Timothy Aaron <tebert at ufl.edu> wrote:
That was good in that the arrows look like part of the figure.
In this problem I have two choices. The easy one is that this is a one-off task. The easy solution (for me) is to to offload the figure to another program and add the arrows manually. They will look like I want and I will be done in 5 to 10 minutes. It might take a few minutes longer if I find that PowerPoint is not quite working out and I need a program like Photoshop. I can copy-paste, or change the output file type. The difficult problem is where this is one case in a broader application where I need to graph a function within the specified range and add arrows to the ends of the function if it continues beyond the plotted limits as expected from the plotted portion. If this is for a classroom it may use methods far beyond the abilities of the students present (programming, and mathematically).

Arrows for sin(x) or tan(x) do not make as much sense as for the x^2 function provided. The program does not catch cases where the function may be undefined or change directions outside the printed range. The arrow gives the impression that the figure will continue in the same direction for all x outside of the printed range.
Calculus gives us a way to check function performance. Something like this, though I suspect this is a start rather than a final solution:
library(ggplot2)
library(grid)
library(numDeriv)

Min_x <- -10
Max_x <- 1
my_function <- "x^2 / (1 - x)"

f_expr <- parse(text = my_function)
f <- function(x) eval(f_expr[[1]], envir = list(x = x))
detect_discontinuity <- function(f, Min_x, Max_x, buffer = 2, n = 1000, jump_tol = 100) {
? x_seq <- seq(Min_x - buffer, Max_x + buffer, length.out = n)
? y_seq <- tryCatch(f(x_seq), error = function(e) rep(NA, length(x_seq)))
? bad_vals <- !is.finite(y_seq)
? jumps <- abs(diff(y_seq))
? big_jump <- which(jumps > jump_tol)
? discont_locs <- sort(unique(c(
? ? x_seq[bad_vals],
? ? x_seq[big_jump]
? )))

? list(
? ? discontinuities = discont_locs,
? ? has_left = any(discont_locs < Min_x),
? ? has_right = any(discont_locs > Max_x)
? )
}
plot_with_discontinuity_check <- function(f, Min_x, Max_x) {
? check <- detect_discontinuity(f, Min_x, Max_x)

? p <- ggplot() +
? ? stat_function(
? ? ? fun = f,
? ? ? color = "blue", linewidth = 1.25,
? ? ? xlim = c(Min_x, Max_x)
? ? ) +
? ? theme_linedraw()
? print(p)
? grid.force()
? if (!check$has_left && !check$has_right) {
? ? grid.edit("GRID.polyline", grep = TRUE, arrow = arrow(ends = "both"))
? } else if (!check$has_left) {
? ? grid.edit("GRID.polyline", grep = TRUE, arrow = arrow(ends = "first"))
? } else if (!check$has_right) {
? ? grid.edit("GRID.polyline", grep = TRUE, arrow = arrow(ends = "last"))
? }
? if (check$has_left) {
? ? grid.text("Left: Discontinuity prevents extension",
? ? ? ? ? ? ? x = unit(0.05, "npc"), y = unit(0.95, "npc"),
? ? ? ? ? ? ? just = "left", gp = gpar(col = "black", fontsize = 8))
? }
? if (check$has_right) {
? ? grid.text("Right: Discontinuity prevents extension",
? ? ? ? ? ? ? x = unit(0.95, "npc"), y = unit(0.95, "npc"),
? ? ? ? ? ? ? just = "right", gp = gpar(col = "black", fontsize = 8))
? }
}
plot_with_discontinuity_check(f, Min_x, Max_x)

I suspect there are better approaches, but some form of error checking is essential to ensure that arrows are appropriate at both ends of the function as graphed. Graphs should not lie, but that is a difficult task in the generic problem. Prove that the function does not change direction or have a discontinuity outside the printed range from -infinity to +infinity. It is also possible that the graph is well behaved on one side but not the other.

Is there a better solution? Are there other ways to check for conditions that might make arrows inappropriate?
One could check and see if the graph ends at a discontinuity and add a bar at the end of the line. One might check to see if a discontinuity occurs within the figure and add a bar at the end of each line at the point of the discontinuity. I think the current graph glosses over the problem. How broadly does one define a function? Is this a function with respect to this problem: Ifelse(x>4,x^2,3x*x^0.5)?

Tim

-----Original Message-----
From: Paul Murrell <paul at stat.auckland.ac.nz>
Sent: Sunday, August 3, 2025 5:01 PM
To: Rui Barradas <ruipbarradas at sapo.pt>; Ebert,Timothy Aaron <tebert at ufl.edu>; Thomas Subia <tgs77m at yahoo.com>; r-help at r-project.org
Subject: Re: [R] ggplot with arrows

[External Email]

Hi

At the risk of being labelled a "purist", you do not have to leave R to gain access to the underlying components and modify them.

The following code combines Rui's tidy up of the ggplot() call with an example of directly modifying the underlying 'grid' grob to add arrows to the ends.? There are more arguments in arrow() that you can explore if you want to.


library(ggplot2)
library(grid)

ggplot() +
? stat_function(
? ? fun = function(x) x^2,
? ? color = "blue", linewidth = 1.25,
? ? xlim = c(-5, 5)
? ) +
? theme_linedraw()

grid.force()
## Check the name of the line grob
## grid.ls()
grid.edit("GRID.polyline", grep=TRUE,
? ? ? ? ? arrow=arrow(ends="both"))


Hope that helps.

Paul
On 2/08/2025 3:24 am, Rui Barradas wrote:

            
--
Dr Paul Murrell (he/him)
Te Kura Tatauranga | Department of Statistics Waipapa Taumata Rau | The University of Auckland Private Bag 92019, Auckland 1142, New Zealand
64 9 3737599 x85392
paul at stat.auckland.ac.nz

http://www.stat.auckland.ac.nz/~paul/
15 days later
#
> Hi
    > At the risk of being labelled a "purist", you do not have to leave R to 
    > gain access to the underlying components and modify them.

    > The following code combines Rui's tidy up of the ggplot() call with an 
    > example of directly modifying the underlying 'grid' grob to add arrows 
    > to the ends.  There are more arguments in arrow() that you can explore 
    > if you want to.


    > library(ggplot2)
    > library(grid)

    > ggplot() +
    > stat_function(
    >   fun = function(x) x^2,
    >   color = "blue", linewidth = 1.25,
    >   xlim = c(-5, 5)
    > ) +
    > theme_linedraw()

    > grid.force()
    > ## Check the name of the line grob
    > ## grid.ls()
    > grid.edit("GRID.polyline", grep=TRUE,
    >           arrow=arrow(ends="both"))


    > Hope that helps.

    > Paul

I'm coming a bit late, but ..

Indeed, the above not only helps but is  "wonderful" -- truly convincing !

-- thanks to Paul M.,  the creator of {grid}
   (and hence *the* enabler of ggplot2 !)

Martin
> On 2/08/2025 3:24 am, Rui Barradas wrote:
>> On 8/1/2025 4:15 PM, Rui Barradas wrote:
>>> On 8/1/2025 3:43 PM, Ebert,Timothy Aaron wrote:
>>>> I would save the graph as a PowerPoint object and then edit it in 
    >>>> PowerPoint.
    >>>> library(ggplot2)
    >>>> library(grid)
    >>>> library(officer)
    >>>> library(rvg)
    >>>> x <- seq(-5, 5, length.out = 100)
    >>>> y <- x^2
    >>>> data <- data.frame(x, y)
    >>>> plot <- ggplot(data, aes(x, y)) +
    >>>> ?? geom_path(color = "blue", linewidth = 1.25) +
    >>>> ?? geom_segment(
    >>>> ???? aes(x = x[1], y = y[1], xend = x[100], yend = y[100]),
    >>>> ???? arrow = arrow(angle = 20, type = "closed", ends = "both", length 
    >>>>                    = unit(0.2, "inches")),
    >>>> ???? color = "red"
    >>>> ?? ) +
    >>>> ?? theme_linedraw()
    >>>> doc <- read_pptx()
    >>>> doc <- add_slide(doc, layout = "Title and Content", master = "Office Theme")
    >>>> doc <- ph_with(doc, dml(ggobj = plot), location = ph_location_fullsize())
    >>>> print(doc, target = "quadratic_with_arrows.pptx")
    >>>> 
    >>>> If I remember I think you have to ungroup it in PowerPoint and then 
    >>>> all elements become editable. The general approach can be done with 
    >>>> other file formats/programs, not just PowerPoint.
    >>>> Tim
    >>>> 
    >>>> -----Original Message-----
    >>>> From: R-help <r-help-bounces at r-project.org> On Behalf Of Thomas Subia 
    >>>> via R-help
    >>>> Sent: Friday, August 1, 2025 10:31 AM
    >>>> To: r-help at r-project.org
    >>>> Subject: [R] ggplot with arrows
    >>>> 
    >>>> [External Email]
    >>>> 
    >>>> Consider:
    >>>> 
    >>>> x <- seq(-5,5,length.out = 100)
    >>>> y <- x^2
    >>>> data <- data.frame(x,y)
    >>>> library(ggplot2)
    >>>> ggplot(data,aes(x,y))+
    >>>> ?? stat_function(
    >>>> ???? fun = function(x) x^2,
    >>>> ???? color = "blue", linewidth = 1.25
    >>>> ?? ) +
    >>>> ?? theme_linedraw()

    >>>> I'd like to add an arrow to the ends of curve to illustrate the curve 
    >>>> continues indefinitely in that direction, ChatGPT suggests using 
    >>>> geom_segment or geom_link but there has an easier way to do this.
    >>>> 
    >>>> Any suggestions would be appreciated.
......
......
#
Colleagues,

Thanks again for the suggestions.

Following your suggestions

library(ggplot2)
library(grid)

p <- ggplot() +
? stat_function(
? ? fun = function(x) x^2,
? ? color = "blue", linewidth = 1.25,
? ? xlim = c(-5, 5)
? ) +
? theme_linedraw() +
? coord_cartesian(clip = "off")? ?# <--- allow arrows outside

p

# Force evaluation so we can edit
grid.force()

# Add arrows to curve ends
grid.edit("GRID.polyline", grep=TRUE,
? ? ? ? ? arrow=arrow(ends="both", type="closed"))
This makes arrow creation so much easier!

Thomas
On Tuesday, August 19, 2025 at 06:19:47 AM PDT, Martin Maechler <maechler at stat.math.ethz.ch> wrote:

            
? ? > Hi
? ? > At the risk of being labelled a "purist", you do not have to leave R to 
? ? > gain access to the underlying components and modify them.

? ? > The following code combines Rui's tidy up of the ggplot() call with an 
? ? > example of directly modifying the underlying 'grid' grob to add arrows 
? ? > to the ends.? There are more arguments in arrow() that you can explore 
? ? > if you want to.


? ? > library(ggplot2)
? ? > library(grid)

? ? > ggplot() +
? ? > stat_function(
? ? >? fun = function(x) x^2,
? ? >? color = "blue", linewidth = 1.25,
? ? >? xlim = c(-5, 5)
? ? > ) +
? ? > theme_linedraw()

? ? > grid.force()
? ? > ## Check the name of the line grob
? ? > ## grid.ls()
? ? > grid.edit("GRID.polyline", grep=TRUE,
? ? >? ? ? ? ? arrow=arrow(ends="both"))


? ? > Hope that helps.

? ? > Paul

I'm coming a bit late, but ..

Indeed, the above not only helps but is? "wonderful" -- truly convincing !

-- thanks to Paul M.,? the creator of {grid}
? (and hence *the* enabler of ggplot2 !)

Martin
? ? > On 2/08/2025 3:24 am, Rui Barradas wrote:
? ? >> On 8/1/2025 4:15 PM, Rui Barradas wrote:
? ? >>> On 8/1/2025 3:43 PM, Ebert,Timothy Aaron wrote:
? ? >>>> I would save the graph as a PowerPoint object and then edit it in 
? ? >>>> PowerPoint.
? ? >>>> library(ggplot2)
? ? >>>> library(grid)
? ? >>>> library(officer)
? ? >>>> library(rvg)
? ? >>>> x <- seq(-5, 5, length.out = 100)
? ? >>>> y <- x^2
? ? >>>> data <- data.frame(x, y)
? ? >>>> plot <- ggplot(data, aes(x, y)) +
? ? >>>> ?? geom_path(color = "blue", linewidth = 1.25) +
? ? >>>> ?? geom_segment(
? ? >>>> ???? aes(x = x[1], y = y[1], xend = x[100], yend = y[100]),
? ? >>>> ???? arrow = arrow(angle = 20, type = "closed", ends = "both", length 
? ? >>>>? ? ? ? ? ? ? ? ? ? = unit(0.2, "inches")),
? ? >>>> ???? color = "red"
? ? >>>> ?? ) +
? ? >>>> ?? theme_linedraw()
? ? >>>> doc <- read_pptx()
? ? >>>> doc <- add_slide(doc, layout = "Title and Content", master = "Office Theme")
? ? >>>> doc <- ph_with(doc, dml(ggobj = plot), location = ph_location_fullsize())
? ? >>>> print(doc, target = "quadratic_with_arrows.pptx")
? ? >>>> 
? ? >>>> If I remember I think you have to ungroup it in PowerPoint and then 
? ? >>>> all elements become editable. The general approach can be done with 
? ? >>>> other file formats/programs, not just PowerPoint.
? ? >>>> Tim
? ? >>>> 
? ? >>>> -----Original Message-----
? ? >>>> From: R-help <r-help-bounces at r-project.org> On Behalf Of Thomas Subia 
? ? >>>> via R-help
? ? >>>> Sent: Friday, August 1, 2025 10:31 AM
? ? >>>> To: r-help at r-project.org
? ? >>>> Subject: [R] ggplot with arrows
? ? >>>> 
? ? >>>> [External Email]
? ? >>>> 
? ? >>>> Consider:
? ? >>>> 
? ? >>>> x <- seq(-5,5,length.out = 100)
? ? >>>> y <- x^2
? ? >>>> data <- data.frame(x,y)
? ? >>>> library(ggplot2)
? ? >>>> ggplot(data,aes(x,y))+
? ? >>>> ?? stat_function(
? ? >>>> ???? fun = function(x) x^2,
? ? >>>> ???? color = "blue", linewidth = 1.25
? ? >>>> ?? ) +
? ? >>>> ?? theme_linedraw()

? ? >>>> I'd like to add an arrow to the ends of curve to illustrate the curve 
? ? >>>> continues indefinitely in that direction, ChatGPT suggests using 
? ? >>>> geom_segment or geom_link but there has an easier way to do this.
? ? >>>> 
? ? >>>> Any suggestions would be appreciated.
......
......