-1

I often hear that, generally speaking, using apply functions is superior (and more proper) than using for loops in R. Does this also apply to printing graphical output (which has no desirable console output or object saving)?

For example:

If I want to print 5 lines on a graph do I use an apply function (e.g, sapply) or a for loop?

plot(x=1:5,y=1:5,type='n')
sapply(1:5,function(z) lines(y = 1:5, x=rep(z, length(1:5))))  #...or....
for(z in 1:5) { lines(y=1:5, x=rep(z, length(1:5))) }

The apply function creates useless console output, but I'm under the impression that apply is better than using a for loop.

Kind of a silly question perhaps, but I'd like my code to be as clear and fast as possible.

(Also, if neither sapply or a for loop are the best approach to coding this command, what is?)

theforestecologist
  • 4,667
  • 5
  • 54
  • 91
  • 1
    This kind of base graphics plotting situation is an example where I would have a strong preference for a `for` loop. – joran Mar 27 '16 at 01:11

1 Answers1

3

1: I think you may have a misunderstanding of the lines() function. It draws connected lines between a sequence of (x,y) points. Calling it to draw a single line segment by passing 5 points along the segment doesn't make sense. Here's an example of what lines() can do:

xlim <- c(0,4); ylim <- c(0,4); plot(NA,xlim=xlim,ylim=ylim,xlab='x',ylab='y');
lines(0:4,c(0,3,4,3,0));

lines

2: To draw separate line segments it is more appropriate to use segments(). Here's how you can draw your 5 lines using this function:

xlim <- c(1,5); ylim <- c(1,5); plot(NA,xlim=xlim,ylim=ylim,xlab='x',ylab='y');
segments(1:5,1,y1=5);

segments

As you can see, segments() is vectorized over its coordinate arguments (just as lines() is), so you can draw multiple lines with a single call. No need for loops.

3: That being said, there are many contexts in graphics production where you have to make multiple calls to graphics functions to produce your desired output. In these cases, I have always used for-loops rather than the *apply() family. The *apply() family is mainly useful for data processing when you care about the return value(s) of the lambda call(s) and therefore either capture the result in a variable, or pass it as an argument to another function call. Graphics functions are almost always called purely for their side effect, namely, the drawing of graphics on a graphics device, and thus the return value of the graphics call is discarded. There is unlikely to be a performance benefit from using the *apply() family, although the analysis is not quite so simple. But any performance benefit you might procure from the *apply() family can only result from increased efficiency with respect to handling the data produced and returned by the lambda, so it can't apply to graphics calls that don't return data. For example, vapply() can in some cases provide a noticeable performance benefit, since it can determine in advance exactly what type and size of object will be returned to the caller. Again, that doesn't apply to graphics calls that don't return anything other than NULL.

4: If you ever find you must call a function that returns data non-invisibly, and you want to prevent it from being displayed in your console, you can manually wrap it in invisible(). Using your sapply() call as an example:

sapply(1:5,function(z) lines(y=1:5,x=rep(z,5L)));
## [[1]]
## NULL
##
## [[2]]
## NULL
##
## [[3]]
## NULL
##
## [[4]]
## NULL
##
## [[5]]
## NULL
##
invisible(sapply(1:5,function(z) lines(y=1:5,x=rep(z,5L)))); ## doesn't print anything
Community
  • 1
  • 1
bgoldst
  • 34,190
  • 6
  • 38
  • 64
  • Great Answer and it answered my question very thoroughly! I did not know about the `segment` function and am excited to add it to my repertoire of functions. I will comment though, that I don't *misunderstand* `lines` and actually use it correctly -- There just happens to be a better way to do it ;p – theforestecologist Mar 27 '16 at 03:01