From e9c0cde8d46b0b0ff30aea175af70ea1e4cd1caf Mon Sep 17 00:00:00 2001 From: Stefan van der Walt Date: Thu, 8 May 2014 20:11:17 +0200 Subject: [PATCH] Move safe_as_int into utils --- skimage/_shared/_safe_as_int.py | 70 ----------------------- skimage/_shared/tests/test_safe_as_int.py | 32 +++++------ skimage/_shared/utils.py | 69 +++++++++++++++++++++- 3 files changed, 84 insertions(+), 87 deletions(-) delete mode 100644 skimage/_shared/_safe_as_int.py diff --git a/skimage/_shared/_safe_as_int.py b/skimage/_shared/_safe_as_int.py deleted file mode 100644 index 200d6efb..00000000 --- a/skimage/_shared/_safe_as_int.py +++ /dev/null @@ -1,70 +0,0 @@ -import numpy as np - -__all__ = ['_safe_as_int'] - - -def safe_as_int(val, atol=1e-3): - """ - Attempt to safely cast values to integer format. - - Parameters - ---------- - val : scalar or iterable of scalars - Number or container of numbers which are intended to be interpreted as - integers, e.g., for indexing purposes, but which may not carry integer - type. - atol : float - Absolute tolerance away from nearest integer to consider values in - ``val`` functionally integers. - - Returns - ------- - val_int : NumPy scalar or ndarray of dtype `np.int64` - Returns the input value(s) coerced to dtype `np.int64` assuming all - were within ``atol`` of the nearest integer. - - Notes - ----- - This operation calculates ``val`` modulo 1, which returns the mantissa of - all values. Then all mantissas greater than 0.5 are subtracted from one. - Finally, the absolute tolerance from zero is calculated. If it is less - than ``atol`` for all value(s) in ``val``, they are rounded and returned - in an integer array. Or, if ``val`` was a scalar, a NumPy scalar type is - returned. - - If any value(s) are outside the specified tolerance, an informative error - is raised. - - Examples - -------- - >>> _safe_as_int(7.0) - 7 - - >>> _safe_as_int([9, 4, 2.9999999999]) - array([9, 4, 3], dtype=int32) - - >>> _safe_as_int(53.01) - Traceback (most recent call last): - ... - ValueError: Integer argument required but received 53.1, check inputs. - - >>> _safe_as_int(53.01, atol=0.01) - 53 - - """ - mod = np.asarray(val) % 1 # Extract mantissa - - # Check for and subtract any mod values > 0.5 from 1 - if mod.ndim == 0: # Scalar input, cannot be indexed - if mod > 0.5: - mod = 1 - mod - else: # Iterable input, now ndarray - mod[mod > 0.5] = 1 - mod[mod > 0.5] # Test on each side of nearest int - - try: - np.testing.assert_allclose(mod, 0, atol=atol) - except AssertionError: - raise ValueError("Integer argument required but received " - "{0}, check inputs.".format(val)) - - return np.round(val).astype(np.int64) diff --git a/skimage/_shared/tests/test_safe_as_int.py b/skimage/_shared/tests/test_safe_as_int.py index 1a3674e0..009fa9a4 100644 --- a/skimage/_shared/tests/test_safe_as_int.py +++ b/skimage/_shared/tests/test_safe_as_int.py @@ -1,30 +1,30 @@ import numpy as np -from skimage._shared.safe_int_cast import _safe_as_int +from skimage._shared.utils import safe_as_int def test_int_cast_not_possible(): - np.testing.assert_raises(ValueError, _safe_as_int, 7.1) - np.testing.assert_raises(ValueError, _safe_as_int, [7.1, 0.9]) - np.testing.assert_raises(ValueError, _safe_as_int, np.r_[7.1, 0.9]) - np.testing.assert_raises(ValueError, _safe_as_int, (7.1, 0.9)) - np.testing.assert_raises(ValueError, _safe_as_int, ((3, 4, 1), + np.testing.assert_raises(ValueError, safe_as_int, 7.1) + np.testing.assert_raises(ValueError, safe_as_int, [7.1, 0.9]) + np.testing.assert_raises(ValueError, safe_as_int, np.r_[7.1, 0.9]) + np.testing.assert_raises(ValueError, safe_as_int, (7.1, 0.9)) + np.testing.assert_raises(ValueError, safe_as_int, ((3, 4, 1), (2, 7.6, 289))) - np.testing.assert_raises(ValueError, _safe_as_int, 7.1, 0.09) - np.testing.assert_raises(ValueError, _safe_as_int, [7.1, 0.9], 0.09) - np.testing.assert_raises(ValueError, _safe_as_int, np.r_[7.1, 0.9], 0.09) - np.testing.assert_raises(ValueError, _safe_as_int, (7.1, 0.9), 0.09) - np.testing.assert_raises(ValueError, _safe_as_int, ((3, 4, 1), + np.testing.assert_raises(ValueError, safe_as_int, 7.1, 0.09) + np.testing.assert_raises(ValueError, safe_as_int, [7.1, 0.9], 0.09) + np.testing.assert_raises(ValueError, safe_as_int, np.r_[7.1, 0.9], 0.09) + np.testing.assert_raises(ValueError, safe_as_int, (7.1, 0.9), 0.09) + np.testing.assert_raises(ValueError, safe_as_int, ((3, 4, 1), (2, 7.6, 289)), 0.25) def test_int_cast_possible(): - np.testing.assert_equal(_safe_as_int(7.1, atol=0.11), 7) - np.testing.assert_equal(_safe_as_int(-7.1, atol=0.11), -7) - np.testing.assert_equal(_safe_as_int(41.9, atol=0.11), 42) - np.testing.assert_array_equal(_safe_as_int([2, 42, 5789234.0, 87, 4]), + np.testing.assert_equal(safe_as_int(7.1, atol=0.11), 7) + np.testing.assert_equal(safe_as_int(-7.1, atol=0.11), -7) + np.testing.assert_equal(safe_as_int(41.9, atol=0.11), 42) + np.testing.assert_array_equal(safe_as_int([2, 42, 5789234.0, 87, 4]), np.r_[2, 42, 5789234, 87, 4]) - np.testing.assert_array_equal(_safe_as_int(np.r_[[[3, 4, 1.000000001], + np.testing.assert_array_equal(safe_as_int(np.r_[[[3, 4, 1.000000001], [7, 2, -8.999999999], [6, 9, -4234918347.]]]), np.r_[[[3, 4, 1], diff --git a/skimage/_shared/utils.py b/skimage/_shared/utils.py index f94edfc5..612a2b63 100644 --- a/skimage/_shared/utils.py +++ b/skimage/_shared/utils.py @@ -1,11 +1,11 @@ import warnings import functools import sys +import numpy as np import six from ._warnings import all_warnings -from ._safe_as_int import safe_as_int __all__ = ['deprecated', 'get_bound_method_class', 'all_warnings', 'safe_as_int'] @@ -74,3 +74,70 @@ def get_bound_method_class(m): """ return m.im_class if sys.version < '3' else m.__self__.__class__ + + +def safe_as_int(val, atol=1e-3): + """ + Attempt to safely cast values to integer format. + + Parameters + ---------- + val : scalar or iterable of scalars + Number or container of numbers which are intended to be interpreted as + integers, e.g., for indexing purposes, but which may not carry integer + type. + atol : float + Absolute tolerance away from nearest integer to consider values in + ``val`` functionally integers. + + Returns + ------- + val_int : NumPy scalar or ndarray of dtype `np.int64` + Returns the input value(s) coerced to dtype `np.int64` assuming all + were within ``atol`` of the nearest integer. + + Notes + ----- + This operation calculates ``val`` modulo 1, which returns the mantissa of + all values. Then all mantissas greater than 0.5 are subtracted from one. + Finally, the absolute tolerance from zero is calculated. If it is less + than ``atol`` for all value(s) in ``val``, they are rounded and returned + in an integer array. Or, if ``val`` was a scalar, a NumPy scalar type is + returned. + + If any value(s) are outside the specified tolerance, an informative error + is raised. + + Examples + -------- + >>> _safe_as_int(7.0) + 7 + + >>> _safe_as_int([9, 4, 2.9999999999]) + array([9, 4, 3], dtype=int32) + + >>> _safe_as_int(53.01) + Traceback (most recent call last): + ... + ValueError: Integer argument required but received 53.1, check inputs. + + >>> _safe_as_int(53.01, atol=0.01) + 53 + + """ + mod = np.asarray(val) % 1 # Extract mantissa + + # Check for and subtract any mod values > 0.5 from 1 + if mod.ndim == 0: # Scalar input, cannot be indexed + if mod > 0.5: + mod = 1 - mod + else: # Iterable input, now ndarray + mod[mod > 0.5] = 1 - mod[mod > 0.5] # Test on each side of nearest int + + try: + np.testing.assert_allclose(mod, 0, atol=atol) + except AssertionError: + raise ValueError("Integer argument required but received " + "{0}, check inputs.".format(val)) + + return np.round(val).astype(np.int64)