2

I am trying to make a custom scale for plotting like is shown http://matplotlib.org/examples/api/custom_scale_example.html and Creating Probability/Frequency Axis Grid (Irregularly Spaced) with Matplotlib, but my scale goes to infinity at both 0 and 1. I am leveraging masking arrays like the examples shows to block values between .01 and .99. I have 4 cases outlined below. My desire is to get seaborn working with a y-axis with no warnings. I found 1 case (case 2) where without seaborn I get no errors, but I have no idea why it works vs case 1

from __future__ import unicode_literals

import numpy as np
from numpy import ma
from matplotlib import scale as mscale
from matplotlib import transforms as mtransforms
from matplotlib.ticker import AutoLocator
from matplotlib import rcParams


# BUG: this example fails with any other setting of axisbelow
rcParams['axes.axisbelow'] = False


class WeibullProbScale(mscale.ScaleBase):

    name = 'weibull'

    def __init__(self, axis, **kwargs):

        mscale.ScaleBase.__init__(self)
        lowerLim = kwargs.pop("lowerLim",.01)
        upperLim = kwargs.pop("upperLim", .99)

        self.lowerLim = lowerLim
        self.upperLim = upperLim

    def get_transform(self):
        return self.WeibullProbTransform(self.lowerLim,self.upperLim)

    def set_default_locators_and_formatters(self, axis):

        axis.set_major_locator(AutoLocator())

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

        def __init__(self, lowerLim, upperLim):
            mtransforms.Transform.__init__(self)
            self.lowerLim = lowerLim
            self.upperLim = upperLim

        def transform_non_affine(self, a):
            masked = ma.masked_where((a < self.lowerLim) | (a > self.upperLim), a)
            if masked.mask.any():
                return ma.log(ma.log(1.0/(1.0 - masked)))
            else:
                return np.log(np.log(1.0/(1.0 - a)))

        def inverted(self):
            return WeibullProbScale.InvertedWeibullProbTransform(
                self.lowerLim, self.upperLim)

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

        def __init__(self, lowerLim, upperLim):
            mtransforms.Transform.__init__(self)
            self.lowerLim = lowerLim
            self.upperLim = upperLim

        def transform_non_affine(self, a):
            return 1.0 - np.exp(-np.exp(a))

        def inverted(self):
            return WeibullProbScale.WeibullProbTransform(self.thresh)

# Now that the Scale class has been defined, it must be registered so
# that ``matplotlib`` can find it.
mscale.register_scale(WeibullProbScale)


if __name__ == '__main__':

    #Setup arrays to plot
    import matplotlib.pyplot as plt
    from scipy.stats import exponweib

    t = np.logspace(1,6,num=100,base=10)
    dist = exponweib(a=1,c=1.3,loc=0,scale=10000)

    # Case 1 - Standard...what I want
    if True:
        plt.plot(t, dist.cdf(t), '-', lw=2)
        plt.gca().set_xscale('log')
        plt.gca().set_yscale('weibull',lowerLim=.01,upperLim=.99)
        plt.ylim([.01,.99])
        plt.xlabel('time')
        plt.ylabel('probability')
        plt.title('Probability Plot')
        plt.grid(True)
        plt.show()

    # Case 2 - For some reason 1.E-10 makes the error go away...
    if True:
        plt.plot(t, dist.cdf(t), '-', lw=2)
        plt.gca().set_xscale('log')
        plt.gca().set_yscale('weibull',lowerLim=1.E-10,upperLim=.99)
        plt.ylim([.01,.99])
        plt.xlabel('time')
        plt.ylabel('probability')
        plt.title('Probability Plot')
        plt.grid(True)
        plt.show()

    # Case 3 - Seaborn works without y label
    if True:
        import seaborn as sns
        plt.plot(t, dist.cdf(t), '-', lw=2)
        plt.gca().set_xscale('log')
        plt.gca().set_yscale('weibull',lowerLim=1.E-10,upperLim=.99)
        plt.ylim([.01,.99])
        plt.xlabel('time')
        plt.title('Probability Plot')
        plt.grid(True)
        plt.show()

    # Case 4 - Seaborn fails with ylabel
    if True:
        plt.plot(t, dist.cdf(t), '-', lw=2)
        plt.gca().set_xscale('log')
        plt.gca().set_yscale('weibull',lowerLim=1.E-10,upperLim=.99)
        plt.ylim([.01,.99])
        plt.xlabel('time')
        plt.ylabel('probability')
        plt.title('Probability Plot')
        plt.grid(True)
        plt.show()

Output of case 1

UserWarning: Warning: converting a masked element to nan.
  warnings.warn("Warning: converting a masked element to nan.")

Figure for basic .01 lower limit

Case 2 changes the lowerLim default value to something like 1.e-10, the x axis works properly and no warning is output, but the reduction in the number doesn't make sens why it removes the warning. Working x axis with very small lowerlim

Case 3 uses seaborn to try to make the plot prettier, everything works, but I can't put a ylabel on the plot. The figure below is the working plot without the y label.

Seaborn without ylabel so the output actually works

Case 4 tries to use a ylabel with seaborn. You can see the error message below.

Traceback (most recent call last):
  File "\AppData\Local\Continuum\Miniconda2\lib\site-packages\matplotlib\backe
nds\backend_qt5agg.py", line 180, in __draw_idle_agg
    FigureCanvasAgg.draw(self)
  File "\AppData\Local\Continuum\Miniconda2\lib\site-packages\matplotlib\backe
nds\backend_agg.py", line 474, in draw
    self.figure.draw(self.renderer)
  File "\AppData\Local\Continuum\Miniconda2\lib\site-packages\matplotlib\artis
t.py", line 62, in draw_wrapper
    draw(artist, renderer, *args, **kwargs)
  File "\AppData\Local\Continuum\Miniconda2\lib\site-packages\matplotlib\figur
e.py", line 1159, in draw
    func(*args)
  File "\AppData\Local\Continuum\Miniconda2\lib\site-packages\matplotlib\artis
t.py", line 62, in draw_wrapper
    draw(artist, renderer, *args, **kwargs)
  File "\AppData\Local\Continuum\Miniconda2\lib\site-packages\matplotlib\axes\
_base.py", line 2319, in draw
    a.draw(renderer)
  File "\AppData\Local\Continuum\Miniconda2\lib\site-packages\matplotlib\artis
t.py", line 62, in draw_wrapper
    draw(artist, renderer, *args, **kwargs)
  File "\AppData\Local\Continuum\Miniconda2\lib\site-packages\matplotlib\axis.
py", line 1122, in draw
    self.label.draw(renderer)
  File "\AppData\Local\Continuum\Miniconda2\lib\site-packages\matplotlib\artis
t.py", line 62, in draw_wrapper
    draw(artist, renderer, *args, **kwargs)
  File "\AppData\Local\Continuum\Miniconda2\lib\site-packages\matplotlib\text.
py", line 757, in draw
    raise ValueError("posx and posy should be finite values")
ValueError: posx and posy should be finite values

It seems as though matplotlib and seaborn are relying on passing 1's and 0's to my transform function, which cannot provide the desired outputs. Any thoughts on how to make this work?

Community
  • 1
  • 1
David Folkner
  • 1,171
  • 1
  • 13
  • 27
  • 1
    I'm a bit lost. In how far is the second plot not what you expect? Also, it's of course good to provide a working example, but would it be possible to reduce its complexity in the sense of a [MCVE]? I' pretty sure the problem does not require this many lines of code to be reproduced. Also, since you are showing many different cases, which work or don't, would it be possibe to reflect this in the code? Like `#case1:` `#case2` etc. – ImportanceOfBeingErnest Jan 23 '17 at 16:56
  • 1
    @ImportanceOfBeingErnest, I cleaned up the code a bit. These classes require a quite a bit of this content, but I tried to simplify while leaving the important parts and include the cases I am trying to get working and why I'm confused. – David Folkner Jan 23 '17 at 17:38

0 Answers0