4

If you've used the arrows function often enough, you've probably run into this warning:

set.seed(438520)
N = 1000
x = rnorm(N, sd = .1)
y = rnorm(N)

png('~/Desktop/arrows.png', height = 240, width = 240)
plot(NA, xlim = c(-1, 1), ylim = c(-3, 3))
arrows(x[-N], y[-N], x[-1L], y[-1L])
dev.off()

Warning message: In arrows(x[-N], y[-N], x[-1L], y[-1L]) : zero-length arrow is of indeterminate angle and so skipped

A cluttered plot showing the arrows; the x axis is labeled "Index" and the y axis is labeled "NA", and the points are in a vertically-oriented ellipse.

How can we determine which arrow(s) are at fault in order to deal with them as we see fit?

MichaelChirico
  • 33,841
  • 14
  • 113
  • 198

1 Answers1

6

The answer is intimated in ?arrows:

The direction of a zero-length arrow is indeterminate, and hence so is the direction of the arrowheads. To allow for rounding error, arrowheads are omitted (with a warning) on any arrow of length less than 1/1000 inch.

Which of course begs the question -- What is an inch?

That Q&A was centered around a related issue, but the learnings can also be applied here:

png('~/Desktop/arrows.png', height = 240, width = 240)
plot(NA, xlim = c(-1, 1), ylim = c(-3, 3))

# get each arrow's length by converting x and y coords to inches
units = par(c('usr', 'pin'))
x_to_inches = with(units, pin[1L]/diff(usr[1:2]))
y_to_inches = with(units, pin[2L]/diff(usr[3:4]))

dists = sqrt((x_to_inches * diff(x))**2 + (y_to_inches * diff(y))**2)

# which arrows are the culprits?
idx = which(dists < .001)

# option: remove the arrow base & head from the culprit pair(s)
arrows(x[-c(N, idx)], y[-c(N, idx)], 
       x[-c(1L, idx + 1L)], y[-c(1L, idx + 1L)])
dev.off()

You can see here in the R source that this approach is almost identical to that used (at the C level) to generate this warning in the first place.

Something that's always bugged me but never enough to sit down and hash it out.

MichaelChirico
  • 33,841
  • 14
  • 113
  • 198
  • Would you happen to know of a way to get rid of these warnings altogether ? Applying this every time I need to plot arrows seems kind of unpractical. When I use `suppressWarnings()`, I still get the *"There were x or more warnings, use..."* which get a little infuriating... – RoB Mar 11 '19 at 14:53
  • @RomainB. write a wrapper function – MichaelChirico Mar 11 '19 at 15:00
  • Well the issue is that most of the time, I'm not saving the outputs to files, with known resolution. If I want to plot, say, in Rstudio's plot panel it's going to get hard to get the height/width of the plot. – RoB Mar 11 '19 at 15:34
  • It seems much easier just to use `suppressWarnings` to get rid of the warning. I mean, you're basically just doing what the warning says anyway – Hong Ooi Oct 18 '20 at 01:59
  • @HongOoi suppressWarnings might be putting the baby out with the bathwater :) zero-length arrows are omitted. This answer provides a solution that's robust to plot resizing, etc. – MichaelChirico Oct 18 '20 at 02:02