@@ -902,10 +902,6 @@ def set_locs(self, locs=None):
902902 self ._sublabels = None
903903 return
904904
905- b = self ._base
906-
907- vmin , vmax = self .axis .get_view_interval ()
908-
909905 # Handle symlog case:
910906 linthresh = self ._linthresh
911907 if linthresh is None :
@@ -914,6 +910,18 @@ def set_locs(self, locs=None):
914910 except AttributeError :
915911 pass
916912
913+ vmin , vmax = self .axis .get_view_interval ()
914+ if vmin > vmax :
915+ vmin , vmax = vmax , vmin
916+
917+ if linthresh is None and vmin <= 0 :
918+ # It's probably a colorbar with
919+ # a format kwarg setting a LogFormatter in the manner
920+ # that worked with 1.5.x, but that doesn't work now.
921+ self ._sublabels = set ((1 ,)) # label powers of base
922+ return
923+
924+ b = self ._base
917925 if linthresh is not None : # symlog
918926 # Only compute the number of decades in the logarithmic part of the
919927 # axis
@@ -943,37 +951,38 @@ def set_locs(self, locs=None):
943951 # Label all integer multiples of base**n.
944952 self ._sublabels = set (np .arange (1 , b + 1 ))
945953
954+ def _num_to_string (self , x , vmin , vmax ):
955+ if x > 10000 :
956+ s = '%1.0e' % x
957+ elif x < 1 :
958+ s = '%1.0e' % x
959+ else :
960+ s = self .pprint_val (x , vmax - vmin )
961+
946962 def __call__ (self , x , pos = None ):
947963 """
948964 Return the format for tick val `x`.
949965 """
950- vmin , vmax = self .axis .get_view_interval ()
951- vmin , vmax = mtransforms .nonsingular (vmin , vmax , expander = 0.05 )
952- d = abs (vmax - vmin )
953- b = self ._base
954- if x == 0.0 :
966+ if x == 0.0 : # Symlog
955967 return '0'
968+
956969 sign = np .sign (x )
957970 x = abs (x )
971+ b = self ._base
958972 # only label the decades
959973 fx = math .log (x ) / math .log (b )
960974 is_x_decade = is_close_to_int (fx )
961975 exponent = np .round (fx ) if is_x_decade else np .floor (fx )
962976 coeff = np .round (x / b ** exponent )
977+
963978 if self .labelOnlyBase and not is_x_decade :
964979 return ''
965980 if self ._sublabels is not None and coeff not in self ._sublabels :
966981 return ''
967982
968- if x > 10000 :
969- s = '%1.0e' % x
970- elif x < 1 :
971- s = '%1.0e' % x
972- else :
973- s = self .pprint_val (x , d )
974- if sign == - 1 :
975- s = '-%s' % s
976-
983+ vmin , vmax = self .axis .get_view_interval ()
984+ vmin , vmax = mtransforms .nonsingular (vmin , vmax , expander = 0.05 )
985+ s = self ._num_to_string (x , vmin , vmax )
977986 return self .fix_minus (s )
978987
979988 def format_data (self , value ):
@@ -1026,41 +1035,16 @@ class LogFormatterExponent(LogFormatter):
10261035 """
10271036 Format values for log axis using ``exponent = log_base(value)``.
10281037 """
1029- def __call__ (self , x , pos = None ):
1030- """
1031- Return the format for tick value `x`.
1032- """
1033- vmin , vmax = self .axis .get_view_interval ()
1034- vmin , vmax = mtransforms .nonsingular (vmin , vmax , expander = 0.05 )
1035- d = abs (vmax - vmin )
1036- b = self ._base
1037- if x == 0 :
1038- return '0'
1039- sign = np .sign (x )
1040- x = abs (x )
1041- # only label the decades
1042- fx = math .log (x ) / math .log (b )
1043-
1044- is_x_decade = is_close_to_int (fx )
1045- exponent = np .round (fx ) if is_x_decade else np .floor (fx )
1046- coeff = np .round (x / b ** exponent )
1047-
1048- if self .labelOnlyBase and not is_x_decade :
1049- return ''
1050- if self ._sublabels is not None and coeff not in self ._sublabels :
1051- return ''
1052-
1038+ def _num_to_string (self , x , vmin , vmax ):
1039+ fx = math .log (x ) / math .log (self ._base )
10531040 if abs (fx ) > 10000 :
10541041 s = '%1.0g' % fx
10551042 elif abs (fx ) < 1 :
10561043 s = '%1.0g' % fx
10571044 else :
1058- fd = math .log (abs ( d )) / math .log (b )
1045+ fd = math .log (vmax - vmin ) / math .log (self . _base )
10591046 s = self .pprint_val (fx , fd )
1060- if sign == - 1 :
1061- s = '-%s' % s
1062-
1063- return self .fix_minus (s )
1047+ return s
10641048
10651049
10661050class LogFormatterMathtext (LogFormatter ):
@@ -1082,35 +1066,34 @@ def __call__(self, x, pos=None):
10821066
10831067 The position `pos` is ignored.
10841068 """
1085- b = self ._base
10861069 usetex = rcParams ['text.usetex' ]
1087-
1088- # only label the decades
1089- if x == 0 :
1070+ if x == 0 : # Symlog
10901071 if usetex :
10911072 return '$0$'
10921073 else :
10931074 return '$%s$' % _mathdefault ('0' )
10941075
10951076 sign_string = '-' if x < 0 else ''
10961077 x = abs (x )
1078+ b = self ._base
10971079
1080+ # only label the decades
10981081 fx = math .log (x ) / math .log (b )
10991082 is_x_decade = is_close_to_int (fx )
11001083 exponent = np .round (fx ) if is_x_decade else np .floor (fx )
11011084 coeff = np .round (x / b ** exponent )
11021085
1086+ if self .labelOnlyBase and not is_x_decade :
1087+ return ''
1088+ if self ._sublabels is not None and coeff not in self ._sublabels :
1089+ return ''
1090+
11031091 # use string formatting of the base if it is not an integer
11041092 if b % 1 == 0.0 :
11051093 base = '%d' % b
11061094 else :
11071095 base = '%s' % b
11081096
1109- if self .labelOnlyBase and not is_x_decade :
1110- return ''
1111- if self ._sublabels is not None and coeff not in self ._sublabels :
1112- return ''
1113-
11141097 if not is_x_decade :
11151098 return self ._non_decade_format (sign_string , base , fx , usetex )
11161099 else :
@@ -2032,36 +2015,41 @@ def view_limits(self, vmin, vmax):
20322015 'Try to choose the view limits intelligently'
20332016 b = self ._base
20342017
2035- if vmax < vmin :
2036- vmin , vmax = vmax , vmin
2018+ vmin , vmax = self .nonsingular (vmin , vmax )
20372019
20382020 if self .axis .axes .name == 'polar' :
20392021 vmax = math .ceil (math .log (vmax ) / math .log (b ))
20402022 vmin = b ** (vmax - self .numdecs )
2041- return vmin , vmax
2042-
2043- minpos = self .axis .get_minpos ()
2044-
2045- if minpos <= 0 or not np .isfinite (minpos ):
2046- raise ValueError (
2047- "Data has no positive values, and therefore can not be "
2048- "log-scaled." )
2049-
2050- if vmin <= 0 :
2051- vmin = minpos
20522023
20532024 if rcParams ['axes.autolimit_mode' ] == 'round_numbers' :
20542025 if not is_decade (vmin , self ._base ):
20552026 vmin = decade_down (vmin , self ._base )
20562027 if not is_decade (vmax , self ._base ):
20572028 vmax = decade_up (vmax , self ._base )
20582029
2059- if vmin == vmax :
2060- vmin = decade_down (vmin , self ._base )
2061- vmax = decade_up (vmax , self ._base )
2030+ return vmin , vmax
20622031
2063- result = mtransforms .nonsingular (vmin , vmax )
2064- return result
2032+ def nonsingular (self , vmin , vmax ):
2033+ if not np .isfinite (vmin ) or not np .isfinite (vmax ):
2034+ return 1 , 10 # initial range, no data plotted yet
2035+
2036+ if vmin > vmax :
2037+ vmin , vmax = vmax , vmin
2038+ if vmax <= 0 :
2039+ warnings .warn (
2040+ "Data has no positive values, and therefore cannot be "
2041+ "log-scaled." )
2042+ return 1 , 10
2043+
2044+ minpos = self .axis .get_minpos ()
2045+ if not np .isfinite (minpos ):
2046+ minpos = 1e-300 # This should never take effect.
2047+ if vmin <= 0 :
2048+ vmin = minpos
2049+ if vmin == vmax :
2050+ vmin = decade_down (vmin , self ._base )
2051+ vmax = decade_up (vmax , self ._base )
2052+ return vmin , vmax
20652053
20662054
20672055class SymmetricalLogLocator (Locator ):
@@ -2260,32 +2248,7 @@ def tick_values(self, vmin, vmax):
22602248 if hasattr (self .axis , 'axes' ) and self .axis .axes .name == 'polar' :
22612249 raise NotImplementedError ('Polar axis cannot be logit scaled yet' )
22622250
2263- # what to do if a window beyond ]0, 1[ is chosen
2264- if vmin <= 0.0 :
2265- if self .axis is not None :
2266- vmin = self .axis .get_minpos ()
2267-
2268- if (vmin <= 0.0 ) or (not np .isfinite (vmin )):
2269- raise ValueError (
2270- "Data has no values in ]0, 1[ and therefore can not be "
2271- "logit-scaled." )
2272-
2273- # NOTE: for vmax, we should query a property similar to get_minpos, but
2274- # related to the maximal, less-than-one data point. Unfortunately,
2275- # get_minpos is defined very deep in the BBox and updated with data,
2276- # so for now we use the trick below.
2277- if vmax >= 1.0 :
2278- if self .axis is not None :
2279- vmax = 1 - self .axis .get_minpos ()
2280-
2281- if (vmax >= 1.0 ) or (not np .isfinite (vmax )):
2282- raise ValueError (
2283- "Data has no values in ]0, 1[ and therefore can not be "
2284- "logit-scaled." )
2285-
2286- if vmax < vmin :
2287- vmin , vmax = vmax , vmin
2288-
2251+ vmin , vmax = self .nonsingular (vmin , vmax )
22892252 vmin = np .log10 (vmin / (1 - vmin ))
22902253 vmax = np .log10 (vmax / (1 - vmax ))
22912254
@@ -2320,6 +2283,36 @@ def tick_values(self, vmin, vmax):
23202283
23212284 return self .raise_if_exceeds (np .array (ticklocs ))
23222285
2286+ def nonsingular (self , vmin , vmax ):
2287+ initial_range = (1e-7 , 1 - 1e-7 )
2288+ if not np .isfinite (vmin ) or not np .isfinite (vmax ):
2289+ return initial_range # no data plotted yet
2290+
2291+ if vmin > vmax :
2292+ vmin , vmax = vmax , vmin
2293+
2294+ # what to do if a window beyond ]0, 1[ is chosen
2295+ if self .axis is not None :
2296+ minpos = self .axis .get_minpos ()
2297+ if not np .isfinite (minpos ):
2298+ return initial_range # again, no data plotted
2299+ else :
2300+ minpos = 1e-7 # should not occur in normal use
2301+
2302+ # NOTE: for vmax, we should query a property similar to get_minpos, but
2303+ # related to the maximal, less-than-one data point. Unfortunately,
2304+ # Bbox._minpos is defined very deep in the BBox and updated with data,
2305+ # so for now we use 1 - minpos as a substitute.
2306+
2307+ if vmin <= 0 :
2308+ vmin = minpos
2309+ if vmax >= 1 :
2310+ vmax = 1 - minpos
2311+ if vmin == vmax :
2312+ return 0.1 * vmin , 1 - 0.1 * vmin
2313+
2314+ return vmin , vmax
2315+
23232316
23242317class AutoLocator (MaxNLocator ):
23252318 def __init__ (self ):
0 commit comments