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.")
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.
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.
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?