2

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?

mrz
  • 11,686
  • 2
  • 25
  • 81

3 Answers3

8
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.

LLlAMnYP
  • 11,486
  • 26
  • 65
  • Dear LLlAMnYP, this is what I wanted. Can that be done also with 2 dimensional lists e.g. Dimensions[list]={720,577}? – mrz May 12 '15 at 09:47
  • 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}}? – LLlAMnYP May 12 '15 at 09:53
  • 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. – mrz May 12 '15 at 09:58
7

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

Carl Woll
  • 130,679
  • 6
  • 243
  • 355
  • 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. – LLlAMnYP Sep 18 '17 at 11:22
5

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

yode
  • 26,686
  • 4
  • 62
  • 167