Skip to content
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

ENH: TDA.total_seconds support non-nano #47421

Merged
merged 4 commits into from
Jun 27, 2022
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 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