pyochain 0.5.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of pyochain might be problematic. Click here for more details.
- pyochain/__init__.py +5 -0
- pyochain/_core/__init__.py +21 -0
- pyochain/_core/_main.py +184 -0
- pyochain/_core/_protocols.py +43 -0
- pyochain/_dict/__init__.py +4 -0
- pyochain/_dict/_exprs.py +115 -0
- pyochain/_dict/_filters.py +273 -0
- pyochain/_dict/_funcs.py +62 -0
- pyochain/_dict/_groups.py +176 -0
- pyochain/_dict/_iter.py +92 -0
- pyochain/_dict/_joins.py +137 -0
- pyochain/_dict/_main.py +307 -0
- pyochain/_dict/_nested.py +218 -0
- pyochain/_dict/_process.py +171 -0
- pyochain/_iter/__init__.py +3 -0
- pyochain/_iter/_aggregations.py +323 -0
- pyochain/_iter/_booleans.py +224 -0
- pyochain/_iter/_constructors.py +155 -0
- pyochain/_iter/_eager.py +195 -0
- pyochain/_iter/_filters.py +503 -0
- pyochain/_iter/_groups.py +264 -0
- pyochain/_iter/_joins.py +407 -0
- pyochain/_iter/_lists.py +306 -0
- pyochain/_iter/_main.py +224 -0
- pyochain/_iter/_maps.py +358 -0
- pyochain/_iter/_partitions.py +148 -0
- pyochain/_iter/_process.py +384 -0
- pyochain/_iter/_rolling.py +247 -0
- pyochain/_iter/_tuples.py +221 -0
- pyochain/py.typed +0 -0
- pyochain-0.5.0.dist-info/METADATA +295 -0
- pyochain-0.5.0.dist-info/RECORD +33 -0
- pyochain-0.5.0.dist-info/WHEEL +4 -0
|
@@ -0,0 +1,384 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import itertools
|
|
4
|
+
from collections.abc import Callable, Generator, Iterable, Iterator
|
|
5
|
+
from functools import partial
|
|
6
|
+
from random import Random
|
|
7
|
+
from typing import TYPE_CHECKING, Any
|
|
8
|
+
|
|
9
|
+
import cytoolz as cz
|
|
10
|
+
import more_itertools as mit
|
|
11
|
+
|
|
12
|
+
from .._core import IterWrapper, Peeked
|
|
13
|
+
|
|
14
|
+
if TYPE_CHECKING:
|
|
15
|
+
from ._main import Iter
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
def _too_short(item_count: int):
|
|
19
|
+
return mit.raise_(
|
|
20
|
+
ValueError,
|
|
21
|
+
f"Too few items in iterable (got {item_count})",
|
|
22
|
+
)
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
def _too_long(item_count: int):
|
|
26
|
+
return mit.raise_(
|
|
27
|
+
ValueError,
|
|
28
|
+
f"Too many items in iterable (got at least {item_count})",
|
|
29
|
+
)
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
class BaseProcess[T](IterWrapper[T]):
|
|
33
|
+
def cycle(self) -> Iter[T]:
|
|
34
|
+
"""
|
|
35
|
+
Repeat the sequence indefinitely.
|
|
36
|
+
|
|
37
|
+
**Warning** ⚠️
|
|
38
|
+
This creates an infinite iterator.
|
|
39
|
+
Be sure to use Iter.take() or Iter.slice() to limit the number of items taken.
|
|
40
|
+
```python
|
|
41
|
+
|
|
42
|
+
Example:
|
|
43
|
+
>>> import pyochain as pc
|
|
44
|
+
>>> pc.Iter.from_([1, 2]).cycle().take(5).into(list)
|
|
45
|
+
[1, 2, 1, 2, 1]
|
|
46
|
+
|
|
47
|
+
```
|
|
48
|
+
"""
|
|
49
|
+
return self.apply(itertools.cycle)
|
|
50
|
+
|
|
51
|
+
def interpose(self, element: T) -> Iter[T]:
|
|
52
|
+
"""
|
|
53
|
+
Interpose element between items and return a new Iterable wrapper.
|
|
54
|
+
|
|
55
|
+
Args:
|
|
56
|
+
element: The element to interpose between items.
|
|
57
|
+
|
|
58
|
+
Example:
|
|
59
|
+
```python
|
|
60
|
+
>>> import pyochain as pc
|
|
61
|
+
>>> pc.Iter.from_([1, 2]).interpose(0).into(list)
|
|
62
|
+
[1, 0, 2]
|
|
63
|
+
|
|
64
|
+
```
|
|
65
|
+
"""
|
|
66
|
+
return self.apply(partial(cz.itertoolz.interpose, element))
|
|
67
|
+
|
|
68
|
+
def random_sample(
|
|
69
|
+
self, probability: float, state: Random | int | None = None
|
|
70
|
+
) -> Iter[T]:
|
|
71
|
+
"""
|
|
72
|
+
Return elements from a sequence with probability of prob.
|
|
73
|
+
|
|
74
|
+
Args:
|
|
75
|
+
probability: The probability of including each element.
|
|
76
|
+
state: Random state or seed for deterministic sampling.
|
|
77
|
+
|
|
78
|
+
Returns a lazy iterator of random items from seq.
|
|
79
|
+
|
|
80
|
+
random_sample considers each item independently and without replacement.
|
|
81
|
+
|
|
82
|
+
See below how the first time it returned 13 items and the next time it returned 6 items.
|
|
83
|
+
```python
|
|
84
|
+
>>> import pyochain as pc
|
|
85
|
+
>>> data = pc.Seq(list(range(100)))
|
|
86
|
+
>>> data.iter().random_sample(0.1).into(list) # doctest: +SKIP
|
|
87
|
+
[6, 9, 19, 35, 45, 50, 58, 62, 68, 72, 78, 86, 95]
|
|
88
|
+
>>> data.iter().random_sample(0.1).into(list) # doctest: +SKIP
|
|
89
|
+
[6, 44, 54, 61, 69, 94]
|
|
90
|
+
```
|
|
91
|
+
Providing an integer seed for random_state will result in deterministic sampling.
|
|
92
|
+
|
|
93
|
+
Given the same seed it will return the same sample every time.
|
|
94
|
+
```python
|
|
95
|
+
>>> data.iter().random_sample(0.1, state=2016).into(list)
|
|
96
|
+
[7, 9, 19, 25, 30, 32, 34, 48, 59, 60, 81, 98]
|
|
97
|
+
>>> data.iter().random_sample(0.1, state=2016).into(list)
|
|
98
|
+
[7, 9, 19, 25, 30, 32, 34, 48, 59, 60, 81, 98]
|
|
99
|
+
|
|
100
|
+
```
|
|
101
|
+
random_state can also be any object with a method random that returns floats between 0.0 and 1.0 (exclusive).
|
|
102
|
+
```python
|
|
103
|
+
>>> from random import Random
|
|
104
|
+
>>> randobj = Random(2016)
|
|
105
|
+
>>> data.iter().random_sample(0.1, state=randobj).into(list)
|
|
106
|
+
[7, 9, 19, 25, 30, 32, 34, 48, 59, 60, 81, 98]
|
|
107
|
+
|
|
108
|
+
```
|
|
109
|
+
"""
|
|
110
|
+
|
|
111
|
+
return self.apply(
|
|
112
|
+
partial(cz.itertoolz.random_sample, probability, random_state=state)
|
|
113
|
+
)
|
|
114
|
+
|
|
115
|
+
def accumulate(self, func: Callable[[T, T], T]) -> Iter[T]:
|
|
116
|
+
"""
|
|
117
|
+
Return cumulative application of binary op provided by the function.
|
|
118
|
+
|
|
119
|
+
Args:
|
|
120
|
+
func: A binary function to apply cumulatively.
|
|
121
|
+
|
|
122
|
+
```python
|
|
123
|
+
>>> import pyochain as pc
|
|
124
|
+
>>> pc.Iter.from_([1, 2, 3]).accumulate(lambda a, b: a + b).into(list)
|
|
125
|
+
[1, 3, 6]
|
|
126
|
+
|
|
127
|
+
```
|
|
128
|
+
"""
|
|
129
|
+
return self.apply(partial(cz.itertoolz.accumulate, func))
|
|
130
|
+
|
|
131
|
+
def insert_left(self, value: T) -> Iter[T]:
|
|
132
|
+
"""
|
|
133
|
+
Prepend value to the sequence and return a new Iterable wrapper.
|
|
134
|
+
|
|
135
|
+
Args:
|
|
136
|
+
value: The value to prepend.
|
|
137
|
+
|
|
138
|
+
```python
|
|
139
|
+
>>> import pyochain as pc
|
|
140
|
+
>>> pc.Iter.from_([2, 3]).insert_left(1).into(list)
|
|
141
|
+
[1, 2, 3]
|
|
142
|
+
|
|
143
|
+
```
|
|
144
|
+
"""
|
|
145
|
+
return self.apply(partial(cz.itertoolz.cons, value))
|
|
146
|
+
|
|
147
|
+
def peekn(self, n: int) -> Iter[T]:
|
|
148
|
+
"""
|
|
149
|
+
Print and return sequence after peeking n items.
|
|
150
|
+
|
|
151
|
+
Args:
|
|
152
|
+
n: Number of items to peek.
|
|
153
|
+
|
|
154
|
+
```python
|
|
155
|
+
>>> import pyochain as pc
|
|
156
|
+
>>> pc.Iter.from_([1, 2, 3]).peekn(2).into(list)
|
|
157
|
+
Peeked 2 values: (1, 2)
|
|
158
|
+
[1, 2, 3]
|
|
159
|
+
|
|
160
|
+
```
|
|
161
|
+
"""
|
|
162
|
+
|
|
163
|
+
def _peekn(data: Iterable[T]) -> Iterator[T]:
|
|
164
|
+
peeked = Peeked(*cz.itertoolz.peekn(n, data))
|
|
165
|
+
print(f"Peeked {n} values: {peeked.value}")
|
|
166
|
+
return peeked.sequence
|
|
167
|
+
|
|
168
|
+
return self.apply(_peekn)
|
|
169
|
+
|
|
170
|
+
def peek(self) -> Iter[T]:
|
|
171
|
+
"""
|
|
172
|
+
Print and return sequence after peeking first item.
|
|
173
|
+
```python
|
|
174
|
+
>>> import pyochain as pc
|
|
175
|
+
>>> pc.Iter.from_([1, 2]).peek().into(list)
|
|
176
|
+
Peeked value: 1
|
|
177
|
+
[1, 2]
|
|
178
|
+
|
|
179
|
+
```
|
|
180
|
+
"""
|
|
181
|
+
|
|
182
|
+
def _peek(data: Iterable[T]) -> Iterator[T]:
|
|
183
|
+
peeked = Peeked(*cz.itertoolz.peek(data))
|
|
184
|
+
print(f"Peeked value: {peeked.value}")
|
|
185
|
+
return peeked.sequence
|
|
186
|
+
|
|
187
|
+
return self.apply(_peek)
|
|
188
|
+
|
|
189
|
+
def merge_sorted(
|
|
190
|
+
self, *others: Iterable[T], sort_on: Callable[[T], Any] | None = None
|
|
191
|
+
) -> Iter[T]:
|
|
192
|
+
"""
|
|
193
|
+
Merge already-sorted sequences.
|
|
194
|
+
|
|
195
|
+
Args:
|
|
196
|
+
others: Other sorted iterables to merge.
|
|
197
|
+
sort_on: Optional key function for sorting.
|
|
198
|
+
|
|
199
|
+
```python
|
|
200
|
+
>>> import pyochain as pc
|
|
201
|
+
>>> pc.Iter.from_([1, 3]).merge_sorted([2, 4]).into(list)
|
|
202
|
+
[1, 2, 3, 4]
|
|
203
|
+
|
|
204
|
+
```
|
|
205
|
+
"""
|
|
206
|
+
return self.apply(cz.itertoolz.merge_sorted, *others, key=sort_on)
|
|
207
|
+
|
|
208
|
+
def interleave(self, *others: Iterable[T]) -> Iter[T]:
|
|
209
|
+
"""
|
|
210
|
+
Interleave multiple sequences element-wise.
|
|
211
|
+
|
|
212
|
+
Args:
|
|
213
|
+
others: Other iterables to interleave.
|
|
214
|
+
|
|
215
|
+
```python
|
|
216
|
+
>>> import pyochain as pc
|
|
217
|
+
>>> pc.Iter.from_([1, 2]).interleave([3, 4]).into(list)
|
|
218
|
+
[1, 3, 2, 4]
|
|
219
|
+
|
|
220
|
+
```
|
|
221
|
+
"""
|
|
222
|
+
|
|
223
|
+
def _interleave(data: Iterable[T]) -> Iterator[T]:
|
|
224
|
+
return cz.itertoolz.interleave((data, *others))
|
|
225
|
+
|
|
226
|
+
return self.apply(_interleave)
|
|
227
|
+
|
|
228
|
+
def chain(self, *others: Iterable[T]) -> Iter[T]:
|
|
229
|
+
"""
|
|
230
|
+
Concatenate zero or more iterables, any of which may be infinite.
|
|
231
|
+
|
|
232
|
+
Args:
|
|
233
|
+
others: Other iterables to concatenate.
|
|
234
|
+
|
|
235
|
+
An infinite sequence will prevent the rest of the arguments from being included.
|
|
236
|
+
|
|
237
|
+
We use chain.from_iterable rather than chain(*seqs) so that seqs can be a generator.
|
|
238
|
+
```python
|
|
239
|
+
>>> import pyochain as pc
|
|
240
|
+
>>> pc.Iter.from_([1, 2]).chain([3, 4], [5]).into(list)
|
|
241
|
+
[1, 2, 3, 4, 5]
|
|
242
|
+
|
|
243
|
+
```
|
|
244
|
+
"""
|
|
245
|
+
|
|
246
|
+
def _chain(data: Iterable[T]) -> Iterator[T]:
|
|
247
|
+
return itertools.chain.from_iterable((data, *others))
|
|
248
|
+
|
|
249
|
+
return self.apply(_chain)
|
|
250
|
+
|
|
251
|
+
def elements(self) -> Iter[T]:
|
|
252
|
+
"""
|
|
253
|
+
Iterator over elements repeating each as many times as its count.
|
|
254
|
+
|
|
255
|
+
Note:
|
|
256
|
+
if an element's count has been set to zero or is a negative
|
|
257
|
+
number, elements() will ignore it.
|
|
258
|
+
```python
|
|
259
|
+
>>> import pyochain as pc
|
|
260
|
+
>>> pc.Iter.from_("ABCABC").elements().sort().unwrap()
|
|
261
|
+
['A', 'A', 'B', 'B', 'C', 'C']
|
|
262
|
+
|
|
263
|
+
```
|
|
264
|
+
Knuth's example for prime factors of 1836: 2**2 * 3**3 * 17**1
|
|
265
|
+
```python
|
|
266
|
+
>>> import math
|
|
267
|
+
>>> data = [2, 2, 3, 3, 3, 17]
|
|
268
|
+
>>> pc.Iter.from_(data).elements().into(math.prod)
|
|
269
|
+
1836
|
|
270
|
+
|
|
271
|
+
```
|
|
272
|
+
"""
|
|
273
|
+
from collections import Counter
|
|
274
|
+
|
|
275
|
+
def _elements(data: Iterable[T]) -> Iterator[T]:
|
|
276
|
+
return Counter(data).elements()
|
|
277
|
+
|
|
278
|
+
return self.apply(_elements)
|
|
279
|
+
|
|
280
|
+
def reverse(self) -> Iter[T]:
|
|
281
|
+
"""
|
|
282
|
+
Return a new Iterable wrapper with elements in reverse order.
|
|
283
|
+
|
|
284
|
+
The result is a new iterable over the reversed sequence.
|
|
285
|
+
|
|
286
|
+
Note:
|
|
287
|
+
This method must consume the entire iterable to perform the reversal.
|
|
288
|
+
```python
|
|
289
|
+
>>> import pyochain as pc
|
|
290
|
+
>>> pc.Iter.from_([1, 2, 3]).reverse().into(list)
|
|
291
|
+
[3, 2, 1]
|
|
292
|
+
|
|
293
|
+
```
|
|
294
|
+
"""
|
|
295
|
+
|
|
296
|
+
def _reverse(data: Iterable[T]) -> Iterator[T]:
|
|
297
|
+
return reversed(list(data))
|
|
298
|
+
|
|
299
|
+
return self.apply(_reverse)
|
|
300
|
+
|
|
301
|
+
def is_strictly_n(
|
|
302
|
+
self,
|
|
303
|
+
n: int,
|
|
304
|
+
too_short: Callable[[int], Iterator[T]] | Callable[[int], None] = _too_short,
|
|
305
|
+
too_long: Callable[[int], Iterator[T]] | Callable[[int], None] = _too_long,
|
|
306
|
+
) -> Iter[T]:
|
|
307
|
+
"""
|
|
308
|
+
Validate that *iterable* has exactly *n* items and return them if it does.
|
|
309
|
+
|
|
310
|
+
Args:
|
|
311
|
+
n: The exact number of items expected.
|
|
312
|
+
too_short: Function to call if there are too few items.
|
|
313
|
+
too_long: Function to call if there are too many items.
|
|
314
|
+
|
|
315
|
+
If it has fewer than *n* items, call function *too_short* with the actual number of items.
|
|
316
|
+
|
|
317
|
+
If it has more than *n* items, call function *too_long* with the number `n + 1`.
|
|
318
|
+
```python
|
|
319
|
+
>>> import pyochain as pc
|
|
320
|
+
>>> iterable = ["a", "b", "c", "d"]
|
|
321
|
+
>>> n = 4
|
|
322
|
+
>>> pc.Iter.from_(iterable).is_strictly_n(n).into(list)
|
|
323
|
+
['a', 'b', 'c', 'd']
|
|
324
|
+
|
|
325
|
+
```
|
|
326
|
+
Note that the returned iterable must be consumed in order for the check to
|
|
327
|
+
be made.
|
|
328
|
+
|
|
329
|
+
By default, *too_short* and *too_long* are functions that raise`ValueError`.
|
|
330
|
+
```python
|
|
331
|
+
>>> pc.Iter.from_("ab").is_strictly_n(3).into(
|
|
332
|
+
... list
|
|
333
|
+
... ) # doctest: +IGNORE_EXCEPTION_DETAIL
|
|
334
|
+
Traceback (most recent call last):
|
|
335
|
+
...
|
|
336
|
+
ValueError: too few items in iterable (got 2)
|
|
337
|
+
|
|
338
|
+
>>> pc.Iter.from_("abc").is_strictly_n(2).into(
|
|
339
|
+
... list
|
|
340
|
+
... ) # doctest: +IGNORE_EXCEPTION_DETAIL
|
|
341
|
+
Traceback (most recent call last):
|
|
342
|
+
...
|
|
343
|
+
ValueError: too many items in iterable (got at least 3)
|
|
344
|
+
|
|
345
|
+
```
|
|
346
|
+
You can instead supply functions that do something else.
|
|
347
|
+
|
|
348
|
+
*too_short* will be called with the number of items in *iterable*.
|
|
349
|
+
|
|
350
|
+
*too_long* will be called with `n + 1`.
|
|
351
|
+
```python
|
|
352
|
+
>>> def too_short(item_count):
|
|
353
|
+
... raise RuntimeError
|
|
354
|
+
>>> pc.Iter.from_("abcd").is_strictly_n(6, too_short=too_short).into(list)
|
|
355
|
+
Traceback (most recent call last):
|
|
356
|
+
...
|
|
357
|
+
RuntimeError
|
|
358
|
+
>>> def too_long(item_count):
|
|
359
|
+
... print("The boss is going to hear about this")
|
|
360
|
+
>>> pc.Iter.from_("abcdef").is_strictly_n(4, too_long=too_long).into(list)
|
|
361
|
+
The boss is going to hear about this
|
|
362
|
+
['a', 'b', 'c', 'd']
|
|
363
|
+
|
|
364
|
+
```
|
|
365
|
+
"""
|
|
366
|
+
|
|
367
|
+
def strictly_n_(iterable: Iterable[T]) -> Generator[T, Any, None]:
|
|
368
|
+
"""from more_itertools.strictly_n"""
|
|
369
|
+
it = iter(iterable)
|
|
370
|
+
|
|
371
|
+
sent = 0
|
|
372
|
+
for item in itertools.islice(it, n):
|
|
373
|
+
yield item
|
|
374
|
+
sent += 1
|
|
375
|
+
|
|
376
|
+
if sent < n:
|
|
377
|
+
too_short(sent)
|
|
378
|
+
return
|
|
379
|
+
|
|
380
|
+
for item in it:
|
|
381
|
+
too_long(n + 1)
|
|
382
|
+
return
|
|
383
|
+
|
|
384
|
+
return self.apply(strictly_n_)
|
|
@@ -0,0 +1,247 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from collections.abc import Callable, Iterable
|
|
4
|
+
from typing import TYPE_CHECKING
|
|
5
|
+
|
|
6
|
+
import rolling
|
|
7
|
+
|
|
8
|
+
from .._core import IterWrapper
|
|
9
|
+
|
|
10
|
+
if TYPE_CHECKING:
|
|
11
|
+
from ._main import Iter
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
class BaseRolling[T](IterWrapper[T]):
|
|
15
|
+
def rolling_mean(self, window_size: int) -> Iter[float]:
|
|
16
|
+
"""
|
|
17
|
+
Compute the rolling mean.
|
|
18
|
+
|
|
19
|
+
Args:
|
|
20
|
+
window_size: Size of the rolling window.
|
|
21
|
+
|
|
22
|
+
```python
|
|
23
|
+
>>> import pyochain as pc
|
|
24
|
+
>>> pc.Iter.from_([1, 2, 3, 4, 5]).rolling_mean(3).into(list)
|
|
25
|
+
[2.0, 3.0, 4.0]
|
|
26
|
+
|
|
27
|
+
```
|
|
28
|
+
"""
|
|
29
|
+
return self.apply(rolling.Mean, window_size)
|
|
30
|
+
|
|
31
|
+
def rolling_median(self, window_size: int) -> Iter[T]:
|
|
32
|
+
"""
|
|
33
|
+
Compute the rolling median.
|
|
34
|
+
|
|
35
|
+
Args:
|
|
36
|
+
window_size: Size of the rolling window.
|
|
37
|
+
|
|
38
|
+
```python
|
|
39
|
+
>>> import pyochain as pc
|
|
40
|
+
>>> pc.Iter.from_([1, 3, 2, 5, 4]).rolling_median(3).into(list)
|
|
41
|
+
[2, 3, 4]
|
|
42
|
+
|
|
43
|
+
```
|
|
44
|
+
"""
|
|
45
|
+
return self.apply(rolling.Median, window_size)
|
|
46
|
+
|
|
47
|
+
def rolling_sum(self, window_size: int) -> Iter[T]:
|
|
48
|
+
"""
|
|
49
|
+
Compute the rolling sum.
|
|
50
|
+
|
|
51
|
+
Args:
|
|
52
|
+
window_size: Size of the rolling window.
|
|
53
|
+
|
|
54
|
+
Will return integers if the input is integers, floats if the input is floats or mixed.
|
|
55
|
+
```python
|
|
56
|
+
>>> import pyochain as pc
|
|
57
|
+
>>> pc.Iter.from_([1.0, 2, 3, 4, 5]).rolling_sum(3).into(list)
|
|
58
|
+
[6.0, 9.0, 12.0]
|
|
59
|
+
|
|
60
|
+
```
|
|
61
|
+
"""
|
|
62
|
+
return self.apply(rolling.Sum, window_size)
|
|
63
|
+
|
|
64
|
+
def rolling_min(self, window_size: int) -> Iter[T]:
|
|
65
|
+
"""
|
|
66
|
+
Compute the rolling minimum.
|
|
67
|
+
|
|
68
|
+
Args:
|
|
69
|
+
window_size: Size of the rolling window.
|
|
70
|
+
```python
|
|
71
|
+
>>> import pyochain as pc
|
|
72
|
+
>>> pc.Iter.from_([3, 1, 4, 1, 5, 9, 2]).rolling_min(3).into(list)
|
|
73
|
+
[1, 1, 1, 1, 2]
|
|
74
|
+
|
|
75
|
+
```
|
|
76
|
+
"""
|
|
77
|
+
return self.apply(rolling.Min, window_size)
|
|
78
|
+
|
|
79
|
+
def rolling_max(self, window_size: int) -> Iter[T]:
|
|
80
|
+
"""
|
|
81
|
+
Compute the rolling maximum.
|
|
82
|
+
|
|
83
|
+
Args:
|
|
84
|
+
window_size: Size of the rolling window.
|
|
85
|
+
```python
|
|
86
|
+
>>> import pyochain as pc
|
|
87
|
+
>>> pc.Iter.from_([3, 1, 4, 1, 5, 9, 2]).rolling_max(3).into(list)
|
|
88
|
+
[4, 4, 5, 9, 9]
|
|
89
|
+
|
|
90
|
+
```
|
|
91
|
+
"""
|
|
92
|
+
return self.apply(rolling.Max, window_size)
|
|
93
|
+
|
|
94
|
+
def rolling_var(self, window_size: int) -> Iter[float]:
|
|
95
|
+
"""
|
|
96
|
+
Compute the rolling variance.
|
|
97
|
+
|
|
98
|
+
Args:
|
|
99
|
+
window_size: Size of the rolling window.
|
|
100
|
+
```python
|
|
101
|
+
>>> import pyochain as pc
|
|
102
|
+
>>> pc.Iter.from_([1, 2, 4, 1, 4]).rolling_var(3).round(2).into(list)
|
|
103
|
+
[2.33, 2.33, 3.0]
|
|
104
|
+
|
|
105
|
+
```
|
|
106
|
+
"""
|
|
107
|
+
return self.apply(rolling.Var, window_size)
|
|
108
|
+
|
|
109
|
+
def rolling_std(self, window_size: int) -> Iter[float]:
|
|
110
|
+
"""
|
|
111
|
+
Compute the rolling standard deviation.
|
|
112
|
+
|
|
113
|
+
Args:
|
|
114
|
+
window_size: Size of the rolling window.
|
|
115
|
+
```python
|
|
116
|
+
>>> import pyochain as pc
|
|
117
|
+
>>> pc.Iter.from_([1, 2, 4, 1, 4]).rolling_std(3).round(2).into(list)
|
|
118
|
+
[1.53, 1.53, 1.73]
|
|
119
|
+
|
|
120
|
+
```
|
|
121
|
+
"""
|
|
122
|
+
return self.apply(rolling.Std, window_size)
|
|
123
|
+
|
|
124
|
+
def rolling_kurtosis(self, window_size: int) -> Iter[float]:
|
|
125
|
+
"""
|
|
126
|
+
Compute the rolling kurtosis.
|
|
127
|
+
|
|
128
|
+
Args:
|
|
129
|
+
window_size: Size of the rolling window.
|
|
130
|
+
|
|
131
|
+
The windows must have at least 4 observations.
|
|
132
|
+
```python
|
|
133
|
+
>>> import pyochain as pc
|
|
134
|
+
>>> pc.Iter.from_([1, 2, 4, 1, 4]).rolling_kurtosis(4).into(list)
|
|
135
|
+
[1.5, -3.901234567901234]
|
|
136
|
+
|
|
137
|
+
```
|
|
138
|
+
"""
|
|
139
|
+
return self.apply(rolling.Kurtosis, window_size)
|
|
140
|
+
|
|
141
|
+
def rolling_skew(self, window_size: int) -> Iter[float]:
|
|
142
|
+
"""
|
|
143
|
+
Compute the rolling skewness.
|
|
144
|
+
|
|
145
|
+
Args:
|
|
146
|
+
window_size: Size of the rolling window.
|
|
147
|
+
|
|
148
|
+
The windows must have at least 3 observations.
|
|
149
|
+
```python
|
|
150
|
+
>>> import pyochain as pc
|
|
151
|
+
>>> pc.Iter.from_([1, 2, 4, 1, 4]).rolling_skew(3).round(2).into(list)
|
|
152
|
+
[0.94, 0.94, -1.73]
|
|
153
|
+
|
|
154
|
+
```
|
|
155
|
+
"""
|
|
156
|
+
return self.apply(rolling.Skew, window_size)
|
|
157
|
+
|
|
158
|
+
def rolling_all(self, window_size: int) -> Iter[bool]:
|
|
159
|
+
"""
|
|
160
|
+
Compute whether all values in the window evaluate to True.
|
|
161
|
+
|
|
162
|
+
Args:
|
|
163
|
+
window_size: Size of the rolling window.
|
|
164
|
+
```python
|
|
165
|
+
>>> import pyochain as pc
|
|
166
|
+
>>> pc.Iter.from_([True, True, False, True, True]).rolling_all(2).into(list)
|
|
167
|
+
[True, False, False, True]
|
|
168
|
+
|
|
169
|
+
```
|
|
170
|
+
"""
|
|
171
|
+
return self.apply(rolling.All, window_size)
|
|
172
|
+
|
|
173
|
+
def rolling_any(self, window_size: int) -> Iter[bool]:
|
|
174
|
+
"""
|
|
175
|
+
Compute whether any value in the window evaluates to True.
|
|
176
|
+
|
|
177
|
+
Args:
|
|
178
|
+
window_size: Size of the rolling window.
|
|
179
|
+
```python
|
|
180
|
+
>>> import pyochain as pc
|
|
181
|
+
>>> pc.Iter.from_([True, True, False, True, True]).rolling_any(2).into(list)
|
|
182
|
+
[True, True, True, True]
|
|
183
|
+
|
|
184
|
+
```
|
|
185
|
+
"""
|
|
186
|
+
return self.apply(rolling.Any, window_size)
|
|
187
|
+
|
|
188
|
+
def rolling_product(self, window_size: int) -> Iter[float]:
|
|
189
|
+
"""
|
|
190
|
+
Compute the rolling product.
|
|
191
|
+
|
|
192
|
+
Args:
|
|
193
|
+
window_size: Size of the rolling window.
|
|
194
|
+
```python
|
|
195
|
+
>>> import pyochain as pc
|
|
196
|
+
>>> pc.Iter.from_([1, 2, 3, 4, 5]).rolling_product(3).into(list)
|
|
197
|
+
[6.0, 24.0, 60.0]
|
|
198
|
+
|
|
199
|
+
```
|
|
200
|
+
"""
|
|
201
|
+
return self.apply(rolling.Product, window_size)
|
|
202
|
+
|
|
203
|
+
def rolling_apply[R](
|
|
204
|
+
self, func: Callable[[Iterable[T]], R], window_size: int
|
|
205
|
+
) -> Iter[R]:
|
|
206
|
+
"""
|
|
207
|
+
Apply a custom function to each rolling window.
|
|
208
|
+
|
|
209
|
+
Args:
|
|
210
|
+
func: Function to apply to each rolling window.
|
|
211
|
+
window_size: Size of the rolling window.
|
|
212
|
+
|
|
213
|
+
The function should accept an iterable and return a single value.
|
|
214
|
+
```python
|
|
215
|
+
>>> import pyochain as pc
|
|
216
|
+
>>> def range_func(window):
|
|
217
|
+
... return max(window) - min(window)
|
|
218
|
+
>>> pc.Iter.from_([1, 3, 2, 5, 4]).rolling_apply(range_func, 3).into(list)
|
|
219
|
+
[2, 3, 3]
|
|
220
|
+
|
|
221
|
+
```
|
|
222
|
+
"""
|
|
223
|
+
return self.apply(rolling.Apply, window_size, "fixed", func)
|
|
224
|
+
|
|
225
|
+
def rolling_apply_pairwise[R](
|
|
226
|
+
self, other: Iterable[T], func: Callable[[T, T], R], window_size: int
|
|
227
|
+
) -> Iter[R]:
|
|
228
|
+
"""
|
|
229
|
+
Apply a custom pairwise function to each rolling window of size 2.
|
|
230
|
+
|
|
231
|
+
Args:
|
|
232
|
+
other: Second iterable to apply the pairwise function.
|
|
233
|
+
func: Function to apply to each pair of elements.
|
|
234
|
+
window_size: Size of the rolling window.
|
|
235
|
+
|
|
236
|
+
The function should accept two arguments and return a single value.
|
|
237
|
+
```python
|
|
238
|
+
>>> import pyochain as pc
|
|
239
|
+
>>> from statistics import correlation as corr
|
|
240
|
+
>>> seq_1 = [1, 2, 3, 4, 5]
|
|
241
|
+
>>> seq_2 = [1, 2, 3, 2, 1]
|
|
242
|
+
>>> pc.Iter.from_(seq_1).rolling_apply_pairwise(seq_2, corr, 3).into(list)
|
|
243
|
+
[1.0, 0.0, -1.0]
|
|
244
|
+
|
|
245
|
+
```
|
|
246
|
+
"""
|
|
247
|
+
return self.apply(rolling.ApplyPairwise, other, window_size, func)
|