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

DEPR: lowercase freqs 'ye', 'qe', etc. raise a ValueError #56910

Merged
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
91 changes: 56 additions & 35 deletions pandas/_libs/tslibs/offsets.pyx
Original file line number Diff line number Diff line change
Expand Up @@ -4711,29 +4711,7 @@ _lite_rule_alias = {
"ns": "ns",
}

_dont_uppercase = {
"h",
"bh",
"cbh",
"MS",
"ms",
"s",
"me",
"qe",
"qe-dec",
"qe-jan",
"qe-feb",
"qe-mar",
"qe-apr",
"qe-may",
"qe-jun",
"qe-jul",
"qe-aug",
"qe-sep",
"qe-oct",
"qe-nov",
"ye",
}
_dont_uppercase = _dont_uppercase = {"h", "bh", "cbh", "MS", "ms", "s"}


INVALID_FREQ_ERR_MSG = "Invalid frequency: {0}"
Expand All @@ -4752,7 +4730,29 @@ def _get_offset(name: str) -> BaseOffset:
--------
_get_offset('EOM') --> BMonthEnd(1)
"""
if name.lower() not in _dont_uppercase:
if (
name not in _lite_rule_alias
and (name.upper() in _lite_rule_alias)
and name != "ms"
):
warnings.warn(
f"\'{name}\' is deprecated and will be removed "
f"in a future version, please use \'{name.upper()}\' instead.",
FutureWarning,
stacklevel=find_stack_level(),
)
elif (
name not in _lite_rule_alias
and (name.lower() in _lite_rule_alias)
and name != "MS"
):
warnings.warn(
f"\'{name}\' is deprecated and will be removed "
f"in a future version, please use \'{name.lower()}\' instead.",
FutureWarning,
stacklevel=find_stack_level(),
)
if name not in _dont_uppercase:
name = name.upper()
name = _lite_rule_alias.get(name, name)
name = _lite_rule_alias.get(name.lower(), name)
Expand Down Expand Up @@ -4860,7 +4860,7 @@ cpdef to_offset(freq, bint is_period=False):

tups = zip(split[0::4], split[1::4], split[2::4])
for n, (sep, stride, name) in enumerate(tups):
if is_period is False and name.upper() in c_OFFSET_DEPR_FREQSTR:
if not is_period and name.upper() in c_OFFSET_DEPR_FREQSTR:
warnings.warn(
f"\'{name}\' is deprecated and will be removed "
f"in a future version, please use "
Expand All @@ -4869,31 +4869,52 @@ cpdef to_offset(freq, bint is_period=False):
stacklevel=find_stack_level(),
)
name = c_OFFSET_DEPR_FREQSTR[name.upper()]
if is_period is True and name in c_REVERSE_OFFSET_DEPR_FREQSTR:
if name.startswith("Y"):
if (not is_period and
name != name.upper() and
name.lower() not in {"s", "ms", "us", "ns"} and
name.upper().split("-")[0].endswith(("S", "E"))):
warnings.warn(
f"\'{name}\' is deprecated and will be removed "
f"in a future version, please use "
f"\'{name.upper()}\' instead.",
FutureWarning,
stacklevel=find_stack_level(),
)
name = name.upper()
if is_period and name.upper() in c_REVERSE_OFFSET_DEPR_FREQSTR:
if name.upper().startswith("Y"):
raise ValueError(
f"for Period, please use \'Y{name[2:]}\' "
f"for Period, please use \'Y{name.upper()[2:]}\' "
f"instead of \'{name}\'"
)
if (name.startswith("B") or
name.startswith("S") or name.startswith("C")):
if (name.upper().startswith("B") or
name.upper().startswith("S") or
name.upper().startswith("C")):
raise ValueError(INVALID_FREQ_ERR_MSG.format(name))
else:
raise ValueError(
f"for Period, please use "
f"\'{c_REVERSE_OFFSET_DEPR_FREQSTR.get(name)}\' "
f"\'{c_REVERSE_OFFSET_DEPR_FREQSTR.get(name.upper())}\' "
f"instead of \'{name}\'"
)
elif is_period is True and name in c_OFFSET_DEPR_FREQSTR:
if name.startswith("A"):
elif is_period and name.upper() in c_OFFSET_DEPR_FREQSTR:
if name.upper().startswith("A"):
warnings.warn(
f"\'{name}\' is deprecated and will be removed in a future "
f"version, please use \'{c_DEPR_ABBREVS.get(name)}\' "
f"version, please use "
f"\'{c_DEPR_ABBREVS.get(name.upper())}\' instead.",
FutureWarning,
stacklevel=find_stack_level(),
)
if name.upper() != name:
warnings.warn(
f"\'{name}\' is deprecated and will be removed in "
f"a future version, please use \'{name.upper()}\' "
f"instead.",
FutureWarning,
stacklevel=find_stack_level(),
)
name = c_OFFSET_DEPR_FREQSTR.get(name)
name = c_OFFSET_DEPR_FREQSTR.get(name.upper())

if sep != "" and not sep.isspace():
raise ValueError("separator must be spaces")
Expand Down
42 changes: 42 additions & 0 deletions pandas/tests/arrays/test_datetimes.py
Original file line number Diff line number Diff line change
Expand Up @@ -766,12 +766,18 @@ def test_iter_zoneinfo_fold(self, tz):
"freq, freq_depr",
[
("2ME", "2M"),
("2SME", "2SM"),
("2SME", "2sm"),
("2QE", "2Q"),
("2QE-SEP", "2Q-SEP"),
("1YE", "1Y"),
("2YE-MAR", "2Y-MAR"),
("1YE", "1A"),
("2YE-MAR", "2A-MAR"),
("2ME", "2m"),
("2QE-SEP", "2q-sep"),
("2YE-MAR", "2a-mar"),
("2YE", "2y"),
],
)
def test_date_range_frequency_M_Q_Y_A_deprecated(self, freq, freq_depr):
Expand All @@ -784,6 +790,42 @@ def test_date_range_frequency_M_Q_Y_A_deprecated(self, freq, freq_depr):
result = pd.date_range("1/1/2000", periods=4, freq=freq_depr)
tm.assert_index_equal(result, expected)

@pytest.mark.parametrize("freq_depr", ["2H", "2CBH", "2MIN", "2S", "2mS", "2Us"])
def test_date_range_uppercase_frequency_deprecated(self, freq_depr):
# GH#9586, GH#54939
depr_msg = f"'{freq_depr[1:]}' is deprecated and will be removed in a "
f"future version. Please use '{freq_depr.lower()[1:]}' instead."

expected = pd.date_range("1/1/2000", periods=4, freq=freq_depr.lower())
with tm.assert_produces_warning(FutureWarning, match=depr_msg):
result = pd.date_range("1/1/2000", periods=4, freq=freq_depr)
tm.assert_index_equal(result, expected)

@pytest.mark.parametrize(
"freq_depr",
[
"2ye-mar",
"2ys",
"2qe",
"2qs-feb",
"2bqs",
"2sms",
"2bms",
"2cbme",
"2me",
"2w",
],
)
def test_date_range_lowercase_frequency_deprecated(self, freq_depr):
# GH#9586, GH#54939
depr_msg = f"'{freq_depr[1:]}' is deprecated and will be removed in a "
f"future version, please use '{freq_depr.upper()[1:]}' instead."

expected = pd.date_range("1/1/2000", periods=4, freq=freq_depr.upper())
with tm.assert_produces_warning(FutureWarning, match=depr_msg):
result = pd.date_range("1/1/2000", periods=4, freq=freq_depr)
tm.assert_index_equal(result, expected)


def test_factorize_sort_without_freq():
dta = DatetimeArray._from_sequence([0, 2, 1], dtype="M8[ns]")
Expand Down
2 changes: 1 addition & 1 deletion pandas/tests/indexes/datetimes/test_partial_slicing.py
Original file line number Diff line number Diff line change
Expand Up @@ -236,7 +236,7 @@ def test_partial_slice_second_precision(self):
rng = date_range(
start=datetime(2005, 1, 1, 0, 0, 59, microsecond=999990),
periods=20,
freq="US",
freq="us",
)
s = Series(np.arange(20), rng)

Expand Down
15 changes: 11 additions & 4 deletions pandas/tests/indexes/period/test_constructors.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,9 +26,12 @@ class TestPeriodIndexDisallowedFreqs:
("2M", "2ME"),
("2Q-MAR", "2QE-MAR"),
("2Y-FEB", "2YE-FEB"),
("2M", "2me"),
("2Q-MAR", "2qe-MAR"),
("2Y-FEB", "2yE-feb"),
],
)
def test_period_index_frequency_ME_error_message(self, freq, freq_depr):
def test_period_index_offsets_frequency_error_message(self, freq, freq_depr):
# GH#52064
msg = f"for Period, please use '{freq[1:]}' instead of '{freq_depr[1:]}'"

Expand All @@ -38,7 +41,7 @@ def test_period_index_frequency_ME_error_message(self, freq, freq_depr):
with pytest.raises(ValueError, match=msg):
period_range(start="2020-01-01", end="2020-01-02", freq=freq_depr)

@pytest.mark.parametrize("freq_depr", ["2SME", "2CBME", "2BYE"])
@pytest.mark.parametrize("freq_depr", ["2SME", "2sme", "2CBME", "2BYE", "2Bye"])
def test_period_index_frequency_invalid_freq(self, freq_depr):
# GH#9586
msg = f"Invalid frequency: {freq_depr[1:]}"
Expand Down Expand Up @@ -547,7 +550,9 @@ def test_period_range_length(self):
assert i1.freq == end_intv.freq
assert i1[-1] == end_intv

end_intv = Period("2006-12-31", "1w")
msg = "'w' is deprecated and will be removed in a future version."
with tm.assert_produces_warning(FutureWarning, match=msg):
end_intv = Period("2006-12-31", "1w")
i2 = period_range(end=end_intv, periods=10)
assert len(i1) == len(i2)
assert (i1 == i2).all()
Expand Down Expand Up @@ -576,7 +581,9 @@ def test_mixed_freq_raises(self):
with tm.assert_produces_warning(FutureWarning, match=msg):
end_intv = Period("2005-05-01", "B")

vals = [end_intv, Period("2006-12-31", "w")]
msg = "'w' is deprecated and will be removed in a future version."
with tm.assert_produces_warning(FutureWarning, match=msg):
vals = [end_intv, Period("2006-12-31", "w")]
msg = r"Input has different freq=W-SUN from PeriodIndex\(freq=B\)"
depr_msg = r"PeriodDtype\[B\] is deprecated"
with pytest.raises(IncompatibleFrequency, match=msg):
Expand Down
44 changes: 32 additions & 12 deletions pandas/tests/indexes/period/test_period_range.py
Original file line number Diff line number Diff line change
Expand Up @@ -181,7 +181,9 @@ def test_construction_from_period(self):

def test_mismatched_start_end_freq_raises(self):
depr_msg = "Period with BDay freq is deprecated"
end_w = Period("2006-12-31", "1w")
msg = "'w' is deprecated and will be removed in a future version."
with tm.assert_produces_warning(FutureWarning, match=msg):
end_w = Period("2006-12-31", "1w")

with tm.assert_produces_warning(FutureWarning, match=depr_msg):
start_b = Period("02-Apr-2005", "B")
Expand All @@ -203,19 +205,37 @@ def test_constructor_U(self):
with pytest.raises(ValueError, match="Invalid frequency: X"):
period_range("2007-1-1", periods=500, freq="X")

def test_H_deprecated_from_time_series(self):
@pytest.mark.parametrize(
"freq,freq_depr",
[
("2Y", "2A"),
("2Y", "2a"),
("2Y-AUG", "2A-AUG"),
("2Y-AUG", "2A-aug"),
],
)
def test_a_deprecated_from_time_series(self, freq, freq_depr):
# GH#52536
msg = "'H' is deprecated and will be removed in a future version."
msg = f"'{freq_depr[1:]}' is deprecated and will be removed in a "
f"future version. Please use '{freq[1:]}' instead."

with tm.assert_produces_warning(FutureWarning, match=msg):
period_range(freq=freq_depr, start="1/1/2001", end="12/1/2009")

@pytest.mark.parametrize("freq_depr", ["2H", "2MIN", "2S", "2US", "2NS"])
def test_uppercase_freq_deprecated_from_time_series(self, freq_depr):
# GH#52536, GH#54939
msg = f"'{freq_depr[1:]}' is deprecated and will be removed in a "
f"future version. Please use '{freq_depr.lower()[1:]}' instead."

with tm.assert_produces_warning(FutureWarning, match=msg):
period_range(freq="2H", start="1/1/2001", end="12/1/2009")
period_range("2020-01-01 00:00:00 00:00", periods=2, freq=freq_depr)

@pytest.mark.parametrize("freq_depr", ["2m", "2q-sep", "2y", "2w"])
def test_lowercase_freq_deprecated_from_time_series(self, freq_depr):
# GH#52536, GH#54939
msg = f"'{freq_depr[1:]}' is deprecated and will be removed in a "
f"future version. Please use '{freq_depr.upper()[1:]}' instead."

@pytest.mark.parametrize("freq_depr", ["2A", "A-DEC", "200A-AUG"])
def test_a_deprecated_from_time_series(self, freq_depr):
# GH#52536
freq_msg = freq_depr[freq_depr.index("A") :]
msg = (
f"'{freq_msg}' is deprecated and will be removed in a future version, "
f"please use 'Y{freq_msg[1:]}' instead."
)
with tm.assert_produces_warning(FutureWarning, match=msg):
period_range(freq=freq_depr, start="1/1/2001", end="12/1/2009")
29 changes: 29 additions & 0 deletions pandas/tests/resample/test_period_index.py
Original file line number Diff line number Diff line change
Expand Up @@ -998,6 +998,32 @@ def test_resample_t_l_deprecated(self):
result = ser.resample("T").mean()
tm.assert_series_equal(result, expected)

@pytest.mark.parametrize(
"freq, freq_depr, freq_res, freq_depr_res, data",
[
("2Q", "2q", "2Y", "2y", [0.5]),
("2M", "2m", "2Q", "2q", [1.0, 3.0]),
],
)
def test_resample_lowercase_frequency_deprecated(
self, freq, freq_depr, freq_res, freq_depr_res, data
):
depr_msg = f"'{freq_depr[1:]}' is deprecated and will be removed in a "
f"future version. Please use '{freq[1:]}' instead."
depr_msg_res = f"'{freq_depr_res[1:]}' is deprecated and will be removed in a "
f"future version. Please use '{freq_res[1:]}' instead."

with tm.assert_produces_warning(FutureWarning, match=depr_msg):
rng_l = period_range("2020-01-01", "2020-08-01", freq=freq_depr)
ser = Series(np.arange(len(rng_l)), index=rng_l)

rng = period_range("2020-01-01", "2020-08-01", freq=freq_res)
expected = Series(data=data, index=rng)

with tm.assert_produces_warning(FutureWarning, match=depr_msg_res):
result = ser.resample(freq_depr_res).mean()
tm.assert_series_equal(result, expected)

@pytest.mark.parametrize(
"offset",
[
Expand All @@ -1023,6 +1049,9 @@ def test_asfreq_invalid_period_freq(self, offset, frame_or_series):
("2Q-FEB", "2QE-FEB"),
("2Y", "2YE"),
("2Y-MAR", "2YE-MAR"),
("2M", "2me"),
("2Q", "2qe"),
("2Y-MAR", "2ye-mar"),
],
)
def test_resample_frequency_ME_QE_YE_error_message(frame_or_series, freq, freq_depr):
Expand Down
4 changes: 3 additions & 1 deletion pandas/tests/scalar/period/test_period.py
Original file line number Diff line number Diff line change
Expand Up @@ -106,7 +106,9 @@ def test_construction(self):
assert i1 == i3

i1 = Period("1982", freq="min")
i2 = Period("1982", freq="MIN")
msg = "'MIN' is deprecated and will be removed in a future version."
with tm.assert_produces_warning(FutureWarning, match=msg):
i2 = Period("1982", freq="MIN")
assert i1 == i2

i1 = Period(year=2005, month=3, day=1, freq="D")
Expand Down
Loading