# Elegantly split a matrix into positive and negative parts?

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?

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

• 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
• 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
• @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

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}

• +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
• Or in one line: {pos, neg} = m * UnitStep /@ {m, -m} – Mechanical snail Dec 20 '13 at 23:07

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] 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).

• Darn - so blindingly obvious :D – Yves Klett Dec 19 '13 at 22:39
• Hey Silvia! Can you help me? cooking.stackexchange.com/q/40424/2882 – Dr. belisarius Dec 19 '13 at 22:45
• @belisarius man you should have asked before eating that! – Yves Klett Dec 19 '13 at 22:49
• @ChrisDegnen "the ideal mind boggling" ? :) – Dr. belisarius Dec 20 '13 at 0:16
• @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

Another one for fun.

Positive:

(m + Abs[m])/2


Negative:

(m - Abs[m])/2

• +1 Instead of Sqrt[m^2], you can also use Abs[m]. – becko Dec 19 '13 at 23:00
• 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. – C. E. Dec 19 '13 at 23:10
• @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. – C. E. Dec 19 '13 at 23:11
• Also, Abs[-m] is the same as Abs[m]. – becko Dec 20 '13 at 16:15

An approach using UnitStep:

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


or with Positive and Negative:

{pos, neg} = {#, #} Boole@Through[{Positive, Negative}@#] &@mat

• 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
• @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

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}]}

• 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