Skip to content

Commit

Permalink
Backport PR pandas-dev#56910: DEPR: lowercase freqs 'ye', 'qe', etc. …
Browse files Browse the repository at this point in the history
…raise a ValueError
  • Loading branch information
natmokval authored and meeseeksmachine committed Jan 17, 2024
1 parent 55c9fbd commit a49839d
Show file tree
Hide file tree
Showing 8 changed files with 218 additions and 53 deletions.
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 @@ -4845,7 +4845,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 @@ -4854,31 +4854,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 @@ -1006,6 +1006,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 @@ -1031,6 +1057,9 @@ def test_asfreq_invalid_period_freq(self, offset, series_and_frame):
("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(series_and_frame, 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

0 comments on commit a49839d

Please sign in to comment.