5

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.

Carl Woll
  • 130,679
  • 6
  • 243
  • 355
AzJ
  • 697
  • 4
  • 14

3 Answers3

12

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}

Carl Woll
  • 130,679
  • 6
  • 243
  • 355
  • Looks great. Can it be written so that it can be defined once and used as a function. – AzJ Jan 17 '18 at 23:23
  • 1
    Did you want List @@ expr instead of List @@ Flatten[{expr}]? – Michael E2 Jan 18 '18 at 03:08
  • @MichaelE2 I wanted the function to work for both Plus and non-Plus objects. Thanks for catching the error. – Carl Woll Jan 18 '18 at 03:39
  • @CarlWoll Thanks just what I was looking for. – AzJ Jan 18 '18 at 16:01
  • I have found this thread by searching and found this routine really useful. Is there a way to modify it though so that it always returns a two-element list? For now, posneg[x] and posneg[-x] give me one-element lists {x} and {-x}, whereas I would prefer {x,0} and {0,-x} so that I can use First[posneg[p]] and Last[posneg[p]] uniformly as positive and negative parts of some polynomial p. – Balazs May 01 '18 at 16:40
  • 1
    @Balazs Please see update. – Carl Woll May 01 '18 at 17:25
6

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

kglr
  • 394,356
  • 18
  • 477
  • 896
3

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?