Take the 2-minute tour ×
Mathematica Stack Exchange is a question and answer site for users of Mathematica. It's 100% free, no registration required.

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?

share|improve this question
    
I think you mean vN=Boole[Negative[v1]]; –  belisarius Jan 24 at 1:58
    
Also, please note that Positive@0 == Negative@0 == False –  belisarius Jan 24 at 2:02
    
vP v1+ (1-vP) v2 ? –  george2079 Jan 24 at 2:05
    
@belisarius. Yes, I mean that. Thanks. –  sam84 Jan 24 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 at 22:43

3 Answers 3

up vote 9 down vote accepted
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

(pos = SparseArray[UnitStep[-v1]]["AdjacencyLists"];
  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.

share|improve this answer
    
This is what OP's v3 does, right (with belisarius' typo fix) –  rm -rf Jan 24 at 2:11
    
Nope. Gives incorrect result. –  rasher Jan 24 at 2:46
    
@rasher -- All four methods give the same answer. –  bill s Jan 24 at 2:47
    
No, they don't. Yours is incorrectly giving v2 entries where v1 is zero. Zero is not positive... –  rasher Jan 24 at 2:50
1  
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 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}
share|improve this answer
    
Clean. I like it, +1 –  rasher Jan 24 at 11:40
    
@rasher Thank you. I wish Position were as fast; then it would be even cleaner. –  Mr.Wizard Jan 24 at 23:15
With[{c = Clip[v1, {0, 0}, {0, 1}]}, c (v1 - v2) + v2]
share|improve this answer

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

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