2
$\begingroup$

I need a compare-operation for two lists of same length (usually > 100000) which does the following:

{2,3,5,4,1,8,7} compare-operation {1,4,6,3,2,8,8} = {2,4,6,4,2,8,8}

The resulting list has at each position the greater (or equal element in case both are equal) of the two lists (because 2>1,4>3,6>5,4>3,2>1,8=8,8>7).

How can that be done?

$\endgroup$
7
$\begingroup$
MapThread[Max, {{2, 3, 5, 4, 1, 8, 7} , {1, 4, 6, 3, 2, 8, 8}}]

{2, 4, 6, 4, 2, 8, 8}

Thread[Unevaluated[Max[{2, 3, 5, 4, 1, 8, 7} , {1, 4, 6, 3, 2, 8, 8}]]]

also works. Unevaluated is necessary, otherwise Max[... evaluates, before Thread acts on it. On the other hand

Thread[Unevaluated[Max[list1, list2]]]

does not work for the same reasons.

list1 = RandomInteger[30, 1*^7];
list2 = RandomInteger[30, 1*^7];
MapThread[Max, {list1, list2}] // AbsoluteTiming

{6.302683, {28, 10, 21, 13 ...

Not too bad for ten million elements. In fact, faster than constructing the lists.

In this simple case however, we can do 10 times better:

(Max /@ Transpose@{list1, list2}) // AbsoluteTiming // First

0.608412

For a list with 2 dimensions the obvious solution is

MapThread[Max, {list1, list2}, 2] // AbsoluteTiming // First

0.702000

Timing is shown for a 10^3 by 10^3 list.

The faster solution uses the, so-to-speak, generalized transpose, which is achieved by using flatten with a matrix as the second argument:

Map[Max, Flatten[{list1, list2}, {{2}, {3}, {1}}], {2}] // AbsoluteTiming // First

0.109200

As before, almost an order of magnitude faster. The general expression for k-dimensional lists would be:

Map[Max, Flatten[{list1, list2}, {{2}, {3}, ..., {k+1}, {1}}], {k}]

Update 18.09.17

Carl Woll's ThreadedMax is hard to beat, but exploiting Compiled listability comes close:

threadMax = 
 Compile[{{x1, _Integer}, {x2, _Integer}}, Max[x1, x2], 
  RuntimeAttributes -> {Listable}, Parallelization -> True, 
  CompilationTarget -> "C", RuntimeOptions -> "Speed"]

It is still a few times slower though and I'd recommend Carl's solution. Simple top-level functions with built-in listability are close to impossible to beat even with compiled functions and are more versatile in the datatypes they accept: e.g., the above threadMax will only work on packed arrays of integers.

If Ramp isn't available in your version of MMA, just like it is in mine (I'm on 10.2), I suggest the following implementation:

ThreadedMax[l1_List, l2_List] := 
 With[{diff = Subtract[l1, l2]}, 
  Check[UnitStep[diff] diff + l2, $Failed]]

I would similarly modify Carl's solution like so for better performance:

ThreadedMax[l1_List, l2_List] := Check[
    Ramp[Subtract[l1, l2]]+l2,
    $Failed
]

See this for further reading.

$\endgroup$
  • $\begingroup$ Dear LLlAMnYP, this is what I wanted. Can that be done also with 2 dimensional lists e.g. Dimensions[list]={720,577}? $\endgroup$ – mrz May 12 '15 at 9:47
  • $\begingroup$ Do you mean element-wise comparisons at the second level of the lists? As in {{1, 6},{3, 9}} ~~ {{2, 5}, {4, 1}} -> {{2, 6},{4, 9}}? $\endgroup$ – LLlAMnYP May 12 '15 at 9:53
  • $\begingroup$ You can imagine that I would have two grey level images of 720 rows and 577 columns and I want to produce an resulting image where each pixel brightness corresponds to the higher brightness value of the images. $\endgroup$ – mrz May 12 '15 at 9:58
6
$\begingroup$

For threading the maximum of a list (or array) with a number, you'll be hard pressed to beat the following:

ThreadedMax[l1_List, l2_?NumericQ] := Clip[l1, {l2, Infinity}]

For example:

l1 = RandomReal[1, 10^8];

ThreadedMax[l1, .5]; //AbsoluteTiming

{0.552601, Null}

For threading the maximum of 2 arrays, the following is the fastest approach I can think of:

ThreadedMax[l1_List, l2_List] := Check[
    Ramp[l1-l2]+l2,
    $Failed
]

Comparing with the answer by @LLlAMnYP:

l1 = RandomReal[1, 10^7];
l2 = RandomReal[1, 10^7];

r1 = ThreadedMax[l1, l2]; //AbsoluteTiming
r2 = MapThread[Max, {l1, l2}]; //AbsoluteTiming
r3 = Max /@ Transpose[{l1, l2}]; //AbsoluteTiming

r1 === r2 === r3

{0.089001, Null}

{5.53821, Null}

{0.602344, Null}

True

For arrays with higher rank, we have:

l1 = RandomReal[1, {3000, 3000}];
l2 = RandomReal[1, {3000, 3000}];

r1 = ThreadedMax[l1, l2]; //AbsoluteTiming
r2 = MapThread[Max, {l1, l2}, 2]; //AbsoluteTiming
r3 = Map[Max, Flatten[{l1, l2}, {{2}, {3}, {1}}], {2}]; //AbsoluteTiming

r1 === r2 === r3

{0.096501, Null}

{5.30103, Null}

{0.694005, Null}

True

$\endgroup$
  • $\begingroup$ Very nice improvement. Since you're using Listable operations inside your ThreadedMax definition, I see no reason to check the Head of the arguments. ThreadedMax[l1_, l2_] := Check[Ramp[Subtract[l1,l2]]+l2,$Failed] will cover all cases and probably give optimum performance, as per my update. $\endgroup$ – LLlAMnYP Sep 18 '17 at 11:22
5
$\begingroup$

If you are in 11.1 or later verion

ThreadingLayer[Max][{{2, 3, 5, 4, 1, 8, 7} , {1, 4, 6, 3, 2, 8, 8}}]

{2.,4.,6.,4.,2.,8.,8.}

Of course,we have build-in method if you are in old verion

Internal`MaxAbs[{2, 3, 5, 4, 1, 8, 7} , {1, 4, 6, 3, 2, 8, 8}]

{2,4,6,4,2,8,8}

Or

Random`Private`MapThreadMax[{{2, 3, 5, 4, 1, 8, 7}, {1, 4, 6, 3, 2, 8, 8}}]

will give a same result

$\endgroup$
  • $\begingroup$ +many if I could, this is a brilliant showcase of an undocumented Internal function. An extra ~3-fold speed increase. $\endgroup$ – LLlAMnYP Sep 18 '17 at 11:29
  • $\begingroup$ Like net function too! $\endgroup$ – partida Sep 18 '17 at 11:30
  • $\begingroup$ Of course, MaxAbs is Max**Abs**, but it seems that OPs use-case works with non-negative numbers anyway. $\endgroup$ – LLlAMnYP Sep 18 '17 at 11:31
  • $\begingroup$ @LLlAMnYP See edit.. $\endgroup$ – yode Sep 18 '17 at 11:36
  • $\begingroup$ Nonexistent in 10.2, it seems. $\endgroup$ – LLlAMnYP Sep 18 '17 at 11:38

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.