[R] Is there better alternative to this loop?

Marc Schwartz marc_schwartz at comcast.net
Thu Feb 22 06:37:21 CET 2007


On Thu, 2007-02-22 at 12:33 +1100, Alfonso Sammassimo wrote:
> Dear List,
> 
> Thanks to those who helped with my enquiry a few days ago.
> 
> I have a another question on loops, in this case I am trying to print out 
> the row of a data frame if the previous 3 values (daily values) in col5 are 
> in descending order. I have this loop which works, but ask whether this can 
> be done differently (without conventional loop) in R:
> 
> flag="T"
> d= 3 # d represents previous down days
> for(i in (d+1): 100)
> {
> for( j in (i-d):(i-1))
> {
> if(x[j,5]<x[i,5]){flag="F"}
> }
> if( flag == "T"){ print(x[i,1])}
> flag="T";
> }
> 
> Any help appreciated,
> 
> Regards,
> Alf Sammassimo
> Melbourne, Australia.

How about this. We'll take it one step at a time.

Get the indices of the rows in the data frame, where a column value in
the prior three rows is in decreasing order.  Let's use the iris data
set as an example:

> iris$Sepal.Length
  [1] 5.1 4.9 4.7 4.6 5.0 5.4 4.6 5.0 4.4 4.9 5.4 4.8 4.8 4.3 5.8 5.7
 [17] 5.4 5.1 5.7 5.1 5.4 5.1 4.6 5.1 4.8 5.0 5.0 5.2 5.2 4.7 4.8 5.4
 [33] 5.2 5.5 4.9 5.0 5.5 4.9 4.4 5.1 5.0 4.5 4.4 5.0 5.1 4.8 5.1 4.6
 [49] 5.3 5.0 7.0 6.4 6.9 5.5 6.5 5.7 6.3 4.9 6.6 5.2 5.0 5.9 6.0 6.1
 [65] 5.6 6.7 5.6 5.8 6.2 5.6 5.9 6.1 6.3 6.1 6.4 6.6 6.8 6.7 6.0 5.7
 [81] 5.5 5.5 5.8 6.0 5.4 6.0 6.7 6.3 5.6 5.5 5.5 6.1 5.8 5.0 5.6 5.7
 [97] 5.7 6.2 5.1 5.7 6.3 5.8 7.1 6.3 6.5 7.6 4.9 7.3 6.7 7.2 6.5 6.4
[113] 6.8 5.7 5.8 6.4 6.5 7.7 7.7 6.0 6.9 5.6 7.7 6.3 6.7 7.2 6.2 6.1
[129] 6.4 7.2 7.4 7.9 6.4 6.3 6.1 7.7 6.3 6.4 6.0 6.9 6.7 6.9 5.8 6.8
[145] 6.7 6.7 6.3 6.5 6.2 5.9


Use diff() to get the differences between successive values in the
vector:

> diff(iris$Sepal.Length, 1)
  [1] -0.2 -0.2 -0.1  0.4  0.4 -0.8  0.4 -0.6  0.5  0.5 -0.6  0.0 -0.5
 [14]  1.5 -0.1 -0.3 -0.3  0.6 -0.6  0.3 -0.3 -0.5  0.5 -0.3  0.2  0.0
 [27]  0.2  0.0 -0.5  0.1  0.6 -0.2  0.3 -0.6  0.1  0.5 -0.6 -0.5  0.7
 [40] -0.1 -0.5 -0.1  0.6  0.1 -0.3  0.3 -0.5  0.7 -0.3  2.0 -0.6  0.5
 [53] -1.4  1.0 -0.8  0.6 -1.4  1.7 -1.4 -0.2  0.9  0.1  0.1 -0.5  1.1
 [66] -1.1  0.2  0.4 -0.6  0.3  0.2  0.2 -0.2  0.3  0.2  0.2 -0.1 -0.7
 [79] -0.3 -0.2  0.0  0.3  0.2 -0.6  0.6  0.7 -0.4 -0.7 -0.1  0.0  0.6
 [92] -0.3 -0.8  0.6  0.1  0.0  0.5 -1.1  0.6  0.6 -0.5  1.3 -0.8  0.2
[105]  1.1 -2.7  2.4 -0.6  0.5 -0.7 -0.1  0.4 -1.1  0.1  0.6  0.1  1.2
[118]  0.0 -1.7  0.9 -1.3  2.1 -1.4  0.4  0.5 -1.0 -0.1  0.3  0.8  0.2
[131]  0.5 -1.5 -0.1 -0.2  1.6 -1.4  0.1 -0.4  0.9 -0.2  0.2 -1.1  1.0
[144] -0.1  0.0 -0.4  0.2 -0.3 -0.3


Then use sign() to get the sign of the differences, positive, negative
or zero:

> sign(diff(iris$Sepal.Length, 1))
  [1] -1 -1 -1  1  1 -1  1 -1  1  1 -1  0 -1  1 -1 -1 -1  1 -1  1 -1 -1
 [23]  1 -1  1  0  1  0 -1  1  1 -1  1 -1  1  1 -1 -1  1 -1 -1 -1  1  1
 [45] -1  1 -1  1 -1  1 -1  1 -1  1 -1  1 -1  1 -1 -1  1  1  1 -1  1 -1
 [67]  1  1 -1  1  1  1 -1  1  1  1 -1 -1 -1 -1  0  1  1 -1  1  1 -1 -1
 [89] -1  0  1 -1 -1  1  1  0  1 -1  1  1 -1  1 -1  1  1 -1  1 -1  1 -1
[111] -1  1 -1  1  1  1  1  0 -1  1 -1  1 -1  1  1 -1 -1  1  1  1  1 -1
[133] -1 -1  1 -1  1 -1  1 -1  1 -1  1 -1  0 -1  1 -1 -1


So, now we essentially want to get the index for a value where it and
the previous value are -1, indicating a decreasing sequence of 3 in the
original data vector. The easiest way to do this may be to actually look
for a moving average of -1 over two successive values in moving windows.
In this case, we use filter():

> which(filter(sign(diff(iris$Sepal.Length)), rep(1/2, 2), 
               sides = 1) == -1)
 [1]   2   3  16  17  22  38  41  42  60  78  79  80  88  89  93 111 127
[18] 133 134 149


That gives us the index of the second value in the sign() sequence.
Since we want the index of the 4th value in the original data vector, we
add 2:

> which(filter(sign(diff(iris$Sepal.Length)), rep(1/2, 2), 
               sides = 1) == -1) + 2
 [1]   4   5  18  19  24  40  43  44  62  80  81  82  90  91  95 113 129
[18] 135 136 151


So these values now should correspond to the indices of the values in
iris$Sepal.Length, where the three preceding values are in a decreasing
sequence.

So to apply that to your dataset, something like the following should
work:

Ind <- which(filter(sign(diff(DF$Col5)), rep(1/2, 2), 
                    sides = 1) == -1) + 2

Then print the rows with:

  DF[Ind, ]

HTH,

Marc Schwartz



More information about the R-help mailing list