pandas-dev/pandas · error · TypeError
cannot subtract from
Error message
cannot subtract {type(other).__name__} from {type(self).__name__} What it means
Raised by _sub_periodlike when self.dtype is not PeriodDtype. Subtracting a Period or PeriodArray to yield a frequency count (object-dtype ndarray of DateOffsets) is only defined when self is itself a PeriodArray; subtracting a Period from a DatetimeArray or TimedeltaArray is rejected.
Solutions
- Convert self to a PeriodArray (via .to_period(freq)) before subtracting a Period.
- If you want point-in-time differences, convert the Period to a Timestamp via .to_timestamp() and subtract Timestamps.
- Confirm dtype: only PeriodDtype - Period/PeriodArray is permitted in this path.
Example fix
// before
res = dta - pd.Period('2020', freq='D') # TypeError
// after
res = dta.to_period('D') - pd.Period('2020', freq='D') Defensive patterns
Strategy: type-guard
Validate before calling
import pandas as pd
def sub_period(arr, period):
if not isinstance(arr.dtype, pd.PeriodDtype):
raise TypeError('self must be a PeriodArray to subtract a Period')
return arr - period Type guard
import pandas as pd
def is_period_array(a) -> bool:
return isinstance(getattr(a, 'dtype', None), pd.PeriodDtype) Try / catch
try:
res = arr - other
except TypeError as e:
if 'cannot subtract' in str(e) and 'Period' in str(e):
res = arr.to_period(other.freq) - other
else:
raise Prevention
- Only subtract Period/PeriodArray from a PeriodArray.
- Convert datetimes via .to_period(freq) before Period subtraction.
- For datetime differences, use .to_timestamp() on the Period operand.
When it happens
Trigger: DatetimeArray - Period(...); TimedeltaArray - PeriodArray; non-Period datetimelike array minus a Period operand.
Common situations: Period vs datetime confusion in calendar/difference code; assuming Period can be subtracted from a Timestamp column to give a duration.
Related errors
- cannot add Period to a
- Cannot add and
- cannot subtract a datelike from a
- cannot subtract from
- cannot add and
AI-assisted analysis of pandas-dev/pandas@3b7651241d (2026-08-11).
Data as JSON: /api/errors/89cfe4c058eead9d.
Report an issue: GitHub.
Appendix: source
Thrown at pandas/core/arrays/datetimelike.py:1241
# like a timedelta.
# For datetime64 dtypes by convention we treat NaT as a datetime, so
# this subtraction returns a timedelta64 dtype.
# For period dtype, timedelta64 is a close-enough return dtype.
result = np.empty(self.shape, dtype=np.int64)
result.fill(iNaT)
if self.dtype.kind in "mM":
# We can retain unit in dtype
self = cast("DatetimeArray| TimedeltaArray", self)
return result.view(f"timedelta64[{self.unit}]")
else:
return result.view("timedelta64[ns]")
@final
def _sub_periodlike(self, other: Period | PeriodArray) -> npt.NDArray[np.object_]:
# If the operation is well-defined, we return an object-dtype ndarray
# of DateOffsets. Null entries are filled with pd.NaT
if not isinstance(self.dtype, PeriodDtype):
raise TypeError(
f"cannot subtract {type(other).__name__} from {type(self).__name__}"
)
self = cast("PeriodArray", self)
self._check_compatible_with(other)
other_i8, o_mask = self._get_i8_values_and_mask(other)
# GH#66552 the difference is a count of periods, not an ordinal, so
# INT64_MIN is a legitimate answer here and not the NaT sentinel.
new_i8_data = add_overflowsafe(
self.asi8, np.asarray(-other_i8, dtype="i8"), sentinel_ok=True
)
# multiply by python ints: numpy's scalar multiply spuriously reports
# overflow for a np.int64 count of INT64_MIN on Windows
counts = new_i8_data.ravel().tolist()
new_data = np.array([self.freq.base * count for count in counts]).reshape(
new_i8_data.shape
)View on GitHub (pinned to 3b7651241d)