# Fast method for combining two lists

I have two lists, v1 and v2. Each element of the lists may be positive or negative. I want to create a list v3 whose entry i is v1[[i]] if v1[[i]] is posive, but otherwise is v2[[i]].

The trivial way to implement this is to use a For-loop with If. Is there any faster way?

I tried using logical operator:

vP = Boole[Positive[v1]];
vN = Boole[Negative[v2]];
v3 = vP v1 + vN v2;


I tought that using logical operations would be faster than going through indices, but instead it is slower. What would be a fast way to do this?

• I think you mean vN=Boole[Negative[v1]]; – Dr. belisarius Jan 24 '14 at 1:58
• Also, please note that Positive@0 == Negative@0 == False – Dr. belisarius Jan 24 '14 at 2:02
• vP v1+ (1-vP) v2 ? – george2079 Jan 24 '14 at 2:05
• @belisarius. Yes, I mean that. Thanks. – sam84 Jan 24 '14 at 16:29
• You got good answers, but I feel that a bit more explanation is in order. You are on the right track with the approach you tried. Doing calculations on complete arrays instead of looping through array elements is usually very fast, and it's called "vectorization". The reason why it wasn't fast in this case is that you were working with symbols rather than numbers: True and False. To get optimal performance from Mathematica you need to use homogeneous numerical arrays, which can be packed. Arrays with symbols in them ... – Szabolcs Jan 24 '14 at 22:43

v1 = RandomChoice[{-1, 1}, 100];
v2 = RandomChoice[{-1, 1}, 100];
vL = UnitStep[v1];
vL*v1 + (1 - vL)*v2


Using PatoCriollo's comparison (and now updated to include rasher and Mr. Wizard's methods):

n = 10^6;
v1 = RandomChoice[{-1, 1}, n];
v2 = RandomChoice[{-1, 1}, n];

(bp = Boole[Positive[v1]];
test1 = bp v1 + (1 - bp) v2); // Timing

test2 = With[{c = Clip[v1, {0, 0}, {0, 1}]}, c (v1 - v2) + v2]; // Timing

test3 = Table[If[Positive[v1[[i]]], v1[[i]], v2[[i]]], {i, Length@v1}]; // Timing

(vL = UnitStep[v1];
test4 = vL*v1 + (1 - vL)*v2;) // Timing

(vL = UnitStep[v1];
test5 = vL (v1 - v2) + v2;) // Timing

(vL = UnitStep[v1];
test6 = vL Subtract[v1, v2] + v2;) // Timing

test7 = v1;
test7[[pos]] = v2[[pos]];) // Timing

{0.479009, Null}
{0.185765, Null}
{0.192930, Null}
{0.100705, Null}
{0.102183, Null}
{0.078348, Null}
{0.087175, Null}

test1 == test2 == test3 == test4 == test5 == test6 == test7
True


To summarize: the UnitStep method seems the fastest, the Clip and Table methods are about the same, while Boole takes up the rear. All the methods give the same answer. Interestingly, when we change RandomChoice to RandomReal, the Clip and the UnitStep methods are about the same speed (and both are faster than Table and Boole). Using Simon's trick of substituting Subtract[v1,v2] for v1-v2 also speeds up the UnitStep calculations by a modest, but measurable amount. Mr. Wizard's sparse array approach seems to be competitive with, but slightly slower than the UnitStep.

• This is what OP's v3 does, right (with belisarius' typo fix) – rm -rf Jan 24 '14 at 2:11
• Nope. Gives incorrect result. – ciao Jan 24 '14 at 2:46
• @rasher -- All four methods give the same answer. – bill s Jan 24 '14 at 2:47
• No, they don't. Yours is incorrectly giving v2 entries where v1 is zero. Zero is not positive... – ciao Jan 24 '14 at 2:50
• You can get a bit more speed by rearranging the expression to remove one of the multiplications: vL (v1 - v2) + v2, and then a bit more by using Subtract: vL Subtract[v1, v2] + v2 – Simon Woods Jan 24 '14 at 9:00

Here is a method using SparseArray properties to find positions (indexes), then Part to make the replacements. It may be a bit more general than the mathematical methods and it appears to be roughly as efficient on my system (running version 7). Note that unless it is acceptable to count zero as positive it is inappropriate to use UnitStep[v1] -- instead use UnitStep[-v1] to watershed in the other direction.

Sample data:

SeedRandom[7]
MatrixForm[ {v1, v2} = RandomInteger[{-5, 5}, {2, 15}] ]


$\left( \begin{array}{ccccccccccccccc} -1 & 2 & -1 & 5 & 3 & 3 & 0 & -2 & -1 & 0 & -1 & 0 & 0 & -5 & 4 \\ 5 & 4 & -2 & 5 & 1 & -3 & 2 & 5 & 1 & -3 & -4 & -2 & -4 & -2 & -4 \end{array} \right)$

pos = SparseArray[UnitStep[-v1]]["AdjacencyLists"];
v3 = v1;
v3[[pos]] = v2[[pos]];
v3

{5, 2, -2, 5, 3, 3, 2, 5, 1, -3, -4, -2, -4, -2, 4}

• Clean. I like it, +1 – ciao Jan 24 '14 at 11:40
• @rasher Thank you. I wish Position were as fast; then it would be even cleaner. – Mr.Wizard Jan 24 '14 at 23:15
With[{c = Clip[v1, {0, 0}, {0, 1}]}, c (v1 - v2) + v2]