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 a matrix $M$ of real components, and I want to split it into two matrices $M^+$ and $M^-$ of the same dimensions as $M$, where $M^+$ contains the positive components of $M$ (the remaining entries being filled with zeros), and $M^-$ contains the negative components, such that $M=M^+ + M^-$.

Suppose the matrices are represented in the usual way in Mathematica (as lists of lists, or as sparse arrays)

How can I do this efficiently and elegantly in Mathematica?

share|improve this question
add comment

6 Answers 6

up vote 21 down vote accepted

Clip is usually quite fast:

m = RandomReal[{-10^6, 10^6}, {3, 3}];

neg = Clip[m, {-Infinity, 0}]
pos = Clip[m, {0, Infinity}]
(*{{0., -181286., -442666.}, {0., -233694., -847828.}, {-128249., 0., -540037.}}

{{947792., 0., 0.}, {755278., 0., 0.}, {0., 63058.1, 0.}}*)
neg + pos == m

True

share|improve this answer
    
You might even save a few characters with things like pos=m-neg... –  Yves Klett Dec 19 '13 at 22:33
    
This is MUCH faster than mine! –  Silvia Dec 20 '13 at 0:58
1  
I bet you didn't think this would be one of your most popular answers ever. +1 :-) –  Mr.Wizard Dec 20 '13 at 17:34
    
@Mr.Wizard Innuendo :D Only what that says about my average contribution, I dare not dwell upon. I was also waiting for you to steal my thunder... –  Yves Klett Dec 20 '13 at 18:20
1  
@Yves What it says is that your average contribution is more esoteric than these little nuggets that have popular appeal and that the voting system provides no way to differentiate between "interesting" and "mindbogglingly amazing." Some of the answers I have put the most thought and effort into scored the lowest, while something minor dropped in passing scores highly. Occasionally something that took a lot of thought is apparently concise and general enough to be popular. –  Mr.Wizard Dec 21 '13 at 4:54
add comment

Here's a way...

mat = RandomInteger[{-10, 10}, {10, 10}];

{neg, pos} = {mat /. x_ /; x > 0 -> 0, mat /. x_ /; x < 0 -> 0};


neg+pos==mat

True

And here's another way using Map:

{neg, pos} = {Map[Min[#, 0] &, mat, {2}], Map[Max[#, 0] &, mat, {2}]}
share|improve this answer
    
neg and pos is somehow universal :D –  Yves Klett Dec 19 '13 at 22:19
    
@YvesKlett, Oh, I like your Clip method... –  kale Dec 19 '13 at 22:20
add comment

You could also use the functions Positive and Negative:

m = RandomInteger[{-10, 10}, {10, 10}];
pos = m Boole[Positive[m]];
neg = m Boole[Negative[m]];

give the positive and negative portions. As becko points out, replacing Boole[Positive[mat]] with UnitStep[m]:

pos = m UnitStep[m];
neg = m UnitStep[-m];

is even more succinct. These can even be combined into one line (as suggested by Mechanical Snail:

{pos, neg} = m*UnitStep[#] & /@ {m, -m}
share|improve this answer
    
+1 Instead of Boole[Positive[mat]], you can also use UnitStep[mat], and instead of Boole[Negative[mat]], you can also use UnitStep[-mat]... No big improvement, just one function instead of two :) –  becko Dec 19 '13 at 22:56
1  
Or in one line: {pos, neg} = m * UnitStep /@ {m, -m} –  Mechanical snail Dec 20 '13 at 23:07
add comment

This is yet another way:

mat = RandomInteger[{-10, 10}, {10, 10}];
matPair = Map[Sort[{#, 0}] &, mat, {2}] // Transpose[#, {2, 3, 1}] &;

Grid[# /. {
           x_?NumericQ :> If[x < 0,
                 Item["", Background -> Darker[Red, .5]],
                 If[x > 0,
                    Item["", Background -> Darker[Green, .4]],
                    Item["", Background -> GrayLevel[.9]]]]
          }, Frame -> True, ItemSize -> {.4, 1}] & /@
 Prepend[matPair, mat]

comparison

It can be seen like this: transpose the 3rd level i.e. the {negative, positive} level to be the 1st level, and leave other levels untouched (thus level-down in order).

share|improve this answer
1  
Darn - so blindingly obvious :D –  Yves Klett Dec 19 '13 at 22:39
1  
Hey Silvia! Can you help me? cooking.stackexchange.com/q/40424/2882 –  belisarius Dec 19 '13 at 22:45
3  
@belisarius man you should have asked before eating that! –  Yves Klett Dec 19 '13 at 22:49
1  
@ChrisDegnen "the ideal mind boggling" ? :) –  belisarius Dec 20 '13 at 0:16
1  
@ChrisDegnen It can be seen like this: transpose the 3rd level i.e. the {-,+} level to be the first level, and leave other levels untouched (thus level-down in order). –  Silvia Dec 20 '13 at 0:39
show 9 more comments

An approach using UnitStep:

{pos, neg} = With[{u = UnitStep}, {# u@#, # u@-#}] &@mat

or with Positive and Negative:

{pos, neg} = {#, #} Boole@Through[{Positive, Negative}@#] &@mat
share|improve this answer
    
Go obfuscious (^_-) –  Yves Klett Dec 19 '13 at 22:47
    
+1... Are you using With[{u = UnitStep}, ...] just to save characters? You're not helping the OP :( –  becko Dec 19 '13 at 23:05
1  
@becko I would never use something like that to save characters... that's Mr.Wizard's modus operandi :) In fact, this takes more characters than just writing out UnitStep in both places. It must've been the wine... Also, why are you referring to yourself in the third person? o_O –  rm -rf Dec 20 '13 at 2:08
add comment

Another one for fun.

Positive:

(m + Abs[m])/2

Negative:

(m - Abs[m])/2
share|improve this answer
3  
+1 Instead of Sqrt[m^2], you can also use Abs[m]. –  becko Dec 19 '13 at 23:00
1  
my only quarrel with this solution is that you could run into numerical issues. –  Yves Klett Dec 19 '13 at 23:03
    
@becko That's better, thanks. –  Pickett Dec 19 '13 at 23:10
1  
@YvesKlett The switch to Abs partly fixes this in that Abs will warn when it reaches the precision limit. Addition and division by two shouldn't be a problem, I think. –  Pickett Dec 19 '13 at 23:11
    
Also, Abs[-m] is the same as Abs[m]. –  becko Dec 20 '13 at 16:15
show 1 more comment

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.