Skip to content

Commit 376871d

Browse files
committed
Merge remote-tracking branch 'upstream/main' into holiday-daysofweek-value-error
2 parents 05dade3 + 8b3bd48 commit 376871d

File tree

10 files changed

+581
-381
lines changed

10 files changed

+581
-381
lines changed

doc/source/whatsnew/v3.0.0.rst

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,7 @@ Other enhancements
6464
- :meth:`Series.nlargest` uses a 'stable' sort internally and will preserve original ordering.
6565
- :class:`ArrowDtype` now supports ``pyarrow.JsonType`` (:issue:`60958`)
6666
- :class:`DataFrameGroupBy` and :class:`SeriesGroupBy` methods ``sum``, ``mean``, ``median``, ``prod``, ``min``, ``max``, ``std``, ``var`` and ``sem`` now accept ``skipna`` parameter (:issue:`15675`)
67+
- :class:`Easter` has gained a new constructor argument ``method`` which specifies the method used to calculate Easter — for example, Orthodox Easter (:issue:`61665`)
6768
- :class:`Holiday` has gained the constructor argument and field ``exclude_dates`` to exclude specific datetimes from a custom holiday calendar (:issue:`54382`)
6869
- :class:`Holiday` constructor argument ``days_of_week`` will raise a ``ValueError`` when type is something other than ``None`` or ``tuple``
6970
- :class:`Rolling` and :class:`Expanding` now support ``nunique`` (:issue:`26958`)
@@ -717,6 +718,7 @@ Numeric
717718
- Bug in :meth:`DataFrame.cov` raises a ``TypeError`` instead of returning potentially incorrect results or other errors (:issue:`53115`)
718719
- Bug in :meth:`DataFrame.quantile` where the column type was not preserved when ``numeric_only=True`` with a list-like ``q`` produced an empty result (:issue:`59035`)
719720
- Bug in :meth:`Series.dot` returning ``object`` dtype for :class:`ArrowDtype` and nullable-dtype data (:issue:`61375`)
721+
- Bug in :meth:`Series.std` and :meth:`Series.var` when using complex-valued data (:issue:`61645`)
720722
- Bug in ``np.matmul`` with :class:`Index` inputs raising a ``TypeError`` (:issue:`57079`)
721723

722724
Conversion

pandas/_libs/tslibs/offsets.pyi

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -230,7 +230,13 @@ class FY5253Quarter(FY5253Mixin):
230230
variation: Literal["nearest", "last"] = ...,
231231
) -> None: ...
232232

233-
class Easter(SingleConstructorOffset): ...
233+
class Easter(SingleConstructorOffset):
234+
def __init__(
235+
self,
236+
n: int = ...,
237+
normalize: bool = ...,
238+
method: int = ...,
239+
) -> None: ...
234240

235241
class _CustomBusinessMonth(BusinessMixin):
236242
def __init__(

pandas/_libs/tslibs/offsets.pyx

Lines changed: 26 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4520,6 +4520,12 @@ cdef class Easter(SingleConstructorOffset):
45204520
The number of years represented.
45214521
normalize : bool, default False
45224522
Normalize start/end dates to midnight before generating date range.
4523+
method : int, default 3
4524+
The method used to calculate the date of Easter. Valid options are:
4525+
- 1 (EASTER_JULIAN): Original calculation in Julian calendar
4526+
- 2 (EASTER_ORTHODOX): Original method, date converted to Gregorian calendar
4527+
- 3 (EASTER_WESTERN): Revised method, in Gregorian calendar
4528+
These constants are defined in the `dateutil.easter` module.
45234529
45244530
See Also
45254531
--------
@@ -4532,15 +4538,32 @@ cdef class Easter(SingleConstructorOffset):
45324538
Timestamp('2022-04-17 00:00:00')
45334539
"""
45344540

4541+
_attributes = tuple(["n", "normalize", "method"])
4542+
4543+
cdef readonly:
4544+
int method
4545+
4546+
from dateutil.easter import EASTER_WESTERN
4547+
4548+
def __init__(self, n=1, normalize=False, method=EASTER_WESTERN):
4549+
BaseOffset.__init__(self, n, normalize)
4550+
4551+
self.method = method
4552+
4553+
if method < 1 or method > 3:
4554+
raise ValueError(f"Method must be 1<=method<=3, got {method}")
4555+
45354556
cpdef __setstate__(self, state):
4557+
from dateutil.easter import EASTER_WESTERN
45364558
self.n = state.pop("n")
45374559
self.normalize = state.pop("normalize")
4560+
self.method = state.pop("method", EASTER_WESTERN)
45384561

45394562
@apply_wraps
45404563
def _apply(self, other: datetime) -> datetime:
45414564
from dateutil.easter import easter
45424565

4543-
current_easter = easter(other.year)
4566+
current_easter = easter(other.year, method=self.method)
45444567
current_easter = datetime(
45454568
current_easter.year, current_easter.month, current_easter.day
45464569
)
@@ -4555,7 +4578,7 @@ cdef class Easter(SingleConstructorOffset):
45554578

45564579
# NOTE: easter returns a datetime.date so we have to convert to type of
45574580
# other
4558-
new = easter(other.year + n)
4581+
new = easter(other.year + n, method=self.method)
45594582
new = datetime(
45604583
new.year,
45614584
new.month,
@@ -4573,7 +4596,7 @@ cdef class Easter(SingleConstructorOffset):
45734596

45744597
from dateutil.easter import easter
45754598

4576-
return date(dt.year, dt.month, dt.day) == easter(dt.year)
4599+
return date(dt.year, dt.month, dt.day) == easter(dt.year, method=self.method)
45774600

45784601

45794602
# ----------------------------------------------------------------------

pandas/core/nanops.py

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1014,7 +1014,11 @@ def nanvar(
10141014
avg = _ensure_numeric(values.sum(axis=axis, dtype=np.float64)) / count
10151015
if axis is not None:
10161016
avg = np.expand_dims(avg, axis)
1017-
sqr = _ensure_numeric((avg - values) ** 2)
1017+
if values.dtype.kind == "c":
1018+
# Need to use absolute value for complex numbers.
1019+
sqr = _ensure_numeric(abs(avg - values) ** 2)
1020+
else:
1021+
sqr = _ensure_numeric((avg - values) ** 2)
10181022
if mask is not None:
10191023
np.putmask(sqr, mask, 0)
10201024
result = sqr.sum(axis=axis, dtype=np.float64) / d

pandas/tests/reductions/test_reductions.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -780,6 +780,12 @@ def test_var_masked_array(self, ddof, exp):
780780
assert result == result_numpy_dtype
781781
assert result == exp
782782

783+
def test_var_complex_array(self):
784+
# GH#61645
785+
ser = Series([-1j, 0j, 1j], dtype=complex)
786+
assert ser.var(ddof=1) == 1.0
787+
assert ser.std(ddof=1) == 1.0
788+
783789
@pytest.mark.parametrize("dtype", ("m8[ns]", "M8[ns]", "M8[ns, UTC]"))
784790
def test_empty_timeseries_reductions_return_nat(self, dtype, skipna):
785791
# covers GH#11245

pandas/tests/tseries/offsets/test_easter.py

Lines changed: 116 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,10 @@
77

88
from datetime import datetime
99

10+
from dateutil.easter import (
11+
EASTER_ORTHODOX,
12+
EASTER_WESTERN,
13+
)
1014
import pytest
1115

1216
from pandas.tests.tseries.offsets.common import assert_offset_equal
@@ -32,3 +36,115 @@ class TestEaster:
3236
)
3337
def test_offset(self, offset, date, expected):
3438
assert_offset_equal(offset, date, expected)
39+
40+
@pytest.mark.parametrize(
41+
"offset,date,expected",
42+
[
43+
(Easter(method=EASTER_WESTERN), datetime(2010, 1, 1), datetime(2010, 4, 4)),
44+
(
45+
Easter(method=EASTER_WESTERN),
46+
datetime(2010, 4, 5),
47+
datetime(2011, 4, 24),
48+
),
49+
(
50+
Easter(2, method=EASTER_WESTERN),
51+
datetime(2010, 1, 1),
52+
datetime(2011, 4, 24),
53+
),
54+
(
55+
Easter(method=EASTER_WESTERN),
56+
datetime(2010, 4, 4),
57+
datetime(2011, 4, 24),
58+
),
59+
(
60+
Easter(2, method=EASTER_WESTERN),
61+
datetime(2010, 4, 4),
62+
datetime(2012, 4, 8),
63+
),
64+
(
65+
-Easter(method=EASTER_WESTERN),
66+
datetime(2011, 1, 1),
67+
datetime(2010, 4, 4),
68+
),
69+
(
70+
-Easter(method=EASTER_WESTERN),
71+
datetime(2010, 4, 5),
72+
datetime(2010, 4, 4),
73+
),
74+
(
75+
-Easter(2, method=EASTER_WESTERN),
76+
datetime(2011, 1, 1),
77+
datetime(2009, 4, 12),
78+
),
79+
(
80+
-Easter(method=EASTER_WESTERN),
81+
datetime(2010, 4, 4),
82+
datetime(2009, 4, 12),
83+
),
84+
(
85+
-Easter(2, method=EASTER_WESTERN),
86+
datetime(2010, 4, 4),
87+
datetime(2008, 3, 23),
88+
),
89+
],
90+
)
91+
def test_western_easter_offset(self, offset, date, expected):
92+
assert_offset_equal(offset, date, expected)
93+
94+
@pytest.mark.parametrize(
95+
"offset,date,expected",
96+
[
97+
(
98+
Easter(method=EASTER_ORTHODOX),
99+
datetime(2010, 1, 1),
100+
datetime(2010, 4, 4),
101+
),
102+
(
103+
Easter(method=EASTER_ORTHODOX),
104+
datetime(2010, 4, 5),
105+
datetime(2011, 4, 24),
106+
),
107+
(
108+
Easter(2, method=EASTER_ORTHODOX),
109+
datetime(2010, 1, 1),
110+
datetime(2011, 4, 24),
111+
),
112+
(
113+
Easter(method=EASTER_ORTHODOX),
114+
datetime(2010, 4, 4),
115+
datetime(2011, 4, 24),
116+
),
117+
(
118+
Easter(2, method=EASTER_ORTHODOX),
119+
datetime(2010, 4, 4),
120+
datetime(2012, 4, 15),
121+
),
122+
(
123+
-Easter(method=EASTER_ORTHODOX),
124+
datetime(2011, 1, 1),
125+
datetime(2010, 4, 4),
126+
),
127+
(
128+
-Easter(method=EASTER_ORTHODOX),
129+
datetime(2010, 4, 5),
130+
datetime(2010, 4, 4),
131+
),
132+
(
133+
-Easter(2, method=EASTER_ORTHODOX),
134+
datetime(2011, 1, 1),
135+
datetime(2009, 4, 19),
136+
),
137+
(
138+
-Easter(method=EASTER_ORTHODOX),
139+
datetime(2010, 4, 4),
140+
datetime(2009, 4, 19),
141+
),
142+
(
143+
-Easter(2, method=EASTER_ORTHODOX),
144+
datetime(2010, 4, 4),
145+
datetime(2008, 4, 27),
146+
),
147+
],
148+
)
149+
def test_orthodox_easter_offset(self, offset, date, expected):
150+
assert_offset_equal(offset, date, expected)

pandas/tests/tseries/offsets/test_offsets.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -239,7 +239,7 @@ def test_offset_freqstr(self, offset_types):
239239
offset = _create_offset(offset_types)
240240

241241
freqstr = offset.freqstr
242-
if freqstr not in ("<Easter>", "<DateOffset: days=1>", "LWOM-SAT"):
242+
if freqstr not in ("<Easter: method=3>", "<DateOffset: days=1>", "LWOM-SAT"):
243243
code = _get_offset(freqstr)
244244
assert offset.rule_code == code
245245

web/pandas/community/benchmarks.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ The [asv-runner](https://github.com/pandas-dev/asv-runner/) repository automatic
3030
for every (or almost every) commit to the `main` branch. It is run on GitHub actions.
3131
See the linked repository for more details. The results are available at:
3232

33-
https://pandas-dev.github.io/asv-runner/
33+
[https://pandas-dev.github.io/asv-runner/](https://pandas-dev.github.io/asv-runner/)
3434

3535
## Community benchmarks
3636

0 commit comments

Comments
 (0)