Friday, May 21, 2021

Published May 21, 2021 by with 0 comment

Negative values with a log axis in Plotly

Although log10(<any number less than or equal to 0>) is not defined, there are situations where you want to visualize data as if it were. How can you get plotly to do that? Another way of asking is 'how can you mimic symlog functionality in plotly?'
First...a real example of when you'd want this. Imagine you do the following:
  • generate a 1 GHz tone
  • measure amplitude at +/- 10 kHz, +/- 100 kHz, +/- 1 MHz, ...
  • generate a 2 GHz tone
  • measure amplitude at +/- 10 kHz, +/- 100 kHz, +/- 1 MHz, ...
  • want to overlay those offset amplitude curves
You could just plot vs absolute frequency to see one, but to overlay you need to center around a tone, and it just makes sense to show 'offset from tone' as the x axis. However, those steps imply a log scale.

Below is a working example of exactly this situation in plotly.js. I've included the ideal here with both positive and negative on a log scale, and the normal linear plot so that the difference in parsing it quickly is obvious:

See the Pen symlog approximation by Robert Hamner (@rhamner) on CodePen.

The basic algorithm is pretty simple:
  • Determine the max and min values and the value closest to zero; largest of max and abs(min) is upper bound...value closest to zero is lower

  • Split all traces into positive and negative (x values here since I just did this for x in the demo)

  • Create two x-axes: one for positive and one for negative
    • give both the same bounds
    • reverse the negative x-axis
    • assign ticks with positive values but negative labels to the negative x-axis
    • put a small buffer between them to represent that zero is undefined

  • Plot positive traces vs positive x-axis and negative traces vs negative x-axis, but make the negative x values positive
In that demo above you can just step through the javascript code and it should all be pretty clear.

If you want a slight variant of this that matches 'symlog' in matplotlib, just add a third, linear axis to connect these two instead of leaving a gap. I personally prefer the gap for this situation.



Post a Comment