Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.11/site-packages/pandas/core/window/rolling.py: 21%

Shortcuts on this page

r m x   toggle line displays

j k   next/prev highlighted chunk

0   (zero) top of page

1   (one) first highlighted chunk

713 statements  

1""" 

2Provide a generic structure to support window functions, 

3similar to how we have a Groupby object. 

4""" 

5 

6from __future__ import annotations 

7 

8import copy 

9from datetime import timedelta 

10from functools import partial 

11import inspect 

12from typing import ( 

13 TYPE_CHECKING, 

14 Any, 

15 Concatenate, 

16 Literal, 

17 Self, 

18 cast, 

19 final, 

20 overload, 

21) 

22 

23import numpy as np 

24 

25from pandas._libs.tslibs import ( 

26 BaseOffset, 

27 Timedelta, 

28 to_offset, 

29) 

30import pandas._libs.window.aggregations as window_aggregations 

31from pandas.compat._optional import import_optional_dependency 

32from pandas.errors import DataError 

33from pandas.util._decorators import set_module 

34 

35from pandas.core.dtypes.common import ( 

36 ensure_float64, 

37 is_bool, 

38 is_integer, 

39 is_numeric_dtype, 

40 needs_i8_conversion, 

41) 

42from pandas.core.dtypes.dtypes import ArrowDtype 

43from pandas.core.dtypes.generic import ( 

44 ABCDataFrame, 

45 ABCSeries, 

46) 

47from pandas.core.dtypes.missing import notna 

48 

49from pandas.core._numba import executor 

50from pandas.core.algorithms import factorize 

51from pandas.core.apply import ( 

52 ResamplerWindowApply, 

53 reconstruct_func, 

54) 

55from pandas.core.arrays import ExtensionArray 

56from pandas.core.base import SelectionMixin 

57import pandas.core.common as com 

58from pandas.core.indexers.objects import ( 

59 BaseIndexer, 

60 FixedWindowIndexer, 

61 GroupbyIndexer, 

62 VariableWindowIndexer, 

63) 

64from pandas.core.indexes.api import ( 

65 DatetimeIndex, 

66 Index, 

67 MultiIndex, 

68 PeriodIndex, 

69 TimedeltaIndex, 

70) 

71from pandas.core.reshape.concat import concat 

72from pandas.core.util.numba_ import ( 

73 get_jit_arguments, 

74 maybe_use_numba, 

75 prepare_function_arguments, 

76) 

77from pandas.core.window.common import ( 

78 flex_binary_moment, 

79 zsqrt, 

80) 

81from pandas.core.window.numba_ import ( 

82 generate_manual_numpy_nan_agg_with_axis, 

83 generate_numba_apply_func, 

84 generate_numba_table_func, 

85) 

86 

87if TYPE_CHECKING: 

88 from collections.abc import Callable 

89 from collections.abc import ( 

90 Hashable, 

91 Iterator, 

92 Sized, 

93 ) 

94 

95 from pandas._typing import ( 

96 ArrayLike, 

97 NDFrameT, 

98 QuantileInterpolation, 

99 P, 

100 TimeUnit, 

101 T, 

102 WindowingRankType, 

103 npt, 

104 ) 

105 

106 from pandas import ( 

107 DataFrame, 

108 Series, 

109 ) 

110 from pandas.core.generic import NDFrame 

111 from pandas.core.groupby.ops import BaseGrouper 

112 

113from pandas.core.arrays.datetimelike import dtype_to_unit 

114 

115 

116class BaseWindow(SelectionMixin): 

117 """Provides utilities for performing windowing operations.""" 

118 

119 _attributes: list[str] = [] 

120 exclusions: frozenset[Hashable] = frozenset() 

121 _on: Index 

122 

123 def __init__( 

124 self, 

125 obj: NDFrame, 

126 window=None, 

127 min_periods: int | None = None, 

128 center: bool | None = False, 

129 win_type: str | None = None, 

130 on: str | Index | None = None, 

131 closed: str | None = None, 

132 step: int | None = None, 

133 method: str = "single", 

134 *, 

135 selection=None, 

136 ) -> None: 

137 self.obj = obj 

138 self.on = on 

139 self.closed = closed 

140 self.step = step 

141 self.window = window 

142 self.min_periods = min_periods 

143 self.center = center 

144 self.win_type = win_type 

145 self.method = method 

146 self._win_freq_i8: int | None = None 

147 if self.on is None: 

148 self._on = self.obj.index 

149 elif isinstance(self.on, Index): 

150 self._on = self.on 

151 elif isinstance(self.obj, ABCDataFrame) and self.on in self.obj.columns: 

152 self._on = Index(self.obj[self.on]) 

153 else: 

154 raise ValueError( 

155 f"invalid on specified as {self.on}, " 

156 "must be a column (of DataFrame), an Index or None" 

157 ) 

158 

159 self._selection = selection 

160 self._validate() 

161 

162 def _validate(self) -> None: 

163 if self.center is not None and not is_bool(self.center): 

164 raise ValueError("center must be a boolean") 

165 if self.min_periods is not None: 

166 if not is_integer(self.min_periods): 

167 raise ValueError("min_periods must be an integer") 

168 if self.min_periods < 0: 

169 raise ValueError("min_periods must be >= 0") 

170 if is_integer(self.window) and self.min_periods > self.window: 

171 raise ValueError( 

172 f"min_periods {self.min_periods} must be <= window {self.window}" 

173 ) 

174 if self.closed is not None and self.closed not in [ 

175 "right", 

176 "both", 

177 "left", 

178 "neither", 

179 ]: 

180 raise ValueError("closed must be 'right', 'left', 'both' or 'neither'") 

181 if not isinstance(self.obj, (ABCSeries, ABCDataFrame)): 

182 raise TypeError(f"invalid type: {type(self)}") 

183 if isinstance(self.window, BaseIndexer): 

184 # Validate that the passed BaseIndexer subclass has 

185 # a get_window_bounds with the correct signature. 

186 get_window_bounds_signature = inspect.signature( 

187 self.window.get_window_bounds 

188 ).parameters.keys() 

189 expected_signature = inspect.signature( 

190 BaseIndexer().get_window_bounds 

191 ).parameters.keys() 

192 if get_window_bounds_signature != expected_signature: 

193 raise ValueError( 

194 f"{type(self.window).__name__} does not implement " 

195 f"the correct signature for get_window_bounds" 

196 ) 

197 if self.method not in ["table", "single"]: 

198 raise ValueError("method must be 'table' or 'single") 

199 if self.step is not None: 

200 if not is_integer(self.step): 

201 raise ValueError("step must be an integer") 

202 if self.step < 0: 

203 raise ValueError("step must be >= 0") 

204 

205 def _check_window_bounds( 

206 self, start: np.ndarray, end: np.ndarray, num_vals: int 

207 ) -> None: 

208 if len(start) != len(end): 

209 raise ValueError( 

210 f"start ({len(start)}) and end ({len(end)}) bounds must be the " 

211 f"same length" 

212 ) 

213 if len(start) != (num_vals + (self.step or 1) - 1) // (self.step or 1): 

214 raise ValueError( 

215 f"start and end bounds ({len(start)}) must be the same length " 

216 f"as the object ({num_vals}) divided by the step ({self.step}) " 

217 f"if given and rounded up" 

218 ) 

219 

220 def _slice_axis_for_step(self, index: Index, result: Sized | None = None) -> Index: 

221 """ 

222 Slices the index for a given result and the preset step. 

223 """ 

224 return ( 

225 index 

226 if result is None or len(result) == len(index) 

227 else index[:: self.step] 

228 ) 

229 

230 def _validate_numeric_only(self, name: str, numeric_only: bool) -> None: 

231 """ 

232 Validate numeric_only argument, raising if invalid for the input. 

233 

234 Parameters 

235 ---------- 

236 name : str 

237 Name of the operator (kernel). 

238 numeric_only : bool 

239 Value passed by user. 

240 """ 

241 if ( 

242 self._selected_obj.ndim == 1 

243 and numeric_only 

244 and not is_numeric_dtype(self._selected_obj.dtype) 

245 ): 

246 raise NotImplementedError( 

247 f"{type(self).__name__}.{name} does not implement numeric_only" 

248 ) 

249 

250 def _make_numeric_only(self, obj: NDFrameT) -> NDFrameT: 

251 """Subset DataFrame to numeric columns. 

252 

253 Parameters 

254 ---------- 

255 obj : DataFrame 

256 

257 Returns 

258 ------- 

259 obj subset to numeric-only columns. 

260 """ 

261 result = obj.select_dtypes(include=["number"], exclude=["timedelta"]) 

262 return result 

263 

264 def _create_data(self, obj: NDFrameT, numeric_only: bool = False) -> NDFrameT: 

265 """ 

266 Split data into blocks & return conformed data. 

267 """ 

268 # filter out the on from the object 

269 if self.on is not None and not isinstance(self.on, Index) and obj.ndim == 2: 

270 obj = obj.reindex(columns=obj.columns.difference([self.on], sort=False)) 

271 if obj.ndim > 1 and numeric_only: 

272 obj = self._make_numeric_only(obj) 

273 return obj 

274 

275 def _gotitem(self, key, ndim, subset=None): 

276 """ 

277 Sub-classes to define. Return a sliced object. 

278 

279 Parameters 

280 ---------- 

281 key : str / list of selections 

282 ndim : {1, 2} 

283 requested ndim of result 

284 subset : object, default None 

285 subset to act on 

286 """ 

287 # create a new object to prevent aliasing 

288 if subset is None: 

289 subset = self.obj 

290 

291 # we need to make a shallow copy of ourselves 

292 # with the same groupby 

293 kwargs = {attr: getattr(self, attr) for attr in self._attributes} 

294 

295 selection = self._infer_selection(key, subset) 

296 new_win = type(self)(subset, selection=selection, **kwargs) 

297 return new_win 

298 

299 def __getattr__(self, attr: str): 

300 if attr in self._internal_names_set: 

301 return object.__getattribute__(self, attr) 

302 if attr in self.obj: 

303 return self[attr] 

304 

305 raise AttributeError( 

306 f"'{type(self).__name__}' object has no attribute '{attr}'" 

307 ) 

308 

309 def _dir_additions(self): 

310 return self.obj._dir_additions() 

311 

312 def __repr__(self) -> str: 

313 """ 

314 Provide a nice str repr of our rolling object. 

315 """ 

316 attrs_list = ( 

317 f"{attr_name}={getattr(self, attr_name)}" 

318 for attr_name in self._attributes 

319 if getattr(self, attr_name, None) is not None and attr_name[0] != "_" 

320 ) 

321 attrs = ",".join(attrs_list) 

322 return f"{type(self).__name__} [{attrs}]" 

323 

324 def __iter__(self) -> Iterator: 

325 obj = self._selected_obj.set_axis(self._on) 

326 obj = self._create_data(obj) 

327 indexer = self._get_window_indexer() 

328 

329 start, end = indexer.get_window_bounds( 

330 num_values=len(obj), 

331 min_periods=self.min_periods, 

332 center=self.center, 

333 closed=self.closed, 

334 step=self.step, 

335 ) 

336 self._check_window_bounds(start, end, len(obj)) 

337 

338 for s, e in zip(start, end, strict=True): 

339 result = obj.iloc[slice(s, e)] 

340 yield result 

341 

342 def _prep_values(self, values: ArrayLike) -> np.ndarray: 

343 """Convert input to numpy arrays for Cython routines""" 

344 if needs_i8_conversion(values.dtype): 

345 raise NotImplementedError( 

346 f"ops for {type(self).__name__} for this " 

347 f"dtype {values.dtype} are not implemented" 

348 ) 

349 # GH #12373 : rolling functions error on float32 data 

350 # make sure the data is coerced to float64 

351 try: 

352 if isinstance(values, ExtensionArray): 

353 values = values.to_numpy(np.float64, na_value=np.nan) 

354 else: 

355 values = ensure_float64(values) 

356 except (ValueError, TypeError) as err: 

357 raise TypeError(f"cannot handle this type -> {values.dtype}") from err 

358 

359 # Convert inf to nan for C funcs 

360 inf = np.isinf(values) 

361 if inf.any(): 

362 values = np.where(inf, np.nan, values) 

363 

364 return values 

365 

366 def _insert_on_column(self, result: DataFrame, obj: DataFrame) -> None: 

367 # if we have an 'on' column we want to put it back into 

368 # the results in the same location 

369 from pandas import Series 

370 

371 if self.on is not None and not self._on.equals(obj.index): 

372 name = self._on.name 

373 extra_col = Series(self._on, index=self.obj.index, name=name, copy=False) 

374 if name in result.columns: 

375 # TODO: sure we want to overwrite results? 

376 result[name] = extra_col 

377 elif name in result.index.names: 

378 pass 

379 elif name in self._selected_obj.columns: 

380 # insert in the same location as we had in _selected_obj 

381 old_cols = self._selected_obj.columns 

382 new_cols = result.columns 

383 old_loc = old_cols.get_loc(name) 

384 overlap = new_cols.intersection(old_cols[:old_loc]) 

385 new_loc = len(overlap) 

386 result.insert(new_loc, name, extra_col) 

387 else: 

388 # insert at the end 

389 result[name] = extra_col 

390 

391 @property 

392 def _index_array(self) -> npt.NDArray[np.int64] | None: 

393 # TODO: why do we get here with e.g. MultiIndex? 

394 if isinstance(self._on, (PeriodIndex, DatetimeIndex, TimedeltaIndex)): 

395 return self._on.asi8 

396 elif isinstance(self._on.dtype, ArrowDtype) and self._on.dtype.kind in "mM": 

397 return self._on.to_numpy(dtype=np.int64) 

398 return None 

399 

400 def _resolve_output(self, out: DataFrame, obj: DataFrame) -> DataFrame: 

401 """Validate and finalize result.""" 

402 if out.shape[1] == 0 and obj.shape[1] > 0: 

403 raise DataError("No numeric types to aggregate") 

404 if out.shape[1] == 0: 

405 return obj.astype("float64") 

406 

407 self._insert_on_column(out, obj) 

408 return out 

409 

410 def _get_window_indexer(self) -> BaseIndexer: 

411 """ 

412 Return an indexer class that will compute the window start and end bounds 

413 """ 

414 if isinstance(self.window, BaseIndexer): 

415 return self.window 

416 if self._win_freq_i8 is not None: 

417 return VariableWindowIndexer( 

418 index_array=self._index_array, 

419 window_size=self._win_freq_i8, 

420 center=self.center, 

421 ) 

422 return FixedWindowIndexer(window_size=self.window) 

423 

424 def _apply_series( 

425 self, homogeneous_func: Callable[..., ArrayLike], name: str | None = None 

426 ) -> Series: 

427 """ 

428 Series version of _apply_columnwise 

429 """ 

430 obj = self._create_data(self._selected_obj) 

431 

432 if name == "count": 

433 # GH 12541: Special case for count where we support date-like types 

434 obj = notna(obj).astype(int) 

435 try: 

436 values = self._prep_values(obj._values) 

437 except (TypeError, NotImplementedError) as err: 

438 raise DataError("No numeric types to aggregate") from err 

439 

440 result = homogeneous_func(values) 

441 index = self._slice_axis_for_step(obj.index, result) 

442 return obj._constructor(result, index=index, name=obj.name) 

443 

444 def _apply_columnwise( 

445 self, 

446 homogeneous_func: Callable[..., ArrayLike], 

447 name: str, 

448 numeric_only: bool = False, 

449 ) -> DataFrame | Series: 

450 """ 

451 Apply the given function to the DataFrame broken down into homogeneous 

452 sub-frames. 

453 """ 

454 self._validate_numeric_only(name, numeric_only) 

455 if self._selected_obj.ndim == 1: 

456 return self._apply_series(homogeneous_func, name) 

457 

458 obj = self._create_data(self._selected_obj, numeric_only) 

459 if name == "count": 

460 # GH 12541: Special case for count where we support date-like types 

461 obj = notna(obj).astype(int) 

462 obj._mgr = obj._mgr.consolidate() 

463 

464 taker = [] 

465 res_values = [] 

466 for i, arr in enumerate(obj._iter_column_arrays()): 

467 # GH#42736 operate column-wise instead of block-wise 

468 # As of 2.0, hfunc will raise for nuisance columns 

469 try: 

470 arr = self._prep_values(arr) 

471 except (TypeError, NotImplementedError) as err: 

472 raise DataError( 

473 f"Cannot aggregate non-numeric type: {arr.dtype}" 

474 ) from err 

475 res = homogeneous_func(arr) 

476 res_values.append(res) 

477 taker.append(i) 

478 

479 index = self._slice_axis_for_step( 

480 obj.index, res_values[0] if len(res_values) > 0 else None 

481 ) 

482 df = type(obj)._from_arrays( 

483 res_values, 

484 index=index, 

485 columns=obj.columns.take(taker), 

486 verify_integrity=False, 

487 ) 

488 

489 return self._resolve_output(df, obj) 

490 

491 def _apply_tablewise( 

492 self, 

493 homogeneous_func: Callable[..., ArrayLike], 

494 name: str | None = None, 

495 numeric_only: bool = False, 

496 ) -> DataFrame | Series: 

497 """ 

498 Apply the given function to the DataFrame across the entire object 

499 """ 

500 if self._selected_obj.ndim == 1: 

501 raise ValueError("method='table' not applicable for Series objects.") 

502 obj = self._create_data(self._selected_obj, numeric_only) 

503 values = self._prep_values(obj.to_numpy()) 

504 result = homogeneous_func(values) 

505 index = self._slice_axis_for_step(obj.index, result) 

506 columns = ( 

507 obj.columns 

508 if result.shape[1] == len(obj.columns) 

509 else obj.columns[:: self.step] 

510 ) 

511 out = obj._constructor(result, index=index, columns=columns) 

512 

513 return self._resolve_output(out, obj) 

514 

515 def _apply_pairwise( 

516 self, 

517 target: DataFrame | Series, 

518 other: DataFrame | Series | None, 

519 pairwise: bool | None, 

520 func: Callable[[DataFrame | Series, DataFrame | Series], DataFrame | Series], 

521 numeric_only: bool, 

522 ) -> DataFrame | Series: 

523 """ 

524 Apply the given pairwise function given 2 pandas objects (DataFrame/Series) 

525 """ 

526 target = self._create_data(target, numeric_only) 

527 if other is None: 

528 other = target 

529 # only default unset 

530 pairwise = True if pairwise is None else pairwise 

531 elif not isinstance(other, (ABCDataFrame, ABCSeries)): 

532 raise ValueError("other must be a DataFrame or Series") 

533 elif other.ndim == 2 and numeric_only: 

534 other = self._make_numeric_only(other) 

535 

536 return flex_binary_moment(target, other, func, pairwise=bool(pairwise)) 

537 

538 def _apply( 

539 self, 

540 func: Callable[..., Any], 

541 name: str, 

542 numeric_only: bool = False, 

543 numba_args: tuple[Any, ...] = (), 

544 **kwargs, 

545 ): 

546 """ 

547 Rolling statistical measure using supplied function. 

548 

549 Designed to be used with passed-in Cython array-based functions. 

550 

551 Parameters 

552 ---------- 

553 func : callable function to apply 

554 name : str, 

555 numba_args : tuple 

556 args to be passed when func is a numba func 

557 **kwargs 

558 additional arguments for rolling function and window function 

559 

560 Returns 

561 ------- 

562 y : type of input 

563 """ 

564 window_indexer = self._get_window_indexer() 

565 min_periods = ( 

566 self.min_periods 

567 if self.min_periods is not None 

568 else window_indexer.window_size 

569 ) 

570 

571 def homogeneous_func(values: np.ndarray): 

572 # calculation function 

573 

574 if values.size == 0: 

575 return values.copy() 

576 

577 def calc(x): 

578 start, end = window_indexer.get_window_bounds( 

579 num_values=len(x), 

580 min_periods=min_periods, 

581 center=self.center, 

582 closed=self.closed, 

583 step=self.step, 

584 ) 

585 self._check_window_bounds(start, end, len(x)) 

586 

587 return func(x, start, end, min_periods, *numba_args) 

588 

589 with np.errstate(all="ignore"): 

590 result = calc(values) 

591 

592 return result 

593 

594 if self.method == "single": 

595 return self._apply_columnwise(homogeneous_func, name, numeric_only) 

596 else: 

597 return self._apply_tablewise(homogeneous_func, name, numeric_only) 

598 

599 def _numba_apply( 

600 self, 

601 func: Callable[..., Any], 

602 engine_kwargs: dict[str, bool] | None = None, 

603 **func_kwargs, 

604 ): 

605 window_indexer = self._get_window_indexer() 

606 min_periods = ( 

607 self.min_periods 

608 if self.min_periods is not None 

609 else window_indexer.window_size 

610 ) 

611 obj = self._create_data(self._selected_obj) 

612 values = self._prep_values(obj.to_numpy()) 

613 if values.ndim == 1: 

614 values = values.reshape(-1, 1) 

615 start, end = window_indexer.get_window_bounds( 

616 num_values=len(values), 

617 min_periods=min_periods, 

618 center=self.center, 

619 closed=self.closed, 

620 step=self.step, 

621 ) 

622 self._check_window_bounds(start, end, len(values)) 

623 # For now, map everything to float to match the Cython impl 

624 # even though it is wrong 

625 # TODO: Could preserve correct dtypes in future 

626 # xref #53214 

627 dtype_mapping = executor.float_dtype_mapping 

628 aggregator = executor.generate_shared_aggregator( 

629 func, 

630 dtype_mapping, 

631 is_grouped_kernel=False, 

632 **get_jit_arguments(engine_kwargs), 

633 ) 

634 result = aggregator( 

635 values.T, start=start, end=end, min_periods=min_periods, **func_kwargs 

636 ).T 

637 index = self._slice_axis_for_step(obj.index, result) 

638 if obj.ndim == 1: 

639 result = result.squeeze() 

640 out = obj._constructor(result, index=index, name=obj.name) 

641 return out 

642 else: 

643 columns = self._slice_axis_for_step(obj.columns, result.T) 

644 out = obj._constructor(result, index=index, columns=columns) 

645 return self._resolve_output(out, obj) 

646 

647 def aggregate(self, func=None, *args, **kwargs): 

648 relabeling, func, columns, order = reconstruct_func(func, **kwargs) 

649 result = ResamplerWindowApply(self, func, args=args, kwargs=kwargs).agg() 

650 if isinstance(result, ABCDataFrame) and relabeling: 

651 result = result.iloc[:, order] 

652 result.columns = columns 

653 if result is None: 

654 return self.apply(func, raw=False, args=args, kwargs=kwargs) 

655 return result 

656 

657 agg = aggregate 

658 

659 

660class BaseWindowGroupby(BaseWindow): 

661 """ 

662 Provide the groupby windowing facilities. 

663 """ 

664 

665 _grouper: BaseGrouper 

666 _as_index: bool 

667 _attributes: list[str] = ["_grouper"] 

668 

669 def __init__( 

670 self, 

671 obj: DataFrame | Series, 

672 *args, 

673 _grouper: BaseGrouper, 

674 _as_index: bool = True, 

675 **kwargs, 

676 ) -> None: 

677 from pandas.core.groupby.ops import BaseGrouper 

678 

679 if not isinstance(_grouper, BaseGrouper): 

680 raise ValueError("Must pass a BaseGrouper object.") 

681 self._grouper = _grouper 

682 self._as_index = _as_index 

683 # GH 32262: It's convention to keep the grouping column in 

684 # groupby.<agg_func>, but unexpected to users in 

685 # groupby.rolling.<agg_func> 

686 obj = obj.drop(columns=self._grouper.names, errors="ignore") 

687 # GH 15354 

688 if kwargs.get("step") is not None: 

689 raise NotImplementedError("step not implemented for groupby") 

690 super().__init__(obj, *args, **kwargs) 

691 

692 def _apply( 

693 self, 

694 func: Callable[..., Any], 

695 name: str, 

696 numeric_only: bool = False, 

697 numba_args: tuple[Any, ...] = (), 

698 **kwargs, 

699 ) -> DataFrame | Series: 

700 result = super()._apply( 

701 func, 

702 name, 

703 numeric_only, 

704 numba_args, 

705 **kwargs, 

706 ) 

707 # Reconstruct the resulting MultiIndex 

708 # 1st set of levels = group by labels 

709 # 2nd set of levels = original DataFrame/Series index 

710 grouped_object_index = self.obj.index 

711 grouped_index_name = [*grouped_object_index.names] 

712 groupby_keys = copy.copy(self._grouper.names) 

713 result_index_names = groupby_keys + grouped_index_name 

714 

715 drop_columns = [ 

716 key 

717 for key in self._grouper.names 

718 if key not in self.obj.index.names or key is None 

719 ] 

720 

721 if len(drop_columns) != len(groupby_keys): 

722 # Our result will have still kept the column in the result 

723 result = result.drop(columns=drop_columns, errors="ignore") 

724 

725 codes = self._grouper.codes 

726 levels = copy.copy(self._grouper.levels) 

727 

728 group_indices = self._grouper.indices.values() 

729 if group_indices: 

730 indexer = np.concatenate(list(group_indices)) 

731 else: 

732 indexer = np.array([], dtype=np.intp) 

733 codes = [c.take(indexer) for c in codes] 

734 

735 # if the index of the original dataframe needs to be preserved, append 

736 # this index (but reordered) to the codes/levels from the groupby 

737 if grouped_object_index is not None: 

738 idx = grouped_object_index.take(indexer) 

739 if not isinstance(idx, MultiIndex): 

740 idx = MultiIndex.from_arrays([idx]) 

741 codes.extend(list(idx.codes)) 

742 levels.extend(list(idx.levels)) 

743 

744 result_index = MultiIndex( 

745 levels, codes, names=result_index_names, verify_integrity=False 

746 ) 

747 

748 result.index = result_index 

749 if not self._as_index: 

750 result = result.reset_index(level=list(range(len(groupby_keys)))) 

751 return result 

752 

753 def _apply_pairwise( 

754 self, 

755 target: DataFrame | Series, 

756 other: DataFrame | Series | None, 

757 pairwise: bool | None, 

758 func: Callable[[DataFrame | Series, DataFrame | Series], DataFrame | Series], 

759 numeric_only: bool, 

760 ) -> DataFrame | Series: 

761 """ 

762 Apply the given pairwise function given 2 pandas objects (DataFrame/Series) 

763 """ 

764 # Manually drop the grouping column first 

765 target = target.drop(columns=self._grouper.names, errors="ignore") 

766 result = super()._apply_pairwise(target, other, pairwise, func, numeric_only) 

767 # 1) Determine the levels + codes of the groupby levels 

768 if other is not None and not all( 

769 len(group) == len(other) for group in self._grouper.indices.values() 

770 ): 

771 # GH 42915 

772 # len(other) != len(any group), so must reindex (expand) the result 

773 # from flex_binary_moment to a "transform"-like result 

774 # per groupby combination 

775 old_result_len = len(result) 

776 result = concat( 

777 [ 

778 result.take(gb_indices).reindex(result.index) 

779 for gb_indices in self._grouper.indices.values() 

780 ] 

781 ) 

782 

783 gb_pairs = ( 

784 com.maybe_make_list(pair) for pair in self._grouper.indices.keys() 

785 ) 

786 groupby_codes = [] 

787 groupby_levels = [] 

788 # e.g. [[1, 2], [4, 5]] as [[1, 4], [2, 5]] 

789 for gb_level_pair in map(list, zip(*gb_pairs, strict=True)): 

790 labels = np.repeat(np.array(gb_level_pair), old_result_len) 

791 codes, levels = factorize(labels) 

792 groupby_codes.append(codes) 

793 groupby_levels.append(levels) 

794 else: 

795 # pairwise=True or len(other) == len(each group), so repeat 

796 # the groupby labels by the number of columns in the original object 

797 groupby_codes = self._grouper.codes 

798 # error: Incompatible types in assignment (expression has type 

799 # "List[Index]", variable has type "List[Union[ndarray, Index]]") 

800 groupby_levels = self._grouper.levels # type: ignore[assignment] 

801 

802 group_indices = self._grouper.indices.values() 

803 if group_indices: 

804 indexer = np.concatenate(list(group_indices)) 

805 else: 

806 indexer = np.array([], dtype=np.intp) 

807 

808 if target.ndim == 1: 

809 repeat_by = 1 

810 else: 

811 repeat_by = len(target.columns) 

812 groupby_codes = [ 

813 np.repeat(c.take(indexer), repeat_by) for c in groupby_codes 

814 ] 

815 # 2) Determine the levels + codes of the result from super()._apply_pairwise 

816 if isinstance(result.index, MultiIndex): 

817 result_codes = list(result.index.codes) 

818 result_levels = list(result.index.levels) 

819 result_names = list(result.index.names) 

820 else: 

821 idx_codes, idx_levels = factorize(result.index) 

822 result_codes = [idx_codes] 

823 result_levels = [idx_levels] 

824 result_names = [result.index.name] 

825 

826 # 3) Create the resulting index by combining 1) + 2) 

827 result_codes = groupby_codes + result_codes 

828 result_levels = groupby_levels + result_levels 

829 result_names = self._grouper.names + result_names 

830 

831 result_index = MultiIndex( 

832 result_levels, result_codes, names=result_names, verify_integrity=False 

833 ) 

834 result.index = result_index 

835 return result 

836 

837 def _create_data(self, obj: NDFrameT, numeric_only: bool = False) -> NDFrameT: 

838 """ 

839 Split data into blocks & return conformed data. 

840 """ 

841 # Ensure the object we're rolling over is monotonically sorted relative 

842 # to the groups 

843 # GH 36197 

844 if not obj.empty: 

845 groupby_order = np.concatenate(list(self._grouper.indices.values())).astype( 

846 np.int64 

847 ) 

848 obj = obj.take(groupby_order) 

849 return super()._create_data(obj, numeric_only) 

850 

851 def _gotitem(self, key, ndim, subset=None): 

852 # we are setting the index on the actual object 

853 # here so our index is carried through to the selected obj 

854 # when we do the splitting for the groupby 

855 if self.on is not None: 

856 # GH 43355 

857 subset = self.obj.set_index(self._on) 

858 return super()._gotitem(key, ndim, subset=subset) 

859 

860 

861@set_module("pandas.api.typing") 

862class Window(BaseWindow): 

863 """ 

864 Provide rolling window calculations. 

865 

866 Parameters 

867 ---------- 

868 window : int, timedelta, str, offset, or BaseIndexer subclass 

869 Interval of the moving window. 

870 

871 If an integer, the delta between the start and end of each window. 

872 The number of points in the window depends on the ``closed`` argument. 

873 

874 If a timedelta, str, or offset, the time period of each window. Each 

875 window will be a variable sized based on the observations included in 

876 the time-period. This is only valid for datetimelike indexes. 

877 To learn more about the offsets & frequency strings, please see 

878 :ref:`this link<timeseries.offset_aliases>`. 

879 

880 If a BaseIndexer subclass, the window boundaries 

881 based on the defined ``get_window_bounds`` method. Additional rolling 

882 keyword arguments, namely ``min_periods``, ``center``, ``closed`` and 

883 ``step`` will be passed to ``get_window_bounds``. 

884 

885 min_periods : int, default None 

886 Minimum number of observations in window required to have a value; 

887 otherwise, result is ``np.nan``. 

888 

889 For a window that is specified by an offset, ``min_periods`` will default to 1. 

890 

891 For a window that is specified by an integer, ``min_periods`` will default 

892 to the size of the window. 

893 

894 center : bool, default False 

895 If False, set the window labels as the right edge of the window index. 

896 

897 If True, set the window labels as the center of the window index. 

898 

899 win_type : str, default None 

900 If ``None``, all points are evenly weighted. 

901 

902 If a string, it must be a valid `scipy.signal window function 

903 <https://docs.scipy.org/doc/scipy/reference/signal.windows.html#module-scipy.signal.windows>`__. 

904 

905 Certain Scipy window types require additional parameters to be passed 

906 in the aggregation function. The additional parameters must match 

907 the keywords specified in the Scipy window type method signature. 

908 

909 on : str, optional 

910 For a DataFrame, a column label or Index level on which 

911 to calculate the rolling window, rather than the DataFrame's index. 

912 

913 Provided integer column is ignored and excluded from result since 

914 an integer index is not used to calculate the rolling window. 

915 

916 closed : str, default None 

917 Determines the inclusivity of points in the window 

918 

919 If ``'right'``, uses the window (first, last] meaning the last point 

920 is included in the calculations. 

921 

922 If ``'left'``, uses the window [first, last) meaning the first point 

923 is included in the calculations. 

924 

925 If ``'both'``, uses the window [first, last] meaning all points in 

926 the window are included in the calculations. 

927 

928 If ``'neither'``, uses the window (first, last) meaning the first 

929 and last points in the window are excluded from calculations. 

930 

931 () and [] are referencing open and closed set 

932 notation respetively. 

933 

934 Default ``None`` (``'right'``). 

935 

936 step : int, default None 

937 Evaluate the window at every ``step`` result, equivalent to slicing as 

938 ``[::step]``. ``window`` must be an integer. Using a step argument other 

939 than None or 1 will produce a result with a different shape than the input. 

940 

941 method : str {'single', 'table'}, default 'single' 

942 

943 Execute the rolling operation per single column or row (``'single'``) 

944 or over the entire object (``'table'``). 

945 

946 This argument is only implemented when specifying ``engine='numba'`` 

947 in the method call. 

948 

949 Returns 

950 ------- 

951 pandas.api.typing.Window or pandas.api.typing.Rolling 

952 An instance of Window is returned if ``win_type`` is passed. Otherwise, 

953 an instance of Rolling is returned. 

954 

955 See Also 

956 -------- 

957 expanding : Provides expanding transformations. 

958 ewm : Provides exponential weighted functions. 

959 

960 Notes 

961 ----- 

962 See :ref:`Windowing Operations <window.generic>` for further usage details 

963 and examples. 

964 

965 Examples 

966 -------- 

967 >>> df = pd.DataFrame({"B": [0, 1, 2, np.nan, 4]}) 

968 >>> df 

969 B 

970 0 0.0 

971 1 1.0 

972 2 2.0 

973 3 NaN 

974 4 4.0 

975 

976 **window** 

977 

978 Rolling sum with a window length of 2 observations. 

979 

980 >>> df.rolling(2).sum() 

981 B 

982 0 NaN 

983 1 1.0 

984 2 3.0 

985 3 NaN 

986 4 NaN 

987 

988 Rolling sum with a window span of 2 seconds. 

989 

990 >>> df_time = pd.DataFrame( 

991 ... {"B": [0, 1, 2, np.nan, 4]}, 

992 ... index=[ 

993 ... pd.Timestamp("20130101 09:00:00"), 

994 ... pd.Timestamp("20130101 09:00:02"), 

995 ... pd.Timestamp("20130101 09:00:03"), 

996 ... pd.Timestamp("20130101 09:00:05"), 

997 ... pd.Timestamp("20130101 09:00:06"), 

998 ... ], 

999 ... ) 

1000 

1001 >>> df_time 

1002 B 

1003 2013-01-01 09:00:00 0.0 

1004 2013-01-01 09:00:02 1.0 

1005 2013-01-01 09:00:03 2.0 

1006 2013-01-01 09:00:05 NaN 

1007 2013-01-01 09:00:06 4.0 

1008 

1009 >>> df_time.rolling("2s").sum() 

1010 B 

1011 2013-01-01 09:00:00 0.0 

1012 2013-01-01 09:00:02 1.0 

1013 2013-01-01 09:00:03 3.0 

1014 2013-01-01 09:00:05 NaN 

1015 2013-01-01 09:00:06 4.0 

1016 

1017 Rolling sum with forward looking windows with 2 observations. 

1018 

1019 >>> indexer = pd.api.indexers.FixedForwardWindowIndexer(window_size=2) 

1020 >>> df.rolling(window=indexer, min_periods=1).sum() 

1021 B 

1022 0 1.0 

1023 1 3.0 

1024 2 2.0 

1025 3 4.0 

1026 4 4.0 

1027 

1028 **min_periods** 

1029 

1030 Rolling sum with a window length of 2 observations, but only needs a minimum of 1 

1031 observation to calculate a value. 

1032 

1033 >>> df.rolling(2, min_periods=1).sum() 

1034 B 

1035 0 0.0 

1036 1 1.0 

1037 2 3.0 

1038 3 2.0 

1039 4 4.0 

1040 

1041 **center** 

1042 

1043 Rolling sum with the result assigned to the center of the window index. 

1044 

1045 >>> df.rolling(3, min_periods=1, center=True).sum() 

1046 B 

1047 0 1.0 

1048 1 3.0 

1049 2 3.0 

1050 3 6.0 

1051 4 4.0 

1052 

1053 >>> df.rolling(3, min_periods=1, center=False).sum() 

1054 B 

1055 0 0.0 

1056 1 1.0 

1057 2 3.0 

1058 3 3.0 

1059 4 6.0 

1060 

1061 **step** 

1062 

1063 Rolling sum with a window length of 2 observations, minimum of 1 observation to 

1064 calculate a value, and a step of 2. 

1065 

1066 >>> df.rolling(2, min_periods=1, step=2).sum() 

1067 B 

1068 0 0.0 

1069 2 3.0 

1070 4 4.0 

1071 

1072 **win_type** 

1073 

1074 Rolling sum with a window length of 2, using the Scipy ``'gaussian'`` 

1075 window type. ``std`` is required in the aggregation function. 

1076 

1077 >>> df.rolling(2, win_type="gaussian").sum(std=3) 

1078 B 

1079 0 NaN 

1080 1 0.986207 

1081 2 2.958621 

1082 3 NaN 

1083 4 NaN 

1084 

1085 **on** 

1086 

1087 Rolling sum with a window length of 2 days. 

1088 

1089 >>> df = pd.DataFrame( 

1090 ... { 

1091 ... "A": [ 

1092 ... pd.to_datetime("2020-01-01"), 

1093 ... pd.to_datetime("2020-01-01"), 

1094 ... pd.to_datetime("2020-01-02"), 

1095 ... ], 

1096 ... "B": [1, 2, 3], 

1097 ... }, 

1098 ... index=pd.date_range("2020", periods=3), 

1099 ... ) 

1100 

1101 >>> df 

1102 A B 

1103 2020-01-01 2020-01-01 1 

1104 2020-01-02 2020-01-01 2 

1105 2020-01-03 2020-01-02 3 

1106 

1107 >>> df.rolling("2D", on="A").sum() 

1108 A B 

1109 2020-01-01 2020-01-01 1.0 

1110 2020-01-02 2020-01-01 3.0 

1111 2020-01-03 2020-01-02 6.0 

1112 """ 

1113 

1114 _attributes = [ 

1115 "window", 

1116 "min_periods", 

1117 "center", 

1118 "win_type", 

1119 "on", 

1120 "closed", 

1121 "step", 

1122 "method", 

1123 ] 

1124 

1125 def _validate(self) -> None: 

1126 super()._validate() 

1127 

1128 if not isinstance(self.win_type, str): 

1129 raise ValueError(f"Invalid win_type {self.win_type}") 

1130 signal = import_optional_dependency( 

1131 "scipy.signal.windows", extra="Scipy is required to generate window weight." 

1132 ) 

1133 self._scipy_weight_generator = getattr(signal, self.win_type, None) 

1134 if self._scipy_weight_generator is None: 

1135 raise ValueError(f"Invalid win_type {self.win_type}") 

1136 

1137 if isinstance(self.window, BaseIndexer): 

1138 raise NotImplementedError( 

1139 "BaseIndexer subclasses not implemented with win_types." 

1140 ) 

1141 if not is_integer(self.window) or self.window < 0: 

1142 raise ValueError("window must be an integer 0 or greater") 

1143 

1144 if self.method != "single": 

1145 raise NotImplementedError("'single' is the only supported method type.") 

1146 

1147 def _center_window(self, result: np.ndarray, offset: int) -> np.ndarray: 

1148 """ 

1149 Center the result in the window for weighted rolling aggregations. 

1150 """ 

1151 if offset > 0: 

1152 lead_indexer = [slice(offset, None)] 

1153 result = np.copy(result[tuple(lead_indexer)]) 

1154 return result 

1155 

1156 def _apply( 

1157 self, 

1158 func: Callable[[np.ndarray, int, int], np.ndarray], 

1159 name: str, 

1160 numeric_only: bool = False, 

1161 numba_args: tuple[Any, ...] = (), 

1162 **kwargs, 

1163 ): 

1164 """ 

1165 Rolling with weights statistical measure using supplied function. 

1166 

1167 Designed to be used with passed-in Cython array-based functions. 

1168 

1169 Parameters 

1170 ---------- 

1171 func : callable function to apply 

1172 name : str, 

1173 numeric_only : bool, default False 

1174 Whether to only operate on bool, int, and float columns 

1175 numba_args : tuple 

1176 unused 

1177 **kwargs 

1178 additional arguments for scipy windows if necessary 

1179 

1180 Returns 

1181 ------- 

1182 y : type of input 

1183 """ 

1184 # "None" not callable [misc] 

1185 window = self._scipy_weight_generator( # type: ignore[misc] 

1186 self.window, **kwargs 

1187 ) 

1188 offset = (len(window) - 1) // 2 if self.center else 0 

1189 

1190 def homogeneous_func(values: np.ndarray): 

1191 # calculation function 

1192 

1193 if values.size == 0: 

1194 return values.copy() 

1195 

1196 def calc(x): 

1197 additional_nans = np.full(offset, np.nan) 

1198 x = np.concatenate((x, additional_nans)) 

1199 return func( 

1200 x, 

1201 window, 

1202 self.min_periods if self.min_periods is not None else len(window), 

1203 ) 

1204 

1205 with np.errstate(all="ignore"): 

1206 # Our weighted aggregations return memoryviews 

1207 result = np.asarray(calc(values)) 

1208 

1209 if self.center: 

1210 result = self._center_window(result, offset) 

1211 

1212 return result 

1213 

1214 return self._apply_columnwise(homogeneous_func, name, numeric_only)[ 

1215 :: self.step 

1216 ] 

1217 

1218 def aggregate(self, func=None, *args, **kwargs): 

1219 """ 

1220 Aggregate using one or more operations over the specified axis. 

1221 

1222 Parameters 

1223 ---------- 

1224 func : function, str, list or dict 

1225 Function to use for aggregating the data. If a function, must either 

1226 work when passed a Series/DataFrame or 

1227 when passed to Series/DataFrame.apply. 

1228 

1229 Accepted combinations are: 

1230 

1231 - function 

1232 - string function name 

1233 - list of functions and/or function names, e.g. ``[np.sum, 'mean']`` 

1234 - dict of axis labels -> functions, function names or list of such. 

1235 

1236 *args 

1237 Positional arguments to pass to `func`. 

1238 **kwargs 

1239 Keyword arguments to pass to `func`. 

1240 

1241 Returns 

1242 ------- 

1243 scalar, Series or DataFrame 

1244 

1245 The return can be: 

1246 

1247 * scalar : when Series.agg is called with single function 

1248 * Series : when DataFrame.agg is called with a single function 

1249 * DataFrame : when DataFrame.agg is called with several functions 

1250 

1251 See Also 

1252 -------- 

1253 DataFrame.aggregate : Similar DataFrame method. 

1254 Series.aggregate : Similar Series method. 

1255 

1256 Notes 

1257 ----- 

1258 The aggregation operations are always performed over an axis, either the 

1259 index (default) or the column axis. This behavior is different from 

1260 `numpy` aggregation functions (`mean`, `median`, `prod`, `sum`, `std`, 

1261 `var`), where the default is to compute the aggregation of the flattened 

1262 array, e.g., ``numpy.mean(arr_2d)`` as opposed to 

1263 ``numpy.mean(arr_2d, axis=0)``. 

1264 

1265 `agg` is an alias for `aggregate`. Use the alias. 

1266 

1267 Functions that mutate the passed object can produce unexpected 

1268 behavior or errors and are not supported. See :ref:`gotchas.udf-mutation` 

1269 for more details. 

1270 

1271 A passed user-defined-function will be passed a Series for evaluation. 

1272 

1273 If ``func`` defines an index relabeling, ``axis`` must be ``0`` or ``index``. 

1274 

1275 Examples 

1276 -------- 

1277 >>> df = pd.DataFrame({"A": [1, 2, 3], "B": [4, 5, 6], "C": [7, 8, 9]}) 

1278 >>> df 

1279 A B C 

1280 0 1 4 7 

1281 1 2 5 8 

1282 2 3 6 9 

1283 

1284 >>> df.rolling(2, win_type="boxcar").agg("mean") 

1285 A B C 

1286 0 NaN NaN NaN 

1287 1 1.5 4.5 7.5 

1288 2 2.5 5.5 8.5 

1289 """ 

1290 result = ResamplerWindowApply(self, func, args=args, kwargs=kwargs).agg() 

1291 if result is None: 

1292 # these must apply directly 

1293 result = func(self) 

1294 

1295 return result 

1296 

1297 agg = aggregate 

1298 

1299 def sum(self, numeric_only: bool = False, **kwargs): 

1300 """ 

1301 Calculate the rolling weighted window sum. 

1302 

1303 Parameters 

1304 ---------- 

1305 numeric_only : bool, default False 

1306 Include only float, int, boolean columns. 

1307 

1308 **kwargs 

1309 Keyword arguments to configure the ``SciPy`` weighted window type. 

1310 

1311 Returns 

1312 ------- 

1313 Series or DataFrame 

1314 Return type is the same as the original object with ``np.float64`` dtype. 

1315 

1316 See Also 

1317 -------- 

1318 Series.rolling : Calling rolling with Series data. 

1319 DataFrame.rolling : Calling rolling with DataFrames. 

1320 Series.sum : Aggregating sum for Series. 

1321 DataFrame.sum : Aggregating sum for DataFrame. 

1322 

1323 Examples 

1324 -------- 

1325 >>> ser = pd.Series([0, 1, 5, 2, 8]) 

1326 

1327 To get an instance of :class:`~pandas.core.window.rolling.Window` we need 

1328 to pass the parameter `win_type`. 

1329 

1330 >>> type(ser.rolling(2, win_type="gaussian")) 

1331 <class 'pandas.api.typing.Window'> 

1332 

1333 In order to use the `SciPy` Gaussian window we need to provide the parameters 

1334 `M` and `std`. The parameter `M` corresponds to 2 in our example. 

1335 We pass the second parameter `std` as a parameter of the following method 

1336 (`sum` in this case): 

1337 

1338 >>> ser.rolling(2, win_type="gaussian").sum(std=3) 

1339 0 NaN 

1340 1 0.986207 

1341 2 5.917243 

1342 3 6.903450 

1343 4 9.862071 

1344 dtype: float64 

1345 """ 

1346 window_func = window_aggregations.roll_weighted_sum 

1347 # error: Argument 1 to "_apply" of "Window" has incompatible type 

1348 # "Callable[[ndarray, ndarray, int], ndarray]"; expected 

1349 # "Callable[[ndarray, int, int], ndarray]" 

1350 return self._apply( 

1351 window_func, # type: ignore[arg-type] 

1352 name="sum", 

1353 numeric_only=numeric_only, 

1354 **kwargs, 

1355 ) 

1356 

1357 def mean(self, numeric_only: bool = False, **kwargs): 

1358 """ 

1359 Calculate the rolling weighted window mean. 

1360 

1361 Parameters 

1362 ---------- 

1363 numeric_only : bool, default False 

1364 Include only float, int, boolean columns. 

1365 

1366 **kwargs 

1367 Keyword arguments to configure the ``SciPy`` weighted window type. 

1368 

1369 Returns 

1370 ------- 

1371 Series or DataFrame 

1372 Return type is the same as the original object with ``np.float64`` dtype. 

1373 

1374 See Also 

1375 -------- 

1376 Series.rolling : Calling rolling with Series data. 

1377 DataFrame.rolling : Calling rolling with DataFrames. 

1378 Series.mean : Aggregating mean for Series. 

1379 DataFrame.mean : Aggregating mean for DataFrame. 

1380 

1381 Examples 

1382 -------- 

1383 >>> ser = pd.Series([0, 1, 5, 2, 8]) 

1384 

1385 To get an instance of :class:`~pandas.core.window.rolling.Window` we need 

1386 to pass the parameter `win_type`. 

1387 

1388 >>> type(ser.rolling(2, win_type="gaussian")) 

1389 <class 'pandas.api.typing.Window'> 

1390 

1391 In order to use the `SciPy` Gaussian window we need to provide the parameters 

1392 `M` and `std`. The parameter `M` corresponds to 2 in our example. 

1393 We pass the second parameter `std` as a parameter of the following method: 

1394 

1395 >>> ser.rolling(2, win_type="gaussian").mean(std=3) 

1396 0 NaN 

1397 1 0.5 

1398 2 3.0 

1399 3 3.5 

1400 4 5.0 

1401 dtype: float64 

1402 """ 

1403 window_func = window_aggregations.roll_weighted_mean 

1404 # error: Argument 1 to "_apply" of "Window" has incompatible type 

1405 # "Callable[[ndarray, ndarray, int], ndarray]"; expected 

1406 # "Callable[[ndarray, int, int], ndarray]" 

1407 return self._apply( 

1408 window_func, # type: ignore[arg-type] 

1409 name="mean", 

1410 numeric_only=numeric_only, 

1411 **kwargs, 

1412 ) 

1413 

1414 def var(self, ddof: int = 1, numeric_only: bool = False, **kwargs): 

1415 """ 

1416 Calculate the rolling weighted window variance. 

1417 

1418 Parameters 

1419 ---------- 

1420 ddof : int, default 1 

1421 Delta Degrees of Freedom. The divisor used in calculations 

1422 is ``N - ddof``, where ``N`` represents the number of elements. 

1423 numeric_only : bool, default False 

1424 Include only float, int, boolean columns. 

1425 

1426 **kwargs 

1427 Keyword arguments to configure the ``SciPy`` weighted window type. 

1428 

1429 Returns 

1430 ------- 

1431 Series or DataFrame 

1432 Return type is the same as the original object with ``np.float64`` dtype. 

1433 

1434 See Also 

1435 -------- 

1436 Series.rolling : Calling rolling with Series data. 

1437 DataFrame.rolling : Calling rolling with DataFrames. 

1438 Series.var : Aggregating var for Series. 

1439 DataFrame.var : Aggregating var for DataFrame. 

1440 

1441 Examples 

1442 -------- 

1443 >>> ser = pd.Series([0, 1, 5, 2, 8]) 

1444 

1445 To get an instance of :class:`~pandas.core.window.rolling.Window` we need 

1446 to pass the parameter `win_type`. 

1447 

1448 >>> type(ser.rolling(2, win_type="gaussian")) 

1449 <class 'pandas.api.typing.Window'> 

1450 

1451 In order to use the `SciPy` Gaussian window we need to provide the parameters 

1452 `M` and `std`. The parameter `M` corresponds to 2 in our example. 

1453 We pass the second parameter `std` as a parameter of the following method: 

1454 

1455 >>> ser.rolling(2, win_type="gaussian").var(std=3) 

1456 0 NaN 

1457 1 0.5 

1458 2 8.0 

1459 3 4.5 

1460 4 18.0 

1461 dtype: float64 

1462 """ 

1463 window_func = partial(window_aggregations.roll_weighted_var, ddof=ddof) 

1464 kwargs.pop("name", None) 

1465 return self._apply(window_func, name="var", numeric_only=numeric_only, **kwargs) 

1466 

1467 def std(self, ddof: int = 1, numeric_only: bool = False, **kwargs): 

1468 """ 

1469 Calculate the rolling weighted window standard deviation. 

1470 

1471 Parameters 

1472 ---------- 

1473 ddof : int, default 1 

1474 Delta Degrees of Freedom. The divisor used in calculations 

1475 is ``N - ddof``, where ``N`` represents the number of elements. 

1476 numeric_only : bool, default False 

1477 Include only float, int, boolean columns. 

1478 

1479 **kwargs 

1480 Keyword arguments to configure the ``SciPy`` weighted window type. 

1481 

1482 Returns 

1483 ------- 

1484 Series or DataFrame 

1485 Return type is the same as the original object with ``np.float64`` dtype. 

1486 

1487 See Also 

1488 -------- 

1489 Series.rolling : Calling rolling with Series data. 

1490 DataFrame.rolling : Calling rolling with DataFrames. 

1491 Series.std : Aggregating std for Series. 

1492 DataFrame.std : Aggregating std for DataFrame. 

1493 

1494 Examples 

1495 -------- 

1496 >>> ser = pd.Series([0, 1, 5, 2, 8]) 

1497 

1498 To get an instance of :class:`~pandas.core.window.rolling.Window` we need 

1499 to pass the parameter `win_type`. 

1500 

1501 >>> type(ser.rolling(2, win_type="gaussian")) 

1502 <class 'pandas.api.typing.Window'> 

1503 

1504 In order to use the `SciPy` Gaussian window we need to provide the parameters 

1505 `M` and `std`. The parameter `M` corresponds to 2 in our example. 

1506 We pass the second parameter `std` as a parameter of the following method: 

1507 

1508 >>> ser.rolling(2, win_type="gaussian").std(std=3) 

1509 0 NaN 

1510 1 0.707107 

1511 2 2.828427 

1512 3 2.121320 

1513 4 4.242641 

1514 dtype: float64 

1515 """ 

1516 return zsqrt( 

1517 self.var(ddof=ddof, name="std", numeric_only=numeric_only, **kwargs) 

1518 ) 

1519 

1520 

1521class RollingAndExpandingMixin(BaseWindow): 

1522 def count(self, numeric_only: bool = False): 

1523 window_func = window_aggregations.roll_sum 

1524 return self._apply(window_func, name="count", numeric_only=numeric_only) 

1525 

1526 def apply( 

1527 self, 

1528 func: Callable[..., Any], 

1529 raw: bool = False, 

1530 engine: Literal["cython", "numba"] | None = None, 

1531 engine_kwargs: dict[str, bool] | None = None, 

1532 args: tuple[Any, ...] | None = None, 

1533 kwargs: dict[str, Any] | None = None, 

1534 ): 

1535 if args is None: 

1536 args = () 

1537 if kwargs is None: 

1538 kwargs = {} 

1539 

1540 if not is_bool(raw): 

1541 raise ValueError("raw parameter must be `True` or `False`") 

1542 

1543 numba_args: tuple[Any, ...] = () 

1544 if maybe_use_numba(engine): 

1545 if raw is False: 

1546 raise ValueError("raw must be `True` when using the numba engine") 

1547 numba_args, kwargs = prepare_function_arguments( 

1548 func, args, kwargs, num_required_args=1 

1549 ) 

1550 if self.method == "single": 

1551 apply_func = generate_numba_apply_func( 

1552 func, **get_jit_arguments(engine_kwargs) 

1553 ) 

1554 else: 

1555 apply_func = generate_numba_table_func( 

1556 func, **get_jit_arguments(engine_kwargs) 

1557 ) 

1558 elif engine in ("cython", None): 

1559 if engine_kwargs is not None: 

1560 raise ValueError("cython engine does not accept engine_kwargs") 

1561 apply_func = self._generate_cython_apply_func(args, kwargs, raw, func) 

1562 else: 

1563 raise ValueError("engine must be either 'numba' or 'cython'") 

1564 

1565 return self._apply( 

1566 apply_func, 

1567 name="apply", 

1568 numba_args=numba_args, 

1569 ) 

1570 

1571 def _generate_cython_apply_func( 

1572 self, 

1573 args: tuple[Any, ...], 

1574 kwargs: dict[str, Any], 

1575 raw: bool | np.bool_, 

1576 function: Callable[..., Any], 

1577 ) -> Callable[[np.ndarray, np.ndarray, np.ndarray, int], np.ndarray]: 

1578 from pandas import Series 

1579 

1580 window_func = partial( 

1581 window_aggregations.roll_apply, 

1582 args=args, 

1583 kwargs=kwargs, 

1584 raw=bool(raw), 

1585 function=function, 

1586 ) 

1587 

1588 def apply_func(values, begin, end, min_periods, raw=raw): 

1589 if not raw: 

1590 # GH 45912 

1591 values = Series(values, index=self._on, copy=False) 

1592 return window_func(values, begin, end, min_periods) 

1593 

1594 return apply_func 

1595 

1596 @overload 

1597 def pipe( 

1598 self, 

1599 func: Callable[Concatenate[Self, P], T], 

1600 *args: P.args, 

1601 **kwargs: P.kwargs, 

1602 ) -> T: ... 

1603 

1604 @overload 

1605 def pipe( 

1606 self, 

1607 func: tuple[Callable[..., T], str], 

1608 *args: Any, 

1609 **kwargs: Any, 

1610 ) -> T: ... 

1611 

1612 def pipe( 

1613 self, 

1614 func: Callable[Concatenate[Self, P], T] | tuple[Callable[..., T], str], 

1615 *args: Any, 

1616 **kwargs: Any, 

1617 ) -> T: 

1618 return com.pipe(self, func, *args, **kwargs) 

1619 

1620 def sum( 

1621 self, 

1622 numeric_only: bool = False, 

1623 engine: Literal["cython", "numba"] | None = None, 

1624 engine_kwargs: dict[str, bool] | None = None, 

1625 ): 

1626 if maybe_use_numba(engine): 

1627 if self.method == "table": 

1628 func = generate_manual_numpy_nan_agg_with_axis(np.nansum) 

1629 return self.apply( 

1630 func, 

1631 raw=True, 

1632 engine=engine, 

1633 engine_kwargs=engine_kwargs, 

1634 ) 

1635 else: 

1636 from pandas.core._numba.kernels import sliding_sum 

1637 

1638 return self._numba_apply(sliding_sum, engine_kwargs) 

1639 window_func = window_aggregations.roll_sum 

1640 return self._apply(window_func, name="sum", numeric_only=numeric_only) 

1641 

1642 def max( 

1643 self, 

1644 numeric_only: bool = False, 

1645 engine: Literal["cython", "numba"] | None = None, 

1646 engine_kwargs: dict[str, bool] | None = None, 

1647 ): 

1648 if maybe_use_numba(engine): 

1649 if self.method == "table": 

1650 func = generate_manual_numpy_nan_agg_with_axis(np.nanmax) 

1651 return self.apply( 

1652 func, 

1653 raw=True, 

1654 engine=engine, 

1655 engine_kwargs=engine_kwargs, 

1656 ) 

1657 else: 

1658 from pandas.core._numba.kernels import sliding_min_max 

1659 

1660 return self._numba_apply(sliding_min_max, engine_kwargs, is_max=True) 

1661 window_func = window_aggregations.roll_max 

1662 return self._apply(window_func, name="max", numeric_only=numeric_only) 

1663 

1664 def min( 

1665 self, 

1666 numeric_only: bool = False, 

1667 engine: Literal["cython", "numba"] | None = None, 

1668 engine_kwargs: dict[str, bool] | None = None, 

1669 ): 

1670 if maybe_use_numba(engine): 

1671 if self.method == "table": 

1672 func = generate_manual_numpy_nan_agg_with_axis(np.nanmin) 

1673 return self.apply( 

1674 func, 

1675 raw=True, 

1676 engine=engine, 

1677 engine_kwargs=engine_kwargs, 

1678 ) 

1679 else: 

1680 from pandas.core._numba.kernels import sliding_min_max 

1681 

1682 return self._numba_apply(sliding_min_max, engine_kwargs, is_max=False) 

1683 window_func = window_aggregations.roll_min 

1684 return self._apply(window_func, name="min", numeric_only=numeric_only) 

1685 

1686 def mean( 

1687 self, 

1688 numeric_only: bool = False, 

1689 engine: Literal["cython", "numba"] | None = None, 

1690 engine_kwargs: dict[str, bool] | None = None, 

1691 ): 

1692 if maybe_use_numba(engine): 

1693 if self.method == "table": 

1694 func = generate_manual_numpy_nan_agg_with_axis(np.nanmean) 

1695 return self.apply( 

1696 func, 

1697 raw=True, 

1698 engine=engine, 

1699 engine_kwargs=engine_kwargs, 

1700 ) 

1701 else: 

1702 from pandas.core._numba.kernels import sliding_mean 

1703 

1704 return self._numba_apply(sliding_mean, engine_kwargs) 

1705 window_func = window_aggregations.roll_mean 

1706 return self._apply(window_func, name="mean", numeric_only=numeric_only) 

1707 

1708 def median( 

1709 self, 

1710 numeric_only: bool = False, 

1711 engine: Literal["cython", "numba"] | None = None, 

1712 engine_kwargs: dict[str, bool] | None = None, 

1713 ): 

1714 if maybe_use_numba(engine): 

1715 if self.method == "table": 

1716 func = generate_manual_numpy_nan_agg_with_axis(np.nanmedian) 

1717 else: 

1718 func = np.nanmedian 

1719 

1720 return self.apply( 

1721 func, 

1722 raw=True, 

1723 engine=engine, 

1724 engine_kwargs=engine_kwargs, 

1725 ) 

1726 window_func = window_aggregations.roll_median_c 

1727 return self._apply(window_func, name="median", numeric_only=numeric_only) 

1728 

1729 def std( 

1730 self, 

1731 ddof: int = 1, 

1732 numeric_only: bool = False, 

1733 engine: Literal["cython", "numba"] | None = None, 

1734 engine_kwargs: dict[str, bool] | None = None, 

1735 ): 

1736 if maybe_use_numba(engine): 

1737 if self.method == "table": 

1738 raise NotImplementedError("std not supported with method='table'") 

1739 from pandas.core._numba.kernels import sliding_var 

1740 

1741 return zsqrt(self._numba_apply(sliding_var, engine_kwargs, ddof=ddof)) 

1742 window_func = window_aggregations.roll_var 

1743 

1744 def zsqrt_func(values, begin, end, min_periods): 

1745 return zsqrt(window_func(values, begin, end, min_periods, ddof=ddof)) 

1746 

1747 return self._apply( 

1748 zsqrt_func, 

1749 name="std", 

1750 numeric_only=numeric_only, 

1751 ) 

1752 

1753 def var( 

1754 self, 

1755 ddof: int = 1, 

1756 numeric_only: bool = False, 

1757 engine: Literal["cython", "numba"] | None = None, 

1758 engine_kwargs: dict[str, bool] | None = None, 

1759 ): 

1760 if maybe_use_numba(engine): 

1761 if self.method == "table": 

1762 raise NotImplementedError("var not supported with method='table'") 

1763 from pandas.core._numba.kernels import sliding_var 

1764 

1765 return self._numba_apply(sliding_var, engine_kwargs, ddof=ddof) 

1766 window_func = partial(window_aggregations.roll_var, ddof=ddof) 

1767 return self._apply( 

1768 window_func, 

1769 name="var", 

1770 numeric_only=numeric_only, 

1771 ) 

1772 

1773 def skew(self, numeric_only: bool = False): 

1774 window_func = window_aggregations.roll_skew 

1775 return self._apply( 

1776 window_func, 

1777 name="skew", 

1778 numeric_only=numeric_only, 

1779 ) 

1780 

1781 def sem(self, ddof: int = 1, numeric_only: bool = False): 

1782 # Raise here so error message says sem instead of std 

1783 self._validate_numeric_only("sem", numeric_only) 

1784 return self.std(numeric_only=numeric_only, ddof=ddof) / ( 

1785 self.count(numeric_only=numeric_only) 

1786 ).pow(0.5) 

1787 

1788 def kurt(self, numeric_only: bool = False): 

1789 window_func = window_aggregations.roll_kurt 

1790 return self._apply( 

1791 window_func, 

1792 name="kurt", 

1793 numeric_only=numeric_only, 

1794 ) 

1795 

1796 def first(self, numeric_only: bool = False): 

1797 window_func = window_aggregations.roll_first 

1798 return self._apply( 

1799 window_func, 

1800 name="first", 

1801 numeric_only=numeric_only, 

1802 ) 

1803 

1804 def last(self, numeric_only: bool = False): 

1805 window_func = window_aggregations.roll_last 

1806 return self._apply( 

1807 window_func, 

1808 name="last", 

1809 numeric_only=numeric_only, 

1810 ) 

1811 

1812 def quantile( 

1813 self, 

1814 q: float, 

1815 interpolation: QuantileInterpolation = "linear", 

1816 numeric_only: bool = False, 

1817 ): 

1818 if q == 1.0: 

1819 window_func = window_aggregations.roll_max 

1820 elif q == 0.0: 

1821 window_func = window_aggregations.roll_min 

1822 else: 

1823 window_func = partial( 

1824 window_aggregations.roll_quantile, 

1825 quantile=q, 

1826 interpolation=interpolation, 

1827 ) 

1828 

1829 return self._apply(window_func, name="quantile", numeric_only=numeric_only) 

1830 

1831 def rank( 

1832 self, 

1833 method: WindowingRankType = "average", 

1834 ascending: bool = True, 

1835 pct: bool = False, 

1836 numeric_only: bool = False, 

1837 ): 

1838 window_func = partial( 

1839 window_aggregations.roll_rank, 

1840 method=method, 

1841 ascending=ascending, 

1842 percentile=pct, 

1843 ) 

1844 

1845 return self._apply(window_func, name="rank", numeric_only=numeric_only) 

1846 

1847 def nunique( 

1848 self, 

1849 numeric_only: bool = False, 

1850 ): 

1851 window_func = partial( 

1852 window_aggregations.roll_nunique, 

1853 ) 

1854 

1855 return self._apply(window_func, name="nunique", numeric_only=numeric_only) 

1856 

1857 def cov( 

1858 self, 

1859 other: DataFrame | Series | None = None, 

1860 pairwise: bool | None = None, 

1861 ddof: int = 1, 

1862 numeric_only: bool = False, 

1863 ): 

1864 if self.step is not None: 

1865 raise NotImplementedError("step not implemented for cov") 

1866 self._validate_numeric_only("cov", numeric_only) 

1867 

1868 from pandas import Series 

1869 

1870 def cov_func(x, y): 

1871 x_array = self._prep_values(x) 

1872 y_array = self._prep_values(y) 

1873 window_indexer = self._get_window_indexer() 

1874 min_periods = ( 

1875 self.min_periods 

1876 if self.min_periods is not None 

1877 else window_indexer.window_size 

1878 ) 

1879 start, end = window_indexer.get_window_bounds( 

1880 num_values=len(x_array), 

1881 min_periods=min_periods, 

1882 center=self.center, 

1883 closed=self.closed, 

1884 step=self.step, 

1885 ) 

1886 self._check_window_bounds(start, end, len(x_array)) 

1887 

1888 with np.errstate(all="ignore"): 

1889 mean_x_y = window_aggregations.roll_mean( 

1890 x_array * y_array, start, end, min_periods 

1891 ) 

1892 mean_x = window_aggregations.roll_mean(x_array, start, end, min_periods) 

1893 mean_y = window_aggregations.roll_mean(y_array, start, end, min_periods) 

1894 count_x_y = window_aggregations.roll_sum( 

1895 notna(x_array + y_array).astype(np.float64), start, end, 0 

1896 ) 

1897 result = (mean_x_y - mean_x * mean_y) * (count_x_y / (count_x_y - ddof)) 

1898 return Series(result, index=x.index, name=x.name, copy=False) 

1899 

1900 return self._apply_pairwise( 

1901 self._selected_obj, other, pairwise, cov_func, numeric_only 

1902 ) 

1903 

1904 def corr( 

1905 self, 

1906 other: DataFrame | Series | None = None, 

1907 pairwise: bool | None = None, 

1908 ddof: int = 1, 

1909 numeric_only: bool = False, 

1910 ): 

1911 if self.step is not None: 

1912 raise NotImplementedError("step not implemented for corr") 

1913 self._validate_numeric_only("corr", numeric_only) 

1914 

1915 from pandas import Series 

1916 

1917 def corr_func(x, y): 

1918 x_array = self._prep_values(x) 

1919 y_array = self._prep_values(y) 

1920 window_indexer = self._get_window_indexer() 

1921 min_periods = ( 

1922 self.min_periods 

1923 if self.min_periods is not None 

1924 else window_indexer.window_size 

1925 ) 

1926 start, end = window_indexer.get_window_bounds( 

1927 num_values=len(x_array), 

1928 min_periods=min_periods, 

1929 center=self.center, 

1930 closed=self.closed, 

1931 step=self.step, 

1932 ) 

1933 self._check_window_bounds(start, end, len(x_array)) 

1934 

1935 with np.errstate(all="ignore"): 

1936 mean_x_y = window_aggregations.roll_mean( 

1937 x_array * y_array, start, end, min_periods 

1938 ) 

1939 mean_x = window_aggregations.roll_mean(x_array, start, end, min_periods) 

1940 mean_y = window_aggregations.roll_mean(y_array, start, end, min_periods) 

1941 count_x_y = window_aggregations.roll_sum( 

1942 notna(x_array + y_array).astype(np.float64), start, end, 0 

1943 ) 

1944 x_var = window_aggregations.roll_var( 

1945 x_array, start, end, min_periods, ddof 

1946 ) 

1947 y_var = window_aggregations.roll_var( 

1948 y_array, start, end, min_periods, ddof 

1949 ) 

1950 numerator = (mean_x_y - mean_x * mean_y) * ( 

1951 count_x_y / (count_x_y - ddof) 

1952 ) 

1953 denominator = (x_var * y_var) ** 0.5 

1954 result = numerator / denominator 

1955 return Series(result, index=x.index, name=x.name, copy=False) 

1956 

1957 return self._apply_pairwise( 

1958 self._selected_obj, other, pairwise, corr_func, numeric_only 

1959 ) 

1960 

1961 

1962@set_module("pandas.api.typing") 

1963class Rolling(RollingAndExpandingMixin): 

1964 _attributes: list[str] = [ 

1965 "window", 

1966 "min_periods", 

1967 "center", 

1968 "win_type", 

1969 "on", 

1970 "closed", 

1971 "step", 

1972 "method", 

1973 ] 

1974 

1975 def _validate(self) -> None: 

1976 super()._validate() 

1977 

1978 # we allow rolling on a datetimelike index 

1979 if ( 

1980 self.obj.empty 

1981 or isinstance(self._on, (DatetimeIndex, TimedeltaIndex, PeriodIndex)) 

1982 or (isinstance(self._on.dtype, ArrowDtype) and self._on.dtype.kind in "mM") 

1983 ) and isinstance(self.window, (str, BaseOffset, timedelta)): 

1984 self._validate_datetimelike_monotonic() 

1985 

1986 # this will raise ValueError on non-fixed freqs 

1987 try: 

1988 freq = to_offset(self.window) 

1989 except (TypeError, ValueError) as err: 

1990 raise ValueError( 

1991 f"passed window {self.window} is not " 

1992 "compatible with a datetimelike index" 

1993 ) from err 

1994 if isinstance(self._on, PeriodIndex): 

1995 # error: Incompatible types in assignment (expression has type 

1996 # "float", variable has type "Optional[int]") 

1997 self._win_freq_i8 = freq.nanos / ( # type: ignore[assignment] 

1998 self._on.freq.nanos / self._on.freq.n 

1999 ) 

2000 else: 

2001 try: 

2002 unit = dtype_to_unit(self._on.dtype) # type: ignore[arg-type] 

2003 except TypeError: 

2004 # if not a datetime dtype, eg for empty dataframes 

2005 unit = "ns" 

2006 unit = cast("TimeUnit", unit) 

2007 self._win_freq_i8 = Timedelta(freq.nanos).as_unit(unit)._value 

2008 

2009 # min_periods must be an integer 

2010 if self.min_periods is None: 

2011 self.min_periods = 1 

2012 

2013 if self.step is not None: 

2014 raise NotImplementedError( 

2015 "step is not supported with frequency windows" 

2016 ) 

2017 

2018 elif isinstance(self.window, BaseIndexer): 

2019 # Passed BaseIndexer subclass should handle all other rolling kwargs 

2020 pass 

2021 elif not is_integer(self.window) or self.window < 0: 

2022 raise ValueError("window must be an integer 0 or greater") 

2023 

2024 def _validate_datetimelike_monotonic(self) -> None: 

2025 """ 

2026 Validate self._on is monotonic (increasing or decreasing) and has 

2027 no NaT values for frequency windows. 

2028 """ 

2029 if self._on.hasnans: 

2030 self._raise_monotonic_error("values must not have NaT") 

2031 if not (self._on.is_monotonic_increasing or self._on.is_monotonic_decreasing): 

2032 self._raise_monotonic_error("values must be monotonic") 

2033 

2034 def _raise_monotonic_error(self, msg: str): 

2035 on = self.on 

2036 if on is None: 

2037 on = "index" 

2038 raise ValueError(f"{on} {msg}") 

2039 

2040 def aggregate(self, func=None, *args, **kwargs): 

2041 """ 

2042 Aggregate using one or more operations over the specified axis. 

2043 

2044 Parameters 

2045 ---------- 

2046 func : function, str, list or dict 

2047 Function to use for aggregating the data. If a function, must either 

2048 work when passed a Series/Dataframe or 

2049 when passed to Series/Dataframe.apply. 

2050 

2051 Accepted combinations are: 

2052 

2053 - function 

2054 - string function name 

2055 - list of functions and/or function names, e.g. ``[np.sum, 'mean']`` 

2056 - dict of axis labels -> functions, function names or list of such. 

2057 

2058 *args 

2059 Positional arguments to pass to `func`. 

2060 **kwargs 

2061 Keyword arguments to pass to `func`. 

2062 

2063 Returns 

2064 ------- 

2065 scalar, Series or DataFrame 

2066 

2067 The return can be: 

2068 

2069 * scalar : when Series.agg is called with single function 

2070 * Series : when DataFrame.agg is called with a single function 

2071 * DataFrame : when DataFrame.agg is called with several functions 

2072 

2073 See Also 

2074 -------- 

2075 Series.rolling : Calling object with Series data. 

2076 DataFrame.rolling : Calling object with DataFrame data. 

2077 

2078 Notes 

2079 ----- 

2080 The aggregation operations are always performed over an axis, either the 

2081 index (default) or the column axis. This behavior is different from 

2082 `numpy` aggregation functions (`mean`, `median`, `prod`, `sum`, `std`, 

2083 `var`), where the default is to compute the aggregation of the flattened 

2084 array, e.g., ``numpy.mean(arr_2d)`` as opposed to 

2085 ``numpy.mean(arr_2d, axis=0)``. 

2086 

2087 `agg` is an alias for `aggregate`. Use the alias. 

2088 

2089 Functions that mutate the passed object can produce unexpected 

2090 behavior or errors and are not supported. See :ref:`gotchas.udf-mutation` 

2091 for more details. 

2092 

2093 A passed user-defined-function will be passed a Series for evaluation. 

2094 

2095 If ``func`` defines an index relabeling, ``axis`` must be ``0`` or ``index``. 

2096 

2097 Examples 

2098 -------- 

2099 >>> df = pd.DataFrame({"A": [1, 2, 3], "B": [4, 5, 6], "C": [7, 8, 9]}) 

2100 >>> df 

2101 A B C 

2102 0 1 4 7 

2103 1 2 5 8 

2104 2 3 6 9 

2105 

2106 >>> df.rolling(2).sum() 

2107 A B C 

2108 0 NaN NaN NaN 

2109 1 3.0 9.0 15.0 

2110 2 5.0 11.0 17.0 

2111 

2112 >>> df.rolling(2).agg({"A": "sum", "B": "min"}) 

2113 A B 

2114 0 NaN NaN 

2115 1 3.0 4.0 

2116 2 5.0 5.0 

2117 """ 

2118 return super().aggregate(func, *args, **kwargs) 

2119 

2120 agg = aggregate 

2121 

2122 def count(self, numeric_only: bool = False): 

2123 """ 

2124 Calculate the rolling count of non NaN observations. 

2125 

2126 Parameters 

2127 ---------- 

2128 numeric_only : bool, default False 

2129 Include only float, int, boolean columns. 

2130 

2131 Returns 

2132 ------- 

2133 Series or DataFrame 

2134 Return type is the same as the original object with ``np.float64`` dtype. 

2135 

2136 See Also 

2137 -------- 

2138 Series.rolling : Calling rolling with Series data. 

2139 DataFrame.rolling : Calling rolling with DataFrames. 

2140 Series.count : Aggregating count for Series. 

2141 DataFrame.count : Aggregating count for DataFrame. 

2142 

2143 Examples 

2144 -------- 

2145 >>> s = pd.Series([2, 3, np.nan, 10]) 

2146 >>> s.rolling(2).count() 

2147 0 NaN 

2148 1 2.0 

2149 2 1.0 

2150 3 1.0 

2151 dtype: float64 

2152 >>> s.rolling(3).count() 

2153 0 NaN 

2154 1 NaN 

2155 2 2.0 

2156 3 2.0 

2157 dtype: float64 

2158 >>> s.rolling(4).count() 

2159 0 NaN 

2160 1 NaN 

2161 2 NaN 

2162 3 3.0 

2163 dtype: float64 

2164 """ 

2165 return super().count(numeric_only) 

2166 

2167 def apply( 

2168 self, 

2169 func: Callable[..., Any], 

2170 raw: bool = False, 

2171 engine: Literal["cython", "numba"] | None = None, 

2172 engine_kwargs: dict[str, bool] | None = None, 

2173 args: tuple[Any, ...] | None = None, 

2174 kwargs: dict[str, Any] | None = None, 

2175 ): 

2176 """ 

2177 Calculate the rolling custom aggregation function. 

2178 

2179 Parameters 

2180 ---------- 

2181 func : function 

2182 Must produce a single value from an ndarray input if ``raw=True`` 

2183 or a single value from a Series if ``raw=False``. Can also accept a 

2184 Numba JIT function with ``engine='numba'`` specified. 

2185 

2186 raw : bool, default False 

2187 * ``False`` : passes each row or column as a Series to the 

2188 function. 

2189 * ``True`` : the passed function will receive ndarray 

2190 objects instead. 

2191 

2192 If you are just applying a NumPy reduction function this will 

2193 achieve much better performance. 

2194 

2195 engine : str, default None 

2196 * ``'cython'`` : Runs rolling apply through C-extensions from cython. 

2197 * ``'numba'`` : Runs rolling apply through JIT compiled code from numba. 

2198 Only available when ``raw`` is set to ``True``. 

2199 * ``None`` : Defaults to ``'cython'`` or 

2200 globally setting ``compute.use_numba``. 

2201 

2202 engine_kwargs : dict, default None 

2203 * For ``'cython'`` engine, there are no accepted ``engine_kwargs`` 

2204 * For ``'numba'`` engine, the engine can accept ``nopython``, ``nogil`` 

2205 and ``parallel`` dictionary keys. The values must either be ``True`` or 

2206 ``False``. 

2207 

2208 The default ``engine_kwargs`` for the ``'numba'`` engine is 

2209 ``{'nopython': True, 'nogil': False, 'parallel': False}`` and will be 

2210 applied to both the ``func`` and the ``apply`` rolling aggregation. 

2211 

2212 args : tuple, default None 

2213 Positional arguments to be passed into func. 

2214 

2215 kwargs : dict, default None 

2216 Keyword arguments to be passed into func. 

2217 

2218 Returns 

2219 ------- 

2220 Series or DataFrame 

2221 Return type is the same as the original object with ``np.float64`` dtype. 

2222 

2223 See Also 

2224 -------- 

2225 Series.rolling : Calling rolling with Series data. 

2226 DataFrame.rolling : Calling rolling with DataFrames. 

2227 Series.apply : Aggregating apply for Series. 

2228 DataFrame.apply : Aggregating apply for DataFrame. 

2229 

2230 Examples 

2231 -------- 

2232 >>> ser = pd.Series([1, 6, 5, 4]) 

2233 >>> ser.rolling(2).apply(lambda s: s.sum() - s.min()) 

2234 0 NaN 

2235 1 6.0 

2236 2 6.0 

2237 3 5.0 

2238 dtype: float64 

2239 """ 

2240 return super().apply( 

2241 func, 

2242 raw=raw, 

2243 engine=engine, 

2244 engine_kwargs=engine_kwargs, 

2245 args=args, 

2246 kwargs=kwargs, 

2247 ) 

2248 

2249 @overload 

2250 def pipe( 

2251 self, 

2252 func: Callable[Concatenate[Self, P], T], 

2253 *args: P.args, 

2254 **kwargs: P.kwargs, 

2255 ) -> T: ... 

2256 

2257 @overload 

2258 def pipe( 

2259 self, 

2260 func: tuple[Callable[..., T], str], 

2261 *args: Any, 

2262 **kwargs: Any, 

2263 ) -> T: ... 

2264 

2265 @final 

2266 def pipe( 

2267 self, 

2268 func: Callable[Concatenate[Self, P], T] | tuple[Callable[..., T], str], 

2269 *args: Any, 

2270 **kwargs: Any, 

2271 ) -> T: 

2272 """ 

2273 Apply a ``func`` with arguments to this Rolling object and return its result. 

2274 

2275 Use `.pipe` when you want to improve readability by chaining together 

2276 functions that expect 

2277 Series, DataFrames, GroupBy, Rolling, Expanding or Resampler 

2278 objects. 

2279 Instead of writing 

2280 

2281 >>> h = lambda x, arg2, arg3: x + 1 - arg2 * arg3 

2282 >>> g = lambda x, arg1: x * 5 / arg1 

2283 >>> f = lambda x: x**4 

2284 >>> df = pd.DataFrame( 

2285 ... {"A": [1, 2, 3, 4]}, index=pd.date_range("2012-08-02", periods=4) 

2286 ... ) 

2287 >>> h(g(f(df.rolling("2D")), arg1=1), arg2=2, arg3=3) # doctest: +SKIP 

2288 

2289 You can write 

2290 

2291 >>> ( 

2292 ... df.rolling("2D").pipe(f).pipe(g, arg1=1).pipe(h, arg2=2, arg3=3) 

2293 ... ) # doctest: +SKIP 

2294 

2295 which is much more readable. 

2296 

2297 Parameters 

2298 ---------- 

2299 func : callable or tuple of (callable, str) 

2300 Function to apply to this Rolling object or, alternatively, 

2301 a `(callable, data_keyword)` tuple where `data_keyword` is a 

2302 string indicating the keyword of `callable` that expects the 

2303 Rolling object. 

2304 *args : iterable, optional 

2305 Positional arguments passed into `func`. 

2306 **kwargs : dict, optional 

2307 A dictionary of keyword arguments passed into `func`. 

2308 

2309 Returns 

2310 ------- 

2311 Rolling 

2312 The original object with the function `func` applied. 

2313 

2314 See Also 

2315 -------- 

2316 Series.pipe : Apply a function with arguments to a series. 

2317 DataFrame.pipe: Apply a function with arguments to a dataframe. 

2318 apply : Apply function to each group instead of to the 

2319 full Rolling object. 

2320 

2321 Notes 

2322 ----- 

2323 See more `here 

2324 <https://pandas.pydata.org/pandas-docs/stable/user_guide/groupby.html#piping-function-calls>`__. 

2325 

2326 Examples 

2327 -------- 

2328 

2329 >>> df = pd.DataFrame( 

2330 ... {"A": [1, 2, 3, 4]}, index=pd.date_range("2012-08-02", periods=4) 

2331 ... ) 

2332 >>> df 

2333 A 

2334 2012-08-02 1 

2335 2012-08-03 2 

2336 2012-08-04 3 

2337 2012-08-05 4 

2338 

2339 To get the difference between each rolling 

2340 2-day window's maximum and minimum 

2341 value in one pass, you can do 

2342 

2343 >>> df.rolling("2D").pipe(lambda x: x.max() - x.min()) 

2344 A 

2345 2012-08-02 0.0 

2346 2012-08-03 1.0 

2347 2012-08-04 1.0 

2348 2012-08-05 1.0 

2349 """ 

2350 return super().pipe(func, *args, **kwargs) 

2351 

2352 def sum( 

2353 self, 

2354 numeric_only: bool = False, 

2355 engine: Literal["cython", "numba"] | None = None, 

2356 engine_kwargs: dict[str, bool] | None = None, 

2357 ): 

2358 """ 

2359 Calculate the rolling sum. 

2360 

2361 Parameters 

2362 ---------- 

2363 numeric_only : bool, default False 

2364 Include only float, int, boolean columns. 

2365 

2366 engine : str, default None 

2367 * ``'cython'`` : Runs the operation through C-extensions from cython. 

2368 * ``'numba'`` : Runs the operation through JIT compiled code from numba. 

2369 * ``None`` : Defaults to ``'cython'`` or 

2370 globally setting ``compute.use_numba`` 

2371 

2372 engine_kwargs : dict, default None 

2373 * For ``'cython'`` engine, there are no accepted ``engine_kwargs`` 

2374 * For ``'numba'`` engine, the engine can accept ``nopython``, ``nogil`` 

2375 and ``parallel`` dictionary keys. The values must either be ``True`` or 

2376 ``False``. The default ``engine_kwargs`` for the ``'numba'`` engine is 

2377 ``{'nopython': True, 'nogil': False, 'parallel': False}``. 

2378 

2379 Returns 

2380 ------- 

2381 Series or DataFrame 

2382 Return type is the same as the original object with ``np.float64`` dtype. 

2383 

2384 See Also 

2385 -------- 

2386 Series.rolling : Calling rolling with Series data. 

2387 DataFrame.rolling : Calling rolling with DataFrames. 

2388 Series.sum : Aggregating sum for Series. 

2389 DataFrame.sum : Aggregating sum for DataFrame. 

2390 

2391 Notes 

2392 ----- 

2393 See :ref:`window.numba_engine` and :ref:`enhancingperf.numba` 

2394 for extended documentation and performance considerations 

2395 for the Numba engine. 

2396 

2397 Examples 

2398 -------- 

2399 >>> s = pd.Series([1, 2, 3, 4, 5]) 

2400 >>> s 

2401 0 1 

2402 1 2 

2403 2 3 

2404 3 4 

2405 4 5 

2406 dtype: int64 

2407 

2408 >>> s.rolling(3).sum() 

2409 0 NaN 

2410 1 NaN 

2411 2 6.0 

2412 3 9.0 

2413 4 12.0 

2414 dtype: float64 

2415 

2416 >>> s.rolling(3, center=True).sum() 

2417 0 NaN 

2418 1 6.0 

2419 2 9.0 

2420 3 12.0 

2421 4 NaN 

2422 dtype: float64 

2423 

2424 For DataFrame, each sum is computed column-wise. 

2425 

2426 >>> df = pd.DataFrame({"A": s, "B": s**2}) 

2427 >>> df 

2428 A B 

2429 0 1 1 

2430 1 2 4 

2431 2 3 9 

2432 3 4 16 

2433 4 5 25 

2434 

2435 >>> df.rolling(3).sum() 

2436 A B 

2437 0 NaN NaN 

2438 1 NaN NaN 

2439 2 6.0 14.0 

2440 3 9.0 29.0 

2441 4 12.0 50.0 

2442 """ 

2443 return super().sum( 

2444 numeric_only=numeric_only, 

2445 engine=engine, 

2446 engine_kwargs=engine_kwargs, 

2447 ) 

2448 

2449 def max( 

2450 self, 

2451 numeric_only: bool = False, 

2452 *args, 

2453 engine: Literal["cython", "numba"] | None = None, 

2454 engine_kwargs: dict[str, bool] | None = None, 

2455 **kwargs, 

2456 ): 

2457 """ 

2458 Calculate the rolling maximum. 

2459 

2460 Parameters 

2461 ---------- 

2462 numeric_only : bool, default False 

2463 Include only float, int, boolean columns. 

2464 

2465 *args : iterable, optional 

2466 Positional arguments passed into ``func``. 

2467 

2468 engine : str, default None 

2469 * ``'cython'`` : Runs the operation through C-extensions from cython. 

2470 * ``'numba'`` : Runs the operation through JIT compiled code from numba. 

2471 * ``None`` : Defaults to ``'cython'`` or 

2472 globally setting ``compute.use_numba`` 

2473 

2474 engine_kwargs : dict, default None 

2475 * For ``'cython'`` engine, there are no accepted ``engine_kwargs`` 

2476 * For ``'numba'`` engine, the engine can accept ``nopython``, ``nogil`` 

2477 and ``parallel`` dictionary keys. The values must either be ``True`` or 

2478 ``False``. 

2479 

2480 The default ``engine_kwargs`` for the ``'numba'`` engine is 

2481 ``{'nopython': True, 'nogil': False, 'parallel': False}``. 

2482 

2483 **kwargs : mapping, optional 

2484 A dictionary of keyword arguments passed into ``func``. 

2485 

2486 Returns 

2487 ------- 

2488 Series or DataFrame 

2489 Return type is the same as the original object with ``np.float64`` dtype. 

2490 

2491 See Also 

2492 -------- 

2493 Series.rolling : Calling rolling with Series data. 

2494 DataFrame.rolling : Calling rolling with DataFrames. 

2495 Series.max : Aggregating max for Series. 

2496 DataFrame.max : Aggregating max for DataFrame. 

2497 

2498 Notes 

2499 ----- 

2500 See :ref:`window.numba_engine` and :ref:`enhancingperf.numba` 

2501 for extended documentation and performance considerations 

2502 for the Numba engine. 

2503 

2504 Examples 

2505 -------- 

2506 >>> ser = pd.Series([1, 2, 3, 4]) 

2507 >>> ser.rolling(2).max() 

2508 0 NaN 

2509 1 2.0 

2510 2 3.0 

2511 3 4.0 

2512 dtype: float64 

2513 """ 

2514 return super().max( 

2515 numeric_only=numeric_only, 

2516 engine=engine, 

2517 engine_kwargs=engine_kwargs, 

2518 ) 

2519 

2520 def min( 

2521 self, 

2522 numeric_only: bool = False, 

2523 engine: Literal["cython", "numba"] | None = None, 

2524 engine_kwargs: dict[str, bool] | None = None, 

2525 ): 

2526 """ 

2527 Calculate the rolling minimum. 

2528 

2529 Parameters 

2530 ---------- 

2531 numeric_only : bool, default False 

2532 Include only float, int, boolean columns. 

2533 

2534 engine : str, default None 

2535 * ``'cython'`` : Runs the operation through C-extensions from cython. 

2536 * ``'numba'`` : Runs the operation through JIT compiled code from numba. 

2537 * ``None`` : Defaults to ``'cython'`` or 

2538 globally setting ``compute.use_numba`` 

2539 

2540 engine_kwargs : dict, default None 

2541 * For ``'cython'`` engine, there are no accepted ``engine_kwargs`` 

2542 * For ``'numba'`` engine, the engine can accept ``nopython``, ``nogil`` 

2543 and ``parallel`` dictionary keys. The values must either be ``True`` or 

2544 ``False``. 

2545 

2546 The default ``engine_kwargs`` for the ``'numba'`` engine is 

2547 ``{'nopython': True, 'nogil': False, 'parallel': False}``. 

2548 

2549 Returns 

2550 ------- 

2551 Series or DataFrame 

2552 Return type is the same as the original object with ``np.float64`` dtype. 

2553 

2554 See Also 

2555 -------- 

2556 Series.rolling : Calling rolling with Series data. 

2557 DataFrame.rolling : Calling rolling with DataFrames. 

2558 Series.min : Aggregating min for Series. 

2559 DataFrame.min : Aggregating min for DataFrame. 

2560 

2561 Notes 

2562 ----- 

2563 See :ref:`window.numba_engine` and :ref:`enhancingperf.numba` 

2564 for extended documentation and performance considerations 

2565 for the Numba engine. 

2566 

2567 Examples 

2568 -------- 

2569 Performing a rolling minimum with a window size of 3. 

2570 

2571 >>> s = pd.Series([4, 3, 5, 2, 6]) 

2572 >>> s.rolling(3).min() 

2573 0 NaN 

2574 1 NaN 

2575 2 3.0 

2576 3 2.0 

2577 4 2.0 

2578 dtype: float64 

2579 """ 

2580 return super().min( 

2581 numeric_only=numeric_only, 

2582 engine=engine, 

2583 engine_kwargs=engine_kwargs, 

2584 ) 

2585 

2586 def mean( 

2587 self, 

2588 numeric_only: bool = False, 

2589 engine: Literal["cython", "numba"] | None = None, 

2590 engine_kwargs: dict[str, bool] | None = None, 

2591 ): 

2592 """ 

2593 Calculate the rolling mean. 

2594 

2595 Parameters 

2596 ---------- 

2597 numeric_only : bool, default False 

2598 Include only float, int, boolean columns. 

2599 

2600 engine : str, default None 

2601 * ``'cython'`` : Runs the operation through C-extensions from cython. 

2602 * ``'numba'`` : Runs the operation through JIT compiled code from numba. 

2603 * ``None`` : Defaults to ``'cython'`` or 

2604 globally setting ``compute.use_numba`` 

2605 

2606 engine_kwargs : dict, default None 

2607 * For ``'cython'`` engine, there are no accepted ``engine_kwargs`` 

2608 * For ``'numba'`` engine, the engine can accept ``nopython``, ``nogil`` 

2609 and ``parallel`` dictionary keys. The values must either be ``True`` or 

2610 ``False``. 

2611 

2612 The default ``engine_kwargs`` for the ``'numba'`` engine is 

2613 ``{'nopython': True, 'nogil': False, 'parallel': False}``. 

2614 

2615 Returns 

2616 ------- 

2617 Series or DataFrame 

2618 Return type is the same as the original object with ``np.float64`` dtype. 

2619 

2620 See Also 

2621 -------- 

2622 Series.rolling : Calling rolling with Series data. 

2623 DataFrame.rolling : Calling rolling with DataFrames. 

2624 Series.mean : Aggregating mean for Series. 

2625 DataFrame.mean : Aggregating mean for DataFrame. 

2626 

2627 Notes 

2628 ----- 

2629 See :ref:`window.numba_engine` and :ref:`enhancingperf.numba` 

2630 for extended documentation and performance considerations 

2631 for the Numba engine. 

2632 

2633 Examples 

2634 -------- 

2635 The below examples will show rolling mean calculations with window sizes of 

2636 two and three, respectively. 

2637 

2638 >>> s = pd.Series([1, 2, 3, 4]) 

2639 >>> s.rolling(2).mean() 

2640 0 NaN 

2641 1 1.5 

2642 2 2.5 

2643 3 3.5 

2644 dtype: float64 

2645 

2646 >>> s.rolling(3).mean() 

2647 0 NaN 

2648 1 NaN 

2649 2 2.0 

2650 3 3.0 

2651 dtype: float64 

2652 """ 

2653 return super().mean( 

2654 numeric_only=numeric_only, 

2655 engine=engine, 

2656 engine_kwargs=engine_kwargs, 

2657 ) 

2658 

2659 def median( 

2660 self, 

2661 numeric_only: bool = False, 

2662 engine: Literal["cython", "numba"] | None = None, 

2663 engine_kwargs: dict[str, bool] | None = None, 

2664 ): 

2665 """ 

2666 Calculate the rolling median. 

2667 

2668 Parameters 

2669 ---------- 

2670 numeric_only : bool, default False 

2671 Include only float, int, boolean columns. 

2672 

2673 engine : str, default None 

2674 * ``'cython'`` : Runs the operation through C-extensions from cython. 

2675 * ``'numba'`` : Runs the operation through JIT compiled code from numba. 

2676 * ``None`` : Defaults to ``'cython'`` or 

2677 globally setting ``compute.use_numba`` 

2678 

2679 engine_kwargs : dict, default None 

2680 * For ``'cython'`` engine, there are no accepted ``engine_kwargs`` 

2681 * For ``'numba'`` engine, the engine can accept ``nopython``, ``nogil`` 

2682 and ``parallel`` dictionary keys. The values must either be ``True`` or 

2683 ``False``. 

2684 

2685 The default ``engine_kwargs`` for the ``'numba'`` engine is 

2686 ``{'nopython': True, 'nogil': False, 'parallel': False}``. 

2687 

2688 Returns 

2689 ------- 

2690 Series or DataFrame 

2691 Return type is the same as the original object with ``np.float64`` dtype. 

2692 

2693 See Also 

2694 -------- 

2695 Series.rolling : Calling rolling with Series data. 

2696 DataFrame.rolling : Calling rolling with DataFrames. 

2697 Series.median : Aggregating median for Series. 

2698 DataFrame.median : Aggregating median for DataFrame. 

2699 

2700 Notes 

2701 ----- 

2702 See :ref:`window.numba_engine` and :ref:`enhancingperf.numba` 

2703 for extended documentation and performance considerations 

2704 for the Numba engine. 

2705 

2706 Examples 

2707 -------- 

2708 Compute the rolling median of a series with a window size of 3. 

2709 

2710 >>> s = pd.Series([0, 1, 2, 3, 4]) 

2711 >>> s.rolling(3).median() 

2712 0 NaN 

2713 1 NaN 

2714 2 1.0 

2715 3 2.0 

2716 4 3.0 

2717 dtype: float64 

2718 """ 

2719 return super().median( 

2720 numeric_only=numeric_only, 

2721 engine=engine, 

2722 engine_kwargs=engine_kwargs, 

2723 ) 

2724 

2725 def std( 

2726 self, 

2727 ddof: int = 1, 

2728 numeric_only: bool = False, 

2729 engine: Literal["cython", "numba"] | None = None, 

2730 engine_kwargs: dict[str, bool] | None = None, 

2731 ): 

2732 """ 

2733 Calculate the rolling standard deviation. 

2734 

2735 Parameters 

2736 ---------- 

2737 ddof : int, default 1 

2738 Delta Degrees of Freedom. The divisor used in calculations 

2739 is ``N - ddof``, where ``N`` represents the number of elements. 

2740 

2741 numeric_only : bool, default False 

2742 Include only float, int, boolean columns. 

2743 

2744 engine : str, default None 

2745 * ``'cython'`` : Runs the operation through C-extensions from cython. 

2746 * ``'numba'`` : Runs the operation through JIT compiled code from numba. 

2747 * ``None`` : Defaults to ``'cython'`` or 

2748 globally setting ``compute.use_numba`` 

2749 

2750 engine_kwargs : dict, default None 

2751 * For ``'cython'`` engine, there are no accepted ``engine_kwargs`` 

2752 * For ``'numba'`` engine, the engine can accept ``nopython``, ``nogil`` 

2753 and ``parallel`` dictionary keys. The values must either be ``True`` or 

2754 ``False``. 

2755 

2756 The default ``engine_kwargs`` for the ``'numba'`` engine is 

2757 ``{'nopython': True, 'nogil': False, 'parallel': False}``. 

2758 

2759 Returns 

2760 ------- 

2761 Series or DataFrame 

2762 Return type is the same as the original object with ``np.float64`` dtype. 

2763 

2764 See Also 

2765 -------- 

2766 numpy.std : Equivalent method for NumPy array. 

2767 Series.rolling : Calling rolling with Series data. 

2768 DataFrame.rolling : Calling rolling with DataFrames. 

2769 Series.std : Aggregating std for Series. 

2770 DataFrame.std : Aggregating std for DataFrame. 

2771 

2772 Notes 

2773 ----- 

2774 The default ``ddof`` of 1 used in :meth:`Series.std` is different 

2775 than the default ``ddof`` of 0 in :func:`numpy.std`. 

2776 

2777 A minimum of one period is required for the rolling calculation. 

2778 

2779 Examples 

2780 -------- 

2781 >>> s = pd.Series([5, 5, 6, 7, 5, 5, 5]) 

2782 >>> s.rolling(3).std() 

2783 0 NaN 

2784 1 NaN 

2785 2 0.577350 

2786 3 1.000000 

2787 4 1.000000 

2788 5 1.154701 

2789 6 0.000000 

2790 dtype: float64 

2791 """ 

2792 return super().std( 

2793 ddof=ddof, 

2794 numeric_only=numeric_only, 

2795 engine=engine, 

2796 engine_kwargs=engine_kwargs, 

2797 ) 

2798 

2799 def var( 

2800 self, 

2801 ddof: int = 1, 

2802 numeric_only: bool = False, 

2803 engine: Literal["cython", "numba"] | None = None, 

2804 engine_kwargs: dict[str, bool] | None = None, 

2805 ): 

2806 """ 

2807 Calculate the rolling variance. 

2808 

2809 Parameters 

2810 ---------- 

2811 ddof : int, default 1 

2812 Delta Degrees of Freedom. The divisor used in calculations 

2813 is ``N - ddof``, where ``N`` represents the number of elements. 

2814 

2815 numeric_only : bool, default False 

2816 Include only float, int, boolean columns. 

2817 

2818 engine : str, default None 

2819 * ``'cython'`` : Runs the operation through C-extensions from cython. 

2820 * ``'numba'`` : Runs the operation through JIT compiled code from numba. 

2821 * ``None`` : Defaults to ``'cython'`` or 

2822 globally setting ``compute.use_numba`` 

2823 

2824 engine_kwargs : dict, default None 

2825 * For ``'cython'`` engine, there are no accepted ``engine_kwargs`` 

2826 * For ``'numba'`` engine, the engine can accept ``nopython``, ``nogil`` 

2827 and ``parallel`` dictionary keys. The values must either be ``True`` or 

2828 ``False``. 

2829 

2830 The default ``engine_kwargs`` for the ``'numba'`` engine is 

2831 ``{'nopython': True, 'nogil': False, 'parallel': False}``. 

2832 

2833 Returns 

2834 ------- 

2835 Series or DataFrame 

2836 Return type is the same as the original object with ``np.float64`` dtype. 

2837 

2838 See Also 

2839 -------- 

2840 numpy.var : Equivalent method for NumPy array. 

2841 Series.rolling : Calling rolling with Series data. 

2842 DataFrame.rolling : Calling rolling with DataFrames. 

2843 Series.var : Aggregating var for Series. 

2844 DataFrame.var : Aggregating var for DataFrame. 

2845 

2846 Notes 

2847 ----- 

2848 The default ``ddof`` of 1 used in :meth:`Series.var` is different 

2849 than the default ``ddof`` of 0 in :func:`numpy.var`. 

2850 

2851 A minimum of one period is required for the rolling calculation. 

2852 

2853 Examples 

2854 -------- 

2855 >>> s = pd.Series([5, 5, 6, 7, 5, 5, 5]) 

2856 >>> s.rolling(3).var() 

2857 0 NaN 

2858 1 NaN 

2859 2 0.333333 

2860 3 1.000000 

2861 4 1.000000 

2862 5 1.333333 

2863 6 0.000000 

2864 dtype: float64 

2865 """ 

2866 return super().var( 

2867 ddof=ddof, 

2868 numeric_only=numeric_only, 

2869 engine=engine, 

2870 engine_kwargs=engine_kwargs, 

2871 ) 

2872 

2873 def skew(self, numeric_only: bool = False): 

2874 """ 

2875 Calculate the rolling unbiased skewness. 

2876 

2877 Parameters 

2878 ---------- 

2879 numeric_only : bool, default False 

2880 Include only float, int, boolean columns. 

2881 

2882 Returns 

2883 ------- 

2884 Series or DataFrame 

2885 Return type is the same as the original object with ``np.float64`` dtype. 

2886 

2887 See Also 

2888 -------- 

2889 scipy.stats.skew : Third moment of a probability density. 

2890 Series.rolling : Calling rolling with Series data. 

2891 DataFrame.rolling : Calling rolling with DataFrames. 

2892 Series.skew : Aggregating skew for Series. 

2893 DataFrame.skew : Aggregating skew for DataFrame. 

2894 

2895 Notes 

2896 ----- 

2897 

2898 A minimum of three periods is required for the rolling calculation. 

2899 

2900 Examples 

2901 -------- 

2902 >>> ser = pd.Series([1, 5, 2, 7, 15, 6]) 

2903 >>> ser.rolling(3).skew().round(6) 

2904 0 NaN 

2905 1 NaN 

2906 2 1.293343 

2907 3 -0.585583 

2908 4 0.670284 

2909 5 1.652317 

2910 dtype: float64 

2911 """ 

2912 return super().skew(numeric_only=numeric_only) 

2913 

2914 def sem(self, ddof: int = 1, numeric_only: bool = False): 

2915 """ 

2916 Calculate the rolling standard error of mean. 

2917 

2918 Parameters 

2919 ---------- 

2920 ddof : int, default 1 

2921 Delta Degrees of Freedom. The divisor used in calculations 

2922 is ``N - ddof``, where ``N`` represents the number of elements. 

2923 

2924 numeric_only : bool, default False 

2925 Include only float, int, boolean columns. 

2926 

2927 Returns 

2928 ------- 

2929 Series or DataFrame 

2930 Return type is the same as the original object with ``np.float64`` dtype. 

2931 

2932 See Also 

2933 -------- 

2934 Series.rolling : Calling rolling with Series data. 

2935 DataFrame.rolling : Calling rolling with DataFrames. 

2936 Series.sem : Aggregating sem for Series. 

2937 DataFrame.sem : Aggregating sem for DataFrame. 

2938 

2939 Notes 

2940 ----- 

2941 A minimum of one period is required for the calculation. 

2942 

2943 Examples 

2944 -------- 

2945 >>> s = pd.Series([0, 1, 2, 3]) 

2946 >>> s.rolling(2, min_periods=1).sem() 

2947 0 NaN 

2948 1 0.5 

2949 2 0.5 

2950 3 0.5 

2951 dtype: float64 

2952 """ 

2953 # Raise here so error message says sem instead of std 

2954 self._validate_numeric_only("sem", numeric_only) 

2955 return self.std(numeric_only=numeric_only, ddof=ddof) / ( 

2956 self.count(numeric_only) 

2957 ).pow(0.5) 

2958 

2959 def kurt(self, numeric_only: bool = False): 

2960 """ 

2961 Calculate the rolling Fisher's definition of kurtosis without bias. 

2962 

2963 Parameters 

2964 ---------- 

2965 numeric_only : bool, default False 

2966 Include only float, int, boolean columns. 

2967 

2968 Returns 

2969 ------- 

2970 Series or DataFrame 

2971 Return type is the same as the original object with ``np.float64`` dtype. 

2972 

2973 See Also 

2974 -------- 

2975 scipy.stats.kurtosis : Reference SciPy method. 

2976 Series.rolling : Calling rolling with Series data. 

2977 DataFrame.rolling : Calling rolling with DataFrames. 

2978 Series.kurt : Aggregating kurt for Series. 

2979 DataFrame.kurt : Aggregating kurt for DataFrame. 

2980 

2981 Notes 

2982 ----- 

2983 A minimum of four periods is required for the calculation. 

2984 

2985 Examples 

2986 -------- 

2987 The example below will show a rolling calculation with a window size of 

2988 four matching the equivalent function call using `scipy.stats`. 

2989 

2990 >>> arr = [1, 2, 3, 4, 999] 

2991 >>> import scipy.stats 

2992 >>> print(f"{scipy.stats.kurtosis(arr[:-1], bias=False):.6f}") 

2993 -1.200000 

2994 >>> print(f"{scipy.stats.kurtosis(arr[1:], bias=False):.6f}") 

2995 3.999946 

2996 >>> s = pd.Series(arr) 

2997 >>> s.rolling(4).kurt() 

2998 0 NaN 

2999 1 NaN 

3000 2 NaN 

3001 3 -1.200000 

3002 4 3.999946 

3003 dtype: float64 

3004 """ 

3005 return super().kurt(numeric_only=numeric_only) 

3006 

3007 def first(self, numeric_only: bool = False): 

3008 """ 

3009 Calculate the rolling First (left-most) element of the window. 

3010 

3011 Parameters 

3012 ---------- 

3013 numeric_only : bool, default False 

3014 Include only float, int, boolean columns. 

3015 

3016 Returns 

3017 ------- 

3018 Series or DataFrame 

3019 Return type is the same as the original object with ``np.float64`` dtype. 

3020 

3021 See Also 

3022 -------- 

3023 GroupBy.first : Similar method for GroupBy objects. 

3024 Rolling.last : Method to get the last element in each window. 

3025 

3026 Examples 

3027 -------- 

3028 The example below will show a rolling calculation with a window size of 

3029 three. 

3030 

3031 >>> s = pd.Series(range(5)) 

3032 >>> s.rolling(3).first() 

3033 0 NaN 

3034 1 NaN 

3035 2 0.0 

3036 3 1.0 

3037 4 2.0 

3038 dtype: float64 

3039 """ 

3040 return super().first(numeric_only=numeric_only) 

3041 

3042 def last(self, numeric_only: bool = False): 

3043 """ 

3044 Calculate the rolling Last (right-most) element of the window. 

3045 

3046 Parameters 

3047 ---------- 

3048 numeric_only : bool, default False 

3049 Include only float, int, boolean columns. 

3050 

3051 Returns 

3052 ------- 

3053 Series or DataFrame 

3054 Return type is the same as the original object with ``np.float64`` dtype. 

3055 

3056 See Also 

3057 -------- 

3058 GroupBy.last : Similar method for GroupBy objects. 

3059 Rolling.first : Method to get the first element in each window. 

3060 

3061 Examples 

3062 -------- 

3063 The example below will show a rolling calculation with a window size of 

3064 three. 

3065 

3066 >>> s = pd.Series(range(5)) 

3067 >>> s.rolling(3).last() 

3068 0 NaN 

3069 1 NaN 

3070 2 2.0 

3071 3 3.0 

3072 4 4.0 

3073 dtype: float64 

3074 """ 

3075 return super().last(numeric_only=numeric_only) 

3076 

3077 def quantile( 

3078 self, 

3079 q: float, 

3080 interpolation: QuantileInterpolation = "linear", 

3081 numeric_only: bool = False, 

3082 ): 

3083 """ 

3084 Calculate the rolling quantile. 

3085 

3086 Parameters 

3087 ---------- 

3088 q : float 

3089 Quantile to compute. 0 <= quantile <= 1. 

3090 

3091 interpolation : {'linear', 'lower', 'higher', 'midpoint', 'nearest'} 

3092 This optional parameter specifies the interpolation method to use, 

3093 when the desired quantile lies between two data points `i` and `j`: 

3094 

3095 * linear: `i + (j - i) * fraction`, where `fraction` is the 

3096 fractional part of the index surrounded by `i` and `j`. 

3097 * lower: `i`. 

3098 * higher: `j`. 

3099 * nearest: `i` or `j` whichever is nearest. 

3100 * midpoint: (`i` + `j`) / 2. 

3101 

3102 numeric_only : bool, default False 

3103 Include only float, int, boolean columns. 

3104 

3105 Returns 

3106 ------- 

3107 Series or DataFrame 

3108 Return type is the same as the original object with ``np.float64`` dtype. 

3109 

3110 See Also 

3111 -------- 

3112 Series.rolling : Calling rolling with Series data. 

3113 DataFrame.rolling : Calling rolling with DataFrames. 

3114 Series.quantile : Aggregating quantile for Series. 

3115 DataFrame.quantile : Aggregating quantile for DataFrame. 

3116 

3117 Examples 

3118 -------- 

3119 >>> s = pd.Series([1, 2, 3, 4]) 

3120 >>> s.rolling(2).quantile(0.4, interpolation="lower") 

3121 0 NaN 

3122 1 1.0 

3123 2 2.0 

3124 3 3.0 

3125 dtype: float64 

3126 

3127 >>> s.rolling(2).quantile(0.4, interpolation="midpoint") 

3128 0 NaN 

3129 1 1.5 

3130 2 2.5 

3131 3 3.5 

3132 dtype: float64 

3133 """ 

3134 return super().quantile( 

3135 q=q, 

3136 interpolation=interpolation, 

3137 numeric_only=numeric_only, 

3138 ) 

3139 

3140 def rank( 

3141 self, 

3142 method: WindowingRankType = "average", 

3143 ascending: bool = True, 

3144 pct: bool = False, 

3145 numeric_only: bool = False, 

3146 ): 

3147 """ 

3148 Calculate the rolling rank. 

3149 

3150 Parameters 

3151 ---------- 

3152 method : {'average', 'min', 'max'}, default 'average' 

3153 How to rank the group of records that have the same value (i.e. ties): 

3154 

3155 * average: average rank of the group 

3156 * min: lowest rank in the group 

3157 * max: highest rank in the group 

3158 

3159 ascending : bool, default True 

3160 Whether or not the elements should be ranked in ascending order. 

3161 

3162 pct : bool, default False 

3163 Whether or not to display the returned rankings in percentile 

3164 form. 

3165 

3166 numeric_only : bool, default False 

3167 Include only float, int, boolean columns. 

3168 

3169 Returns 

3170 ------- 

3171 Series or DataFrame 

3172 Return type is the same as the original object with ``np.float64`` dtype. 

3173 

3174 See Also 

3175 -------- 

3176 Series.rolling : Calling rolling with Series data. 

3177 DataFrame.rolling : Calling rolling with DataFrames. 

3178 Series.rank : Aggregating rank for Series. 

3179 DataFrame.rank : Aggregating rank for DataFrame. 

3180 

3181 Examples 

3182 -------- 

3183 >>> s = pd.Series([1, 4, 2, 3, 5, 3]) 

3184 >>> s.rolling(3).rank() 

3185 0 NaN 

3186 1 NaN 

3187 2 2.0 

3188 3 2.0 

3189 4 3.0 

3190 5 1.5 

3191 dtype: float64 

3192 

3193 >>> s.rolling(3).rank(method="max") 

3194 0 NaN 

3195 1 NaN 

3196 2 2.0 

3197 3 2.0 

3198 4 3.0 

3199 5 2.0 

3200 dtype: float64 

3201 

3202 >>> s.rolling(3).rank(method="min") 

3203 0 NaN 

3204 1 NaN 

3205 2 2.0 

3206 3 2.0 

3207 4 3.0 

3208 5 1.0 

3209 dtype: float64 

3210 """ 

3211 return super().rank( 

3212 method=method, 

3213 ascending=ascending, 

3214 pct=pct, 

3215 numeric_only=numeric_only, 

3216 ) 

3217 

3218 def nunique( 

3219 self, 

3220 numeric_only: bool = False, 

3221 ): 

3222 """ 

3223 Calculate the rolling nunique. 

3224 

3225 .. versionadded:: 3.0.0 

3226 

3227 Parameters 

3228 ---------- 

3229 numeric_only : bool, default False 

3230 Include only float, int, boolean columns. 

3231 

3232 Returns 

3233 ------- 

3234 Series or DataFrame 

3235 Return type is the same as the original object with ``np.float64`` dtype. 

3236 

3237 See Also 

3238 -------- 

3239 Series.rolling : Calling rolling with Series data. 

3240 DataFrame.rolling : Calling rolling with DataFrames. 

3241 Series.nunique : Aggregating nunique for Series. 

3242 DataFrame.nunique : Aggregating nunique for DataFrame. 

3243 

3244 Examples 

3245 -------- 

3246 >>> s = pd.Series([1, 4, 2, np.nan, 3, 3, 4, 5]) 

3247 >>> s.rolling(3).nunique() 

3248 0 NaN 

3249 1 NaN 

3250 2 3.0 

3251 3 NaN 

3252 4 NaN 

3253 5 NaN 

3254 6 2.0 

3255 7 3.0 

3256 dtype: float64 

3257 """ 

3258 return super().nunique( 

3259 numeric_only=numeric_only, 

3260 ) 

3261 

3262 def cov( 

3263 self, 

3264 other: DataFrame | Series | None = None, 

3265 pairwise: bool | None = None, 

3266 ddof: int = 1, 

3267 numeric_only: bool = False, 

3268 ): 

3269 """ 

3270 Calculate the rolling sample covariance. 

3271 

3272 Parameters 

3273 ---------- 

3274 other : Series or DataFrame, optional 

3275 If not supplied then will default to self and produce pairwise 

3276 output. 

3277 

3278 pairwise : bool, default None 

3279 If False then only matching columns between self and other will be 

3280 used and the output will be a DataFrame. 

3281 If True then all pairwise combinations will be calculated and the 

3282 output will be a MultiIndexed DataFrame in the case of DataFrame 

3283 inputs. In the case of missing elements, only complete pairwise 

3284 observations will be used. 

3285 

3286 ddof : int, default 1 

3287 Delta Degrees of Freedom. The divisor used in calculations 

3288 is ``N - ddof``, where ``N`` represents the number of elements. 

3289 

3290 numeric_only : bool, default False 

3291 Include only float, int, boolean columns. 

3292 

3293 Returns 

3294 ------- 

3295 Series or DataFrame 

3296 Return type is the same as the original object with ``np.float64`` dtype. 

3297 

3298 See Also 

3299 -------- 

3300 Series.rolling : Calling rolling with Series data. 

3301 DataFrame.rolling : Calling rolling with DataFrames. 

3302 Series.cov : Aggregating cov for Series. 

3303 DataFrame.cov : Aggregating cov for DataFrame. 

3304 

3305 Examples 

3306 -------- 

3307 >>> ser1 = pd.Series([1, 2, 3, 4]) 

3308 >>> ser2 = pd.Series([1, 4, 5, 8]) 

3309 >>> ser1.rolling(2).cov(ser2) 

3310 0 NaN 

3311 1 1.5 

3312 2 0.5 

3313 3 1.5 

3314 dtype: float64 

3315 """ 

3316 return super().cov( 

3317 other=other, 

3318 pairwise=pairwise, 

3319 ddof=ddof, 

3320 numeric_only=numeric_only, 

3321 ) 

3322 

3323 def corr( 

3324 self, 

3325 other: DataFrame | Series | None = None, 

3326 pairwise: bool | None = None, 

3327 ddof: int = 1, 

3328 numeric_only: bool = False, 

3329 ): 

3330 """ 

3331 Calculate the rolling correlation. 

3332 

3333 Parameters 

3334 ---------- 

3335 other : Series or DataFrame, optional 

3336 If not supplied then will default to self and produce pairwise 

3337 output. 

3338 

3339 pairwise : bool, default None 

3340 If False then only matching columns between self and other will be 

3341 used and the output will be a DataFrame. 

3342 If True then all pairwise combinations will be calculated and the 

3343 output will be a MultiIndexed DataFrame in the case of DataFrame 

3344 inputs. In the case of missing elements, only complete pairwise 

3345 observations will be used. 

3346 

3347 ddof : int, default 1 

3348 Delta Degrees of Freedom. The divisor used in calculations 

3349 is ``N - ddof``, where ``N`` represents the number of elements. 

3350 

3351 numeric_only : bool, default False 

3352 Include only float, int, boolean columns. 

3353 

3354 Returns 

3355 ------- 

3356 Series or DataFrame 

3357 Return type is the same as the original object with ``np.float64`` dtype. 

3358 

3359 See Also 

3360 -------- 

3361 cov : Similar method to calculate covariance. 

3362 numpy.corrcoef : NumPy Pearson's correlation calculation. 

3363 Series.rolling : Calling rolling with Series data. 

3364 DataFrame.rolling : Calling rolling with DataFrames. 

3365 Series.corr : Aggregating corr for Series. 

3366 DataFrame.corr : Aggregating corr for DataFrame. 

3367 

3368 Notes 

3369 ----- 

3370 This function uses Pearson's definition of correlation 

3371 (https://en.wikipedia.org/wiki/Pearson_correlation_coefficient). 

3372 

3373 When `other` is not specified, the output will be self correlation (e.g. 

3374 all 1's), except for :class:`~pandas.DataFrame` inputs with `pairwise` 

3375 set to `True`. 

3376 

3377 Function will return ``NaN`` for correlations of equal valued sequences; 

3378 this is the result of a 0/0 division error. 

3379 

3380 When `pairwise` is set to `False`, only matching columns between `self` and 

3381 `other` will be used. 

3382 

3383 When `pairwise` is set to `True`, the output will be a MultiIndex DataFrame 

3384 with the original index on the first level, and the `other` DataFrame 

3385 columns on the second level. 

3386 

3387 In the case of missing elements, only complete pairwise observations 

3388 will be used. 

3389 

3390 Examples 

3391 -------- 

3392 The below example shows a rolling calculation with a window size of 

3393 four matching the equivalent function call using :meth:`numpy.corrcoef`. 

3394 

3395 >>> v1 = [3, 3, 3, 5, 8] 

3396 >>> v2 = [3, 4, 4, 4, 8] 

3397 >>> np.corrcoef(v1[:-1], v2[:-1]) 

3398 array([[1. , 0.33333333], 

3399 [0.33333333, 1. ]]) 

3400 >>> np.corrcoef(v1[1:], v2[1:]) 

3401 array([[1. , 0.9169493], 

3402 [0.9169493, 1. ]]) 

3403 >>> s1 = pd.Series(v1) 

3404 >>> s2 = pd.Series(v2) 

3405 >>> s1.rolling(4).corr(s2) 

3406 0 NaN 

3407 1 NaN 

3408 2 NaN 

3409 3 0.333333 

3410 4 0.916949 

3411 dtype: float64 

3412 

3413 The below example shows a similar rolling calculation on a 

3414 DataFrame using the pairwise option. 

3415 

3416 >>> matrix = np.array( 

3417 ... [[51.0, 35.0], [49.0, 30.0], [47.0, 32.0], [46.0, 31.0], [50.0, 36.0]] 

3418 ... ) 

3419 >>> np.corrcoef(matrix[:-1, 0], matrix[:-1, 1]) 

3420 array([[1. , 0.6263001], 

3421 [0.6263001, 1. ]]) 

3422 >>> np.corrcoef(matrix[1:, 0], matrix[1:, 1]) 

3423 array([[1. , 0.55536811], 

3424 [0.55536811, 1. ]]) 

3425 >>> df = pd.DataFrame(matrix, columns=["X", "Y"]) 

3426 >>> df 

3427 X Y 

3428 0 51.0 35.0 

3429 1 49.0 30.0 

3430 2 47.0 32.0 

3431 3 46.0 31.0 

3432 4 50.0 36.0 

3433 >>> df.rolling(4).corr(pairwise=True) 

3434 X Y 

3435 0 X NaN NaN 

3436 Y NaN NaN 

3437 1 X NaN NaN 

3438 Y NaN NaN 

3439 2 X NaN NaN 

3440 Y NaN NaN 

3441 3 X 1.000000 0.626300 

3442 Y 0.626300 1.000000 

3443 4 X 1.000000 0.555368 

3444 Y 0.555368 1.000000 

3445 """ 

3446 return super().corr( 

3447 other=other, 

3448 pairwise=pairwise, 

3449 ddof=ddof, 

3450 numeric_only=numeric_only, 

3451 ) 

3452 

3453 

3454Rolling.__doc__ = Window.__doc__ 

3455 

3456 

3457@set_module("pandas.api.typing") 

3458class RollingGroupby(BaseWindowGroupby, Rolling): 

3459 """ 

3460 Provide a rolling groupby implementation. 

3461 """ 

3462 

3463 _attributes = Rolling._attributes + BaseWindowGroupby._attributes 

3464 

3465 def _get_window_indexer(self) -> GroupbyIndexer: 

3466 """ 

3467 Return an indexer class that will compute the window start and end bounds 

3468 

3469 Returns 

3470 ------- 

3471 GroupbyIndexer 

3472 """ 

3473 rolling_indexer: type[BaseIndexer] 

3474 indexer_kwargs: dict[str, Any] | None = None 

3475 index_array = self._index_array 

3476 if isinstance(self.window, BaseIndexer): 

3477 rolling_indexer = type(self.window) 

3478 indexer_kwargs = self.window.__dict__.copy() 

3479 assert isinstance(indexer_kwargs, dict) # for mypy 

3480 # We'll be using the index of each group later 

3481 indexer_kwargs.pop("index_array", None) 

3482 window = self.window 

3483 elif self._win_freq_i8 is not None: 

3484 rolling_indexer = VariableWindowIndexer 

3485 # error: Incompatible types in assignment (expression has type 

3486 # "int", variable has type "BaseIndexer") 

3487 window = self._win_freq_i8 # type: ignore[assignment] 

3488 else: 

3489 rolling_indexer = FixedWindowIndexer 

3490 window = self.window 

3491 window_indexer = GroupbyIndexer( 

3492 index_array=index_array, 

3493 window_size=window, 

3494 groupby_indices=self._grouper.indices, 

3495 window_indexer=rolling_indexer, 

3496 indexer_kwargs=indexer_kwargs, 

3497 ) 

3498 return window_indexer 

3499 

3500 def _validate_datetimelike_monotonic(self) -> None: 

3501 """ 

3502 Validate that each group in self._on is monotonic 

3503 """ 

3504 # GH 46061 

3505 if self._on.hasnans: 

3506 self._raise_monotonic_error("values must not have NaT") 

3507 for group_indices in self._grouper.indices.values(): 

3508 group_on = self._on.take(group_indices) 

3509 if not ( 

3510 group_on.is_monotonic_increasing or group_on.is_monotonic_decreasing 

3511 ): 

3512 on = "index" if self.on is None else self.on 

3513 raise ValueError( 

3514 f"Each group within {on} must be monotonic. " 

3515 f"Sort the values in {on} first." 

3516 )