6
6
from functools import partial
7
7
import inspect
8
8
from textwrap import dedent
9
- from typing import Callable , Dict , List , Optional , Set , Tuple , Type , Union
9
+ from typing import (
10
+ TYPE_CHECKING ,
11
+ Callable ,
12
+ Dict ,
13
+ List ,
14
+ Optional ,
15
+ Set ,
16
+ Tuple ,
17
+ Type ,
18
+ Union ,
19
+ )
10
20
11
21
import numpy as np
12
22
13
23
from pandas ._libs .tslibs import BaseOffset , to_offset
14
24
import pandas ._libs .window .aggregations as window_aggregations
15
- from pandas ._typing import ArrayLike , Axis , FrameOrSeries , Label
25
+ from pandas ._typing import ArrayLike , Axis , FrameOrSeriesUnion , Label
16
26
from pandas .compat ._optional import import_optional_dependency
17
27
from pandas .compat .numpy import function as nv
18
28
from pandas .util ._decorators import Appender , Substitution , cache_readonly , doc
55
65
)
56
66
from pandas .core .window .numba_ import generate_numba_apply_func
57
67
68
+ if TYPE_CHECKING :
69
+ from pandas import Series
70
+
58
71
59
72
def calculate_center_offset (window ) -> int :
60
73
"""
@@ -145,7 +158,7 @@ class _Window(PandasObject, ShallowMixin, SelectionMixin):
145
158
146
159
def __init__ (
147
160
self ,
148
- obj : FrameOrSeries ,
161
+ obj : FrameOrSeriesUnion ,
149
162
window = None ,
150
163
min_periods : Optional [int ] = None ,
151
164
center : bool = False ,
@@ -219,7 +232,7 @@ def _validate_get_window_bounds_signature(window: BaseIndexer) -> None:
219
232
f"get_window_bounds"
220
233
)
221
234
222
- def _create_blocks (self , obj : FrameOrSeries ):
235
+ def _create_blocks (self , obj : FrameOrSeriesUnion ):
223
236
"""
224
237
Split data into blocks & return conformed data.
225
238
"""
@@ -381,7 +394,7 @@ def _wrap_result(self, result, block=None, obj=None):
381
394
return type (obj )(result , index = index , columns = block .columns )
382
395
return result
383
396
384
- def _wrap_results (self , results , obj , skipped : List [int ]) -> FrameOrSeries :
397
+ def _wrap_results (self , results , obj , skipped : List [int ]) -> FrameOrSeriesUnion :
385
398
"""
386
399
Wrap the results.
387
400
@@ -394,22 +407,23 @@ def _wrap_results(self, results, obj, skipped: List[int]) -> FrameOrSeries:
394
407
"""
395
408
from pandas import Series , concat
396
409
410
+ if obj .ndim == 1 :
411
+ if not results :
412
+ raise DataError ("No numeric types to aggregate" )
413
+ assert len (results ) == 1
414
+ return Series (results [0 ], index = obj .index , name = obj .name )
415
+
397
416
exclude : List [Label ] = []
398
- if obj .ndim == 2 :
399
- orig_blocks = list (obj ._to_dict_of_blocks (copy = False ).values ())
400
- for i in skipped :
401
- exclude .extend (orig_blocks [i ].columns )
402
- else :
403
- orig_blocks = [obj ]
417
+ orig_blocks = list (obj ._to_dict_of_blocks (copy = False ).values ())
418
+ for i in skipped :
419
+ exclude .extend (orig_blocks [i ].columns )
404
420
405
421
kept_blocks = [blk for i , blk in enumerate (orig_blocks ) if i not in skipped ]
406
422
407
423
final = []
408
424
for result , block in zip (results , kept_blocks ):
409
425
410
- result = self ._wrap_result (result , block = block , obj = obj )
411
- if result .ndim == 1 :
412
- return result
426
+ result = type (obj )(result , index = obj .index , columns = block .columns )
413
427
final .append (result )
414
428
415
429
exclude = exclude or []
@@ -488,13 +502,31 @@ def _get_window_indexer(self, window: int) -> BaseIndexer:
488
502
return VariableWindowIndexer (index_array = self ._on .asi8 , window_size = window )
489
503
return FixedWindowIndexer (window_size = window )
490
504
505
+ def _apply_series (self , homogeneous_func : Callable [..., ArrayLike ]) -> "Series" :
506
+ """
507
+ Series version of _apply_blockwise
508
+ """
509
+ _ , obj = self ._create_blocks (self ._selected_obj )
510
+ values = obj .values
511
+
512
+ try :
513
+ values = self ._prep_values (obj .values )
514
+ except (TypeError , NotImplementedError ) as err :
515
+ raise DataError ("No numeric types to aggregate" ) from err
516
+
517
+ result = homogeneous_func (values )
518
+ return obj ._constructor (result , index = obj .index , name = obj .name )
519
+
491
520
def _apply_blockwise (
492
521
self , homogeneous_func : Callable [..., ArrayLike ]
493
- ) -> FrameOrSeries :
522
+ ) -> FrameOrSeriesUnion :
494
523
"""
495
524
Apply the given function to the DataFrame broken down into homogeneous
496
525
sub-frames.
497
526
"""
527
+ if self ._selected_obj .ndim == 1 :
528
+ return self ._apply_series (homogeneous_func )
529
+
498
530
# This isn't quite blockwise, since `blocks` is actually a collection
499
531
# of homogenenous DataFrames.
500
532
blocks , obj = self ._create_blocks (self ._selected_obj )
@@ -505,12 +537,9 @@ def _apply_blockwise(
505
537
try :
506
538
values = self ._prep_values (b .values )
507
539
508
- except (TypeError , NotImplementedError ) as err :
509
- if isinstance (obj , ABCDataFrame ):
510
- skipped .append (i )
511
- continue
512
- else :
513
- raise DataError ("No numeric types to aggregate" ) from err
540
+ except (TypeError , NotImplementedError ):
541
+ skipped .append (i )
542
+ continue
514
543
515
544
result = homogeneous_func (values )
516
545
results .append (result )
@@ -2234,7 +2263,7 @@ def _apply(
2234
2263
def _constructor (self ):
2235
2264
return Rolling
2236
2265
2237
- def _create_blocks (self , obj : FrameOrSeries ):
2266
+ def _create_blocks (self , obj : FrameOrSeriesUnion ):
2238
2267
"""
2239
2268
Split data into blocks & return conformed data.
2240
2269
"""
@@ -2275,7 +2304,7 @@ def _get_window_indexer(self, window: int) -> GroupbyRollingIndexer:
2275
2304
if isinstance (self .window , BaseIndexer ):
2276
2305
rolling_indexer = type (self .window )
2277
2306
indexer_kwargs = self .window .__dict__
2278
- assert isinstance (indexer_kwargs , dict )
2307
+ assert isinstance (indexer_kwargs , dict ) # for mypy
2279
2308
# We'll be using the index of each group later
2280
2309
indexer_kwargs .pop ("index_array" , None )
2281
2310
elif self .is_freq_type :
0 commit comments