4
$\begingroup$

Python's matplotlib has the possibility to use a symmetric log (symlog) axis, as demonstrated in the documentation. You can set a negative threshold and positive threshold, between which the plot is linear (so it can go through 0), and then beyond those thresholds the scale is logarithmic (decades go in both the positive and negative directions).

Then, you can show sign-indefinite functions that grow large in absolute value, even if in some places they grow very negative.

Is there any easy way to achieve this effect or a utility to make such a plot in Mathematica?

$\endgroup$
4
  • $\begingroup$ A Mathematica equivalent is to apply the LogisticSigmoid to your function. $\endgroup$
    – yarchik
    Mar 15, 2020 at 6:57
  • $\begingroup$ That doesn't seem to be true. In mpl's symplot asymptotically you'd plot linear growth if your function were exp(x). But LogisticSigmoid[f[x]] = 1/(1+Exp[-f[x]]) which asymptotes to 1. Moreover, interpreting such a graph would tricky, because the ticks wouldn't show logarithmic decades. $\endgroup$
    – evanb
    Mar 15, 2020 at 13:41
  • 1
    $\begingroup$ Yes, you are right, it was a provocative comment. But I use it to quickly get an idea about some functions. At least it has a linear behavior around zero and smoothly represents diverging functions. $\endgroup$
    – yarchik
    Mar 15, 2020 at 15:26
  • 1
    $\begingroup$ This can now be achieved with ScalingFunctions -> "SignedLog". See the ScalingFunctions help for details. $\endgroup$
    – evanb
    Mar 28, 2022 at 8:31

3 Answers 3

8
$\begingroup$

You may use the ScalingFunctions option which is available for most plotting functions.

We need a logarithmic function that passes through zero that we can flip and mirror in the negative domain. With such a function we will not need to specify a linear portion and thus have a smooth function over the domain. A simple solution is Log[x + 1].

Plot[Log[x + 1], {x, 0, 10}]

Mathematica graphics

Using Abs and Sign this equation and its inverse can be setup as scaling functions.

symlog =
  {
   Function[x, Sign[x] (Log[Abs[x] + 1])],
   Function[y, Sign[y] (Exp[Abs[y]] - 1)]
   };

Then with Plot

Plot[x, {x, -10, 10},
 ScalingFunctions -> symlog]

Mathematica graphics

and ListPlot

ListPlot[
 Transpose@ConstantArray[Range[-10, 10, .25], 2],
 ScalingFunctions -> symlog]

Mathematica graphics

Hope this helps.

$\endgroup$
1
  • $\begingroup$ this answer should be accepted. And surprisingly, SymLog is not one of embedded options for ScalingFunctions. $\endgroup$
    – sunt05
    Feb 5, 2021 at 16:46
4
$\begingroup$

Creating this plot we need the function that maps from and to these new axes. Just like we have with logarithmic axes we need the function Log and its opposite Exp in the ScalingFunction option.

So let's define these two functions:

ClearAll[ConvertPoint, UnConvertPoint]
ConvertPoint[n_?NumericQ, {down_, up_}] := Module[{},
  If[n < 0,
   -ConvertPoint[-n, {-up, -down}]
   ,
   If[n < up,
    n
    ,
    Log[n/up] + up
    ]
   ]
  ]
UnConvertPoint[n_?NumericQ, {down_, up_}] := Module[{},
  If[n < 0,
   -UnConvertPoint[-n, {-up, -down}]
   ,
   If[n < up,
    n
    ,
    Exp[n - up] up
    ]
   ]
  ]

Given an input these will convert these back and from these new coordinates.

Now we modify the built-in ListPlot function:

ClearAll[ListSymmetricLogPlot];
ListSymmetricLogPlot[data_List, threshold_?NumericQ, opts : OptionsPattern[]] := 
 ListSymmetricLogPlot[data, {-threshold, threshold}, opts]
ListSymmetricLogPlot[data_List, thresholds : {downthres_, upthres_}, opts : OptionsPattern[]] := 
 Module[{xmin, xmax, ymin, ymax, vticks1, vticks2, vticks3, vticks, vticksright, tmp},
  {{xmin, xmax}, {ymin, ymax}} = CoordinateBounds[data];
  vticks1 = If[ymin < downthres,
    tmp = Charting`ScaledTicks[{Log, Exp}][Log[-downthres], Log[-ymin]];
    tmp[[All, 1]] = Minus@*Exp /@ tmp[[All, 1]];
    tmp[[All, 2]] = Replace[tmp[[All, 2]], {x_?NumericQ :> -x, _Superscript[a_, b_] :> Superscript[-a, b]}, {1}];
    tmp
    ,
    {}
    ];
  vticks2 = Charting`ScaledTicks["Linear"][downthres, upthres, 4];
  vticks3 = If[ymax > upthres,
    tmp = Charting`ScaledTicks[{Log, Exp}][Log@upthres, Log@ymax];
    tmp[[All, 1]] = Exp /@ tmp[[All, 1]];
    tmp
    ,
    {}
    ];
  vticks = vticksright = DeleteDuplicatesBy[SortBy[Join[vticks1, vticks2, vticks3], First],
      First];
  vticksright[[All, 2]] = "";
  ListPlot[data, opts,
   ScalingFunctions -> {None, {ConvertPoint[#, thresholds] &, UnConvertPoint[#, thresholds] &}},
   PlotRange -> All,
   FrameTicks -> {{vticks, vticksright}, Automatic},
   Ticks -> {Automatic, vticks}
   ]
  ]

We can now test it out:

ListSymmetricLogPlot[{#,#}&/@Range[-10,10,0.2],0.5,ImageSize->600]
ListSymmetricLogPlot[{#,Tan[#]}&/@Range[-0.4995Pi,0.4995Pi,0.001Pi],{-1,1},Joined->True,Frame->True,ImageSize->600]

Giving:

enter image description here

Another test:

ListSymmetricLogPlot[Join[Table[{-x,-Exp[x-5]},{x,0,10,0.01}],Table[{x,Exp[x-5]},{x,0,10,0.01}]],{-3,3}]

Giving:

enter image description here

One limitation is now that you need to input {x,y} pairs as data just the y values {y1,y2,y3,…} does not work.

$\endgroup$
1
  • $\begingroup$ This is a great answer! I'm using Mathematica 12.1 and the ticks on the y axis have sometimes no minus sign for values in the range (-1;0). Compare ListSymmetricLogPlot[{#, #} & /@ Range[-10, 10, 0.2], 0.01, ImageSize -> 600] your example with threshold 0.01 instead of 0.5. $\endgroup$
    – Qbyte
    Aug 11, 2020 at 13:27
2
$\begingroup$

Edmund's Answer works, but there is a small error in it: it is missing parentheses to correctly define the inverse function. So the correct symlog function definition should be:

symlog = {
  Function[x, Sign[x] * Log[Abs[x] + 1]], 
  Function[y, Sign[y] * (Exp[Abs[y]] - 1)]};
}

The Error only becomes visible when you try to plot values in the range [-1,1]. Without this fix, mathematica shows a vertical axis without ticks. The fix resolves this.

Moreover, there has been made the suggestion to apply a scale to the functions, as shown here. This allows to observe a proper log-scale even if the function scale is at very small orders of magnitude. In Mathematica, I use this scaled version of symlog:

ssymlog[mag_] := {
  Function[x, Sign[x]*Log[1 + Abs[x]*10^mag]], 
  Function[y, Sign[y]*10^-mag*(Exp[Abs[y]] - 1)]
}

Here is an image that shows the difference on a function of mine, where the ordinary symlog scale does not really add much of a stretch to the function:

enter image description here

$\endgroup$
2
  • 1
    $\begingroup$ Thanks for pointing this out; I have incorporated your correction into Edmund's answer! $\endgroup$
    – evanb
    Aug 11, 2021 at 15:45
  • 1
    $\begingroup$ Thanks for the fast reply. I have added another extension in my post, addressing scaling issues. $\endgroup$
    – euphrat
    Aug 11, 2021 at 16:31

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.