@@ -926,9 +926,9 @@ class LogFormatter(Formatter):
926
926
avoid crowding. If ``numdec > subset`` then no minor ticks will
927
927
be labeled.
928
928
929
- linthresh : None or float, default: None
930
- If a symmetric log scale is in use, its ``linthresh``
931
- parameter must be supplied here.
929
+ linthresh, linscale : None or float, default: None
930
+ If a symmetric log scale is in use, its ``linthresh`` and ``linscale``
931
+ parameters must be supplied here.
932
932
933
933
Notes
934
934
-----
@@ -958,7 +958,7 @@ class LogFormatter(Formatter):
958
958
959
959
def __init__ (self , base = 10.0 , labelOnlyBase = False ,
960
960
minor_thresholds = None ,
961
- linthresh = None ):
961
+ linthresh = None , linscale = None ):
962
962
963
963
self .set_base (base )
964
964
self .set_label_minor (labelOnlyBase )
@@ -970,6 +970,9 @@ def __init__(self, base=10.0, labelOnlyBase=False,
970
970
self .minor_thresholds = minor_thresholds
971
971
self ._sublabels = None
972
972
self ._linthresh = linthresh
973
+ self ._linscale = linscale
974
+ self ._symlogutil = None
975
+ self ._firstsublabels = None
973
976
974
977
def set_base (self , base ):
975
978
"""
@@ -991,6 +994,21 @@ def set_label_minor(self, labelOnlyBase):
991
994
"""
992
995
self .labelOnlyBase = labelOnlyBase
993
996
997
+ @property
998
+ def _symlog (self ):
999
+ if self ._symlogutil is not None :
1000
+ return True
1001
+ if self ._linthresh is not None and self ._linscale is not None :
1002
+ self ._symlogutil = _SymmetricalLogUtil (base = self ._base ,
1003
+ linthresh = self ._linthresh ,
1004
+ linscale = self ._linscale )
1005
+ return True
1006
+ transf = self .axis .get_transform ()
1007
+ if hasattr (transf , 'linthresh' ):
1008
+ self ._symlogutil = _SymmetricalLogUtil (transf )
1009
+ return True
1010
+ return False
1011
+
994
1012
def set_locs (self , locs = None ):
995
1013
"""
996
1014
Use axis view limits to control which ticks are labeled.
@@ -1001,36 +1019,20 @@ def set_locs(self, locs=None):
1001
1019
self ._sublabels = None
1002
1020
return
1003
1021
1004
- # Handle symlog case:
1005
- linthresh = self ._linthresh
1006
- if linthresh is None :
1007
- try :
1008
- linthresh = self .axis .get_transform ().linthresh
1009
- except AttributeError :
1010
- pass
1011
-
1012
1022
vmin , vmax = self .axis .get_view_interval ()
1013
1023
if vmin > vmax :
1014
1024
vmin , vmax = vmax , vmin
1015
1025
1016
- if linthresh is None and vmin <= 0 :
1026
+ if not self . _symlog and vmin <= 0 :
1017
1027
# It's probably a colorbar with
1018
1028
# a format kwarg setting a LogFormatter in the manner
1019
1029
# that worked with 1.5.x, but that doesn't work now.
1020
1030
self ._sublabels = {1 } # label powers of base
1021
1031
return
1022
1032
1023
1033
b = self ._base
1024
- if linthresh is not None : # symlog
1025
- # Only compute the number of decades in the logarithmic part of the
1026
- # axis
1027
- numdec = 0
1028
- if vmin < - linthresh :
1029
- rhs = min (vmax , - linthresh )
1030
- numdec += math .log (vmin / rhs ) / math .log (b )
1031
- if vmax > linthresh :
1032
- lhs = max (vmin , linthresh )
1033
- numdec += math .log (vmax / lhs ) / math .log (b )
1034
+ if self ._symlog :
1035
+ numdec = self ._symlogutil .pos (vmax ) - self ._symlogutil .pos (vmin )
1034
1036
else :
1035
1037
vmin = math .log (vmin ) / math .log (b )
1036
1038
vmax = math .log (vmax ) / math .log (b )
@@ -1039,16 +1041,25 @@ def set_locs(self, locs=None):
1039
1041
if numdec > self .minor_thresholds [0 ]:
1040
1042
# Label only bases
1041
1043
self ._sublabels = {1 }
1044
+ if self ._symlog :
1045
+ self ._firstsublabels = {0 }
1042
1046
elif numdec > self .minor_thresholds [1 ]:
1043
1047
# Add labels between bases at log-spaced coefficients;
1044
1048
# include base powers in case the locations include
1045
1049
# "major" and "minor" points, as in colorbar.
1046
1050
c = np .geomspace (1 , b , int (b )// 2 + 1 )
1047
1051
self ._sublabels = set (np .round (c ))
1048
1052
# For base 10, this yields (1, 2, 3, 4, 6, 10).
1053
+ if self ._symlog :
1054
+ # For the linear part of the scale we use an analog selection.
1055
+ c = np .linspace (2 , b , int (b ) // 2 )
1056
+ self ._firstsublabels = set (np .round (c ))
1057
+ # For base 10, this yields (0, 2, 4, 6, 8, 10).
1049
1058
else :
1050
1059
# Label all integer multiples of base**n.
1051
1060
self ._sublabels = set (np .arange (1 , b + 1 ))
1061
+ if self ._symlog :
1062
+ self ._firstsublabels = set (np .arange (0 , b + 1 ))
1052
1063
1053
1064
def _num_to_string (self , x , vmin , vmax ):
1054
1065
if x > 10000 :
@@ -1073,10 +1084,17 @@ def __call__(self, x, pos=None):
1073
1084
exponent = round (fx ) if is_x_decade else np .floor (fx )
1074
1085
coeff = round (b ** (fx - exponent ))
1075
1086
1076
- if self .labelOnlyBase and not is_x_decade :
1077
- return ''
1078
- if self ._sublabels is not None and coeff not in self ._sublabels :
1079
- return ''
1087
+ _ , firstpow = self ._symlogutil .firstdec () if self ._symlog else None , 0
1088
+ if x < firstpow :
1089
+ if self .labelOnlyBase :
1090
+ return ''
1091
+ if self ._firstsublabels is not None and coeff not in self ._firstsublabels :
1092
+ return ''
1093
+ else :
1094
+ if self .labelOnlyBase and not is_x_decade :
1095
+ return ''
1096
+ if self ._sublabels is not None and coeff not in self ._sublabels :
1097
+ return ''
1080
1098
1081
1099
vmin , vmax = self .axis .get_view_interval ()
1082
1100
vmin , vmax = mtransforms .nonsingular (vmin , vmax , expander = 0.05 )
@@ -1154,10 +1172,17 @@ def __call__(self, x, pos=None):
1154
1172
exponent = round (fx ) if is_x_decade else np .floor (fx )
1155
1173
coeff = round (b ** (fx - exponent ))
1156
1174
1157
- if self .labelOnlyBase and not is_x_decade :
1158
- return ''
1159
- if self ._sublabels is not None and coeff not in self ._sublabels :
1160
- return ''
1175
+ _ , firstpow = self ._symlogutil .firstdec () if self ._symlog else (None , 0 )
1176
+ if x < firstpow :
1177
+ if self .labelOnlyBase :
1178
+ return ''
1179
+ if self ._firstsublabels is not None and coeff not in self ._firstsublabels :
1180
+ return ''
1181
+ else :
1182
+ if self .labelOnlyBase and not is_x_decade :
1183
+ return ''
1184
+ if self ._sublabels is not None and coeff not in self ._sublabels :
1185
+ return ''
1161
1186
1162
1187
if is_x_decade :
1163
1188
fx = round (fx )
0 commit comments