diff --git a/doc/source/whatsnew/v0.19.2.txt b/doc/source/whatsnew/v0.19.2.txt index 978e7616c1018..f193de7fbdbd0 100644 --- a/doc/source/whatsnew/v0.19.2.txt +++ b/doc/source/whatsnew/v0.19.2.txt @@ -33,6 +33,7 @@ Bug Fixes - Bug in ``pd.cut`` with negative values and a single bin (:issue:`14652`) +- Bug in ``pd.to_numeric`` where a 0 was not unsigned on a ``downcast='unsigned'`` argument (:issue:`14401`) diff --git a/pandas/tools/tests/test_util.py b/pandas/tools/tests/test_util.py index 8c16308d79a31..f9647721e3c5b 100644 --- a/pandas/tools/tests/test_util.py +++ b/pandas/tools/tests/test_util.py @@ -4,9 +4,10 @@ import nose import numpy as np +from numpy import iinfo import pandas as pd -from pandas import date_range, Index +from pandas import (date_range, Index, _np_version_under1p9) import pandas.util.testing as tm from pandas.tools.util import cartesian_product, to_numeric @@ -401,6 +402,41 @@ def test_downcast(self): res = pd.to_numeric(data, downcast=downcast) tm.assert_numpy_array_equal(res, expected) + def test_downcast_limits(self): + # Test the limits of each downcast. Bug: #14401. + # Check to make sure numpy is new enough to run this test. + if _np_version_under1p9: + raise nose.SkipTest("Numpy version is under 1.9") + + i = 'integer' + u = 'unsigned' + dtype_downcast_min_max = [ + ('int8', i, [iinfo(np.int8).min, iinfo(np.int8).max]), + ('int16', i, [iinfo(np.int16).min, iinfo(np.int16).max]), + ('int32', i, [iinfo(np.int32).min, iinfo(np.int32).max]), + ('int64', i, [iinfo(np.int64).min, iinfo(np.int64).max]), + ('uint8', u, [iinfo(np.uint8).min, iinfo(np.uint8).max]), + ('uint16', u, [iinfo(np.uint16).min, iinfo(np.uint16).max]), + ('uint32', u, [iinfo(np.uint32).min, iinfo(np.uint32).max]), + # Test will be skipped until there is more uint64 support. + # ('uint64', u, [iinfo(uint64).min, iinfo(uint64).max]), + ('int16', i, [iinfo(np.int8).min, iinfo(np.int8).max + 1]), + ('int32', i, [iinfo(np.int16).min, iinfo(np.int16).max + 1]), + ('int64', i, [iinfo(np.int32).min, iinfo(np.int32).max + 1]), + ('int16', i, [iinfo(np.int8).min - 1, iinfo(np.int16).max]), + ('int32', i, [iinfo(np.int16).min - 1, iinfo(np.int32).max]), + ('int64', i, [iinfo(np.int32).min - 1, iinfo(np.int64).max]), + ('uint16', u, [iinfo(np.uint8).min, iinfo(np.uint8).max + 1]), + ('uint32', u, [iinfo(np.uint16).min, iinfo(np.uint16).max + 1]), + # Test will be skipped until there is more uint64 support. + # ('uint64', u, [iinfo(np.uint32).min, iinfo(np.uint32).max + 1]), + ] + + for dtype, downcast, min_max in dtype_downcast_min_max: + series = pd.to_numeric(pd.Series(min_max), downcast=downcast) + tm.assert_equal(series.dtype, dtype) + + if __name__ == '__main__': nose.runmodule(argv=[__file__, '-vvs', '-x', '--pdb', '--pdb-failure'], exit=False) diff --git a/pandas/tools/util.py b/pandas/tools/util.py index fec56328c1721..b50bf9dc448bc 100644 --- a/pandas/tools/util.py +++ b/pandas/tools/util.py @@ -205,7 +205,7 @@ def to_numeric(arg, errors='raise', downcast=None): if downcast in ('integer', 'signed'): typecodes = np.typecodes['Integer'] - elif downcast == 'unsigned' and np.min(values) > 0: + elif downcast == 'unsigned' and np.min(values) >= 0: typecodes = np.typecodes['UnsignedInteger'] elif downcast == 'float': typecodes = np.typecodes['Float']