implement shift_quarters --> apply_index for quarters and years (#18522) · pandas-dev/pandas@88ab693 (original) (raw)
`@@ -27,7 +27,7 @@
`
27
27
`apply_index_wraps,
`
28
28
`roll_yearday,
`
29
29
`shift_month,
`
30
``
`-
BeginMixin, EndMixin,
`
``
30
`+
EndMixin,
`
31
31
`BaseOffset)
`
32
32
``
33
33
``
`@@ -1028,10 +1028,7 @@ def cbday(self):
`
1028
1028
``
1029
1029
`@cache_readonly
`
1030
1030
`def m_offset(self):
`
1031
``
`-
kwds = self.kwds
`
1032
``
`-
kwds = {key: kwds[key] for key in kwds
`
1033
``
`-
if key not in ['calendar', 'weekmask', 'holidays', 'offset']}
`
1034
``
`-
return MonthEnd(n=1, normalize=self.normalize, **kwds)
`
``
1031
`+
return MonthEnd(n=1, normalize=self.normalize)
`
1035
1032
``
1036
1033
`@apply_wraps
`
1037
1034
`def apply(self, other):
`
`@@ -1106,10 +1103,7 @@ def cbday(self):
`
1106
1103
``
1107
1104
`@cache_readonly
`
1108
1105
`def m_offset(self):
`
1109
``
`-
kwds = self.kwds
`
1110
``
`-
kwds = {key: kwds[key] for key in kwds
`
1111
``
`-
if key not in ['calendar', 'weekmask', 'holidays', 'offset']}
`
1112
``
`-
return MonthBegin(n=1, normalize=self.normalize, **kwds)
`
``
1106
`+
return MonthBegin(n=1, normalize=self.normalize)
`
1113
1107
``
1114
1108
`@apply_wraps
`
1115
1109
`def apply(self, other):
`
`@@ -1254,12 +1248,9 @@ def onOffset(self, dt):
`
1254
1248
``
1255
1249
`def _apply(self, n, other):
`
1256
1250
`# if other.day is not day_of_month move to day_of_month and update n
`
1257
``
`-
if other.day < self.day_of_month:
`
1258
``
`-
other = other.replace(day=self.day_of_month)
`
1259
``
`-
if n > 0:
`
1260
``
`-
n -= 1
`
``
1251
`+
if n > 0 and other.day < self.day_of_month:
`
``
1252
`+
n -= 1
`
1261
1253
`elif other.day > self.day_of_month:
`
1262
``
`-
other = other.replace(day=self.day_of_month)
`
1263
1254
`n += 1
`
1264
1255
``
1265
1256
`months = n // 2
`
`@@ -1309,12 +1300,9 @@ def onOffset(self, dt):
`
1309
1300
`def _apply(self, n, other):
`
1310
1301
`# if other.day is not day_of_month move to day_of_month and update n
`
1311
1302
`if other.day < self.day_of_month:
`
1312
``
`-
other = other.replace(day=self.day_of_month)
`
1313
1303
`n -= 1
`
1314
``
`-
elif other.day > self.day_of_month:
`
1315
``
`-
other = other.replace(day=self.day_of_month)
`
1316
``
`-
if n <= 0:
`
1317
``
`-
n += 1
`
``
1304
`+
elif n <= 0 and other.day > self.day_of_month:
`
``
1305
`+
n += 1
`
1318
1306
``
1319
1307
`months = n // 2 + n % 2
`
1320
1308
`day = 1 if n % 2 else self.day_of_month
`
`@@ -1471,6 +1459,7 @@ def apply(self, other):
`
1471
1459
`def getOffsetOfMonth(self, dt):
`
1472
1460
`w = Week(weekday=self.weekday)
`
1473
1461
`d = datetime(dt.year, dt.month, 1, tzinfo=dt.tzinfo)
`
``
1462
`+
TODO: Is this DST-safe?
`
1474
1463
`d = w.rollforward(d)
`
1475
1464
`return d + timedelta(weeks=self.week)
`
1476
1465
``
`@@ -1550,6 +1539,7 @@ def getOffsetOfMonth(self, dt):
`
1550
1539
`d = datetime(dt.year, dt.month, 1, dt.hour, dt.minute,
`
1551
1540
`dt.second, dt.microsecond, tzinfo=dt.tzinfo)
`
1552
1541
`eom = m.rollforward(d)
`
``
1542
`+
TODO: Is this DST-safe?
`
1553
1543
`w = Week(weekday=self.weekday)
`
1554
1544
`return w.rollback(eom)
`
1555
1545
``
`@@ -1635,6 +1625,12 @@ def onOffset(self, dt):
`
1635
1625
`modMonth = (dt.month - self.startingMonth) % 3
`
1636
1626
`return modMonth == 0 and dt.day == self._get_offset_day(dt)
`
1637
1627
``
``
1628
`+
@apply_index_wraps
`
``
1629
`+
def apply_index(self, dtindex):
`
``
1630
`+
shifted = liboffsets.shift_quarters(dtindex.asi8, self.n,
`
``
1631
`+
self.startingMonth, self._day_opt)
`
``
1632
`+
return dtindex._shallow_copy(shifted)
`
``
1633
+
1638
1634
``
1639
1635
`class BQuarterEnd(QuarterOffset):
`
1640
1636
`"""DateOffset increments between business Quarter dates
`
`@@ -1659,7 +1655,7 @@ class BQuarterBegin(QuarterOffset):
`
1659
1655
`_day_opt = 'business_start'
`
1660
1656
``
1661
1657
``
1662
``
`-
class QuarterEnd(EndMixin, QuarterOffset):
`
``
1658
`+
class QuarterEnd(QuarterOffset):
`
1663
1659
`"""DateOffset increments between business Quarter dates
`
1664
1660
` startingMonth = 1 corresponds to dates like 1/31/2007, 4/30/2007, ...
`
1665
1661
` startingMonth = 2 corresponds to dates like 2/28/2007, 5/31/2007, ...
`
`@@ -1670,25 +1666,14 @@ class QuarterEnd(EndMixin, QuarterOffset):
`
1670
1666
`_prefix = 'Q'
`
1671
1667
`_day_opt = 'end'
`
1672
1668
``
1673
``
`-
@apply_index_wraps
`
1674
``
`-
def apply_index(self, i):
`
1675
``
`-
return self._end_apply_index(i, self.freqstr)
`
1676
``
-
1677
1669
``
1678
``
`-
class QuarterBegin(BeginMixin, QuarterOffset):
`
``
1670
`+
class QuarterBegin(QuarterOffset):
`
1679
1671
`_outputName = 'QuarterBegin'
`
1680
1672
`_default_startingMonth = 3
`
1681
1673
`_from_name_startingMonth = 1
`
1682
1674
`_prefix = 'QS'
`
1683
1675
`_day_opt = 'start'
`
1684
1676
``
1685
``
`-
@apply_index_wraps
`
1686
``
`-
def apply_index(self, i):
`
1687
``
`-
freq_month = 12 if self.startingMonth == 1 else self.startingMonth - 1
`
1688
``
`-
month = liboffsets._int_to_month[freq_month]
`
1689
``
`-
freqstr = 'Q-{month}'.format(month=month)
`
1690
``
`-
return self._beg_apply_index(i, freqstr)
`
1691
``
-
1692
1677
``
1693
1678
`# ---------------------------------------------------------------------
`
1694
1679
`# Year-Based Offset Classes
`
`@@ -1709,6 +1694,13 @@ def apply(self, other):
`
1709
1694
`months = years * 12 + (self.month - other.month)
`
1710
1695
`return shift_month(other, months, self._day_opt)
`
1711
1696
``
``
1697
`+
@apply_index_wraps
`
``
1698
`+
def apply_index(self, dtindex):
`
``
1699
`+
shifted = liboffsets.shift_quarters(dtindex.asi8, self.n,
`
``
1700
`+
self.month, self._day_opt,
`
``
1701
`+
modby=12)
`
``
1702
`+
return dtindex._shallow_copy(shifted)
`
``
1703
+
1712
1704
`def onOffset(self, dt):
`
1713
1705
`if self.normalize and not _is_normalized(dt):
`
1714
1706
`return False
`
`@@ -1752,31 +1744,19 @@ class BYearBegin(YearOffset):
`
1752
1744
`_day_opt = 'business_start'
`
1753
1745
``
1754
1746
``
1755
``
`-
class YearEnd(EndMixin, YearOffset):
`
``
1747
`+
class YearEnd(YearOffset):
`
1756
1748
`"""DateOffset increments between calendar year ends"""
`
1757
1749
`_default_month = 12
`
1758
1750
`_prefix = 'A'
`
1759
1751
`_day_opt = 'end'
`
1760
1752
``
1761
``
`-
@apply_index_wraps
`
1762
``
`-
def apply_index(self, i):
`
1763
``
`-
convert month anchor to annual period tuple
`
1764
``
`-
return self._end_apply_index(i, self.freqstr)
`
1765
1753
``
1766
``
-
1767
``
`-
class YearBegin(BeginMixin, YearOffset):
`
``
1754
`+
class YearBegin(YearOffset):
`
1768
1755
`"""DateOffset increments between calendar year begin dates"""
`
1769
1756
`_default_month = 1
`
1770
1757
`_prefix = 'AS'
`
1771
1758
`_day_opt = 'start'
`
1772
1759
``
1773
``
`-
@apply_index_wraps
`
1774
``
`-
def apply_index(self, i):
`
1775
``
`-
freq_month = 12 if self.month == 1 else self.month - 1
`
1776
``
`-
month = liboffsets._int_to_month[freq_month]
`
1777
``
`-
freqstr = 'A-{month}'.format(month=month)
`
1778
``
`-
return self._beg_apply_index(i, freqstr)
`
1779
``
-
1780
1760
``
1781
1761
`# ---------------------------------------------------------------------
`
1782
1762
`# Special Offset Classes
`
`@@ -2245,7 +2225,8 @@ def eq(self, other):
`
2245
2225
`if isinstance(other, Tick):
`
2246
2226
`return self.delta == other.delta
`
2247
2227
`else:
`
2248
``
`-
return DateOffset.eq(self, other)
`
``
2228
`+
TODO: Are there cases where this should raise TypeError?
`
``
2229
`+
return False
`
2249
2230
``
2250
2231
`# This is identical to DateOffset.hash, but has to be redefined here
`
2251
2232
`# for Python 3, because we've redefined eq.
`
`@@ -2261,7 +2242,8 @@ def ne(self, other):
`
2261
2242
`if isinstance(other, Tick):
`
2262
2243
`return self.delta != other.delta
`
2263
2244
`else:
`
2264
``
`-
return DateOffset.ne(self, other)
`
``
2245
`+
TODO: Are there cases where this should raise TypeError?
`
``
2246
`+
return True
`
2265
2247
``
2266
2248
`@property
`
2267
2249
`def delta(self):
`