pyochain 0.5.3__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.
@@ -0,0 +1,324 @@
1
+ from __future__ import annotations
2
+
3
+ import functools
4
+ import statistics
5
+ from collections.abc import Callable, Iterable
6
+ from typing import TYPE_CHECKING, Literal, NamedTuple
7
+
8
+ import cytoolz as cz
9
+ import more_itertools as mit
10
+
11
+ from .._core import IterWrapper
12
+
13
+ if TYPE_CHECKING:
14
+ from ._main import Iter
15
+
16
+
17
+ class Unzipped[T, V](NamedTuple):
18
+ first: Iter[T]
19
+ second: Iter[V]
20
+
21
+
22
+ class BaseAgg[T](IterWrapper[T]):
23
+ def unzip[U, V](self: IterWrapper[tuple[U, V]]) -> Unzipped[U, V]:
24
+ """
25
+ Converts an iterator of pairs into a pair of iterators.
26
+
27
+ `Iter.unzip()` consumes the iterator of pairs.
28
+
29
+ Returns an Unzipped NamedTuple, containing two iterators:
30
+
31
+ - one from the left elements of the pairs
32
+ - one from the right elements.
33
+
34
+ This function is, in some sense, the opposite of zip.
35
+ ```python
36
+ >>> import pyochain as pc
37
+ >>> data = [(1, "a"), (2, "b"), (3, "c")]
38
+ >>> unzipped = pc.Iter.from_(data).unzip()
39
+ >>> unzipped.first.into(list)
40
+ [1, 2, 3]
41
+ >>> unzipped.second.into(list)
42
+ ['a', 'b', 'c']
43
+
44
+ ```
45
+ """
46
+ from ._main import Iter
47
+
48
+ def _unzip(data: Iterable[tuple[U, V]]) -> Unzipped[U, V]:
49
+ d: list[tuple[U, V]] = list(data)
50
+ return Unzipped(Iter(x[0] for x in d), Iter(x[1] for x in d))
51
+
52
+ return self.into(_unzip)
53
+
54
+ def reduce(self, func: Callable[[T, T], T]) -> T:
55
+ """
56
+ Apply a function of two arguments cumulatively to the items of an iterable, from left to right.
57
+
58
+ Args:
59
+ func: Function to apply cumulatively to the items of the iterable.
60
+
61
+ This effectively reduces the iterable to a single value.
62
+
63
+ If initial is present, it is placed before the items of the iterable in the calculation.
64
+
65
+ It then serves as a default when the iterable is empty.
66
+ ```python
67
+ >>> import pyochain as pc
68
+ >>> pc.Iter.from_([1, 2, 3]).reduce(lambda a, b: a + b)
69
+ 6
70
+
71
+ ```
72
+ """
73
+ return self.into(functools.partial(functools.reduce, func))
74
+
75
+ def combination_index(self, r: Iterable[T]) -> int:
76
+ """
77
+ Equivalent to list(combinations(iterable, r)).index(element).
78
+
79
+ Args:
80
+ r: The combination to find the index of.
81
+
82
+ The subsequences of iterable that are of length r can be ordered lexicographically.
83
+
84
+ combination_index computes the index of the first element, without computing the previous combinations.
85
+
86
+ ValueError will be raised if the given element isn't one of the combinations of iterable.
87
+ ```python
88
+ >>> import pyochain as pc
89
+ >>> pc.Iter.from_("abcdefg").combination_index("adf")
90
+ 10
91
+
92
+ ```
93
+ """
94
+ return self.into(functools.partial(mit.combination_index, r))
95
+
96
+ def first(self) -> T:
97
+ """
98
+ Return the first element.
99
+ ```python
100
+ >>> import pyochain as pc
101
+ >>> pc.Iter.from_([9]).first()
102
+ 9
103
+
104
+ ```
105
+ """
106
+ return self.into(cz.itertoolz.first)
107
+
108
+ def second(self) -> T:
109
+ """
110
+ Return the second element.
111
+ ```python
112
+ >>> import pyochain as pc
113
+ >>> pc.Iter.from_([9, 8]).second()
114
+ 8
115
+
116
+ ```
117
+ """
118
+ return self.into(cz.itertoolz.second)
119
+
120
+ def last(self) -> T:
121
+ """
122
+ Return the last element.
123
+ ```python
124
+ >>> import pyochain as pc
125
+ >>> pc.Iter.from_([7, 8, 9]).last()
126
+ 9
127
+
128
+ ```
129
+ """
130
+ return self.into(cz.itertoolz.last)
131
+
132
+ def count(self) -> int:
133
+ """
134
+ Return the length of the sequence.
135
+ Like the builtin len but works on lazy sequences.
136
+ ```python
137
+ >>> import pyochain as pc
138
+ >>> pc.Iter.from_([1, 2]).count()
139
+ 2
140
+
141
+ ```
142
+ """
143
+ return self.into(cz.itertoolz.count)
144
+
145
+ def item(self, index: int) -> T:
146
+ """
147
+ Return item at index.
148
+
149
+ Args:
150
+ index: The index of the item to retrieve.
151
+
152
+ ```python
153
+ >>> import pyochain as pc
154
+ >>> pc.Iter.from_([10, 20]).item(1)
155
+ 20
156
+
157
+ ```
158
+ """
159
+ return self.into(functools.partial(cz.itertoolz.nth, index))
160
+
161
+ def argmax[U](self, key: Callable[[T], U] | None = None) -> int:
162
+ """
163
+ Index of the first occurrence of a maximum value in an iterable.
164
+
165
+ Args:
166
+ key: Optional function to determine the value for comparison.
167
+
168
+ ```python
169
+ >>> import pyochain as pc
170
+ >>> pc.Iter.from_("abcdefghabcd").argmax()
171
+ 7
172
+ >>> pc.Iter.from_([0, 1, 2, 3, 3, 2, 1, 0]).argmax()
173
+ 3
174
+
175
+ ```
176
+ For example, identify the best machine learning model:
177
+ ```python
178
+ >>> models = pc.Iter.from_(["svm", "random forest", "knn", "naïve bayes"])
179
+ >>> accuracy = pc.Seq([68, 61, 84, 72])
180
+ >>> # Most accurate model
181
+ >>> models.item(accuracy.argmax())
182
+ 'knn'
183
+ >>>
184
+ >>> # Best accuracy
185
+ >>> accuracy.into(max)
186
+ 84
187
+
188
+ ```
189
+ """
190
+ return self.into(mit.argmax, key=key)
191
+
192
+ def argmin[U](self, key: Callable[[T], U] | None = None) -> int:
193
+ """
194
+ Index of the first occurrence of a minimum value in an iterable.
195
+
196
+ Args:
197
+ key: Optional function to determine the value for comparison.
198
+
199
+ ```python
200
+ >>> import pyochain as pc
201
+ >>> pc.Iter.from_("efghabcdijkl").argmin()
202
+ 4
203
+ >>> pc.Iter.from_([3, 2, 1, 0, 4, 2, 1, 0]).argmin()
204
+ 3
205
+
206
+ ```
207
+
208
+ For example, look up a label corresponding to the position of a value that minimizes a cost function:
209
+ ```python
210
+ >>> def cost(x):
211
+ ... "Days for a wound to heal given a subject's age."
212
+ ... return x**2 - 20 * x + 150
213
+ >>> labels = pc.Iter.from_(["homer", "marge", "bart", "lisa", "maggie"])
214
+ >>> ages = pc.Seq([35, 30, 10, 9, 1])
215
+ >>> # Fastest healing family member
216
+ >>> labels.item(ages.argmin(key=cost))
217
+ 'bart'
218
+ >>> # Age with fastest healing
219
+ >>> ages.into(min, key=cost)
220
+ 10
221
+
222
+ ```
223
+ """
224
+ return self.into(mit.argmin, key=key)
225
+
226
+ def sum[U: int | float](self: IterWrapper[U]) -> U | Literal[0]:
227
+ """
228
+ Return the sum of the sequence.
229
+ ```python
230
+ >>> import pyochain as pc
231
+ >>> pc.Iter.from_([1, 2, 3]).sum()
232
+ 6
233
+
234
+ ```
235
+ """
236
+ return self.into(sum)
237
+
238
+ def min[U: int | float](self: IterWrapper[U]) -> U:
239
+ """
240
+ Return the minimum of the sequence.
241
+ ```python
242
+ >>> import pyochain as pc
243
+ >>> pc.Iter.from_([3, 1, 2]).min()
244
+ 1
245
+
246
+ ```
247
+ """
248
+ return self.into(min)
249
+
250
+ def max[U: int | float](self: IterWrapper[U]) -> U:
251
+ """
252
+ Return the maximum of the sequence.
253
+ ```python
254
+ >>> import pyochain as pc
255
+ >>> pc.Iter.from_([3, 1, 2]).max()
256
+ 3
257
+
258
+ ```
259
+ """
260
+ return self.into(max)
261
+
262
+ def mean[U: int | float](self: IterWrapper[U]) -> float:
263
+ """
264
+ Return the mean of the sequence.
265
+ ```python
266
+ >>> import pyochain as pc
267
+ >>> pc.Iter.from_([1, 2, 3]).mean()
268
+ 2
269
+
270
+ ```
271
+ """
272
+ return self.into(statistics.mean)
273
+
274
+ def median[U: int | float](self: IterWrapper[U]) -> float:
275
+ """
276
+ Return the median of the sequence.
277
+ ```python
278
+ >>> import pyochain as pc
279
+ >>> pc.Iter.from_([1, 3, 2]).median()
280
+ 2
281
+
282
+ ```
283
+ """
284
+ return self.into(statistics.median)
285
+
286
+ def mode[U: int | float](self: IterWrapper[U]) -> U:
287
+ """
288
+ Return the mode of the sequence.
289
+ ```python
290
+ >>> import pyochain as pc
291
+ >>> pc.Iter.from_([1, 2, 2, 3]).mode()
292
+ 2
293
+
294
+ ```
295
+ """
296
+ return self.into(statistics.mode)
297
+
298
+ def stdev[U: int | float](
299
+ self: IterWrapper[U],
300
+ ) -> float:
301
+ """
302
+ Return the standard deviation of the sequence.
303
+ ```python
304
+ >>> import pyochain as pc
305
+ >>> pc.Iter.from_([1, 2, 3]).stdev()
306
+ 1.0
307
+
308
+ ```
309
+ """
310
+ return self.into(statistics.stdev)
311
+
312
+ def variance[U: int | float](
313
+ self: IterWrapper[U],
314
+ ) -> float:
315
+ """
316
+ Return the variance of the sequence.
317
+ ```python
318
+ >>> import pyochain as pc
319
+ >>> pc.Iter.from_([1, 2, 3, 7, 8]).variance()
320
+ 9.7
321
+
322
+ ```
323
+ """
324
+ return self.into(statistics.variance)
@@ -0,0 +1,227 @@
1
+ from __future__ import annotations
2
+
3
+ from collections.abc import Callable, Iterable
4
+ from typing import overload
5
+
6
+ import cytoolz as cz
7
+ import more_itertools as mit
8
+
9
+ from .._core import IterWrapper
10
+
11
+
12
+ class BaseBool[T](IterWrapper[T]):
13
+ def all(self, predicate: Callable[[T], bool] = lambda x: bool(x)) -> bool:
14
+ """
15
+ Tests if every element of the iterator matches a predicate.
16
+
17
+ `Iter.all()` takes a closure that returns true or false.
18
+
19
+ It applies this closure to each element of the iterator, and if they all return true, then so does `Iter.all()`.
20
+
21
+ If any of them return false, it returns false.
22
+
23
+ An empty iterator returns true.
24
+
25
+ Args:
26
+ predicate: Function to evaluate each item. Defaults to checking truthiness.
27
+ Example:
28
+ ```python
29
+ >>> import pyochain as pc
30
+ >>> pc.Iter.from_([1, True]).all()
31
+ True
32
+ >>> pc.Iter.from_([]).all()
33
+ True
34
+ >>> pc.Iter.from_([1, 0]).all()
35
+ False
36
+ >>> def is_even(x: int) -> bool:
37
+ ... return x % 2 == 0
38
+ >>> pc.Iter.from_([2, 4, 6]).all(is_even)
39
+ True
40
+
41
+ ```
42
+ """
43
+
44
+ def _all(data: Iterable[T]) -> bool:
45
+ return all(predicate(x) for x in data)
46
+
47
+ return self.into(_all)
48
+
49
+ def any(self, predicate: Callable[[T], bool] = lambda x: bool(x)) -> bool:
50
+ """
51
+ Tests if any element of the iterator matches a predicate.
52
+
53
+
54
+ `Iter.any()` takes a closure that returns true or false.
55
+
56
+ It applies this closure to each element of the iterator, and if any of them return true, then so does `Iter.any()`.
57
+
58
+ If they all return false, it returns false.
59
+
60
+ An empty iterator returns false.
61
+
62
+ Args:
63
+ predicate: Function to evaluate each item. Defaults to checking truthiness.
64
+ Example:
65
+ ```python
66
+ >>> import pyochain as pc
67
+ >>> pc.Iter.from_([0, 1]).any()
68
+ True
69
+ >>> pc.Iter.from_(range(0)).any()
70
+ False
71
+ >>> def is_even(x: int) -> bool:
72
+ ... return x % 2 == 0
73
+ >>> pc.Iter.from_([1, 3, 4]).any(is_even)
74
+ True
75
+
76
+ ```
77
+ """
78
+
79
+ def _any(data: Iterable[T]) -> bool:
80
+ return any(predicate(x) for x in data)
81
+
82
+ return self.into(_any)
83
+
84
+ def is_distinct(self) -> bool:
85
+ """
86
+ Return True if all items are distinct.
87
+ ```python
88
+ >>> import pyochain as pc
89
+ >>> pc.Iter.from_([1, 2]).is_distinct()
90
+ True
91
+
92
+ ```
93
+ """
94
+ return self.into(cz.itertoolz.isdistinct)
95
+
96
+ def all_equal[U](self, key: Callable[[T], U] | None = None) -> bool:
97
+ """
98
+ Return True if all items are equal.
99
+
100
+ Args:
101
+ key: Function to transform items before comparison. Defaults to None.
102
+ Example:
103
+ ```python
104
+ >>> import pyochain as pc
105
+ >>> pc.Iter.from_([1, 1, 1]).all_equal()
106
+ True
107
+
108
+ ```
109
+ A function that accepts a single argument and returns a transformed version of each input item can be specified with key:
110
+ ```python
111
+ >>> pc.Iter.from_("AaaA").all_equal(key=str.casefold)
112
+ True
113
+ >>> pc.Iter.from_([1, 2, 3]).all_equal(key=lambda x: x < 10)
114
+ True
115
+
116
+ ```
117
+ """
118
+ return self.into(mit.all_equal, key=key)
119
+
120
+ def all_unique[U](self, key: Callable[[T], U] | None = None) -> bool:
121
+ """
122
+ Returns True if all the elements of iterable are unique.
123
+
124
+ Args:
125
+ key: Function to transform items before comparison. Defaults to None.
126
+ Example:
127
+ ```python
128
+ >>> import pyochain as pc
129
+ >>> pc.Iter.from_("ABCB").all_unique()
130
+ False
131
+
132
+ ```
133
+ If a key function is specified, it will be used to make comparisons.
134
+ ```python
135
+ >>> pc.Iter.from_("ABCb").all_unique()
136
+ True
137
+ >>> pc.Iter.from_("ABCb").all_unique(str.lower)
138
+ False
139
+
140
+ ```
141
+ The function returns as soon as the first non-unique element is encountered.
142
+
143
+ Iterables with a mix of hashable and unhashable items can be used, but the function will be slower for unhashable items
144
+
145
+ """
146
+ return self.into(mit.all_unique, key=key)
147
+
148
+ def is_sorted[U](
149
+ self,
150
+ key: Callable[[T], U] | None = None,
151
+ reverse: bool = False,
152
+ strict: bool = False,
153
+ ) -> bool:
154
+ """
155
+ Returns True if the items of iterable are in sorted order.
156
+
157
+ Args:
158
+ key: Function to transform items before comparison. Defaults to None.
159
+ reverse: Whether to check for descending order. Defaults to False.
160
+ strict: Whether to enforce strict sorting (no equal elements). Defaults to False.
161
+ Example:
162
+ ```python
163
+ >>> import pyochain as pc
164
+ >>> pc.Iter.from_(["1", "2", "3", "4", "5"]).is_sorted(key=int)
165
+ True
166
+ >>> pc.Iter.from_([5, 4, 3, 1, 2]).is_sorted(reverse=True)
167
+ False
168
+
169
+ If strict, tests for strict sorting, that is, returns False if equal elements are found:
170
+ ```python
171
+ >>> pc.Iter.from_([1, 2, 2]).is_sorted()
172
+ True
173
+ >>> pc.Iter.from_([1, 2, 2]).is_sorted(strict=True)
174
+ False
175
+
176
+ ```
177
+
178
+ The function returns False after encountering the first out-of-order item.
179
+
180
+ This means it may produce results that differ from the built-in sorted function for objects with unusual comparison dynamics (like math.nan).
181
+
182
+ If there are no out-of-order items, the iterable is exhausted.
183
+ """
184
+ return self.into(mit.is_sorted, key=key, reverse=reverse, strict=strict)
185
+
186
+ @overload
187
+ def find(
188
+ self, default: None = None, predicate: Callable[[T], bool] | None = ...
189
+ ) -> T | None: ...
190
+ @overload
191
+ def find(self, default: T, predicate: Callable[[T], bool] | None = ...) -> T: ...
192
+
193
+ def find[U](
194
+ self, default: U = None, predicate: Callable[[T], bool] | None = None
195
+ ) -> U | T:
196
+ """
197
+ Searches for an element of an iterator that satisfies a `predicate`, by:
198
+
199
+ - Taking a closure that returns true or false as `predicate` (optional).
200
+ - Using the identity function if no `predicate` is provided.
201
+ - Applying this closure to each element of the iterator.
202
+ - Returning the first element that satisfies the `predicate`.
203
+
204
+ If all the elements return false, `Iter.find()` returns the default value.
205
+
206
+ Args:
207
+ default: Value to return if no element satisfies the predicate. Defaults to None.
208
+ predicate: Function to evaluate each item. Defaults to checking truthiness.
209
+ Example:
210
+ ```python
211
+ >>> import pyochain as pc
212
+ >>> def gt_five(x: int) -> bool:
213
+ ... return x > 5
214
+ >>>
215
+ >>> def gt_nine(x: int) -> bool:
216
+ ... return x > 9
217
+ >>>
218
+ >>> pc.Iter.from_(range(10)).find()
219
+ 1
220
+ >>> pc.Iter.from_(range(10)).find(predicate=gt_five)
221
+ 6
222
+ >>> pc.Iter.from_(range(10)).find(default="missing", predicate=gt_nine)
223
+ 'missing'
224
+
225
+ ```
226
+ """
227
+ return self.into(mit.first_true, default, predicate)