diff --git a/doc/source/whatsnew/v0.17.0.txt b/doc/source/whatsnew/v0.17.0.txt index 8e03fe02bcc97..447a42c43d24e 100644 --- a/doc/source/whatsnew/v0.17.0.txt +++ b/doc/source/whatsnew/v0.17.0.txt @@ -393,5 +393,6 @@ Bug Fixes - Bug in operator equal on Index not being consistent with Series (:issue:`9947`) - Reading "famafrench" data via ``DataReader`` results in HTTP 404 error because of the website url is changed (:issue:`10591`). - Bug in `read_msgpack` where DataFrame to decode has duplicate column names (:issue:`9618`) - - Bug in ``io.common.get_filepath_or_buffer`` which caused reading of valid S3 files to fail if the bucket also contained keys for which the user does not have read permission (:issue:`10604`) +- Bug in vectorised setting of timestamp columns with python ``datetime.date`` and numpy ``datetime64`` (:issue:`10408`, :issue:`10412`) + diff --git a/pandas/core/internals.py b/pandas/core/internals.py index 6a87f5a0b08e0..5953e783f6c4d 100644 --- a/pandas/core/internals.py +++ b/pandas/core/internals.py @@ -2,7 +2,7 @@ import itertools import re import operator -from datetime import datetime, timedelta +from datetime import datetime, timedelta, date from collections import defaultdict import numpy as np @@ -1839,7 +1839,7 @@ def _try_coerce_args(self, values, other): if is_null_datelike_scalar(other): other = tslib.iNaT - elif isinstance(other, datetime): + elif isinstance(other, (datetime, np.datetime64, date)): other = lib.Timestamp(other).asm8.view('i8') elif hasattr(other, 'dtype') and com.is_integer_dtype(other): other = other.view('i8') diff --git a/pandas/tests/test_frame.py b/pandas/tests/test_frame.py index aeb28524e5cc1..4e78e1549fb0e 100644 --- a/pandas/tests/test_frame.py +++ b/pandas/tests/test_frame.py @@ -3,7 +3,7 @@ from __future__ import print_function # pylint: disable-msg=W0612,E1101 from copy import deepcopy -from datetime import datetime, timedelta, time +from datetime import datetime, timedelta, time, date import sys import operator import re @@ -4248,6 +4248,16 @@ def test_datetimelike_setitem_with_inference(self): expected = Series([np.dtype('timedelta64[ns]')]*6+[np.dtype('datetime64[ns]')]*2,index=list('ABCDEFGH')) assert_series_equal(result,expected) + def test_setitem_datetime_coercion(self): + # GH 1048 + df = pd.DataFrame({'c': [pd.Timestamp('2010-10-01')]*3}) + df.loc[0:1, 'c'] = np.datetime64('2008-08-08') + self.assertEqual(pd.Timestamp('2008-08-08'), df.loc[0, 'c']) + self.assertEqual(pd.Timestamp('2008-08-08'), df.loc[1, 'c']) + df.loc[2, 'c'] = date(2005, 5, 5) + self.assertEqual(pd.Timestamp('2005-05-05'), df.loc[2, 'c']) + + def test_new_empty_index(self): df1 = DataFrame(randn(0, 3)) df2 = DataFrame(randn(0, 3)) diff --git a/pandas/tests/test_internals.py b/pandas/tests/test_internals.py index 6d2c87a187995..5a1eb719270c4 100644 --- a/pandas/tests/test_internals.py +++ b/pandas/tests/test_internals.py @@ -1,6 +1,8 @@ # -*- coding: utf-8 -*- # pylint: disable=W0102 +from datetime import datetime, date + import nose import numpy as np @@ -286,6 +288,26 @@ def test_repr(self): pass +class TestDatetimeBlock(tm.TestCase): + _multiprocess_can_split_ = True + + def test_try_coerce_arg(self): + block = create_block('datetime', [0]) + + # coerce None + none_coerced = block._try_coerce_args(block.values, None)[1] + self.assertTrue(pd.Timestamp(none_coerced) is pd.NaT) + + # coerce different types of date bojects + vals = (np.datetime64('2010-10-10'), + datetime(2010, 10, 10), + date(2010, 10, 10)) + for val in vals: + coerced = block._try_coerce_args(block.values, val)[1] + self.assertEqual(np.int64, type(coerced)) + self.assertEqual(pd.Timestamp('2010-10-10'), pd.Timestamp(coerced)) + + class TestBlockManager(tm.TestCase): _multiprocess_can_split_ = True