1- import numpy as np
2- import matplotlib
3- from matplotlib .transforms import Transform
1+ import numpy
42from matplotlib .scale import ScaleBase
53from matplotlib .ticker import (
64 FixedLocator ,
75 NullLocator ,
8- Formatter ,
96 NullFormatter ,
107 FuncFormatter ,
118)
129
13-
14- def _mask_non_positives (a ):
15- """
16- Return a Numpy array where all values outside ]0, 1[ are
17- replaced with NaNs. If all values are inside ]0, 1[, the original
18- array is returned.
19- """
20- mask = (a <= 0.0 ) | (a >= 1.0 )
21- if mask .any ():
22- return np .where (mask , np .nan , a )
23- return a
24-
25-
26- def _clip_non_positives (a ):
27- a = np .array (a , float )
28- a [a <= 0.0 ] = 1e-300
29- a [a >= 1.0 ] = 1 - 1e-300
30- return a
10+ from .transforms import ProbTransform
11+ from .formatters import PctFormatter , ProbFormatter
3112
3213
3314class _minimal_norm (object ):
34- _A = - (8 * (np .pi - 3.0 ) / (3.0 * np .pi * (np .pi - 4.0 )))
15+ _A = - (8 * (numpy .pi - 3.0 ) / (3.0 * numpy .pi * (numpy .pi - 4.0 )))
3516
3617 @classmethod
3718 def _approx_erf (cls , x ):
@@ -41,8 +22,8 @@ def _approx_erf(cls, x):
4122
4223 """
4324
44- guts = - x ** 2 * (4.0 / np .pi + cls ._A * x ** 2 ) / (1.0 + cls ._A * x ** 2 )
45- return np .sign (x ) * np .sqrt (1.0 - np .exp (guts ))
25+ guts = - x ** 2 * (4.0 / numpy .pi + cls ._A * x ** 2 ) / (1.0 + cls ._A * x ** 2 )
26+ return numpy .sign (x ) * numpy .sqrt (1.0 - numpy .exp (guts ))
4627
4728 @classmethod
4829 def _approx_inv_erf (cls , z ):
@@ -52,9 +33,9 @@ def _approx_inv_erf(cls, z):
5233
5334 """
5435
55- _b = (2 / np .pi / cls ._A ) + (0.5 * np .log (1 - z ** 2 ))
56- _c = np .log (1 - z ** 2 ) / cls ._A
57- return np .sign (z ) * np .sqrt (np .sqrt (_b ** 2 - _c ) - _b )
36+ _b = (2 / numpy .pi / cls ._A ) + (0.5 * numpy .log (1 - z ** 2 ))
37+ _c = numpy .log (1 - z ** 2 ) / cls ._A
38+ return numpy .sign (z ) * numpy .sqrt (numpy .sqrt (_b ** 2 - _c ) - _b )
5839
5940 @classmethod
6041 def ppf (cls , q ):
@@ -63,7 +44,7 @@ def ppf(cls, q):
6344 Wikipedia: https://goo.gl/Rtxjme
6445
6546 """
66- return np .sqrt (2 ) * cls ._approx_inv_erf (2 * q - 1 )
47+ return numpy .sqrt (2 ) * cls ._approx_inv_erf (2 * q - 1 )
6748
6849 @classmethod
6950 def cdf (cls , x ):
@@ -72,151 +53,7 @@ def cdf(cls, x):
7253 Wikipedia: https://goo.gl/ciUNLx
7354
7455 """
75- return 0.5 * (1 + cls ._approx_erf (x / np .sqrt (2 )))
76-
77-
78- class _FormatterMixin (Formatter ):
79- @classmethod
80- def _sig_figs (cls , x , n , expthresh = 5 , forceint = False ):
81- """ Formats a number with the correct number of sig figs.
82-
83- Parameters
84- ----------
85- x : int or float
86- The number you want to format.
87- n : int
88- The number of significan figures it should have.
89- expthresh : int, optional (default = 5)
90- The absolute value of the order of magnitude at which numbers
91- are formatted in exponential notation.
92- forceint : bool, optional (default is False)
93- If true, simply returns int(x)
94-
95- Returns
96- -------
97- formatted : str
98- The formatted number as a string
99-
100- Examples
101- --------
102- >>> print(_sig_figs(1247.15, 3))
103- 1250
104- >>> print(_sig_figs(1247.15, 7))
105- 1247.150
106-
107- """
108-
109- # return a string value unaltered
110- if isinstance (x , str ) or x == 0.0 :
111- out = str (x )
112-
113- # check on the number provided
114- elif x is not None and not np .isinf (x ) and not np .isnan (x ):
115-
116- # check on the _sig_figs
117- if n < 1 :
118- raise ValueError ("number of sig figs (n) must be greater than zero" )
119-
120- elif forceint :
121- out = '{:,.0f}' .format (x )
122-
123- # logic to do all of the rounding
124- else :
125- order = np .floor (np .log10 (np .abs (x )))
126-
127- if (- 1.0 * expthresh <= order <= expthresh ):
128- decimal_places = int (n - 1 - order )
129-
130- if decimal_places <= 0 :
131- out = '{0:,.0f}' .format (round (x , decimal_places ))
132-
133- else :
134- fmt = '{0:,.%df}' % decimal_places
135- out = fmt .format (x )
136-
137- else :
138- decimal_places = n - 1
139- fmt = '{0:.%de}' % decimal_places
140- out = fmt .format (x )
141-
142- # with NAs and INFs, just return 'NA'
143- else :
144- out = 'NA'
145-
146- return out
147-
148- def __call__ (self , x , pos = None ):
149- if x < (10 / self .factor ):
150- out = self ._sig_figs (x , 1 )
151- elif x <= (99 / self .factor ):
152- out = self ._sig_figs (x , 2 )
153- else :
154- order = np .ceil (np .round (np .abs (np .log10 (self .top - x )), 6 ))
155- out = self ._sig_figs (x , order + self .offset )
156-
157- return '{}' .format (out )
158-
159-
160- class PctFormatter (_FormatterMixin ):
161- factor = 1.0
162- offset = 2
163- top = 100
164-
165-
166- class ProbFormatter (_FormatterMixin ):
167- factor = 100.0
168- offset = 0
169- top = 1
170-
171-
172- class _ProbTransformMixin (Transform ):
173- input_dims = 1
174- output_dims = 1
175- is_separable = True
176- has_inverse = True
177-
178- def __init__ (self , dist , as_pct = True , nonpos = 'mask' ):
179- Transform .__init__ (self )
180- self .dist = dist
181- if as_pct :
182- self .factor = 100.0
183- else :
184- self .factor = 1.0
185-
186- if nonpos == 'mask' :
187- self ._handle_nonpos = _mask_non_positives
188- elif nonpos == 'clip' :
189- self ._handle_nonpos = _clip_non_positives
190- else :
191- raise ValueError ("`nonpos` muse be either 'mask' or 'clip'" )
192-
193-
194- class ProbTransform (_ProbTransformMixin ):
195- def transform_non_affine (self , prob ):
196- q = self .dist .ppf (prob / self .factor )
197- return q
198-
199- def inverted (self ):
200- return QuantileTransform (self .dist , as_pct = self .as_pct , nonpos = self .nonpos )
201-
202-
203- class QuantileTransform (_ProbTransformMixin ):
204- def transform_non_affine (self , q ):
205- prob = self .dist .cdf (q ) * self .factor
206- return prob
207-
208- class InvertedProbTransform (_ProbTransformMixin ):
209- def transform_non_affine (self , a ):
210- return self .dist .cdf (a ) * self .factor
211-
212-
213- class InvertedProbTransform (_ProbTransformMixin ):
214- def transform_non_affine (self , q ):
215- prob = self .dist .cdf (q ) * self .factor
216- return prob
217-
218- def inverted (self ):
219- return ProbTransform (self .dist , as_pct = self .as_pct , nonpos = self .nonpos )
56+ return 0.5 * (1 + cls ._approx_erf (x / numpy .sqrt (2 )))
22057
22158
22259class ProbScale (ScaleBase ):
@@ -237,7 +74,7 @@ class ProbScale(ScaleBase):
23774 >>> from matplotlib import pyplot
23875 >>> import probscale
23976 >>> fig, ax = pyplot.subplots()
240- >>> ax.set_scale ('prob')
77+ >>> ax.set_xscale ('prob')
24178
24279 """
24380
@@ -259,21 +96,21 @@ def _get_probs(cls, nobs, as_pct):
25996 else :
26097 factor = 100.0
26198
262- order = int (np .floor (np .log10 (nobs )))
263- base_probs = np .array ([10 , 20 , 30 , 40 , 50 , 60 , 70 , 80 , 90 ])
99+ order = int (numpy .floor (numpy .log10 (nobs )))
100+ base_probs = numpy .array ([10 , 20 , 30 , 40 , 50 , 60 , 70 , 80 , 90 ])
264101
265102 axis_probs = base_probs .copy ()
266103 for n in range (order ):
267104 if n <= 2 :
268- lower_fringe = np .array ([1 , 2 , 5 ])
269- upper_fringe = np .array ([5 , 8 , 9 ])
105+ lower_fringe = numpy .array ([1 , 2 , 5 ])
106+ upper_fringe = numpy .array ([5 , 8 , 9 ])
270107 else :
271- lower_fringe = np .array ([1 ])
272- upper_fringe = np .array ([9 ])
108+ lower_fringe = numpy .array ([1 ])
109+ upper_fringe = numpy .array ([9 ])
273110
274111 new_lower = lower_fringe / 10 ** (n )
275112 new_upper = upper_fringe / 10 ** (n ) + axis_probs .max ()
276- axis_probs = np .hstack ([new_lower , axis_probs , new_upper ])
113+ axis_probs = numpy .hstack ([new_lower , axis_probs , new_upper ])
277114 locs = axis_probs / factor
278115 return locs
279116
0 commit comments