@@ -292,6 +292,7 @@ def __init__(self, base, nonpositive='clip'):
292292 self .base = base
293293 self ._clip = _api .getitem_checked (
294294 {"clip" : True , "mask" : False }, nonpositive = nonpositive )
295+ self ._log_funcs = {np .e : np .log , 2 : np .log2 , 10 : np .log10 }
295296
296297 def __str__ (self ):
297298 return "{}(base={}, nonpositive={!r})" .format (
@@ -300,12 +301,11 @@ def __str__(self):
300301 def transform_non_affine (self , values ):
301302 # Ignore invalid values due to nans being passed to the transform.
302303 with np .errstate (divide = "ignore" , invalid = "ignore" ):
303- log = { np . e : np . log , 2 : np . log2 , 10 : np . log10 } .get (self .base )
304- if log : # If possible, do everything in a single call to NumPy.
305- out = log (values )
304+ log_func = self . _log_funcs .get (self .base )
305+ if log_func :
306+ out = log_func (values )
306307 else :
307- out = np .log (values )
308- out /= np .log (self .base )
308+ out = np .log (values ) / np .log (self .base )
309309 if self ._clip :
310310 # SVG spec says that conforming viewers must support values up
311311 # to 3.4e38 (C float); however experiments suggest that
@@ -329,12 +329,17 @@ class InvertedLogTransform(Transform):
329329 def __init__ (self , base ):
330330 super ().__init__ ()
331331 self .base = base
332+ self ._exp_funcs = {np .e : np .exp , 2 : np .exp2 }
332333
333334 def __str__ (self ):
334335 return f"{ type (self ).__name__ } (base={ self .base } )"
335336
336337 def transform_non_affine (self , values ):
337- return np .power (self .base , values )
338+ exp_func = self ._exp_funcs .get (self .base )
339+ if exp_func :
340+ return exp_func (values )
341+ else :
342+ return np .exp (values * np .log (self .base ))
338343
339344 def inverted (self ):
340345 return LogTransform (self .base )
@@ -449,17 +454,20 @@ def __init__(self, base, linthresh, linscale):
449454 self .base = base
450455 self .linthresh = linthresh
451456 self .linscale = linscale
452- self ._linscale_adj = (linscale / (1.0 - self .base ** - 1 ))
453- self ._log_base = np .log (base )
454457
455458 def transform_non_affine (self , values ):
459+ linscale_adj = self .linscale / (1.0 - 1.0 / self .base )
460+ log_base = np .log (self .base )
461+
456462 abs_a = np .abs (values )
463+ inside = abs_a <= self .linthresh
464+ if np .all (inside ): # Fast path: all values in linear region
465+ return values * linscale_adj
457466 with np .errstate (divide = "ignore" , invalid = "ignore" ):
458467 out = np .sign (values ) * self .linthresh * (
459- self ._linscale_adj +
460- np .log (abs_a / self .linthresh ) / self ._log_base )
461- inside = abs_a <= self .linthresh
462- out [inside ] = values [inside ] * self ._linscale_adj
468+ linscale_adj - np .log (self .linthresh ) / log_base +
469+ np .log (abs_a ) / log_base )
470+ out [inside ] = values [inside ] * linscale_adj
463471 return out
464472
465473 def inverted (self ):
@@ -472,21 +480,35 @@ class InvertedSymmetricalLogTransform(Transform):
472480
473481 def __init__ (self , base , linthresh , linscale ):
474482 super ().__init__ ()
475- symlog = SymmetricalLogTransform (base , linthresh , linscale )
483+ if base <= 1.0 :
484+ raise ValueError ("'base' must be larger than 1" )
485+ if linthresh <= 0.0 :
486+ raise ValueError ("'linthresh' must be positive" )
487+ if linscale <= 0.0 :
488+ raise ValueError ("'linscale' must be positive" )
476489 self .base = base
477490 self .linthresh = linthresh
478- self .invlinthresh = symlog .transform (linthresh )
479491 self .linscale = linscale
480- self ._linscale_adj = (linscale / (1.0 - self .base ** - 1 ))
492+
493+ @_api .deprecated ("3.11" , name = "invlinthresh" , obj_type = "attribute" ,
494+ alternative = ".inverted().transform(linthresh)" )
495+ @property
496+ def invlinthresh (self ):
497+ invlinthresh = self .inverted ().transform (self .linthresh )
498+ return invlinthresh
481499
482500 def transform_non_affine (self , values ):
501+ linscale_adj = self .linscale / (1.0 - 1.0 / self .base )
502+ invlinthresh = self .inverted ().transform (self .linthresh )
503+
483504 abs_a = np .abs (values )
505+ inside = abs_a <= invlinthresh
506+ if np .all (inside ): # Fast path: all values in linear region
507+ return values / linscale_adj
484508 with np .errstate (divide = "ignore" , invalid = "ignore" ):
485- out = np .sign (values ) * self .linthresh * (
486- np .power (self .base ,
487- abs_a / self .linthresh - self ._linscale_adj ))
488- inside = abs_a <= self .invlinthresh
489- out [inside ] = values [inside ] / self ._linscale_adj
509+ out = np .sign (values ) * self .linthresh * np .exp (
510+ (abs_a / self .linthresh - linscale_adj ) * np .log (self .base ))
511+ out [inside ] = values [inside ] / linscale_adj
490512 return out
491513
492514 def inverted (self ):
0 commit comments