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

4 Answers 4

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$
11
  • 1
    $\begingroup$ And maybe include Pick[#, Sign[#[[All, 1]]], 1][[1]] &@T1 ? $\endgroup$
    – user1066
    Oct 11, 2017 at 21:00
  • $\begingroup$ Does mma performs any kind of optimization when storing a list with many same elements? $\endgroup$
    – ercegovac
    Oct 11, 2017 at 21:33
  • $\begingroup$ aardvark2012, i replaced lenwhile with two versions. $\endgroup$
    – kglr
    Oct 12, 2017 at 11:40
  • $\begingroup$ @tomd Done. Sorry I missed it first time around. $\endgroup$ Oct 12, 2017 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$ Oct 12, 2017 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 and acknowledge you have read our privacy policy.

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