diff --git a/doc/changelog.rst b/doc/changelog.rst index ab43f27..b52e1fa 100644 --- a/doc/changelog.rst +++ b/doc/changelog.rst @@ -1,10 +1,29 @@ Changelog ========= +v0.5.1 +------ +*(2018-15-10)* + +Bug Fixes +********* + +- Fixed issue with :class:`~mizani.breaks.log_breaks`, so that it does + not fail needlessly when the limits in the (0, 1) range. + +Enhancements +************ + +- Changed :class:`~mizani.formatters.log_format` to return better + formatted breaks. + v0.5.0 ------ *(2018-11-10)* +.. image:: https://zenodo.org/badge/DOI/10.5281/zenodo.62319878.svg + :target: https://doi.org/10.5281/zenodo.62319878 + API Changes *********** diff --git a/mizani/breaks.py b/mizani/breaks.py index e21a8e1..bdaff96 100644 --- a/mizani/breaks.py +++ b/mizani/breaks.py @@ -110,6 +110,8 @@ class log_breaks: array([ 100, 1000, 10000, 100000, 1000000]) >>> log_breaks(2)(limits) array([ 100, 100000]) + >>> log_breaks()([0.1, 1]) + array([0.1, 0.3, 1. , 3. ]) """ def __init__(self, n=5, base=10): @@ -139,6 +141,10 @@ def __call__(self, limits): _min = int(np.floor(rng[0])) _max = int(np.ceil(rng[1])) + # numpy arrays with -ve number(s) and of dtype=int + # cannot be powers i.e. base ** arr fails + dtype = float if _min < 0 or _max < 0 else int + if _max == _min: return base ** _min @@ -148,7 +154,7 @@ def __call__(self, limits): # _log_sub_breaks by = int(np.floor((_max-_min)/n)) + 1 for step in range(by, 0, -1): - breaks = base ** np.arange(_min, _max+1, step=step) + breaks = base ** np.arange(_min, _max+1, step=step, dtype=dtype) relevant_breaks = ( (limits[0] <= breaks) & (breaks <= limits[1]) @@ -189,6 +195,7 @@ def __call__(self, limits): rng = np.log(limits)/np.log(base) _min = int(np.floor(rng[0])) _max = int(np.ceil(rng[1])) + dtype = float if _min < 0 or _max < 0 else int steps = [1] def delta(x): @@ -216,7 +223,7 @@ def delta(x): candidate = np.delete(candidate, best) breaks = np.outer( - base ** np.arange(_min, _max+1), steps).ravel() + base ** np.arange(_min, _max+1, dtype=dtype), steps).ravel() relevant_breaks = ( (limits[0] <= breaks) & (breaks <= limits[1])) diff --git a/mizani/formatters.py b/mizani/formatters.py index 7043c95..c4cd6e3 100644 --- a/mizani/formatters.py +++ b/mizani/formatters.py @@ -442,9 +442,20 @@ def __call__(self, x): out : list List of strings. """ + def _as_integers(x): + """ + Try converting all numbers to integers + """ + nums = [np.round(i, 11) for i in x] + if np.all([float(i).is_integer() for i in nums]): + return [int(i) for i in nums] + return x + if len(x) == 0: return [] + x = _as_integers(x) + # Decide on using exponents if self.base == 10: # Order of magnitude of the minimum and maximum @@ -452,13 +463,15 @@ def __call__(self, x): dmax = np.log(np.max(x))/np.log(self.base) if same_log10_order_of_magnitude((dmin, dmax)): return mpl_format()(x) - + all_multiples = np.all( + [np.log10(num).is_integer() for num in x]) has_small_number = dmin < -3 has_large_number = dmax > 3 has_large_range = (dmax - dmin) > self.exponent_threshold - use_exponent = (has_small_number or - has_large_number or - has_large_range) + use_exponent = (all_multiples and + (has_small_number or + has_large_number or + has_large_range)) else: use_exponent = False return [self._format_num(num, use_exponent) for num in x] diff --git a/mizani/tests/test_formatters.py b/mizani/tests/test_formatters.py index 0d21cbf..f78936c 100644 --- a/mizani/tests/test_formatters.py +++ b/mizani/tests/test_formatters.py @@ -84,11 +84,10 @@ def test_log_format(): assert formatter([0.001, 0.1, 1000]) == ['1e-3', '1e-1', '1e3'] assert formatter([35, 60]) == ['35', '60'] assert formatter([34.99999999999, 60.0000000001]) == ['35', '60'] + assert formatter([3000.0000000000014, 4999.999999999999]) == \ + ['3000', '5000'] assert formatter([1, 35, 60, 1000]) == ['1', '35', '60', '1000'] - assert formatter([1, 35, 60, 10000]) == ['1', '', '', '1e4'] - - formatter = log_format() - assert formatter([1, 35, 60, 10000]) == ['1', '', '', '1e4'] + assert formatter([1, 35, 60, 10000]) == ['1', '35', '60', '10000'] formatter = log_format(base=2) assert formatter([1, 10, 11, 1011]) == ['1', '10', '11', '1011']