Skip to content

Commit

Permalink
ENH: TDA.total_seconds support non-nano (pandas-dev#47421)
Browse files Browse the repository at this point in the history
* ENH: TDA.total_seconds support non-nano

* fix doctest

* mypy fixup
  • Loading branch information
jbrockmendel authored and yehoshuadimarsky committed Jul 13, 2022
1 parent 580b9fd commit 69f5587
Show file tree
Hide file tree
Showing 7 changed files with 34 additions and 6 deletions.
2 changes: 2 additions & 0 deletions pandas/_libs/tslibs/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,13 +29,15 @@
"astype_overflowsafe",
"get_unit_from_dtype",
"periods_per_day",
"periods_per_second",
]

from pandas._libs.tslibs import dtypes
from pandas._libs.tslibs.conversion import localize_pydatetime
from pandas._libs.tslibs.dtypes import (
Resolution,
periods_per_day,
periods_per_second,
)
from pandas._libs.tslibs.nattype import (
NaT,
Expand Down
2 changes: 1 addition & 1 deletion pandas/_libs/tslibs/dtypes.pxd
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ cdef str npy_unit_to_abbrev(NPY_DATETIMEUNIT unit)
cdef NPY_DATETIMEUNIT abbrev_to_npy_unit(str abbrev)
cdef NPY_DATETIMEUNIT freq_group_code_to_npy_unit(int freq) nogil
cpdef int64_t periods_per_day(NPY_DATETIMEUNIT reso=*) except? -1
cdef int64_t periods_per_second(NPY_DATETIMEUNIT reso) except? -1
cpdef int64_t periods_per_second(NPY_DATETIMEUNIT reso) except? -1
cdef int64_t get_conversion_factor(NPY_DATETIMEUNIT from_unit, NPY_DATETIMEUNIT to_unit) except? -1

cdef dict attrname_to_abbrevs
Expand Down
1 change: 1 addition & 0 deletions pandas/_libs/tslibs/dtypes.pyi
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ _attrname_to_abbrevs: dict[str, str]
_period_code_map: dict[str, int]

def periods_per_day(reso: int) -> int: ...
def periods_per_second(reso: int) -> int: ...

class PeriodDtypeBase:
_dtype_code: int # PeriodDtypeCode
Expand Down
2 changes: 1 addition & 1 deletion pandas/_libs/tslibs/dtypes.pyx
Original file line number Diff line number Diff line change
Expand Up @@ -403,7 +403,7 @@ cpdef int64_t periods_per_day(NPY_DATETIMEUNIT reso=NPY_DATETIMEUNIT.NPY_FR_ns)
return day_units


cdef int64_t periods_per_second(NPY_DATETIMEUNIT reso) except? -1:
cpdef int64_t periods_per_second(NPY_DATETIMEUNIT reso) except? -1:
if reso == NPY_DATETIMEUNIT.NPY_FR_ns:
return 1_000_000_000
elif reso == NPY_DATETIMEUNIT.NPY_FR_us:
Expand Down
8 changes: 5 additions & 3 deletions pandas/core/arrays/timedeltas.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
Timedelta,
astype_overflowsafe,
iNaT,
periods_per_second,
to_offset,
)
from pandas._libs.tslibs.conversion import precision_from_unit
Expand Down Expand Up @@ -818,10 +819,11 @@ def total_seconds(self) -> npt.NDArray[np.float64]:
dtype='timedelta64[ns]', freq=None)
>>> idx.total_seconds()
Float64Index([0.0, 86400.0, 172800.0, 259200.00000000003, 345600.0],
Float64Index([0.0, 86400.0, 172800.0, 259200.0, 345600.0],
dtype='float64')
"""
return self._maybe_mask_results(1e-9 * self.asi8, fill_value=None)
pps = periods_per_second(self._reso)
return self._maybe_mask_results(self.asi8 / pps, fill_value=None)

def to_pytimedelta(self) -> npt.NDArray[np.object_]:
"""
Expand All @@ -832,7 +834,7 @@ def to_pytimedelta(self) -> npt.NDArray[np.object_]:
-------
timedeltas : ndarray[object]
"""
return tslibs.ints_to_pytimedelta(self._ndarray)
return ints_to_pytimedelta(self._ndarray)

days = _field_accessor("days", "days", "Number of days for each element.")
seconds = _field_accessor(
Expand Down
24 changes: 23 additions & 1 deletion pandas/tests/arrays/test_timedeltas.py
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ def test_non_nano(self, unit, reso):
assert tda[0]._reso == reso

@pytest.mark.parametrize("field", TimedeltaArray._field_ops)
def test_fields(self, unit, reso, field):
def test_fields(self, unit, field):
arr = np.arange(5, dtype=np.int64).view(f"m8[{unit}]")
tda = TimedeltaArray._simple_new(arr, dtype=arr.dtype)

Expand All @@ -44,6 +44,28 @@ def test_fields(self, unit, reso, field):
expected = getattr(tda_nano, field)
tm.assert_numpy_array_equal(result, expected)

def test_to_pytimedelta(self, unit):
arr = np.arange(5, dtype=np.int64).view(f"m8[{unit}]")
tda = TimedeltaArray._simple_new(arr, dtype=arr.dtype)

as_nano = arr.astype("m8[ns]")
tda_nano = TimedeltaArray._simple_new(as_nano, dtype=as_nano.dtype)

result = tda.to_pytimedelta()
expected = tda_nano.to_pytimedelta()
tm.assert_numpy_array_equal(result, expected)

def test_total_seconds(self, unit):
arr = np.arange(5, dtype=np.int64).view(f"m8[{unit}]")
tda = TimedeltaArray._simple_new(arr, dtype=arr.dtype)

as_nano = arr.astype("m8[ns]")
tda_nano = TimedeltaArray._simple_new(as_nano, dtype=as_nano.dtype)

result = tda.total_seconds()
expected = tda_nano.total_seconds()
tm.assert_numpy_array_equal(result, expected)


class TestTimedeltaArray:
@pytest.mark.parametrize("dtype", [int, np.int32, np.int64, "uint32", "uint64"])
Expand Down
1 change: 1 addition & 0 deletions pandas/tests/tslibs/test_api.py
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@ def test_namespace():
"astype_overflowsafe",
"get_unit_from_dtype",
"periods_per_day",
"periods_per_second",
]

expected = set(submodules + api)
Expand Down

0 comments on commit 69f5587

Please sign in to comment.