13
$\begingroup$

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?

$\endgroup$
0

6 Answers 6

23
$\begingroup$

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

$\endgroup$
5
  • $\begingroup$ You might even save a few characters with things like pos=m-neg... $\endgroup$
    – Yves Klett
    Dec 19, 2013 at 22:33
  • $\begingroup$ This is MUCH faster than mine! $\endgroup$
    – Silvia
    Dec 20, 2013 at 0:58
  • 1
    $\begingroup$ I bet you didn't think this would be one of your most popular answers ever. +1 :-) $\endgroup$
    – Mr.Wizard
    Dec 20, 2013 at 17:34
  • $\begingroup$ @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... $\endgroup$
    – Yves Klett
    Dec 20, 2013 at 18:20
  • 1
    $\begingroup$ @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. $\endgroup$
    – Mr.Wizard
    Dec 21, 2013 at 4:54
13
$\begingroup$

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}
$\endgroup$
2
  • $\begingroup$ +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 :) $\endgroup$
    – becko
    Dec 19, 2013 at 22:56
  • 1
    $\begingroup$ Or in one line: {pos, neg} = m * UnitStep /@ {m, -m} $\endgroup$ Dec 20, 2013 at 23:07
9
$\begingroup$

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

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

Another one for fun.

Positive:

(m + Abs[m])/2

Negative:

(m - Abs[m])/2
$\endgroup$
6
  • 3
    $\begingroup$ +1 Instead of Sqrt[m^2], you can also use Abs[m]. $\endgroup$
    – becko
    Dec 19, 2013 at 23:00
  • 1
    $\begingroup$ my only quarrel with this solution is that you could run into numerical issues. $\endgroup$
    – Yves Klett
    Dec 19, 2013 at 23:03
  • $\begingroup$ @becko That's better, thanks. $\endgroup$
    – C. E.
    Dec 19, 2013 at 23:10
  • 1
    $\begingroup$ @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. $\endgroup$
    – C. E.
    Dec 19, 2013 at 23:11
  • 1
    $\begingroup$ Also, Abs[-m] is the same as Abs[m]. $\endgroup$
    – becko
    Dec 20, 2013 at 16:15
6
$\begingroup$

An approach using UnitStep:

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

or with Positive and Negative:

{pos, neg} = {#, #} Boole@Through[{Positive, Negative}@#] &@mat
$\endgroup$
3
  • $\begingroup$ Go obfuscious (^_-) $\endgroup$
    – Yves Klett
    Dec 19, 2013 at 22:47
  • $\begingroup$ +1... Are you using With[{u = UnitStep}, ...] just to save characters? You're not helping the OP :( $\endgroup$
    – becko
    Dec 19, 2013 at 23:05
  • 2
    $\begingroup$ @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 $\endgroup$
    – rm -rf
    Dec 20, 2013 at 2:08
5
$\begingroup$

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}]}
$\endgroup$
2
  • $\begingroup$ neg and pos is somehow universal :D $\endgroup$
    – Yves Klett
    Dec 19, 2013 at 22:19
  • $\begingroup$ @YvesKlett, Oh, I like your Clip method... $\endgroup$
    – kale
    Dec 19, 2013 at 22:20

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

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