Skip to content

Commit 405f9f1

Browse files
committed
rebase & cleanup, fixup some edge cases
closes #16402
1 parent 04ad63f commit 405f9f1

File tree

13 files changed

+383
-340
lines changed

13 files changed

+383
-340
lines changed

doc/source/whatsnew/v0.21.0.txt

+2-1
Original file line numberDiff line numberDiff line change
@@ -84,7 +84,8 @@ Bug Fixes
8484
Conversion
8585
^^^^^^^^^^
8686

87-
- Bug in assignment against datetime-like data with ``int`` may incorrectly converted to datetime-like (:issue:`14145`)
87+
- Inconsistent behavior in ``.where()`` with datetimelikes which would raise rather than coerce to ``object`` (:issue:`16402`)
88+
- Bug in assignment against datetime-like data with ``int`` may incorrectly convert to datetime-like (:issue:`14145`)
8889
- Bug in assignment against ``int64`` data with ``np.ndarray`` with ``float64`` dtype may keep ``int64`` dtype (:issue:`14001`)
8990

9091

pandas/_libs/index.pyx

+13-6
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ cimport tslib
1919
from hashtable cimport *
2020
from pandas._libs import tslib, algos, hashtable as _hash
2121
from pandas._libs.tslib import Timestamp, Timedelta
22+
from datetime import datetime, timedelta
2223

2324
from datetime cimport (get_datetime64_value, _pydatetime_to_dts,
2425
pandas_datetimestruct)
@@ -507,24 +508,30 @@ cdef class TimedeltaEngine(DatetimeEngine):
507508
return 'm8[ns]'
508509

509510
cpdef convert_scalar(ndarray arr, object value):
511+
# we don't turn intgers
512+
# into datetimes/timedeltas
513+
510514
if arr.descr.type_num == NPY_DATETIME:
511515
if isinstance(value, np.ndarray):
512516
pass
513-
elif isinstance(value, Timestamp):
514-
return value.value
517+
elif isinstance(value, datetime):
518+
return Timestamp(value).value
515519
elif value is None or value != value:
516520
return iNaT
517-
else:
521+
elif util.is_string_object(value):
518522
return Timestamp(value).value
523+
raise ValueError("cannot set a Timestamp with a non-timestamp")
524+
519525
elif arr.descr.type_num == NPY_TIMEDELTA:
520526
if isinstance(value, np.ndarray):
521527
pass
522-
elif isinstance(value, Timedelta):
523-
return value.value
528+
elif isinstance(value, timedelta):
529+
return Timedelta(value).value
524530
elif value is None or value != value:
525531
return iNaT
526-
else:
532+
elif util.is_string_object(value):
527533
return Timedelta(value).value
534+
raise ValueError("cannot set a Timedelta with a non-timedelta")
528535

529536
if issubclass(arr.dtype.type, (np.integer, np.bool_)):
530537
if util.is_float_object(value) and value != value:

pandas/core/dtypes/cast.py

+15-3
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
import warnings
77

88
from pandas._libs import tslib, lib
9-
from pandas._libs.tslib import iNaT
9+
from pandas._libs.tslib import iNaT, Timestamp
1010
from pandas.compat import string_types, text_type, PY3
1111
from .common import (_ensure_object, is_bool, is_integer, is_float,
1212
is_complex, is_datetimetz, is_categorical_dtype,
@@ -1028,13 +1028,25 @@ def find_common_type(types):
10281028
return np.find_common_type(types, [])
10291029

10301030

1031-
def _cast_scalar_to_array(shape, value, dtype=None):
1031+
def cast_scalar_to_array(shape, value, dtype=None):
10321032
"""
10331033
create np.ndarray of specified shape and dtype, filled with values
1034+
1035+
Parameters
1036+
----------
1037+
shape : tuple
1038+
value : scalar value
1039+
dtype : np.dtype, optional
1040+
dtype to coerce
1041+
1042+
Returns
1043+
-------
1044+
ndarray of shape, filled with value, of specified / inferred dtype
1045+
10341046
"""
10351047

10361048
if dtype is None:
1037-
dtype, fill_value = _infer_dtype_from_scalar(value)
1049+
dtype, fill_value = infer_dtype_from_scalar(value)
10381050
else:
10391051
fill_value = value
10401052

pandas/core/frame.py

+6-5
Original file line numberDiff line numberDiff line change
@@ -25,14 +25,15 @@
2525
import numpy.ma as ma
2626

2727
from pandas.core.dtypes.cast import (
28-
maybe_upcast, infer_dtype_from_scalar,
28+
maybe_upcast,
2929
maybe_cast_to_datetime,
3030
maybe_infer_to_datetimelike,
3131
maybe_convert_platform,
3232
maybe_downcast_to_dtype,
3333
invalidate_string_dtypes,
3434
coerce_to_dtypes,
3535
maybe_upcast_putmask,
36+
cast_scalar_to_array,
3637
find_common_type)
3738
from pandas.core.dtypes.common import (
3839
is_categorical_dtype,
@@ -343,8 +344,8 @@ def __init__(self, data=None, index=None, columns=None, dtype=None,
343344
raise_with_traceback(exc)
344345

345346
if arr.ndim == 0 and index is not None and columns is not None:
346-
values = _cast_scalar_to_array((len(index), len(columns)),
347-
data, dtype=dtype)
347+
values = cast_scalar_to_array((len(index), len(columns)),
348+
data, dtype=dtype)
348349
mgr = self._init_ndarray(values, index, columns,
349350
dtype=values.dtype, copy=False)
350351
else:
@@ -2734,8 +2735,8 @@ def reindexer(value):
27342735

27352736
else:
27362737
# upcast the scalar
2737-
value = _cast_scalar_to_array(len(self.index), value)
2738-
value = _possibly_cast_to_datetime(value, value.dtype)
2738+
value = cast_scalar_to_array(len(self.index), value)
2739+
value = maybe_cast_to_datetime(value, value.dtype)
27392740

27402741
# return internal types directly
27412742
if is_extension_type(value):

pandas/core/generic.py

-43
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,6 @@
1212
from pandas._libs import tslib, lib
1313
from pandas.core.dtypes.common import (
1414
_ensure_int64,
15-
needs_i8_conversion,
1615
is_scalar,
1716
is_number,
1817
is_integer, is_bool,
@@ -5030,48 +5029,6 @@ def _where(self, cond, other=np.nan, inplace=False, axis=None, level=None,
50305029
raise NotImplemented("cannot align with a higher dimensional "
50315030
"NDFrame")
50325031

5033-
elif is_list_like(other):
5034-
5035-
if self.ndim == 1:
5036-
5037-
# try to set the same dtype as ourselves
5038-
try:
5039-
new_other = np.array(other, dtype=self.dtype)
5040-
except ValueError:
5041-
new_other = np.array(other)
5042-
except TypeError:
5043-
new_other = other
5044-
5045-
# we can end up comparing integers and m8[ns]
5046-
# which is a numpy no no
5047-
is_i8 = needs_i8_conversion(self.dtype)
5048-
if is_i8:
5049-
matches = False
5050-
else:
5051-
matches = (new_other == np.array(other))
5052-
5053-
if matches is False or not matches.all():
5054-
5055-
# coerce other to a common dtype if we can
5056-
if needs_i8_conversion(self.dtype):
5057-
try:
5058-
other = np.array(other, dtype=self.dtype)
5059-
except:
5060-
other = np.array(other)
5061-
else:
5062-
other = np.asarray(other)
5063-
other = np.asarray(other,
5064-
dtype=np.common_type(other,
5065-
new_other))
5066-
5067-
# we need to use the new dtype
5068-
try_quick = False
5069-
else:
5070-
other = new_other
5071-
else:
5072-
5073-
other = np.array(other)
5074-
50755032
if isinstance(other, np.ndarray):
50765033

50775034
if other.shape != self.shape:

0 commit comments

Comments
 (0)