FitzKaos FitzKaos - 1 month ago 8
R Question

R: Creating a circular loop that compares last value with first

I have what may be a very simple R question but I have searched and could not find the answer. I have tried a number of different search terms and thus far nothing. I apologise if this has been asked before though. Anyway...

The question is this: How do I make a loop that compares the last value to the first value and then stops.

Hypothetical [trivial] example: I have 10 dinner guests sitting around a circular table. I wish to find the difference between the ages going clockwise around the circle.

Ages<-c(27,83,65,91,23,10,47,9,71,101)
for(i in 1:length(Ages)){
Difference[i]<-Ages[i]-Ages[i+1]
}


Now of course this runs into difficulty at the end because with person 10, i+1 is 11 and therefore out of the bounds. But I wish to have the age of person 10 compared with the age of person 1. And then stop (i.e. not continue round and round indefinitely).

Alternatively if this can be done more easily using any of the apply functions then that can work too, however a loop might be easier.

Thank you for your help!

Answer

A general treatment is padding. We can pad the section that will be recycled in the end, then proceed as normal.

For your example, we need to recycle the first element, so we do

Ages1 <- c(Ages, Ages[1])
-diff(Ages1)

I have also replaced the loop with a vectorized operation.

If we want to take Ages[i] - Ages[i + 2], we need to recycle the first 2 elements:

Ages1 <- c(Ages, Ages[1:2])
-diff(Ages1, lag = 2)

I had thought of this, too. It seems that repeating part of the vector/element will be necessary. I was just wondering if there was a bit more elegant way in which to do it, since might get more complex with non-trivial examples...

I don't know what you mean by "elegant". Maybe you are looking for something with %%, the modulo operation.

i <- 1:length(Ages)
# [1]  1  2  3  4  5  6  7  8  9 10

j <- (i + 1) %% length(Ages)
# [1]  2  3  4  5  6  7  8  9  0  1

j[j == 0] <- length(Ages)  ## have to replace 0 with `length(Ages)`
# [1]  2  3  4  5  6  7  8  9 10  1

Ages[i] - Ages[j]

This may not be elegant to your provided example, but depending on your real settings, this could be a fair solution.