Skip to content

Commit 47968e6

Browse files
mroeschkeAnkurDedania
authored andcommitted
BUG: Floating point accuracy with DatetimeIndex.round (pandas-dev#14440)
closes pandas-dev#14440 Employs @eoincondron's fix for float point inaccuracies when rounding by milliseconds for `DatetimeIndex.round` and `Timestamp.round` Author: Matt Roeschke <[email protected]> Closes pandas-dev#15568 from mroeschke/fix_14440 and squashes the following commits: c5a7cbc [Matt Roeschke] BUG:Floating point accuracy with DatetimeIndex.round (pandas-dev#14440)
1 parent 388aab9 commit 47968e6

File tree

5 files changed

+24
-2
lines changed

5 files changed

+24
-2
lines changed

doc/source/whatsnew/v0.20.0.txt

+1
Original file line numberDiff line numberDiff line change
@@ -652,6 +652,7 @@ Bug Fixes
652652
- Bug in ``Index`` power operations with reversed operands (:issue:`14973`)
653653
- Bug in ``TimedeltaIndex`` addition where overflow was being allowed without error (:issue:`14816`)
654654
- Bug in ``TimedeltaIndex`` raising a ``ValueError`` when boolean indexing with ``loc`` (:issue:`14946`)
655+
- Bug in ``DatetimeIndex.round()`` and ``Timestamp.round()`` floating point accuracy when rounding by milliseconds (:issue: `14440`)
655656
- Bug in ``astype()`` where ``inf`` values were incorrectly converted to integers. Now raises error now with ``astype()`` for Series and DataFrames (:issue:`14265`)
656657
- Bug in ``DataFrame(..).apply(to_numeric)`` when values are of type decimal.Decimal. (:issue:`14827`)
657658
- Bug in ``describe()`` when passing a numpy array which does not contain the median to the ``percentiles`` keyword argument (:issue:`14908`)

pandas/tests/indexes/datetimes/test_ops.py

+11
Original file line numberDiff line numberDiff line change
@@ -175,6 +175,17 @@ def test_round(self):
175175
tm.assertRaisesRegexp(ValueError, msg, rng.round, freq='M')
176176
tm.assertRaisesRegexp(ValueError, msg, elt.round, freq='M')
177177

178+
# GH 14440
179+
index = pd.DatetimeIndex(['2016-10-17 12:00:00.0015'], tz=tz)
180+
result = index.round('ms')
181+
expected = pd.DatetimeIndex(['2016-10-17 12:00:00.002000'], tz=tz)
182+
tm.assert_index_equal(result, expected)
183+
184+
index = pd.DatetimeIndex(['2016-10-17 12:00:00.00149'], tz=tz)
185+
result = index.round('ms')
186+
expected = pd.DatetimeIndex(['2016-10-17 12:00:00.001000'], tz=tz)
187+
tm.assert_index_equal(result, expected)
188+
178189
def test_repeat_range(self):
179190
rng = date_range('1/1/2000', '1/1/2001')
180191

pandas/tests/scalar/test_timestamp.py

+9
Original file line numberDiff line numberDiff line change
@@ -732,6 +732,15 @@ def test_round(self):
732732
for freq in ['Y', 'M', 'foobar']:
733733
self.assertRaises(ValueError, lambda: dti.round(freq))
734734

735+
# GH 14440
736+
result = pd.Timestamp('2016-10-17 12:00:00.0015').round('ms')
737+
expected = pd.Timestamp('2016-10-17 12:00:00.002000')
738+
self.assertEqual(result, expected)
739+
740+
result = pd.Timestamp('2016-10-17 12:00:00.00149').round('ms')
741+
expected = pd.Timestamp('2016-10-17 12:00:00.001000')
742+
self.assertEqual(result, expected)
743+
735744
def test_class_ops_pytz(self):
736745
tm._skip_if_no_pytz()
737746
from pytz import timezone

pandas/tseries/base.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -83,7 +83,7 @@ def _round(self, freq, rounder):
8383
# round the local times
8484
values = _ensure_datetimelike_to_i8(self)
8585

86-
result = (unit * rounder(values / float(unit))).astype('i8')
86+
result = (unit * rounder(values / float(unit)).astype('i8'))
8787
result = self._maybe_mask_results(result, fill_value=tslib.NaT)
8888
attribs = self._get_attributes_dict()
8989
if 'freq' in attribs:

pandas/tslib.pyx

+2-1
Original file line numberDiff line numberDiff line change
@@ -421,7 +421,8 @@ class Timestamp(_Timestamp):
421421
value = self.tz_localize(None).value
422422
else:
423423
value = self.value
424-
result = Timestamp(unit * rounder(value / float(unit)), unit='ns')
424+
result = (unit * rounder(value / float(unit)).astype('i8'))
425+
result = Timestamp(result, unit='ns')
425426
if self.tz is not None:
426427
result = result.tz_localize(self.tz)
427428
return result

0 commit comments

Comments
 (0)