-
-
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 5 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 |
---|---|---|
|
@@ -155,6 +155,12 @@ 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 | ||
|
||
def __hash__(_Timestamp self): | ||
if self.nanosecond: | ||
return hash(self.value) | ||
|
@@ -263,7 +269,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,14 +369,13 @@ 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)) | ||
|
@@ -400,7 +407,13 @@ cdef class _Timestamp(ABCTimestamp): | |
if self.freq is None: | ||
# fast-path for non-business frequencies | ||
return self.day == 1 | ||
return self._get_start_end_field("is_month_start") | ||
warnings.warn( | ||
"Timestamp.freq is deprecated and will be removed in a future version. " | ||
"When you have a freq, use freq.is_month_start(timestamp) instead", | ||
FutureWarning, | ||
stacklevel=2, | ||
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. Same as above, I think this warning message is rather vague / unclear (but let's discuss above) |
||
) | ||
return self._get_start_end_field("is_month_start", self.freq) | ||
|
||
@property | ||
def is_month_end(self) -> bool: | ||
|
@@ -420,7 +433,13 @@ cdef class _Timestamp(ABCTimestamp): | |
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") | ||
warnings.warn( | ||
"Timestamp.freq is deprecated and will be removed in a future version. " | ||
"When you have a freq, use freq.is_month_end(timestamp) instead", | ||
FutureWarning, | ||
stacklevel=2, | ||
) | ||
return self._get_start_end_field("is_month_end", self.freq) | ||
|
||
@property | ||
def is_quarter_start(self) -> bool: | ||
|
@@ -440,7 +459,13 @@ cdef class _Timestamp(ABCTimestamp): | |
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") | ||
warnings.warn( | ||
"Timestamp.freq is deprecated and will be removed in a future version. " | ||
"When you have a freq, use freq.is_quarter_start(timestamp) instead", | ||
FutureWarning, | ||
stacklevel=2, | ||
) | ||
return self._get_start_end_field("is_quarter_start", self.freq) | ||
|
||
@property | ||
def is_quarter_end(self) -> bool: | ||
|
@@ -460,7 +485,13 @@ cdef class _Timestamp(ABCTimestamp): | |
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") | ||
warnings.warn( | ||
"Timestamp.freq is deprecated and will be removed in a future version. " | ||
"When you have a freq, use freq.is_quarter_end(timestamp) instead", | ||
FutureWarning, | ||
stacklevel=2, | ||
) | ||
return self._get_start_end_field("is_quarter_end", self.freq) | ||
|
||
@property | ||
def is_year_start(self) -> bool: | ||
|
@@ -480,7 +511,13 @@ cdef class _Timestamp(ABCTimestamp): | |
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") | ||
warnings.warn( | ||
"Timestamp.freq is deprecated and will be removed in a future version. " | ||
"When you have a freq, use freq.is_year_start(timestamp) instead", | ||
FutureWarning, | ||
stacklevel=2, | ||
) | ||
return self._get_start_end_field("is_year_start", self.freq) | ||
|
||
@property | ||
def is_year_end(self) -> bool: | ||
|
@@ -500,7 +537,13 @@ cdef class _Timestamp(ABCTimestamp): | |
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") | ||
warnings.warn( | ||
"Timestamp.freq is deprecated and will be removed in a future version. " | ||
"When you have a freq, use freq.is_year_end(timestamp) instead", | ||
FutureWarning, | ||
stacklevel=2, | ||
) | ||
return self._get_start_end_field("is_year_end", self.freq) | ||
|
||
cdef _get_date_name_field(self, str field, object locale): | ||
cdef: | ||
|
@@ -878,6 +921,12 @@ cdef class _Timestamp(ABCTimestamp): | |
|
||
if freq is None: | ||
freq = self.freq | ||
warnings.warn( | ||
"Timestamp.freq is deprecated and will be removed in a future " | ||
"version. Pass `freq` to Timestamp.to_period explicitly", | ||
jbrockmendel marked this conversation as resolved.
Show resolved
Hide resolved
|
||
FutureWarning, | ||
stacklevel=2, | ||
) | ||
|
||
return Period(self, freq=freq) | ||
|
||
|
@@ -1147,7 +1196,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. | ||
|
@@ -1277,8 +1326,15 @@ 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) | ||
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) | ||
|
||
|
@@ -1647,12 +1703,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 +1769,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 | ||
|
||
|
@@ -1722,7 +1787,7 @@ default 'raise' | |
microsecond=None, | ||
nanosecond=None, | ||
tzinfo=object, | ||
fold=None, | ||
fold=None | ||
): | ||
""" | ||
Implements datetime.replace, handles nanoseconds. | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -1208,7 +1208,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" | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -510,7 +510,11 @@ def _check_compatible_with(self, other, setitem: bool = False): | |
# Descriptive Properties | ||
|
||
def _box_func(self, x) -> Timestamp | NaTType: | ||
return Timestamp(x, freq=self.freq, tz=self.tz) | ||
with warnings.catch_warnings(): | ||
# filter out warnings about Timestamp.freq | ||
warnings.filterwarnings("ignore", category=FutureWarning) | ||
|
||
return Timestamp(x, freq=self.freq, tz=self.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 not just set this to None? (or we have to because you are deprecating) 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. if we could change this without a deprecation cycle itd be convenient, but that would change what you get from 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 you use the internal |
||
|
||
@property | ||
# error: Return type "Union[dtype, DatetimeTZDtype]" of "dtype" | ||
|
@@ -603,13 +607,18 @@ def __iter__(self): | |
length = len(self) | ||
chunksize = 10000 | ||
chunks = (length // chunksize) + 1 | ||
for i in range(chunks): | ||
start_i = i * chunksize | ||
end_i = min((i + 1) * chunksize, length) | ||
converted = ints_to_pydatetime( | ||
data[start_i:end_i], tz=self.tz, freq=self.freq, box="timestamp" | ||
) | ||
yield from converted | ||
|
||
with warnings.catch_warnings(): | ||
# filter out warnings about Timestamp.freq | ||
warnings.filterwarnings("ignore", category=FutureWarning) | ||
|
||
for i in range(chunks): | ||
start_i = i * chunksize | ||
end_i = min((i + 1) * chunksize, length) | ||
converted = ints_to_pydatetime( | ||
data[start_i:end_i], tz=self.tz, freq=self.freq, box="timestamp" | ||
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 you just NOT pass self.freq? 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. Not without changing the behavior. ATM |
||
) | ||
yield from converted | ||
|
||
def astype(self, dtype, copy: bool = True): | ||
# We handle | ||
|
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)