7

Consider the following code:

from numpy import log2
import matplotlib.pyplot as plt

xdata = [log2(x)*(10/log2(10)) for x in range(1,11)]
ydata = range(10)
plt.plot(xdata, ydata)
plt.show()

This produces the following plot: The plot I do not want My question is, how can I modify this, so that the plot, with the exact same data as input, appears as a straight line? This basically requires scaling the x-axis appropriately, but I cannot figure how to do this. The reason for doing this is that I am displaying a function that changes very little at the beginning, but starts to fluctuate more towards the end of the valid interval, so I want to have a higher horizontal resolution towards the end. If anyone can propose an alternative solution to my approach, feel free to do so!

Björn Pollex
  • 75,346
  • 28
  • 201
  • 283

2 Answers2

7

Here is how it is done. A good example to follow. You just subclass the ScaleBase class.

Here's your transform. It's not too complicated when you whittle out all the custom formatters and stuff. Just a little verbose.

from numpy import log2
import matplotlib.pyplot as plt

from matplotlib import scale as mscale
from matplotlib import transforms as mtransforms

class CustomScale(mscale.ScaleBase):
    name = 'custom'

    def __init__(self, axis, **kwargs):
        mscale.ScaleBase.__init__(self)
        self.thresh = None #thresh

    def get_transform(self):
        return self.CustomTransform(self.thresh)

    def set_default_locators_and_formatters(self, axis):
        pass

    class CustomTransform(mtransforms.Transform):
        input_dims = 1
        output_dims = 1
        is_separable = True

        def __init__(self, thresh):
            mtransforms.Transform.__init__(self)
            self.thresh = thresh

        def transform_non_affine(self, a):
            return 10**(a/10)

        def inverted(self):
            return CustomScale.InvertedCustomTransform(self.thresh)

    class InvertedCustomTransform(mtransforms.Transform):
        input_dims = 1
        output_dims = 1
        is_separable = True

        def __init__(self, thresh):
            mtransforms.Transform.__init__(self)
            self.thresh = thresh

        def transform_non_affine(self, a):
            return log2(a)*(10/log2(10))

        def inverted(self):
            return CustomScale.CustomTransform(self.thresh)


mscale.register_scale(CustomScale)

xdata = [log2(x)*(10/log2(10)) for x in range(1,11)]
ydata = range(10)
plt.plot(xdata, ydata)

plt.gca().set_xscale('custom')
plt.show()
HD189733b
  • 144
  • 7
Paul
  • 42,322
  • 15
  • 106
  • 123
  • Actually, you should be able to just monkey patch things without defining a new scale and transform... `ax.xaxis._scale._transform = ax.xaxis._scale._transform.inverted()` This doesn't work for some reason, though... (or, rather, it works until you plot data...) Obviously, your example works, albeit rather verbose! – Joe Kington Feb 04 '11 at 16:52
  • Thanks! I discovered that example myself, but I was kinda hoping there might be a simpler way. – Björn Pollex Feb 04 '11 at 19:03
2

The easiest way is to use semilogy

from numpy import log2
import matplotlib.pyplot as plt

xdata = log2(range(1,11)) * (10/log2(10))
ydata = range(10)
plt.semilogy(xdata, ydata)
plt.show()

enter image description here

Joe Kington
  • 275,208
  • 71
  • 604
  • 463
  • This has the problem that it does not show y values smaller than 1. Also, it defeats the purpose of having a higher horizontal resolution for high x values. – Björn Pollex Feb 04 '11 at 15:21
  • It will certainly show values below 1! (i.e. 0.001 --> 10^-3) It won't show values at or below 0, though. I'm slightly confused as to what you mean by "higher horizontal resolution for high x values"... Do you meant the resolution of your actual plot (i.e. the number of segments in the line) or where your xticks are... Alternatively, you could just have a broken axis... – Joe Kington Feb 04 '11 at 15:42
  • Ah! I just realized what you're wanting to do... You effectively want the reverse of a semilog-xaxis, right? I.e. you want the x-axis to grow exponentially, not logarithmically? (Of course, that's essentially what you said in your original question, it just took awhile to get through my thick skull...) – Joe Kington Feb 04 '11 at 15:54
  • You've got it right now! I looked, and it seems this would be possible by creating a custom transform, but that seems rather complicated, I will have to look into it. Still hope there is a simpler solution though. – Björn Pollex Feb 04 '11 at 15:59
  • The simple thing would be to change your data and put custom labels on the axis, but that would make the interactive plot wrong with regard to mouse selections and stuff. I think the only way to do it right is with a custom transform. (Which i did for you -- see my edit.) – Paul Feb 04 '11 at 16:50