3
$\begingroup$

I have a table of order $30 \times 3$. I want to pick a row from this table which is first to have its first element to be positive, irrespective of the values of the rest two elements.

For example, if

T = {{-2, 1, 2}, {-4, 2, 5}, {-6, 4, 3}, {-2, 2, 1}, {2, 4, 4}, {1, 5, 3}}

How can I tell Mathematica to pick the row {2, 4, 4} from the T.

$\endgroup$
  • 6
    $\begingroup$ This works SelectFirst[T, #[[1]] > 0 &] which gives (Out[1]={2, 4, 4}). $\endgroup$ – ercegovac Oct 10 '17 at 20:07
  • 2
    $\begingroup$ Pick[#, Sign[#[[All, 1]]], 1][[1]] &@T $\endgroup$ – user1066 Oct 11 '17 at 11:35
7
$\begingroup$

@Nasser's FirstCase gets my vote for speed and simplicity, but different methods will work faster depending on where the first positive first element could show up. Compare the different approaches in this thread along with a new function using Throw and Catch in a Do loop:

firstcase[list_] := FirstCase[list, x_ /; Positive[First[x]]] (* Nasser *)

select[list_] := Select[list, First@*Positive, 1][[1]] (* m_goldberg *)

selfirst[list_] := SelectFirst[list, #[[1]] > 0 &] (* ercegovac *)

pick[list_] := Pick[#, Sign[#[[All, 1]]], 1][[1]] &@list (* tomd *)

lenwhile1[list_] := list[[1 + LengthWhile[list[[All, 1]], # <= 0 &]]]; (* kglr *)

lenwhile2[list_] := list[[1 + LengthWhile[list, #[[1]] <= 0 &]]]; (* kglr *)

throwcatch[list_] := Catch@Do[If[list[[i, 1]] > 0, Throw[list[[i]]]], {i, Length[list]}]

on two lists:

SeedRandom[1]
T1 = RandomInteger[{-5, -1}, {100000, 3}]~Join~{{2, 4, 4}}; (* The "hard" problem *)
T2 = RandomInteger[{-5, 5}, {100000, 3}]; (* The "easy" problem *)

Obviously the first list is "harder", but there are interesting performance differences between the different functions:

functions = {firstcase, select, selfirst, pick, lenwhile1, lenwhile2, throwcatch};
res = Map[{First@AbsoluteTiming[#[T1]], First@AbsoluteTiming[#[T2]]} &, functions]

TextGrid[Join[{{"", "T1", "T2"}}, Transpose[
  Join[{{"firstcase", "select", "selfirst", "pick", "lenwhile1", "lenwhile2", "throwcatch"}}, 
  Transpose[res]]]]] // TeXForm

\begin{array}{l|l|l} \text{} & \text{T1} & \text{T2} \\ \hline \text{firstcase} & 0.104779 & 0.0229505 \\ \text{select} & 0.177957 & 0.0214707 \\ \text{selfirst} & 0.12791 & 0.0221214 \\ \text{pick} & 0.00207038 & 0.00345685 \\ \hline \text{lenwhile1}^* & 0.0588038 & 0.00159609 \\ \text{lenwhile2}^* & 0.129428 & 0.0000127513 \\ \text{throwcatch} & 0.108226 & 0.0000205265 \\ \end{array} ${}^*$ Apparently @kglr's LengthWhile-based functions run slower on my machine. The free browser version of Mathematica 11.2 gets the timings for lenwhile1 /@ {T1, T2} to be {0.04873300, 0.00145900} and for lenwhile2 /@ {T1, T2} to be {0.09734200, 3.0*10^-6}. The timings above were obtained on Mathematica 11.2 running ion Win10.

The advantage of the LengthWhile and Throw/Catch methods on the easy problem presumably comes from the fact that they explicitly exit when the result is found, although I have no idea how FirstCase and SelectFirst actually work, so they may be doing a similar thing. pick outperforms everything else by an order of magnitude on the hard problem, but for reasons which baffle me, actually takes longer on the easy problem. (throwcatch can also be Compiled, which increases its speed by another order of magnitude on the easy problem, and two orders of magnitude on the hard problem.)

All of which is just to say that knowing a little about the structure of the problem can help make a choice about the best algorithm to tackle it. Of course, in the OP's example there were only 30 elements, so all this is kind of moot...

$\endgroup$
  • 1
    $\begingroup$ And maybe include Pick[#, Sign[#[[All, 1]]], 1][[1]] &@T1 ? $\endgroup$ – user1066 Oct 11 '17 at 21:00
  • $\begingroup$ Does mma performs any kind of optimization when storing a list with many same elements? $\endgroup$ – ercegovac Oct 11 '17 at 21:33
  • $\begingroup$ aardvark2012, i replaced lenwhile with two versions. $\endgroup$ – kglr Oct 12 '17 at 11:40
  • $\begingroup$ @tomd Done. Sorry I missed it first time around. $\endgroup$ – aardvark2012 Oct 12 '17 at 11:42
  • $\begingroup$ @ercegovac I know very little about how Mathematica works under the surface, but you might find what-are-the-main-differences-between-rawarray-and-packedarray useful for intensive data storage. $\endgroup$ – aardvark2012 Oct 12 '17 at 11:43
4
$\begingroup$
ClearAll[lenwhile1, lenwhile2]
lenwhile1 = #[[1 + LengthWhile[#[[All, 1]], # <= 0 &]]] &;
lenwhile2 = #[[1 + LengthWhile[#, #[[1]] <= 0 &]]] &;

lenwhile1 @ T

{2, 4, 4}

lenwhile1 @ T == lenwhile2 @ T

True

Timings:

Using aardvark2012's setup with

functions = {firstcase, lenwhile1, lenwhile2, pick, select, selfirst, throwcatch};

where

pick =  Pick[#, Sign[#[[All, 1]]], 1][[1]] &;

is suggested by tomd in a comment.

SeedRandom[1]
T1 = RandomInteger[{-5, -1}, {100000, 3}]~Join~{{2, 4, 4}};
T2 = RandomInteger[{-5, 5}, {100000, 3}];

enter image description here

Equal @@ (#[T1]& /@ functions)

True

Equal @@ (#[T2]& /@ functions)

True

$\endgroup$
2
$\begingroup$

Another possibility is to use FirstCase

T0 = {{-2,1,2},{-4,2,5},{-6,4,3},{-2,2,1},{2,4,4},{1,5,3}};
FirstCase[ T0, x_/; Positive[First[x]]  ]

Mathematica graphics

$\endgroup$
1
$\begingroup$

Will this work for you?

T = {{-2, 1, 2}, {-4, 2, 5}, {-6, 4, 3}, {-2, 2, 1}, {2, 4, 4}, {1, 5, 3}}
Select[T, First@*Positive, 1][[1]]

{2, 4, 4}

$\endgroup$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

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