@@ -86,19 +86,38 @@ cdef bint _is_normalized(datetime dt):
86
86
return True
87
87
88
88
89
+ def apply_wrapper_core (func , self , other ) -> ndarray:
90
+ result = func(self , other)
91
+ result = np.asarray(result)
92
+
93
+ if self.normalize:
94
+ # TODO: Avoid circular/runtime import
95
+ from .vectorized import normalize_i8_timestamps
96
+ result = normalize_i8_timestamps(result.view(" i8" ), None )
97
+
98
+ return result
99
+
100
+
89
101
def apply_index_wraps(func ):
90
102
# Note: normally we would use `@functools.wraps(func)`, but this does
91
103
# not play nicely with cython class methods
92
- def wrapper (self , other ) -> np.ndarray :
104
+ def wrapper (self , other ):
93
105
# other is a DatetimeArray
106
+ result = apply_wrapper_core(func, self , other)
107
+ result = type (other)(result)
108
+ warnings.warn(" 'Offset.apply_index(other)' is deprecated. "
109
+ " Use 'offset + other' instead." , FutureWarning )
110
+ return result
94
111
95
- result = func(self , other)
96
- result = np.asarray(result)
112
+ return wrapper
97
113
98
- if self.normalize:
99
- # TODO: Avoid circular/runtime import
100
- from .vectorized import normalize_i8_timestamps
101
- result = normalize_i8_timestamps(result.view(" i8" ), None )
114
+
115
+ def apply_array_wraps (func ):
116
+ # Note: normally we would use `@functools.wraps(func)`, but this does
117
+ # not play nicely with cython class methods
118
+ def wrapper (self , other ) -> np.ndarray:
119
+ # other is a DatetimeArray
120
+ result = apply_wrapper_core(func, self , other)
102
121
return result
103
122
104
123
# do @functools.wraps(func ) manually since it doesn't work on cdef funcs
@@ -515,19 +534,36 @@ cdef class BaseOffset:
515
534
raises NotImplementedError for offsets without a
516
535
vectorized implementation.
517
536
537
+ .. deprecated:: 1.1.0
538
+
539
+ Use ``offset + dtindex`` instead.
540
+
518
541
Parameters
519
542
----------
520
543
index : DatetimeIndex
521
544
522
545
Returns
523
546
-------
524
547
DatetimeIndex
548
+
549
+ Raises
550
+ ------
551
+ NotImplementedError
552
+ When the specific offset subclass does not have a vectorized
553
+ implementation.
525
554
"""
526
555
raise NotImplementedError (
527
556
f" DateOffset subclass {type(self).__name__} "
528
557
" does not have a vectorized implementation"
529
558
)
530
559
560
+ @apply_array_wraps
561
+ def _apply_array (self , dtarr ):
562
+ raise NotImplementedError (
563
+ f" DateOffset subclass {type(self).__name__} "
564
+ " does not have a vectorized implementation"
565
+ )
566
+
531
567
def rollback (self , dt ) -> datetime:
532
568
"""
533
569
Roll provided date backward to next offset only if not on offset.
@@ -992,7 +1028,11 @@ cdef class RelativeDeltaOffset(BaseOffset):
992
1028
-------
993
1029
ndarray[datetime64[ns]]
994
1030
"""
995
- dt64other = np.asarray(dtindex)
1031
+ return self ._apply_array(dtindex)
1032
+
1033
+ @apply_array_wraps
1034
+ def _apply_array (self , dtarr ):
1035
+ dt64other = np.asarray(dtarr)
996
1036
kwds = self .kwds
997
1037
relativedelta_fast = {
998
1038
" years" ,
@@ -1321,7 +1361,11 @@ cdef class BusinessDay(BusinessMixin):
1321
1361
1322
1362
@apply_index_wraps
1323
1363
def apply_index (self , dtindex ):
1324
- i8other = dtindex.view(" i8" )
1364
+ return self ._apply_array(dtindex)
1365
+
1366
+ @apply_array_wraps
1367
+ def _apply_array (self , dtarr ):
1368
+ i8other = dtarr.view(" i8" )
1325
1369
return shift_bdays(i8other, self .n)
1326
1370
1327
1371
def is_on_offset (self , dt: datetime ) -> bool:
@@ -1804,8 +1848,12 @@ cdef class YearOffset(SingleConstructorOffset):
1804
1848
1805
1849
@apply_index_wraps
1806
1850
def apply_index(self , dtindex ):
1851
+ return self ._apply_array(dtindex)
1852
+
1853
+ @apply_array_wraps
1854
+ def _apply_array (self , dtarr ):
1807
1855
shifted = shift_quarters(
1808
- dtindex .view(" i8" ), self .n, self .month, self ._day_opt, modby = 12
1856
+ dtarr .view(" i8" ), self .n, self .month, self ._day_opt, modby = 12
1809
1857
)
1810
1858
return shifted
1811
1859
@@ -1957,8 +2005,12 @@ cdef class QuarterOffset(SingleConstructorOffset):
1957
2005
1958
2006
@apply_index_wraps
1959
2007
def apply_index(self , dtindex ):
2008
+ return self ._apply_array(dtindex)
2009
+
2010
+ @apply_array_wraps
2011
+ def _apply_array (self , dtarr ):
1960
2012
shifted = shift_quarters(
1961
- dtindex .view(" i8" ), self .n, self .startingMonth, self ._day_opt
2013
+ dtarr .view(" i8" ), self .n, self .startingMonth, self ._day_opt
1962
2014
)
1963
2015
return shifted
1964
2016
@@ -2072,7 +2124,11 @@ cdef class MonthOffset(SingleConstructorOffset):
2072
2124
2073
2125
@apply_index_wraps
2074
2126
def apply_index(self , dtindex ):
2075
- shifted = shift_months(dtindex.view(" i8" ), self .n, self ._day_opt)
2127
+ return self ._apply_array(dtindex)
2128
+
2129
+ @apply_array_wraps
2130
+ def _apply_array (self , dtarr ):
2131
+ shifted = shift_months(dtarr.view(" i8" ), self .n, self ._day_opt)
2076
2132
return shifted
2077
2133
2078
2134
cpdef __setstate__(self , state):
@@ -2209,8 +2265,14 @@ cdef class SemiMonthOffset(SingleConstructorOffset):
2209
2265
@ cython.wraparound (False )
2210
2266
@ cython.boundscheck (False )
2211
2267
def apply_index (self , dtindex ):
2268
+ return self ._apply_array(dtindex)
2269
+
2270
+ @apply_array_wraps
2271
+ @ cython.wraparound (False )
2272
+ @ cython.boundscheck (False )
2273
+ def _apply_array (self , dtarr ):
2212
2274
cdef:
2213
- int64_t[:] i8other = dtindex .view(" i8" )
2275
+ int64_t[:] i8other = dtarr .view(" i8" )
2214
2276
Py_ssize_t i, count = len (i8other)
2215
2277
int64_t val
2216
2278
int64_t[:] out = np.empty(count, dtype = " i8" )
@@ -2368,12 +2430,16 @@ cdef class Week(SingleConstructorOffset):
2368
2430
2369
2431
@apply_index_wraps
2370
2432
def apply_index (self , dtindex ):
2433
+ return self ._apply_array(dtindex)
2434
+
2435
+ @apply_array_wraps
2436
+ def _apply_array (self , dtarr ):
2371
2437
if self .weekday is None :
2372
2438
td = timedelta(days = 7 * self .n)
2373
2439
td64 = np.timedelta64(td, " ns" )
2374
- return dtindex + td64
2440
+ return dtarr + td64
2375
2441
else :
2376
- i8other = dtindex .view(" i8" )
2442
+ i8other = dtarr .view(" i8" )
2377
2443
return self ._end_apply_index(i8other)
2378
2444
2379
2445
@ cython.wraparound (False )
@@ -3146,6 +3212,9 @@ cdef class CustomBusinessDay(BusinessDay):
3146
3212
def apply_index (self , dtindex ):
3147
3213
raise NotImplementedError
3148
3214
3215
+ def _apply_array (self , dtarr ):
3216
+ raise NotImplementedError
3217
+
3149
3218
def is_on_offset (self , dt: datetime ) -> bool:
3150
3219
if self.normalize and not _is_normalized(dt ):
3151
3220
return False
0 commit comments