Take the 2-minute tour ×
Mathematica Stack Exchange is a question and answer site for users of Mathematica. It's 100% free, no registration required.

I'm looking for an efficient way of extracting the first element after the first sequence of N consecutive elements in which the values are increasing. If these are the data of a toy example:

data = {{1, 4}, {2, 3}, {3, 4}, {4, 5}, {5, 6}, {6, 4}, {7, 6}, {8, 
   3}, {9, 4}, {10, 5}, {11, 6}, {12, 7}, {13, 8}, {14, 9}, {15, 10}};

and I would like to get the first element after the first sequence of 5 consecutive increasing values (from those in second position), I should obtain the row {13,8}.

I was playing around with:

Split[data, #2[[2]] > #1[[2]] &]

that groups my data in sequences of increasing values, but I wasn't able to find a way to add the additional constraints I need. Any hint is much appreciated.

share|improve this question
    
You mean the first element of column 1 after the FIRST sequence of N consecutive increasing values in column 2? –  Rojo Sep 3 '12 at 14:22
    
@Rojo If we talk about columns and rows, I mean the first row after the first sequence of N consecutive increasing values in column 2. I edited the question. –  VLC Sep 3 '12 at 14:26
    
Wouldn't in your example be {14,9} the answer? The increasing sequence is {8,3}->{9,4}->{10, 5}->{11,6}->{12, 7}->{13,8}, so the row after is {14, 9}? –  Rojo Sep 3 '12 at 16:34
    
@Rojo It depends on the definition. I'm fine with {13,8}. –  VLC Sep 3 '12 at 17:01
    
My real question isn't 4 vs 5. It's more about: the result is the next row following the sequence or the last one on the sequence? If instead of {13, 8} you had {13, -8} would that row be your result? –  Rojo Sep 3 '12 at 17:40

7 Answers 7

up vote 10 down vote accepted

Following your first approach this satisfies your needs :

Select[Split[data, #2[[2]] > #1[[2]] &], Length @ # > 5 &][[All, 6]]
{{13, 8}}

Edit

The OP was edited and now it asks for the first element after the first sequence of 5 consecutive increasing values... Thus we can use Select with the third argument n to select the first n elements satisfying the criterion, i.e. in our case (n=1) :

Select[ Split[ data, #2[[2]] > #1[[2]]& ], Length @ # > 5 &, 1][[All, 6]]
share|improve this answer
1  
Nice one +1. I'd add 1 as the third argument to Select since he only seems to want the first. Careful however if he has a length 5 increasing sequence, you probably will get an error and you won't be finding the "next row" following the sequence –  Rojo Sep 3 '12 at 15:23
    
@Rojo Thanks, interesting remarks ! My solution doesn't produce an error even if there is no increasing sequence of length > 5 as your first method does. Anyway I like them both +1. –  Artes Sep 3 '12 at 16:17
    
Yeah, no error in {}[[All, 6]], I expected that wrongly. Mines both produce an error when there's no sequence –  Rojo Sep 3 '12 at 16:29

Using NestWhile and OrderedQ:

ClearAll[takeNextAfterTest];
takeNextAfterTest = Function[{data, num, col, comp},
    With[{temp = NestWhile[Rest, data, 
       (Length@# > num && !OrderedQ[#[[;; num, col]], comp]) &]},
    If[Length@temp == num, {}, temp[[1 + num]]]]];

Examples:

data2 = {{1, 4}, {2, 3}, {5, 2}, {3, 4}, {4, 5}, {5, 6}, {6, 4}, {7, 6}, {8, 3}, 
        {9, 4}, {10, 5}, {11, 6}, {12, 7}, {13, 8}, {14, 9}, {15, 10}, {8, 9}, 
        {9, 8}, {10, 7}, {11, 6}, {12, 5}, {13, 10}, {14, 10}, {15, 11}};

takeNextAfterTest[data2, 5, 2, Less]
(* {13, 8} *)
takeNextAfterTest[data2, 3, 2, #2 == 1 + #1 &]
(* {6, 4} *)
takeNextAfterTest[data2, 5, 2, Greater]
(*  {12,5} *)
takeNextAfterTest[data2, 2, 2, Equal]
(* {15, 11} *)

Using NestWhile and Ordering:

ClearAll[takeNextAfterOrderingPattern];
takeNextAfterOrderingPattern = Function[{data, num, col, orderedlike},
  With[{temp = 
    NestWhile[Rest, data,
     (Length@# > num && (Ordering[#[[;; num, col]]] =!= Ordering[orderedlike])) &]},
  If[Length@temp == num, {}, temp[[1 + num]]]]];

Examples:

takeNextAfterOrderingPattern[data2, 5, 2, Range[5]]
(* {13,8} *)
takeNextAfterOrderingPattern[data2, 5, 2, Range[5, 1, -1]]
(* {12,5} *)
takeNextAfterOrderingPattern[data2, 5, 2, {3, 4, 2, 4, 1}]  
(*  {9, 4} *)
share|improve this answer
    
Thanks. Especially the takeNextAfterTest seems to be much faster than Artes solution. –  VLC Sep 5 '12 at 9:41

Here is another try at a version that does not unpack; the first version was not quite right.

data = Developer`ToPackedArray@{{1, 4}, {2, 3}, {3, 4}, {4, 5}, {5, 
     6}, {6, 4}, {7, 6}, {8, 3}, {9, 4}, {10, 5}, {11, 6}, {12, 
     7}, {13, 8}, {14, 9}, {15, 10}};
(*data={{a,1},{b,2},{c,-2},{d,-1},{e,0},{f,3},{g,5},{h,6},{j,7},{k,8}}\
*)
ClearAll[select]
select[p_Integer] := 
 Compile[{{in, _Integer, 1}}, Module[{pos = 0, val = 0},
   Do[
    val += in[[i]];
    If[in[[i]] == 0, val = 0; Continue[]];
    If[val == p, pos = i; Break[];];
    , {i, Length[in]}];
   pos
   ]
  ]
On["Packing"]
cf = select[5];
res = UnitStep[data[[All, -1]] - RotateRight[data[[All, -1]]]];
data[[cf[res]]]
(* {13, 8} *)
share|improve this answer
1  
I don't think this works correctly for other lists. Try {{a, 1}, {b, 2}, {c, -2}, {d, -1}, {e, 0}, {f, 3}, {g, 5}, {h, 6}, {j,7}, {k, 8}} –  Rojo Sep 3 '12 at 14:56
    
@Rojo, yes, that first version was not quite right. It should be fixed with this version. Thanks. –  user21 Sep 3 '12 at 16:09

Yet another route:

data = {{1, 4}, {2, 3}, {3, 4}, {4, 5}, {5, 6}, {6, 4}, {7, 6}, {8, 3},
        {9, 4}, {10, 5}, {11, 6}, {12, 7}, {13, 8}, {14, 9}, {15, 10}};

With[{k = 5}, 
     data[[Total[TakeWhile[Length /@ SplitBy[Differences[data],
           Composition[Positive, Last]], # < k &]] + k + 1]]]
   {13, 8}

Using Verde's example:

data2 = {{{a, 1}, {b, 2}, {c, 3}, {d, 4}, {e, 5}, {f, 6}, {g, 7}, {h, 8}}};

With[{k = 5}, 
     data2[[Total[TakeWhile[Length /@ SplitBy[Differences[data2],
           Composition[Positive, Last]], # < k &]] + k + 1]]]
   {f, 6}
share|improve this answer
    
The increment by 1 was just in my toy example. Thanks anyway. –  VLC Sep 3 '12 at 15:35
    
@VLC, I've generalized the original routine. –  J. M. Sep 3 '12 at 15:46

Here's another way, somewhat similar to Rojo's.

selectAfterN[data_, n_] := Pick[data, Accumulate /@ SplitBy@
    UnitStep[{0}~Join~Differences[data][[All, 2]]] // Flatten, n]

selectAfterN[data, 5]
(* {13, 8} *)
share|improve this answer

Perhaps

q = 5;

Transpose@data /. {_, index_} :> 
  First@Extract[data, 
    1 + Position[Flatten[Accumulate /@ Split@Sign@Differences@index], 
      q]]

An alternative, somewhat @Verde-ish, is

Replace[data, {___, 
   i : Repeated[{_, _}, {q}] /; 
    q - 1 == Total@Sign@Differences[{i}[[All, 2]]], n_, ___} :> 
  n]
share|improve this answer
Cases[{{{a, 1}, {b, 2}, {c, 3}, {d, 4}, {e, 5}, {f, 6}, {g, 7}, {h, 8}}}, 
      {___, {_, a_}, {_, b_}, {_, c_}, {_, d_}, {_, e_}, {ff_, f_}, ___} /;
       a < b < c < d < e < f :> {ff, f}]

(*
-> {f,6}
*)

If you want to detect only sequences increasing by 1:

Cases[{{{a, 1}, {b, 2}, {c, 3}, {d, 4}, {e, 5}, {f, 6}, {g, 7}, {h,8}}}, 
       {___, {_, a_}, {_, b_}, {_, c_}, {_, d_}, {_, e_}, {ff_, f_}, ___} /; 
       f == e + 1 == d + 2 == c + 3 == b + 4 == a + 5 :> {ff, f}]

If you want to stick with your StringSplit thing:

data = {{1, 4}, {2, 3}, {3, 4}, {4, 5}, {5, 6}, {6, 4}, {7, 6}, {8, 3}, {9, 4}, 
        {10, 5}, {11, 6}, {12, 7}, {13, 8}, {14, 9}, {15, 10}};
(Select[#, Length@# > 5 &] &@Split[data, #2[[2]] > #1[[2]] &])[[All, 6]]

(*
-> {{13, 8}}
*)
share|improve this answer
4  
I think I need an Alka-Seltzer to digest this one. –  VLC Sep 3 '12 at 14:35
    
@VLC Your question is pretty simple, that's why you received so many answers. I stuck by Brian Kerninghan's advice: stackoverflow.com/questions/1103299/… So, don't be unthankful –  belisarius Sep 3 '12 at 15:12
    
I always like to see many variations on the theme, so I'm thankful to you and all the others. Some stuff is simply out of my reach now. –  VLC Sep 3 '12 at 15:22
    
+1, your quote totally convinced me –  Rojo Sep 3 '12 at 15:26

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.