5
$\begingroup$

I am using Mathematica to manipulate/keep track of a long series of symbol manipulations.

One thing that I would like to do is separate or order the terms of an expression by positive and negative sign.

For example separating enter image description here or something more simple that we can use as an example

b+ c^2 -c^2 +a^4 -c^5

I say order by positive or negative sign as (for my problem) we assume that the constants are all positive.

I looked at this post but I don't know how I can apply this to my problem.

$\endgroup$

3 Answers 3

12
$\begingroup$

Perhaps something like:

Values @ GroupBy[
    List @@ (b+c^2-c^2+a^4-c^5),
    Internal`SyntacticNegativeQ,
    Total
]

{a^4 + b, -c^5}

Addendum

OP requested a function:

posneg[expr_] := Values @ GroupBy[
    Replace[expr,
        {
        a_Plus :> List@@a,
        a_ :> {a}
        }
    ],
    Internal`SyntacticNegativeQ,
    Total
]

A couple examples:

posneg[b+c^2-c^3+a^4-c^5]
posneg[x-2]

{a^4 + b + c^2, -c^3 - c^5}

{-2, x}

Update

A comment requested a version that always returns both the negative and positive parts, padding with 0 if necessary.

One way to do this is to add both a positive and a negative number to the list, and then remove those numbers at the end. Here is a variation that does this:

posneg[expr_]:=Values@GroupBy[
    Replace[expr,
        {
        a_Plus:>Join[{-1,1}, List@@a],
        a_:>{-1, 1, a}
        }
    ],
    Internal`SyntacticNegativeQ,
    Total @* Rest
]

Examples:

posneg[-x]
posneg[x]

{-x, 0}

{0, x}

$\endgroup$
6
  • $\begingroup$ Looks great. Can it be written so that it can be defined once and used as a function. $\endgroup$
    – AzJ
    Commented Jan 17, 2018 at 23:23
  • 1
    $\begingroup$ Did you want List @@ expr instead of List @@ Flatten[{expr}]? $\endgroup$
    – Michael E2
    Commented Jan 18, 2018 at 3:08
  • $\begingroup$ @MichaelE2 I wanted the function to work for both Plus and non-Plus objects. Thanks for catching the error. $\endgroup$
    – Carl Woll
    Commented Jan 18, 2018 at 3:39
  • $\begingroup$ @CarlWoll Thanks just what I was looking for. $\endgroup$
    – AzJ
    Commented Jan 18, 2018 at 16:01
  • 1
    $\begingroup$ @Balazs Please see update. $\endgroup$
    – Carl Woll
    Commented May 1, 2018 at 17:25
6
$\begingroup$

perhaps

ClearAll[order]
order[Times[x_?Negative, _]| _?Negative] := -1
order[_] := 1;

GatherBy[List@@(b + c^2 - c^3 + a^6 - c^5), order]

{{a^6, b, c^2}, {-c^3, -c^5}}

Values @ GroupBy[List @@ (b + c^2 - c^3 + a^6 - c^5), order]

{{a^6, b, c^2}, {-c^3, -c^5}}

Values @ GroupBy[List @@ (b + c^2 - c^3 + a^6 - c^5), order, Total]

    {a^6 + b + c^2, -c^3 - c^5}

Values @ Merge[Identity][order[#] -> # & /@ (List @@ (b + c^2 - c^3 + a^6 - c^5))]

{{a^6, b, c^2}, {-c^3, -c^5}}

Values @ Merge[Total][order[#] -> # & /@ (List @@ (b + c^2 - c^3 + a^6 - c^5))]

{a^6 + b + c^2, -c^3 - c^5}

SortBy[Inactivate[b + c^2 - c^3 + a^6 - c^5, Plus], order]

-c^3 + -c^5 + a^6 + b + c^2

$\endgroup$
3
  • $\begingroup$ Can you tell me what the second thing does? $\endgroup$
    – AzJ
    Commented Jan 17, 2018 at 23:21
  • $\begingroup$ It groups the elements of the set according to the condition order, as previously defined. $\endgroup$ Commented Jan 17, 2018 at 23:24
  • $\begingroup$ Thank you @David. AzJ, wrapped it with Values now to get a list of groups rather than an Association. $\endgroup$
    – kglr
    Commented Jan 17, 2018 at 23:45
3
$\begingroup$

How about this?

Definitions:

sum = b + c^2 - c^2 + a^4 - c^5;

signList = List[];

posList = List[];

negList = List[];

Function to find the sign of the term by dividing the term by the absolute value of the term, in the list on the end you can put the assumptions on the constants.

sign[i_, expr_] := 
 Refine[expr[[i]]/Abs[expr[[i]]], {a > 0, b > 0, c > 0}]

Make a list where you find all the signs. For-loop going through all the terms, using the fn sign to find the sign and put it in signList.

findOrder[expr_] := For[i = 1, i < (Length[expr] + 1), i++, signList = Append[signList, N[sign[i, expr]]]]

Then order the terms in two separate lists, depending on their sign.

orderTerms[expr_] := For[i = 1, i < (Length[expr] + 1), i++, If[signList[[i]] > 0, posList = Append[posList, expr[[i]]], negList = Append[negList, expr[[i]]]]]

So for this example:

findOrder[sum]

orderTerms[sum]

posList

{a^4, b}

negList

{-c^5}

Is this what you wanted?

$\endgroup$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

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