Skip to content

Commit 02011f2

Browse files
authored
DEPR: lowercase freqs 'ye', 'qe', etc. raise a ValueError (#56910)
* correct dict _dont_uppercase, def to_offset, fix test * fix tests, add tests
1 parent 0e11d6d commit 02011f2

File tree

8 files changed

+218
-53
lines changed

8 files changed

+218
-53
lines changed

pandas/_libs/tslibs/offsets.pyx

+56-35
Original file line numberDiff line numberDiff line change
@@ -4711,29 +4711,7 @@ _lite_rule_alias = {
47114711
"ns": "ns",
47124712
}
47134713

4714-
_dont_uppercase = {
4715-
"h",
4716-
"bh",
4717-
"cbh",
4718-
"MS",
4719-
"ms",
4720-
"s",
4721-
"me",
4722-
"qe",
4723-
"qe-dec",
4724-
"qe-jan",
4725-
"qe-feb",
4726-
"qe-mar",
4727-
"qe-apr",
4728-
"qe-may",
4729-
"qe-jun",
4730-
"qe-jul",
4731-
"qe-aug",
4732-
"qe-sep",
4733-
"qe-oct",
4734-
"qe-nov",
4735-
"ye",
4736-
}
4714+
_dont_uppercase = _dont_uppercase = {"h", "bh", "cbh", "MS", "ms", "s"}
47374715

47384716

47394717
INVALID_FREQ_ERR_MSG = "Invalid frequency: {0}"
@@ -4752,7 +4730,29 @@ def _get_offset(name: str) -> BaseOffset:
47524730
--------
47534731
_get_offset('EOM') --> BMonthEnd(1)
47544732
"""
4755-
if name.lower() not in _dont_uppercase:
4733+
if (
4734+
name not in _lite_rule_alias
4735+
and (name.upper() in _lite_rule_alias)
4736+
and name != "ms"
4737+
):
4738+
warnings.warn(
4739+
f"\'{name}\' is deprecated and will be removed "
4740+
f"in a future version, please use \'{name.upper()}\' instead.",
4741+
FutureWarning,
4742+
stacklevel=find_stack_level(),
4743+
)
4744+
elif (
4745+
name not in _lite_rule_alias
4746+
and (name.lower() in _lite_rule_alias)
4747+
and name != "MS"
4748+
):
4749+
warnings.warn(
4750+
f"\'{name}\' is deprecated and will be removed "
4751+
f"in a future version, please use \'{name.lower()}\' instead.",
4752+
FutureWarning,
4753+
stacklevel=find_stack_level(),
4754+
)
4755+
if name not in _dont_uppercase:
47564756
name = name.upper()
47574757
name = _lite_rule_alias.get(name, name)
47584758
name = _lite_rule_alias.get(name.lower(), name)
@@ -4860,7 +4860,7 @@ cpdef to_offset(freq, bint is_period=False):
48604860

48614861
tups = zip(split[0::4], split[1::4], split[2::4])
48624862
for n, (sep, stride, name) in enumerate(tups):
4863-
if is_period is False and name.upper() in c_OFFSET_DEPR_FREQSTR:
4863+
if not is_period and name.upper() in c_OFFSET_DEPR_FREQSTR:
48644864
warnings.warn(
48654865
f"\'{name}\' is deprecated and will be removed "
48664866
f"in a future version, please use "
@@ -4869,31 +4869,52 @@ cpdef to_offset(freq, bint is_period=False):
48694869
stacklevel=find_stack_level(),
48704870
)
48714871
name = c_OFFSET_DEPR_FREQSTR[name.upper()]
4872-
if is_period is True and name in c_REVERSE_OFFSET_DEPR_FREQSTR:
4873-
if name.startswith("Y"):
4872+
if (not is_period and
4873+
name != name.upper() and
4874+
name.lower() not in {"s", "ms", "us", "ns"} and
4875+
name.upper().split("-")[0].endswith(("S", "E"))):
4876+
warnings.warn(
4877+
f"\'{name}\' is deprecated and will be removed "
4878+
f"in a future version, please use "
4879+
f"\'{name.upper()}\' instead.",
4880+
FutureWarning,
4881+
stacklevel=find_stack_level(),
4882+
)
4883+
name = name.upper()
4884+
if is_period and name.upper() in c_REVERSE_OFFSET_DEPR_FREQSTR:
4885+
if name.upper().startswith("Y"):
48744886
raise ValueError(
4875-
f"for Period, please use \'Y{name[2:]}\' "
4887+
f"for Period, please use \'Y{name.upper()[2:]}\' "
48764888
f"instead of \'{name}\'"
48774889
)
4878-
if (name.startswith("B") or
4879-
name.startswith("S") or name.startswith("C")):
4890+
if (name.upper().startswith("B") or
4891+
name.upper().startswith("S") or
4892+
name.upper().startswith("C")):
48804893
raise ValueError(INVALID_FREQ_ERR_MSG.format(name))
48814894
else:
48824895
raise ValueError(
48834896
f"for Period, please use "
4884-
f"\'{c_REVERSE_OFFSET_DEPR_FREQSTR.get(name)}\' "
4897+
f"\'{c_REVERSE_OFFSET_DEPR_FREQSTR.get(name.upper())}\' "
48854898
f"instead of \'{name}\'"
48864899
)
4887-
elif is_period is True and name in c_OFFSET_DEPR_FREQSTR:
4888-
if name.startswith("A"):
4900+
elif is_period and name.upper() in c_OFFSET_DEPR_FREQSTR:
4901+
if name.upper().startswith("A"):
48894902
warnings.warn(
48904903
f"\'{name}\' is deprecated and will be removed in a future "
4891-
f"version, please use \'{c_DEPR_ABBREVS.get(name)}\' "
4904+
f"version, please use "
4905+
f"\'{c_DEPR_ABBREVS.get(name.upper())}\' instead.",
4906+
FutureWarning,
4907+
stacklevel=find_stack_level(),
4908+
)
4909+
if name.upper() != name:
4910+
warnings.warn(
4911+
f"\'{name}\' is deprecated and will be removed in "
4912+
f"a future version, please use \'{name.upper()}\' "
48924913
f"instead.",
48934914
FutureWarning,
48944915
stacklevel=find_stack_level(),
48954916
)
4896-
name = c_OFFSET_DEPR_FREQSTR.get(name)
4917+
name = c_OFFSET_DEPR_FREQSTR.get(name.upper())
48974918

48984919
if sep != "" and not sep.isspace():
48994920
raise ValueError("separator must be spaces")

pandas/tests/arrays/test_datetimes.py

+42
Original file line numberDiff line numberDiff line change
@@ -766,12 +766,18 @@ def test_iter_zoneinfo_fold(self, tz):
766766
"freq, freq_depr",
767767
[
768768
("2ME", "2M"),
769+
("2SME", "2SM"),
770+
("2SME", "2sm"),
769771
("2QE", "2Q"),
770772
("2QE-SEP", "2Q-SEP"),
771773
("1YE", "1Y"),
772774
("2YE-MAR", "2Y-MAR"),
773775
("1YE", "1A"),
774776
("2YE-MAR", "2A-MAR"),
777+
("2ME", "2m"),
778+
("2QE-SEP", "2q-sep"),
779+
("2YE-MAR", "2a-mar"),
780+
("2YE", "2y"),
775781
],
776782
)
777783
def test_date_range_frequency_M_Q_Y_A_deprecated(self, freq, freq_depr):
@@ -784,6 +790,42 @@ def test_date_range_frequency_M_Q_Y_A_deprecated(self, freq, freq_depr):
784790
result = pd.date_range("1/1/2000", periods=4, freq=freq_depr)
785791
tm.assert_index_equal(result, expected)
786792

793+
@pytest.mark.parametrize("freq_depr", ["2H", "2CBH", "2MIN", "2S", "2mS", "2Us"])
794+
def test_date_range_uppercase_frequency_deprecated(self, freq_depr):
795+
# GH#9586, GH#54939
796+
depr_msg = f"'{freq_depr[1:]}' is deprecated and will be removed in a "
797+
f"future version. Please use '{freq_depr.lower()[1:]}' instead."
798+
799+
expected = pd.date_range("1/1/2000", periods=4, freq=freq_depr.lower())
800+
with tm.assert_produces_warning(FutureWarning, match=depr_msg):
801+
result = pd.date_range("1/1/2000", periods=4, freq=freq_depr)
802+
tm.assert_index_equal(result, expected)
803+
804+
@pytest.mark.parametrize(
805+
"freq_depr",
806+
[
807+
"2ye-mar",
808+
"2ys",
809+
"2qe",
810+
"2qs-feb",
811+
"2bqs",
812+
"2sms",
813+
"2bms",
814+
"2cbme",
815+
"2me",
816+
"2w",
817+
],
818+
)
819+
def test_date_range_lowercase_frequency_deprecated(self, freq_depr):
820+
# GH#9586, GH#54939
821+
depr_msg = f"'{freq_depr[1:]}' is deprecated and will be removed in a "
822+
f"future version, please use '{freq_depr.upper()[1:]}' instead."
823+
824+
expected = pd.date_range("1/1/2000", periods=4, freq=freq_depr.upper())
825+
with tm.assert_produces_warning(FutureWarning, match=depr_msg):
826+
result = pd.date_range("1/1/2000", periods=4, freq=freq_depr)
827+
tm.assert_index_equal(result, expected)
828+
787829

788830
def test_factorize_sort_without_freq():
789831
dta = DatetimeArray._from_sequence([0, 2, 1], dtype="M8[ns]")

pandas/tests/indexes/datetimes/test_partial_slicing.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -236,7 +236,7 @@ def test_partial_slice_second_precision(self):
236236
rng = date_range(
237237
start=datetime(2005, 1, 1, 0, 0, 59, microsecond=999990),
238238
periods=20,
239-
freq="US",
239+
freq="us",
240240
)
241241
s = Series(np.arange(20), rng)
242242

pandas/tests/indexes/period/test_constructors.py

+11-4
Original file line numberDiff line numberDiff line change
@@ -26,9 +26,12 @@ class TestPeriodIndexDisallowedFreqs:
2626
("2M", "2ME"),
2727
("2Q-MAR", "2QE-MAR"),
2828
("2Y-FEB", "2YE-FEB"),
29+
("2M", "2me"),
30+
("2Q-MAR", "2qe-MAR"),
31+
("2Y-FEB", "2yE-feb"),
2932
],
3033
)
31-
def test_period_index_frequency_ME_error_message(self, freq, freq_depr):
34+
def test_period_index_offsets_frequency_error_message(self, freq, freq_depr):
3235
# GH#52064
3336
msg = f"for Period, please use '{freq[1:]}' instead of '{freq_depr[1:]}'"
3437

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

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

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

579-
vals = [end_intv, Period("2006-12-31", "w")]
584+
msg = "'w' is deprecated and will be removed in a future version."
585+
with tm.assert_produces_warning(FutureWarning, match=msg):
586+
vals = [end_intv, Period("2006-12-31", "w")]
580587
msg = r"Input has different freq=W-SUN from PeriodIndex\(freq=B\)"
581588
depr_msg = r"PeriodDtype\[B\] is deprecated"
582589
with pytest.raises(IncompatibleFrequency, match=msg):

pandas/tests/indexes/period/test_period_range.py

+32-12
Original file line numberDiff line numberDiff line change
@@ -181,7 +181,9 @@ def test_construction_from_period(self):
181181

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

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

206-
def test_H_deprecated_from_time_series(self):
208+
@pytest.mark.parametrize(
209+
"freq,freq_depr",
210+
[
211+
("2Y", "2A"),
212+
("2Y", "2a"),
213+
("2Y-AUG", "2A-AUG"),
214+
("2Y-AUG", "2A-aug"),
215+
],
216+
)
217+
def test_a_deprecated_from_time_series(self, freq, freq_depr):
207218
# GH#52536
208-
msg = "'H' is deprecated and will be removed in a future version."
219+
msg = f"'{freq_depr[1:]}' is deprecated and will be removed in a "
220+
f"future version. Please use '{freq[1:]}' instead."
221+
222+
with tm.assert_produces_warning(FutureWarning, match=msg):
223+
period_range(freq=freq_depr, start="1/1/2001", end="12/1/2009")
224+
225+
@pytest.mark.parametrize("freq_depr", ["2H", "2MIN", "2S", "2US", "2NS"])
226+
def test_uppercase_freq_deprecated_from_time_series(self, freq_depr):
227+
# GH#52536, GH#54939
228+
msg = f"'{freq_depr[1:]}' is deprecated and will be removed in a "
229+
f"future version. Please use '{freq_depr.lower()[1:]}' instead."
230+
209231
with tm.assert_produces_warning(FutureWarning, match=msg):
210-
period_range(freq="2H", start="1/1/2001", end="12/1/2009")
232+
period_range("2020-01-01 00:00:00 00:00", periods=2, freq=freq_depr)
233+
234+
@pytest.mark.parametrize("freq_depr", ["2m", "2q-sep", "2y", "2w"])
235+
def test_lowercase_freq_deprecated_from_time_series(self, freq_depr):
236+
# GH#52536, GH#54939
237+
msg = f"'{freq_depr[1:]}' is deprecated and will be removed in a "
238+
f"future version. Please use '{freq_depr.upper()[1:]}' instead."
211239

212-
@pytest.mark.parametrize("freq_depr", ["2A", "A-DEC", "200A-AUG"])
213-
def test_a_deprecated_from_time_series(self, freq_depr):
214-
# GH#52536
215-
freq_msg = freq_depr[freq_depr.index("A") :]
216-
msg = (
217-
f"'{freq_msg}' is deprecated and will be removed in a future version, "
218-
f"please use 'Y{freq_msg[1:]}' instead."
219-
)
220240
with tm.assert_produces_warning(FutureWarning, match=msg):
221241
period_range(freq=freq_depr, start="1/1/2001", end="12/1/2009")

pandas/tests/resample/test_period_index.py

+29
Original file line numberDiff line numberDiff line change
@@ -998,6 +998,32 @@ def test_resample_t_l_deprecated(self):
998998
result = ser.resample("T").mean()
999999
tm.assert_series_equal(result, expected)
10001000

1001+
@pytest.mark.parametrize(
1002+
"freq, freq_depr, freq_res, freq_depr_res, data",
1003+
[
1004+
("2Q", "2q", "2Y", "2y", [0.5]),
1005+
("2M", "2m", "2Q", "2q", [1.0, 3.0]),
1006+
],
1007+
)
1008+
def test_resample_lowercase_frequency_deprecated(
1009+
self, freq, freq_depr, freq_res, freq_depr_res, data
1010+
):
1011+
depr_msg = f"'{freq_depr[1:]}' is deprecated and will be removed in a "
1012+
f"future version. Please use '{freq[1:]}' instead."
1013+
depr_msg_res = f"'{freq_depr_res[1:]}' is deprecated and will be removed in a "
1014+
f"future version. Please use '{freq_res[1:]}' instead."
1015+
1016+
with tm.assert_produces_warning(FutureWarning, match=depr_msg):
1017+
rng_l = period_range("2020-01-01", "2020-08-01", freq=freq_depr)
1018+
ser = Series(np.arange(len(rng_l)), index=rng_l)
1019+
1020+
rng = period_range("2020-01-01", "2020-08-01", freq=freq_res)
1021+
expected = Series(data=data, index=rng)
1022+
1023+
with tm.assert_produces_warning(FutureWarning, match=depr_msg_res):
1024+
result = ser.resample(freq_depr_res).mean()
1025+
tm.assert_series_equal(result, expected)
1026+
10011027
@pytest.mark.parametrize(
10021028
"offset",
10031029
[
@@ -1023,6 +1049,9 @@ def test_asfreq_invalid_period_freq(self, offset, frame_or_series):
10231049
("2Q-FEB", "2QE-FEB"),
10241050
("2Y", "2YE"),
10251051
("2Y-MAR", "2YE-MAR"),
1052+
("2M", "2me"),
1053+
("2Q", "2qe"),
1054+
("2Y-MAR", "2ye-mar"),
10261055
],
10271056
)
10281057
def test_resample_frequency_ME_QE_YE_error_message(frame_or_series, freq, freq_depr):

pandas/tests/scalar/period/test_period.py

+3-1
Original file line numberDiff line numberDiff line change
@@ -106,7 +106,9 @@ def test_construction(self):
106106
assert i1 == i3
107107

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

112114
i1 = Period(year=2005, month=3, day=1, freq="D")

0 commit comments

Comments
 (0)