Skip to content

CLN: miscellaneous cleanups / fixes #21861

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 5 commits into from
Jul 12, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions doc/source/whatsnew/v0.24.0.txt
Original file line number Diff line number Diff line change
Expand Up @@ -301,6 +301,7 @@ Datetimelike
^^^^^^^^^^^^

- Fixed bug where two :class:`DateOffset` objects with different ``normalize`` attributes could evaluate as equal (:issue:`21404`)
- Fixed bug where :meth:`Timestamp.resolution` incorrectly returned 1-microsecond ``timedelta`` instead of 1-nanosecond :class:`Timedelta` (:issue:`21336`,:issue:`21365`)

Timedelta
^^^^^^^^^
Expand Down Expand Up @@ -369,6 +370,7 @@ Missing
^^^^^^^

- Bug in :func:`DataFrame.fillna` where a ``ValueError`` would raise when one column contained a ``datetime64[ns, tz]`` dtype (:issue:`15522`)
- Bug in :func:`Series.hasnans` that could be incorrectly cached and return incorrect answers if null elements are introduced after an initial call (:issue:`19700`)

MultiIndex
^^^^^^^^^^
Expand Down
9 changes: 9 additions & 0 deletions pandas/_libs/tslibs/timestamps.pyx
Original file line number Diff line number Diff line change
Expand Up @@ -407,6 +407,15 @@ cdef class _Timestamp(datetime):
def asm8(self):
return np.datetime64(self.value, 'ns')

@property
def resolution(self):
"""
Return resolution describing the smallest difference between two
times that can be represented by Timestamp object_state
"""
# GH#21336, GH#21365
return Timedelta(nanoseconds=1)

def timestamp(self):
"""Return POSIX timestamp as float."""
# py27 compat, see GH#17329
Expand Down
7 changes: 6 additions & 1 deletion pandas/core/dtypes/inference.py
Original file line number Diff line number Diff line change
Expand Up @@ -278,10 +278,15 @@ def is_list_like(obj):
False
>>> is_list_like(1)
False
>>> is_list_like(np.array([2]))
True
>>> is_list_like(np.array(2)))
False
"""

return (isinstance(obj, Iterable) and
not isinstance(obj, string_and_binary_types))
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

in the future PR can you add some comments around what each of these clauses do

not isinstance(obj, string_and_binary_types) and
not (isinstance(obj, np.ndarray) and obj.ndim == 0))


def is_array_like(obj):
Expand Down
3 changes: 3 additions & 0 deletions pandas/core/frame.py
Original file line number Diff line number Diff line change
Expand Up @@ -7679,6 +7679,9 @@ def convert(v):
try:
if is_list_like(values[0]) or hasattr(values[0], 'len'):
values = np.array([convert(v) for v in values])
elif isinstance(values[0], np.ndarray) and values[0].ndim == 0:
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

maybe could add is_numpy_scalar method in common

# GH#21861
values = np.array([convert(v) for v in values])
else:
values = convert(values)
except:
Expand Down
5 changes: 2 additions & 3 deletions pandas/core/indexes/datetimes.py
Original file line number Diff line number Diff line change
Expand Up @@ -93,7 +93,7 @@ def _dt_index_cmp(opname, cls):
def wrapper(self, other):
func = getattr(super(DatetimeIndex, self), opname)

if isinstance(other, (datetime, compat.string_types)):
if isinstance(other, (datetime, np.datetime64, compat.string_types)):
if isinstance(other, datetime):
# GH#18435 strings get a pass from tzawareness compat
self._assert_tzawareness_compat(other)
Expand All @@ -105,8 +105,7 @@ def wrapper(self, other):
else:
if isinstance(other, list):
other = DatetimeIndex(other)
elif not isinstance(other, (np.datetime64, np.ndarray,
Index, ABCSeries)):
elif not isinstance(other, (np.ndarray, Index, ABCSeries)):
# Following Timestamp convention, __eq__ is all-False
# and __ne__ is all True, others raise TypeError.
if opname == '__eq__':
Expand Down
4 changes: 4 additions & 0 deletions pandas/core/series.py
Original file line number Diff line number Diff line change
Expand Up @@ -166,6 +166,10 @@ class Series(base.IndexOpsMixin, generic.NDFrame):
['asobject', 'sortlevel', 'reshape', 'get_value', 'set_value',
'from_csv', 'valid'])

# Override cache_readonly bc Series is mutable
hasnans = property(base.IndexOpsMixin.hasnans.func,
doc=base.IndexOpsMixin.hasnans.__doc__)

def __init__(self, data=None, index=None, dtype=None, name=None,
copy=False, fastpath=False):

Expand Down
2 changes: 1 addition & 1 deletion pandas/core/tools/timedeltas.py
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,7 @@ def to_timedelta(arg, unit='ns', box=True, errors='raise'):
elif isinstance(arg, ABCIndexClass):
return _convert_listlike(arg, unit=unit, box=box,
errors=errors, name=arg.name)
elif is_list_like(arg) and getattr(arg, 'ndim', 1) == 0:
elif isinstance(arg, np.ndarray) and arg.ndim == 0:
# extract array scalar and process below
arg = arg.item()
elif is_list_like(arg) and getattr(arg, 'ndim', 1) == 1:
Expand Down
5 changes: 3 additions & 2 deletions pandas/tests/dtypes/test_inference.py
Original file line number Diff line number Diff line change
Expand Up @@ -67,13 +67,14 @@ def __getitem__(self):
[
[], [1], (1, ), (1, 2), {'a': 1},
set([1, 'a']), Series([1]),
Series([]), Series(['a']).str])
Series([]), Series(['a']).str,
np.array([2])])
def test_is_list_like_passes(ll):
assert inference.is_list_like(ll)


@pytest.mark.parametrize(
"ll", [1, '2', object(), str])
"ll", [1, '2', object(), str, np.array(2)])
def test_is_list_like_fails(ll):
assert not inference.is_list_like(ll)

Expand Down
5 changes: 5 additions & 0 deletions pandas/tests/scalar/timestamp/test_timestamp.py
Original file line number Diff line number Diff line change
Expand Up @@ -172,6 +172,11 @@ def test_woy_boundary(self):
2005, 1, 1), (2005, 1, 2)]])
assert (result == [52, 52, 53, 53]).all()

def test_resolution(self):
# GH#21336, GH#21365
dt = Timestamp('2100-01-01 00:00:00')
assert dt.resolution == Timedelta(nanoseconds=1)


class TestTimestampConstructors(object):

Expand Down
14 changes: 14 additions & 0 deletions pandas/tests/series/test_internals.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
from pandas import Series
from pandas.core.indexes.datetimes import Timestamp
import pandas._libs.lib as lib
import pandas as pd

from pandas.util.testing import assert_series_equal
import pandas.util.testing as tm
Expand Down Expand Up @@ -309,3 +310,16 @@ def test_convert_preserve_all_bool(self):
r = s._convert(datetime=True, numeric=True)
e = Series([False, True, False, False], dtype=bool)
tm.assert_series_equal(r, e)


def test_hasnans_unchached_for_series():
# GH#19700
idx = pd.Index([0, 1])
assert not idx.hasnans
assert 'hasnans' in idx._cache
ser = idx.to_series()
assert not ser.hasnans
assert not hasattr(ser, '_cache')
ser.iloc[-1] = np.nan
assert ser.hasnans
assert pd.Series.hasnans.__doc__ == pd.Index.hasnans.__doc__