From ff31c3dee8ddf05abbeb8981cfa01e65de8cecea Mon Sep 17 00:00:00 2001 From: Aleksey Bilogur Date: Sun, 5 Mar 2017 23:25:38 -0500 Subject: [PATCH 1/4] Add fill validation methods. --- pandas/types/common.py | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/pandas/types/common.py b/pandas/types/common.py index e58e0826ea49a..83b8a60b63bf9 100644 --- a/pandas/types/common.py +++ b/pandas/types/common.py @@ -491,3 +491,27 @@ def pandas_dtype(dtype): return dtype return np.dtype(dtype) + + +def _is_fillable_value(value): + pandas_ts_types = ('Timestamp', 'Period', 'Timedelta') + pandas_block_types = ('Series', 'DataFrame') + + if any([isinstance(value, (list, dict)), + callable(value), + (not (isinstance(value, string_types) or + isinstance(value, (int, float, complex, str, None.__class__)) or + is_numeric_dtype(value) or + is_datetime_or_timedelta_dtype(value) or + is_period_dtype(value) or + type(value).__name__ in pandas_ts_types) or + type(value).__name__ in pandas_block_types)]): + return False + else: + return True + + +def validate_fill_value(value): + if not _is_fillable_value(value): + raise TypeError('"value" parameter must be a scalar, but ' + 'you passed a "{0}"'.format(type(value).__name__)) From 61153cae6b53d7ee5b52a13f7ebed9b804bbbc41 Mon Sep 17 00:00:00 2001 From: Aleksey Bilogur Date: Sun, 5 Mar 2017 23:31:26 -0500 Subject: [PATCH 2/4] Correction. --- pandas/types/common.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pandas/types/common.py b/pandas/types/common.py index 83b8a60b63bf9..5a109d877b40d 100644 --- a/pandas/types/common.py +++ b/pandas/types/common.py @@ -504,8 +504,8 @@ def _is_fillable_value(value): is_numeric_dtype(value) or is_datetime_or_timedelta_dtype(value) or is_period_dtype(value) or - type(value).__name__ in pandas_ts_types) or - type(value).__name__ in pandas_block_types)]): + type(value).__name__ in pandas_ts_types)), + type(value).__name__ in pandas_block_types]): return False else: return True From 1efdc4aca5d21949bcd091a24ed4e2c0b0907653 Mon Sep 17 00:00:00 2001 From: Aleksey Bilogur Date: Tue, 7 Mar 2017 14:35:18 -0500 Subject: [PATCH 3/4] Experimental validator. --- pandas/core/missing.py | 32 +++++++++++++++++++++++++++++++- pandas/core/reshape.py | 4 ++++ pandas/types/common.py | 24 ------------------------ 3 files changed, 35 insertions(+), 25 deletions(-) diff --git a/pandas/core/missing.py b/pandas/core/missing.py index ffd0423572f5e..3028b8eeb4638 100644 --- a/pandas/core/missing.py +++ b/pandas/core/missing.py @@ -12,7 +12,11 @@ is_float_dtype, is_datetime64_dtype, is_datetime64tz_dtype, is_integer_dtype, _ensure_float64, is_scalar, - needs_i8_conversion, is_integer) + needs_i8_conversion, is_integer, + is_list_like, is_dict_like, + is_numeric_dtype, + is_datetime64_any_dtype, is_float, + is_complex, is_timedelta64_dtype) from pandas.types.missing import isnull @@ -621,3 +625,29 @@ def fill_zeros(result, x, y, name, fill): result = result.reshape(shape) return result + + +def validate_fill_value(value, dtype): + if is_list_like(value) or is_dict_like(value) or callable(value): + raise TypeError('"fill_value" parameter must be ' + 'a scalar, but you passed a ' + '"{0}"'.format(type(value).__name__)) + elif not isnull(value): + from datetime import datetime, timedelta + + if is_numeric_dtype(dtype): + if not (is_float(value) or is_integer(value) or is_complex(value)): + raise TypeError('"fill_value" parameter must be ' + 'numeric, but you passed a ' + '"{0}"'.format(type(value).__name__)) + elif is_datetime64_any_dtype(dtype): + if not isinstance(value, (np.datetime64, datetime)): + raise TypeError('"fill_value" parameter must be a ' + 'datetime, but you passed a ' + '"{0}"'.format(type(value).__name__)) + elif is_timedelta64_dtype(dtype): + if not isinstance(value, (np.timedelta64, timedelta)): + raise TypeError('"value" parameter must be ' + 'a timedelta, but you passed a ' + '"{0}"'.format(type(value).__name__)) + # if object dtype, do nothing. diff --git a/pandas/core/reshape.py b/pandas/core/reshape.py index 87cb088c2e91e..d48f7065f31d3 100644 --- a/pandas/core/reshape.py +++ b/pandas/core/reshape.py @@ -405,6 +405,10 @@ def _slow_pivot(index, columns, values): def unstack(obj, level, fill_value=None): + if fill_value: + from pandas.core.missing import validate_fill_value + validate_fill_value(fill_value, obj.values.dtype) + if isinstance(level, (tuple, list)): return _unstack_multiple(obj, level) diff --git a/pandas/types/common.py b/pandas/types/common.py index 5a109d877b40d..e58e0826ea49a 100644 --- a/pandas/types/common.py +++ b/pandas/types/common.py @@ -491,27 +491,3 @@ def pandas_dtype(dtype): return dtype return np.dtype(dtype) - - -def _is_fillable_value(value): - pandas_ts_types = ('Timestamp', 'Period', 'Timedelta') - pandas_block_types = ('Series', 'DataFrame') - - if any([isinstance(value, (list, dict)), - callable(value), - (not (isinstance(value, string_types) or - isinstance(value, (int, float, complex, str, None.__class__)) or - is_numeric_dtype(value) or - is_datetime_or_timedelta_dtype(value) or - is_period_dtype(value) or - type(value).__name__ in pandas_ts_types)), - type(value).__name__ in pandas_block_types]): - return False - else: - return True - - -def validate_fill_value(value): - if not _is_fillable_value(value): - raise TypeError('"value" parameter must be a scalar, but ' - 'you passed a "{0}"'.format(type(value).__name__)) From 9eaa0f241388ff7e5110d3396fefc1d8c3c4bab4 Mon Sep 17 00:00:00 2001 From: Aleksey Bilogur Date: Tue, 7 Mar 2017 16:49:56 -0500 Subject: [PATCH 4/4] Move around imports, stub out test. --- pandas/core/missing.py | 32 +----------------------------- pandas/core/reshape.py | 2 +- pandas/tests/types/test_missing.py | 11 +++++++++- pandas/types/missing.py | 32 +++++++++++++++++++++++++++++- 4 files changed, 43 insertions(+), 34 deletions(-) diff --git a/pandas/core/missing.py b/pandas/core/missing.py index 3028b8eeb4638..ffd0423572f5e 100644 --- a/pandas/core/missing.py +++ b/pandas/core/missing.py @@ -12,11 +12,7 @@ is_float_dtype, is_datetime64_dtype, is_datetime64tz_dtype, is_integer_dtype, _ensure_float64, is_scalar, - needs_i8_conversion, is_integer, - is_list_like, is_dict_like, - is_numeric_dtype, - is_datetime64_any_dtype, is_float, - is_complex, is_timedelta64_dtype) + needs_i8_conversion, is_integer) from pandas.types.missing import isnull @@ -625,29 +621,3 @@ def fill_zeros(result, x, y, name, fill): result = result.reshape(shape) return result - - -def validate_fill_value(value, dtype): - if is_list_like(value) or is_dict_like(value) or callable(value): - raise TypeError('"fill_value" parameter must be ' - 'a scalar, but you passed a ' - '"{0}"'.format(type(value).__name__)) - elif not isnull(value): - from datetime import datetime, timedelta - - if is_numeric_dtype(dtype): - if not (is_float(value) or is_integer(value) or is_complex(value)): - raise TypeError('"fill_value" parameter must be ' - 'numeric, but you passed a ' - '"{0}"'.format(type(value).__name__)) - elif is_datetime64_any_dtype(dtype): - if not isinstance(value, (np.datetime64, datetime)): - raise TypeError('"fill_value" parameter must be a ' - 'datetime, but you passed a ' - '"{0}"'.format(type(value).__name__)) - elif is_timedelta64_dtype(dtype): - if not isinstance(value, (np.timedelta64, timedelta)): - raise TypeError('"value" parameter must be ' - 'a timedelta, but you passed a ' - '"{0}"'.format(type(value).__name__)) - # if object dtype, do nothing. diff --git a/pandas/core/reshape.py b/pandas/core/reshape.py index d48f7065f31d3..b76e52acb8be4 100644 --- a/pandas/core/reshape.py +++ b/pandas/core/reshape.py @@ -25,6 +25,7 @@ import pandas.core.algorithms as algos import pandas.algos as _algos +from pandas.core.missing import validate_fill_value from pandas.core.index import MultiIndex, _get_na_value @@ -406,7 +407,6 @@ def _slow_pivot(index, columns, values): def unstack(obj, level, fill_value=None): if fill_value: - from pandas.core.missing import validate_fill_value validate_fill_value(fill_value, obj.values.dtype) if isinstance(level, (tuple, list)): diff --git a/pandas/tests/types/test_missing.py b/pandas/tests/types/test_missing.py index cab44f1122ae1..bdbebfe01985b 100644 --- a/pandas/tests/types/test_missing.py +++ b/pandas/tests/types/test_missing.py @@ -12,7 +12,8 @@ DatetimeIndex, TimedeltaIndex, date_range) from pandas.types.dtypes import DatetimeTZDtype from pandas.types.missing import (array_equivalent, isnull, notnull, - na_value_for_dtype) + na_value_for_dtype, + validate_fill_value) def test_notnull(): @@ -301,3 +302,11 @@ def test_na_value_for_dtype(): for dtype in ['O']: assert np.isnan(na_value_for_dtype(np.dtype(dtype))) + + +class TestValidateFillValue(tm.TestCase): + # TODO: Fill out the test cases. + def test_validate_fill_value(self): + # validate_fill_value() + # import pdb; pdb.set_trace() + pass diff --git a/pandas/types/missing.py b/pandas/types/missing.py index e6791b79bf3bd..9d36a38b59c38 100644 --- a/pandas/types/missing.py +++ b/pandas/types/missing.py @@ -19,7 +19,10 @@ is_object_dtype, is_integer, _TD_DTYPE, - _NS_DTYPE) + _NS_DTYPE, + is_datetime64_any_dtype, is_float, + is_numeric_dtype, is_complex) +from datetime import datetime, timedelta from .inference import is_list_like @@ -391,3 +394,30 @@ def na_value_for_dtype(dtype): elif is_bool_dtype(dtype): return False return np.nan + + +def validate_fill_value(value, dtype): + """ + Make sure the fill value is appropriate for the given dtype. + """ + if not is_scalar(value): + raise TypeError('"fill_value" parameter must be ' + 'a scalar, but you passed a ' + '"{0}"'.format(type(value).__name__)) + elif not isnull(value): + if is_numeric_dtype(dtype): + if not (is_float(value) or is_integer(value) or is_complex(value)): + raise TypeError('"fill_value" parameter must be ' + 'numeric, but you passed a ' + '"{0}"'.format(type(value).__name__)) + elif is_datetime64_any_dtype(dtype): + if not isinstance(value, (np.datetime64, datetime)): + raise TypeError('"fill_value" parameter must be a ' + 'datetime, but you passed a ' + '"{0}"'.format(type(value).__name__)) + elif is_timedelta64_dtype(dtype): + if not isinstance(value, (np.timedelta64, timedelta)): + raise TypeError('"value" parameter must be ' + 'a timedelta, but you passed a ' + '"{0}"'.format(type(value).__name__)) + # if object dtype, do nothing.