-
-
Notifications
You must be signed in to change notification settings - Fork 18.4k
DEPR: Timestamp.freq #41586
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
DEPR: Timestamp.freq #41586
Changes from all commits
07a131d
7f231e7
a511ceb
8f4666d
913ed4b
d222341
1cf43a8
2df27d2
e5edf28
1b8bc03
2f8e61b
7d9d524
c0974c3
4066dd2
3435954
b93b6c1
069ae4c
e10a053
33df636
e6f1c6b
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Large diffs are not rendered by default.
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -123,7 +123,7 @@ cdef inline object create_timestamp_from_ts(int64_t value, | |
dts.day, dts.hour, dts.min, | ||
dts.sec, dts.us, tz, fold=fold) | ||
ts_base.value = value | ||
ts_base.freq = freq | ||
ts_base._freq = freq | ||
ts_base.nanosecond = dts.ps // 1000 | ||
|
||
return ts_base | ||
|
@@ -155,6 +155,21 @@ cdef class _Timestamp(ABCTimestamp): | |
dayofweek = _Timestamp.day_of_week | ||
dayofyear = _Timestamp.day_of_year | ||
|
||
cpdef void _set_freq(self, freq): | ||
# set the ._freq attribute without going through the constructor, | ||
# which would issue a warning | ||
# Caller is responsible for validation | ||
self._freq = freq | ||
|
||
@property | ||
def freq(self): | ||
warnings.warn( | ||
"Timestamp.freq is deprecated and will be removed in a future version", | ||
FutureWarning, | ||
stacklevel=1, | ||
) | ||
return self._freq | ||
|
||
def __hash__(_Timestamp self): | ||
if self.nanosecond: | ||
return hash(self.value) | ||
|
@@ -263,7 +278,9 @@ cdef class _Timestamp(ABCTimestamp): | |
|
||
if is_any_td_scalar(other): | ||
nanos = delta_to_nanoseconds(other) | ||
result = type(self)(self.value + nanos, tz=self.tzinfo, freq=self.freq) | ||
result = type(self)(self.value + nanos, tz=self.tzinfo) | ||
if result is not NaT: | ||
result._set_freq(self._freq) # avoid warning in constructor | ||
return result | ||
|
||
elif is_integer_object(other): | ||
|
@@ -361,18 +378,17 @@ cdef class _Timestamp(ABCTimestamp): | |
val = self.value | ||
return val | ||
|
||
cdef bint _get_start_end_field(self, str field): | ||
cdef bint _get_start_end_field(self, str field, freq): | ||
cdef: | ||
int64_t val | ||
dict kwds | ||
ndarray[uint8_t, cast=True] out | ||
int month_kw | ||
|
||
freq = self.freq | ||
if freq: | ||
kwds = freq.kwds | ||
month_kw = kwds.get('startingMonth', kwds.get('month', 12)) | ||
freqstr = self.freqstr | ||
freqstr = self._freqstr | ||
else: | ||
month_kw = 12 | ||
freqstr = None | ||
|
@@ -382,6 +398,31 @@ cdef class _Timestamp(ABCTimestamp): | |
field, freqstr, month_kw) | ||
return out[0] | ||
|
||
cdef _warn_on_field_deprecation(self, freq, str field): | ||
""" | ||
Warn if the removal of .freq change the value of start/end properties. | ||
""" | ||
cdef: | ||
bint needs = False | ||
|
||
if freq is not None: | ||
kwds = freq.kwds | ||
month_kw = kwds.get("startingMonth", kwds.get("month", 12)) | ||
freqstr = self._freqstr | ||
if month_kw != 12: | ||
needs = True | ||
if freqstr.startswith("B"): | ||
needs = True | ||
|
||
if needs: | ||
warnings.warn( | ||
"Timestamp.freq is deprecated and will be removed in a future " | ||
"version. When you have a freq, use " | ||
f"freq.{field}(timestamp) instead", | ||
FutureWarning, | ||
stacklevel=1, | ||
) | ||
|
||
@property | ||
def is_month_start(self) -> bool: | ||
""" | ||
|
@@ -397,10 +438,11 @@ cdef class _Timestamp(ABCTimestamp): | |
>>> ts.is_month_start | ||
True | ||
""" | ||
if self.freq is None: | ||
if self._freq is None: | ||
# fast-path for non-business frequencies | ||
return self.day == 1 | ||
return self._get_start_end_field("is_month_start") | ||
self._warn_on_field_deprecation(self._freq, "is_month_start") | ||
return self._get_start_end_field("is_month_start", self._freq) | ||
|
||
@property | ||
def is_month_end(self) -> bool: | ||
|
@@ -417,10 +459,11 @@ cdef class _Timestamp(ABCTimestamp): | |
>>> ts.is_month_end | ||
True | ||
""" | ||
if self.freq is None: | ||
if self._freq is None: | ||
# fast-path for non-business frequencies | ||
return self.day == self.days_in_month | ||
return self._get_start_end_field("is_month_end") | ||
self._warn_on_field_deprecation(self._freq, "is_month_end") | ||
return self._get_start_end_field("is_month_end", self._freq) | ||
|
||
@property | ||
def is_quarter_start(self) -> bool: | ||
|
@@ -437,10 +480,11 @@ cdef class _Timestamp(ABCTimestamp): | |
>>> ts.is_quarter_start | ||
True | ||
""" | ||
if self.freq is None: | ||
if self._freq is None: | ||
# fast-path for non-business frequencies | ||
return self.day == 1 and self.month % 3 == 1 | ||
return self._get_start_end_field("is_quarter_start") | ||
self._warn_on_field_deprecation(self._freq, "is_quarter_start") | ||
return self._get_start_end_field("is_quarter_start", self._freq) | ||
|
||
@property | ||
def is_quarter_end(self) -> bool: | ||
|
@@ -457,10 +501,11 @@ cdef class _Timestamp(ABCTimestamp): | |
>>> ts.is_quarter_end | ||
True | ||
""" | ||
if self.freq is None: | ||
if self._freq is None: | ||
# fast-path for non-business frequencies | ||
return (self.month % 3) == 0 and self.day == self.days_in_month | ||
return self._get_start_end_field("is_quarter_end") | ||
self._warn_on_field_deprecation(self._freq, "is_quarter_end") | ||
return self._get_start_end_field("is_quarter_end", self._freq) | ||
|
||
@property | ||
def is_year_start(self) -> bool: | ||
|
@@ -477,10 +522,11 @@ cdef class _Timestamp(ABCTimestamp): | |
>>> ts.is_year_start | ||
True | ||
""" | ||
if self.freq is None: | ||
if self._freq is None: | ||
# fast-path for non-business frequencies | ||
return self.day == self.month == 1 | ||
return self._get_start_end_field("is_year_start") | ||
self._warn_on_field_deprecation(self._freq, "is_year_start") | ||
return self._get_start_end_field("is_year_start", self._freq) | ||
|
||
@property | ||
def is_year_end(self) -> bool: | ||
|
@@ -497,10 +543,11 @@ cdef class _Timestamp(ABCTimestamp): | |
>>> ts.is_year_end | ||
True | ||
""" | ||
if self.freq is None: | ||
if self._freq is None: | ||
# fast-path for non-business frequencies | ||
return self.month == 12 and self.day == 31 | ||
return self._get_start_end_field("is_year_end") | ||
self._warn_on_field_deprecation(self._freq, "is_year_end") | ||
return self._get_start_end_field("is_year_end", self._freq) | ||
|
||
cdef _get_date_name_field(self, str field, object locale): | ||
cdef: | ||
|
@@ -673,11 +720,11 @@ cdef class _Timestamp(ABCTimestamp): | |
|
||
def __setstate__(self, state): | ||
self.value = state[0] | ||
self.freq = state[1] | ||
self._freq = state[1] | ||
self.tzinfo = state[2] | ||
|
||
def __reduce__(self): | ||
object_state = self.value, self.freq, self.tzinfo | ||
object_state = self.value, self._freq, self.tzinfo | ||
return (Timestamp, object_state) | ||
|
||
# ----------------------------------------------------------------- | ||
|
@@ -719,7 +766,7 @@ cdef class _Timestamp(ABCTimestamp): | |
pass | ||
|
||
tz = f", tz='{zone}'" if zone is not None else "" | ||
freq = "" if self.freq is None else f", freq='{self.freqstr}'" | ||
freq = "" if self._freq is None else f", freq='{self._freqstr}'" | ||
|
||
return f"Timestamp('{stamp}'{tz}{freq})" | ||
|
||
|
@@ -877,7 +924,13 @@ cdef class _Timestamp(ABCTimestamp): | |
) | ||
|
||
if freq is None: | ||
freq = self.freq | ||
freq = self._freq | ||
warnings.warn( | ||
"In a future version, calling 'Timestamp.to_period()' without " | ||
"passing a 'freq' will raise an exception.", | ||
FutureWarning, | ||
stacklevel=2, | ||
) | ||
|
||
return Period(self, freq=freq) | ||
|
||
|
@@ -1147,7 +1200,7 @@ class Timestamp(_Timestamp): | |
nanosecond=None, | ||
tzinfo_type tzinfo=None, | ||
*, | ||
fold=None | ||
fold=None, | ||
): | ||
# The parameter list folds together legacy parameter names (the first | ||
# four) and positional and keyword parameter names from pydatetime. | ||
|
@@ -1276,9 +1329,16 @@ class Timestamp(_Timestamp): | |
|
||
if freq is None: | ||
# GH 22311: Try to extract the frequency of a given Timestamp input | ||
freq = getattr(ts_input, 'freq', None) | ||
elif not is_offset_object(freq): | ||
freq = to_offset(freq) | ||
freq = getattr(ts_input, '_freq', None) | ||
else: | ||
warnings.warn( | ||
"The 'freq' argument in Timestamp is deprecated and will be " | ||
"removed in a future version.", | ||
FutureWarning, | ||
stacklevel=1, | ||
) | ||
if not is_offset_object(freq): | ||
freq = to_offset(freq) | ||
|
||
return create_timestamp_from_ts(ts.value, ts.dts, ts.tzinfo, freq, ts.fold) | ||
|
||
|
@@ -1551,12 +1611,21 @@ timedelta}, default 'raise' | |
"Use tz_localize() or tz_convert() as appropriate" | ||
) | ||
|
||
@property | ||
def _freqstr(self): | ||
return getattr(self._freq, "freqstr", self._freq) | ||
|
||
@property | ||
def freqstr(self): | ||
""" | ||
Return the total number of days in the month. | ||
""" | ||
return getattr(self.freq, 'freqstr', self.freq) | ||
warnings.warn( | ||
"Timestamp.freqstr is deprecated and will be removed in a future version.", | ||
FutureWarning, | ||
stacklevel=1, | ||
) | ||
return self._freqstr | ||
|
||
def tz_localize(self, tz, ambiguous='raise', nonexistent='raise'): | ||
""" | ||
|
@@ -1647,12 +1716,18 @@ default 'raise' | |
value = tz_localize_to_utc_single(self.value, tz, | ||
ambiguous=ambiguous, | ||
nonexistent=nonexistent) | ||
return Timestamp(value, tz=tz, freq=self.freq) | ||
out = Timestamp(value, tz=tz) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. can we some just avoid setting this? |
||
if out is not NaT: | ||
out._set_freq(self._freq) # avoid warning in constructor | ||
return out | ||
else: | ||
if tz is None: | ||
# reset tz | ||
value = tz_convert_from_utc_single(self.value, self.tz) | ||
return Timestamp(value, tz=tz, freq=self.freq) | ||
out = Timestamp(value, tz=tz) | ||
if out is not NaT: | ||
out._set_freq(self._freq) # avoid warning in constructor | ||
return out | ||
else: | ||
raise TypeError( | ||
"Cannot localize tz-aware Timestamp, use tz_convert for conversions" | ||
|
@@ -1707,7 +1782,10 @@ default 'raise' | |
) | ||
else: | ||
# Same UTC timestamp, different time zone | ||
return Timestamp(self.value, tz=tz, freq=self.freq) | ||
out = Timestamp(self.value, tz=tz) | ||
if out is not NaT: | ||
out._set_freq(self._freq) # avoid warning in constructor | ||
return out | ||
|
||
astimezone = tz_convert | ||
|
||
|
@@ -1840,7 +1918,7 @@ default 'raise' | |
if value != NPY_NAT: | ||
check_dts_bounds(&dts) | ||
|
||
return create_timestamp_from_ts(value, dts, tzobj, self.freq, fold) | ||
return create_timestamp_from_ts(value, dts, tzobj, self._freq, fold) | ||
|
||
def to_julian_date(self) -> np.float64: | ||
""" | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -1189,7 +1189,11 @@ def _addsub_object_array(self, other: np.ndarray, op): | |
# Caller is responsible for broadcasting if necessary | ||
assert self.shape == other.shape, (self.shape, other.shape) | ||
|
||
res_values = op(self.astype("O"), np.asarray(other)) | ||
with warnings.catch_warnings(): | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. shouln't these not have freq to begin with? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
|
||
# filter out warnings about Timestamp.freq | ||
warnings.filterwarnings("ignore", category=FutureWarning) | ||
res_values = op(self.astype("O"), np.asarray(other)) | ||
|
||
result = pd_array(res_values.ravel()) | ||
# error: Item "ExtensionArray" of "Union[Any, ExtensionArray]" has no attribute | ||
# "reshape" | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't find the explanation of "for properties that use it .., when you have a
freq
, ..." very clear. Adding an actual example might clarify.First, I assume people who do
ts.is_month_start
(withts
being a Timestamp) don't necessarily know that they are relying on somefreq
.Also, the attributes are not going away, right? It's only the fastpath for computing it based on the freq that goes away? Are there actually cases that will change behaviour, or is it only performance? (and if only performance, that might not matter much for scalars, or maybe not worth warning about).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, e.g.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
And in principle we can know when this will change? (depending on the
freq
, we can know whether it can give different answers?)Could we warn only in those cases?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good idea, will do
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Updated to warn in fewer cases. Still haven't updated the whatsnew note per the first comment here. suggestions on wording?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
i agree here. i think we are simply deprecating freq. why do we need to even say anything about
is_month_start
for example?There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
bc the result of is_month_start is going to change in some cases
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
One consequence of deprecating Timestamp.freq is that when having a timeseries with a freq, the following equivalency won't no longer always hold:
That's a bit unfortunate, I would say, and probably something we should explicitly document.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ok for this as a followup documentation (but for 1.3)