123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440 |
- import decimal
- import numpy as np
- from numpy import iinfo
- import pytest
- import pandas as pd
- from pandas import to_numeric
- from pandas.util import testing as tm
- class TestToNumeric(object):
- def test_empty(self):
- # see gh-16302
- s = pd.Series([], dtype=object)
- res = to_numeric(s)
- expected = pd.Series([], dtype=np.int64)
- tm.assert_series_equal(res, expected)
- # Original issue example
- res = to_numeric(s, errors='coerce', downcast='integer')
- expected = pd.Series([], dtype=np.int8)
- tm.assert_series_equal(res, expected)
- def test_series(self):
- s = pd.Series(['1', '-3.14', '7'])
- res = to_numeric(s)
- expected = pd.Series([1, -3.14, 7])
- tm.assert_series_equal(res, expected)
- s = pd.Series(['1', '-3.14', 7])
- res = to_numeric(s)
- tm.assert_series_equal(res, expected)
- def test_series_numeric(self):
- s = pd.Series([1, 3, 4, 5], index=list('ABCD'), name='XXX')
- res = to_numeric(s)
- tm.assert_series_equal(res, s)
- s = pd.Series([1., 3., 4., 5.], index=list('ABCD'), name='XXX')
- res = to_numeric(s)
- tm.assert_series_equal(res, s)
- # bool is regarded as numeric
- s = pd.Series([True, False, True, True],
- index=list('ABCD'), name='XXX')
- res = to_numeric(s)
- tm.assert_series_equal(res, s)
- def test_error(self):
- s = pd.Series([1, -3.14, 'apple'])
- msg = 'Unable to parse string "apple" at position 2'
- with pytest.raises(ValueError, match=msg):
- to_numeric(s, errors='raise')
- res = to_numeric(s, errors='ignore')
- expected = pd.Series([1, -3.14, 'apple'])
- tm.assert_series_equal(res, expected)
- res = to_numeric(s, errors='coerce')
- expected = pd.Series([1, -3.14, np.nan])
- tm.assert_series_equal(res, expected)
- s = pd.Series(['orange', 1, -3.14, 'apple'])
- msg = 'Unable to parse string "orange" at position 0'
- with pytest.raises(ValueError, match=msg):
- to_numeric(s, errors='raise')
- def test_error_seen_bool(self):
- s = pd.Series([True, False, 'apple'])
- msg = 'Unable to parse string "apple" at position 2'
- with pytest.raises(ValueError, match=msg):
- to_numeric(s, errors='raise')
- res = to_numeric(s, errors='ignore')
- expected = pd.Series([True, False, 'apple'])
- tm.assert_series_equal(res, expected)
- # coerces to float
- res = to_numeric(s, errors='coerce')
- expected = pd.Series([1., 0., np.nan])
- tm.assert_series_equal(res, expected)
- def test_list(self):
- s = ['1', '-3.14', '7']
- res = to_numeric(s)
- expected = np.array([1, -3.14, 7])
- tm.assert_numpy_array_equal(res, expected)
- def test_list_numeric(self):
- s = [1, 3, 4, 5]
- res = to_numeric(s)
- tm.assert_numpy_array_equal(res, np.array(s, dtype=np.int64))
- s = [1., 3., 4., 5.]
- res = to_numeric(s)
- tm.assert_numpy_array_equal(res, np.array(s))
- # bool is regarded as numeric
- s = [True, False, True, True]
- res = to_numeric(s)
- tm.assert_numpy_array_equal(res, np.array(s))
- def test_numeric(self):
- s = pd.Series([1, -3.14, 7], dtype='O')
- res = to_numeric(s)
- expected = pd.Series([1, -3.14, 7])
- tm.assert_series_equal(res, expected)
- s = pd.Series([1, -3.14, 7])
- res = to_numeric(s)
- tm.assert_series_equal(res, expected)
- # GH 14827
- df = pd.DataFrame(dict(
- a=[1.2, decimal.Decimal(3.14), decimal.Decimal("infinity"), '0.1'],
- b=[1.0, 2.0, 3.0, 4.0],
- ))
- expected = pd.DataFrame(dict(
- a=[1.2, 3.14, np.inf, 0.1],
- b=[1.0, 2.0, 3.0, 4.0],
- ))
- # Test to_numeric over one column
- df_copy = df.copy()
- df_copy['a'] = df_copy['a'].apply(to_numeric)
- tm.assert_frame_equal(df_copy, expected)
- # Test to_numeric over multiple columns
- df_copy = df.copy()
- df_copy[['a', 'b']] = df_copy[['a', 'b']].apply(to_numeric)
- tm.assert_frame_equal(df_copy, expected)
- def test_numeric_lists_and_arrays(self):
- # Test to_numeric with embedded lists and arrays
- df = pd.DataFrame(dict(
- a=[[decimal.Decimal(3.14), 1.0], decimal.Decimal(1.6), 0.1]
- ))
- df['a'] = df['a'].apply(to_numeric)
- expected = pd.DataFrame(dict(
- a=[[3.14, 1.0], 1.6, 0.1],
- ))
- tm.assert_frame_equal(df, expected)
- df = pd.DataFrame(dict(
- a=[np.array([decimal.Decimal(3.14), 1.0]), 0.1]
- ))
- df['a'] = df['a'].apply(to_numeric)
- expected = pd.DataFrame(dict(
- a=[[3.14, 1.0], 0.1],
- ))
- tm.assert_frame_equal(df, expected)
- def test_all_nan(self):
- s = pd.Series(['a', 'b', 'c'])
- res = to_numeric(s, errors='coerce')
- expected = pd.Series([np.nan, np.nan, np.nan])
- tm.assert_series_equal(res, expected)
- @pytest.mark.parametrize("errors", [None, "ignore", "raise", "coerce"])
- def test_type_check(self, errors):
- # see gh-11776
- df = pd.DataFrame({"a": [1, -3.14, 7], "b": ["4", "5", "6"]})
- kwargs = dict(errors=errors) if errors is not None else dict()
- error_ctx = pytest.raises(TypeError, match="1-d array")
- with error_ctx:
- to_numeric(df, **kwargs)
- def test_scalar(self):
- assert pd.to_numeric(1) == 1
- assert pd.to_numeric(1.1) == 1.1
- assert pd.to_numeric('1') == 1
- assert pd.to_numeric('1.1') == 1.1
- with pytest.raises(ValueError):
- to_numeric('XX', errors='raise')
- assert to_numeric('XX', errors='ignore') == 'XX'
- assert np.isnan(to_numeric('XX', errors='coerce'))
- def test_numeric_dtypes(self):
- idx = pd.Index([1, 2, 3], name='xxx')
- res = pd.to_numeric(idx)
- tm.assert_index_equal(res, idx)
- res = pd.to_numeric(pd.Series(idx, name='xxx'))
- tm.assert_series_equal(res, pd.Series(idx, name='xxx'))
- res = pd.to_numeric(idx.values)
- tm.assert_numpy_array_equal(res, idx.values)
- idx = pd.Index([1., np.nan, 3., np.nan], name='xxx')
- res = pd.to_numeric(idx)
- tm.assert_index_equal(res, idx)
- res = pd.to_numeric(pd.Series(idx, name='xxx'))
- tm.assert_series_equal(res, pd.Series(idx, name='xxx'))
- res = pd.to_numeric(idx.values)
- tm.assert_numpy_array_equal(res, idx.values)
- def test_str(self):
- idx = pd.Index(['1', '2', '3'], name='xxx')
- exp = np.array([1, 2, 3], dtype='int64')
- res = pd.to_numeric(idx)
- tm.assert_index_equal(res, pd.Index(exp, name='xxx'))
- res = pd.to_numeric(pd.Series(idx, name='xxx'))
- tm.assert_series_equal(res, pd.Series(exp, name='xxx'))
- res = pd.to_numeric(idx.values)
- tm.assert_numpy_array_equal(res, exp)
- idx = pd.Index(['1.5', '2.7', '3.4'], name='xxx')
- exp = np.array([1.5, 2.7, 3.4])
- res = pd.to_numeric(idx)
- tm.assert_index_equal(res, pd.Index(exp, name='xxx'))
- res = pd.to_numeric(pd.Series(idx, name='xxx'))
- tm.assert_series_equal(res, pd.Series(exp, name='xxx'))
- res = pd.to_numeric(idx.values)
- tm.assert_numpy_array_equal(res, exp)
- def test_datetime_like(self, tz_naive_fixture):
- idx = pd.date_range("20130101", periods=3,
- tz=tz_naive_fixture, name="xxx")
- res = pd.to_numeric(idx)
- tm.assert_index_equal(res, pd.Index(idx.asi8, name="xxx"))
- res = pd.to_numeric(pd.Series(idx, name="xxx"))
- tm.assert_series_equal(res, pd.Series(idx.asi8, name="xxx"))
- res = pd.to_numeric(idx.values)
- tm.assert_numpy_array_equal(res, idx.asi8)
- def test_timedelta(self):
- idx = pd.timedelta_range('1 days', periods=3, freq='D', name='xxx')
- res = pd.to_numeric(idx)
- tm.assert_index_equal(res, pd.Index(idx.asi8, name='xxx'))
- res = pd.to_numeric(pd.Series(idx, name='xxx'))
- tm.assert_series_equal(res, pd.Series(idx.asi8, name='xxx'))
- res = pd.to_numeric(idx.values)
- tm.assert_numpy_array_equal(res, idx.asi8)
- def test_period(self):
- idx = pd.period_range('2011-01', periods=3, freq='M', name='xxx')
- res = pd.to_numeric(idx)
- tm.assert_index_equal(res, pd.Index(idx.asi8, name='xxx'))
- # TODO: enable when we can support native PeriodDtype
- # res = pd.to_numeric(pd.Series(idx, name='xxx'))
- # tm.assert_series_equal(res, pd.Series(idx.asi8, name='xxx'))
- def test_non_hashable(self):
- # Test for Bug #13324
- s = pd.Series([[10.0, 2], 1.0, 'apple'])
- res = pd.to_numeric(s, errors='coerce')
- tm.assert_series_equal(res, pd.Series([np.nan, 1.0, np.nan]))
- res = pd.to_numeric(s, errors='ignore')
- tm.assert_series_equal(res, pd.Series([[10.0, 2], 1.0, 'apple']))
- with pytest.raises(TypeError, match="Invalid object type"):
- pd.to_numeric(s)
- @pytest.mark.parametrize("data", [
- ["1", 2, 3],
- [1, 2, 3],
- np.array(["1970-01-02", "1970-01-03",
- "1970-01-04"], dtype="datetime64[D]")
- ])
- def test_downcast_basic(self, data):
- # see gh-13352
- invalid_downcast = "unsigned-integer"
- msg = "invalid downcasting method provided"
- with pytest.raises(ValueError, match=msg):
- pd.to_numeric(data, downcast=invalid_downcast)
- expected = np.array([1, 2, 3], dtype=np.int64)
- # Basic function tests.
- res = pd.to_numeric(data)
- tm.assert_numpy_array_equal(res, expected)
- res = pd.to_numeric(data, downcast=None)
- tm.assert_numpy_array_equal(res, expected)
- # Basic dtype support.
- smallest_uint_dtype = np.dtype(np.typecodes["UnsignedInteger"][0])
- # Support below np.float32 is rare and far between.
- float_32_char = np.dtype(np.float32).char
- smallest_float_dtype = float_32_char
- expected = np.array([1, 2, 3], dtype=smallest_uint_dtype)
- res = pd.to_numeric(data, downcast="unsigned")
- tm.assert_numpy_array_equal(res, expected)
- expected = np.array([1, 2, 3], dtype=smallest_float_dtype)
- res = pd.to_numeric(data, downcast="float")
- tm.assert_numpy_array_equal(res, expected)
- @pytest.mark.parametrize("signed_downcast", ["integer", "signed"])
- @pytest.mark.parametrize("data", [
- ["1", 2, 3],
- [1, 2, 3],
- np.array(["1970-01-02", "1970-01-03",
- "1970-01-04"], dtype="datetime64[D]")
- ])
- def test_signed_downcast(self, data, signed_downcast):
- # see gh-13352
- smallest_int_dtype = np.dtype(np.typecodes["Integer"][0])
- expected = np.array([1, 2, 3], dtype=smallest_int_dtype)
- res = pd.to_numeric(data, downcast=signed_downcast)
- tm.assert_numpy_array_equal(res, expected)
- def test_ignore_downcast_invalid_data(self):
- # If we can't successfully cast the given
- # data to a numeric dtype, do not bother
- # with the downcast parameter.
- data = ["foo", 2, 3]
- expected = np.array(data, dtype=object)
- res = pd.to_numeric(data, errors="ignore",
- downcast="unsigned")
- tm.assert_numpy_array_equal(res, expected)
- def test_ignore_downcast_neg_to_unsigned(self):
- # Cannot cast to an unsigned integer
- # because we have a negative number.
- data = ["-1", 2, 3]
- expected = np.array([-1, 2, 3], dtype=np.int64)
- res = pd.to_numeric(data, downcast="unsigned")
- tm.assert_numpy_array_equal(res, expected)
- @pytest.mark.parametrize("downcast", ["integer", "signed", "unsigned"])
- @pytest.mark.parametrize("data,expected", [
- (["1.1", 2, 3],
- np.array([1.1, 2, 3], dtype=np.float64)),
- ([10000.0, 20000, 3000, 40000.36, 50000, 50000.00],
- np.array([10000.0, 20000, 3000,
- 40000.36, 50000, 50000.00], dtype=np.float64))
- ])
- def test_ignore_downcast_cannot_convert_float(
- self, data, expected, downcast):
- # Cannot cast to an integer (signed or unsigned)
- # because we have a float number.
- res = pd.to_numeric(data, downcast=downcast)
- tm.assert_numpy_array_equal(res, expected)
- @pytest.mark.parametrize("downcast,expected_dtype", [
- ("integer", np.int16),
- ("signed", np.int16),
- ("unsigned", np.uint16)
- ])
- def test_downcast_not8bit(self, downcast, expected_dtype):
- # the smallest integer dtype need not be np.(u)int8
- data = ["256", 257, 258]
- expected = np.array([256, 257, 258], dtype=expected_dtype)
- res = pd.to_numeric(data, downcast=downcast)
- tm.assert_numpy_array_equal(res, expected)
- @pytest.mark.parametrize("dtype,downcast,min_max", [
- ("int8", "integer", [iinfo(np.int8).min,
- iinfo(np.int8).max]),
- ("int16", "integer", [iinfo(np.int16).min,
- iinfo(np.int16).max]),
- ('int32', "integer", [iinfo(np.int32).min,
- iinfo(np.int32).max]),
- ('int64', "integer", [iinfo(np.int64).min,
- iinfo(np.int64).max]),
- ('uint8', "unsigned", [iinfo(np.uint8).min,
- iinfo(np.uint8).max]),
- ('uint16', "unsigned", [iinfo(np.uint16).min,
- iinfo(np.uint16).max]),
- ('uint32', "unsigned", [iinfo(np.uint32).min,
- iinfo(np.uint32).max]),
- ('uint64', "unsigned", [iinfo(np.uint64).min,
- iinfo(np.uint64).max]),
- ('int16', "integer", [iinfo(np.int8).min,
- iinfo(np.int8).max + 1]),
- ('int32', "integer", [iinfo(np.int16).min,
- iinfo(np.int16).max + 1]),
- ('int64', "integer", [iinfo(np.int32).min,
- iinfo(np.int32).max + 1]),
- ('int16', "integer", [iinfo(np.int8).min - 1,
- iinfo(np.int16).max]),
- ('int32', "integer", [iinfo(np.int16).min - 1,
- iinfo(np.int32).max]),
- ('int64', "integer", [iinfo(np.int32).min - 1,
- iinfo(np.int64).max]),
- ('uint16', "unsigned", [iinfo(np.uint8).min,
- iinfo(np.uint8).max + 1]),
- ('uint32', "unsigned", [iinfo(np.uint16).min,
- iinfo(np.uint16).max + 1]),
- ('uint64', "unsigned", [iinfo(np.uint32).min,
- iinfo(np.uint32).max + 1])
- ])
- def test_downcast_limits(self, dtype, downcast, min_max):
- # see gh-14404: test the limits of each downcast.
- series = pd.to_numeric(pd.Series(min_max), downcast=downcast)
- assert series.dtype == dtype
- def test_coerce_uint64_conflict(self):
- # see gh-17007 and gh-17125
- #
- # Still returns float despite the uint64-nan conflict,
- # which would normally force the casting to object.
- df = pd.DataFrame({"a": [200, 300, "", "NaN", 30000000000000000000]})
- expected = pd.Series([200, 300, np.nan, np.nan,
- 30000000000000000000], dtype=float, name="a")
- result = to_numeric(df["a"], errors="coerce")
- tm.assert_series_equal(result, expected)
- s = pd.Series(["12345678901234567890", "1234567890", "ITEM"])
- expected = pd.Series([12345678901234567890,
- 1234567890, np.nan], dtype=float)
- result = to_numeric(s, errors="coerce")
- tm.assert_series_equal(result, expected)
- # For completeness, check against "ignore" and "raise"
- result = to_numeric(s, errors="ignore")
- tm.assert_series_equal(result, s)
- msg = "Unable to parse string"
- with pytest.raises(ValueError, match=msg):
- to_numeric(s, errors="raise")
|