dataiter 2.0__tar.gz → 2.1__tar.gz

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.
Files changed (30) hide show
  1. {dataiter-2.0 → dataiter-2.1}/PKG-INFO +1 -1
  2. {dataiter-2.0 → dataiter-2.1}/dataiter/__init__.py +9 -1
  3. {dataiter-2.0 → dataiter-2.1}/dataiter/aggregate.py +75 -34
  4. {dataiter-2.0 → dataiter-2.1}/dataiter/data_frame.py +257 -51
  5. {dataiter-2.0 → dataiter-2.1}/dataiter/deco.py +9 -0
  6. {dataiter-2.0 → dataiter-2.1}/dataiter/dt.py +46 -17
  7. {dataiter-2.0 → dataiter-2.1}/dataiter/io.py +8 -0
  8. {dataiter-2.0 → dataiter-2.1}/dataiter/list_of_dicts.py +3 -0
  9. {dataiter-2.0 → dataiter-2.1}/dataiter/test/test_aggregate.py +20 -0
  10. {dataiter-2.0 → dataiter-2.1}/dataiter/test/test_data_frame.py +177 -0
  11. {dataiter-2.0 → dataiter-2.1}/dataiter/test/test_dt.py +34 -0
  12. {dataiter-2.0 → dataiter-2.1}/dataiter/test/test_io.py +10 -0
  13. {dataiter-2.0 → dataiter-2.1}/dataiter/test/test_util.py +10 -0
  14. {dataiter-2.0 → dataiter-2.1}/dataiter/test/test_vector.py +11 -2
  15. dataiter-2.1/dataiter/test/test_window.py +112 -0
  16. {dataiter-2.0 → dataiter-2.1}/dataiter/util.py +34 -2
  17. {dataiter-2.0 → dataiter-2.1}/dataiter/vector.py +35 -15
  18. dataiter-2.1/dataiter/window.py +165 -0
  19. {dataiter-2.0 → dataiter-2.1}/pyproject.toml +1 -0
  20. {dataiter-2.0 → dataiter-2.1}/.gitignore +0 -0
  21. {dataiter-2.0 → dataiter-2.1}/AUTHORS.md +0 -0
  22. {dataiter-2.0 → dataiter-2.1}/COPYING +0 -0
  23. {dataiter-2.0 → dataiter-2.1}/README.md +0 -0
  24. {dataiter-2.0 → dataiter-2.1}/dataiter/dtypes.py +0 -0
  25. {dataiter-2.0 → dataiter-2.1}/dataiter/geojson.py +0 -0
  26. {dataiter-2.0 → dataiter-2.1}/dataiter/regex.py +0 -0
  27. {dataiter-2.0 → dataiter-2.1}/dataiter/test/__init__.py +0 -0
  28. {dataiter-2.0 → dataiter-2.1}/dataiter/test/test_geojson.py +0 -0
  29. {dataiter-2.0 → dataiter-2.1}/dataiter/test/test_list_of_dicts.py +0 -0
  30. {dataiter-2.0 → dataiter-2.1}/dataiter/test/test_regex.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: dataiter
3
- Version: 2.0
3
+ Version: 2.1
4
4
  Summary: Simple, light-weight data frames for Python
5
5
  Project-URL: Homepage, https://github.com/otsaloma/dataiter
6
6
  Project-URL: Documentation, https://dataiter.readthedocs.io/
@@ -20,7 +20,7 @@
20
20
  # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21
21
  # THE SOFTWARE.
22
22
 
23
- __version__ = "2.0"
23
+ __version__ = "2.1"
24
24
 
25
25
  #: Default amount of elements to return from :meth:`Vector.head`,
26
26
  #: :meth:`Vector.sample` and :meth:`Vector.tail`.
@@ -89,12 +89,20 @@ from dataiter.aggregate import std
89
89
  from dataiter.aggregate import sum
90
90
  from dataiter.aggregate import var
91
91
 
92
+ from dataiter.window import cummax
93
+ from dataiter.window import cummin
94
+ from dataiter.window import cumsum
95
+ from dataiter.window import rank
96
+ from dataiter.window import shift
97
+
92
98
  from dataiter.io import read_csv
99
+ from dataiter.io import read_duckdb
93
100
  from dataiter.io import read_geojson
94
101
  from dataiter.io import read_json
95
102
  from dataiter.io import read_jsonl
96
103
  from dataiter.io import read_npz
97
104
  from dataiter.io import read_parquet
105
+ from dataiter.io import read_parquet_chunked
98
106
 
99
107
  from dataiter import util
100
108
 
@@ -49,7 +49,8 @@ def all(x, *, drop_na=True):
49
49
  Return whether all elements of `x` evaluate to ``True``.
50
50
 
51
51
  If `x` is a string, return a function usable with
52
- :meth:`.DataFrame.aggregate` that operates group-wise on column `x`.
52
+ :meth:`.DataFrame.aggregate` and :meth:`.DataFrame.modify` that operates
53
+ group-wise on column `x`.
53
54
 
54
55
  Equivalent to ``numpy.all``, see the NumPy documentation for details:
55
56
  https://numpy.org/doc/stable/reference/generated/numpy.all.html
@@ -86,7 +87,8 @@ def any(x, *, drop_na=True):
86
87
  Return whether any element of `x` evaluates to ``True``.
87
88
 
88
89
  If `x` is a string, return a function usable with
89
- :meth:`.DataFrame.aggregate` that operates group-wise on column `x`.
90
+ :meth:`.DataFrame.aggregate` and :meth:`.DataFrame.modify` that operates
91
+ group-wise on column `x`.
90
92
 
91
93
  Equivalent to ``numpy.any``, see the NumPy documentation for details:
92
94
  https://numpy.org/doc/stable/reference/generated/numpy.any.html
@@ -107,10 +109,10 @@ def count(x="", *, drop_na=False):
107
109
  Return the amount of elements in `x`.
108
110
 
109
111
  If `x` is a string, return a function usable with
110
- :meth:`.DataFrame.aggregate` that operates group-wise on column `x`. Since
111
- all columns in a data frame should have the same amount of elements (i.e.
112
- rows), you can just leave the x argument at its default blank string, which
113
- will give you that row count.
112
+ :meth:`.DataFrame.aggregate` and :meth:`.DataFrame.modify` that operates
113
+ group-wise on column `x`. Since all columns in a data frame should have the
114
+ same amount of elements (i.e. rows), you can just leave the x argument at
115
+ its default blank string, which will give you that row count.
114
116
 
115
117
  >>> di.count(di.Vector([1, 2, 3]))
116
118
  >>> di.count()
@@ -123,6 +125,8 @@ def count(x="", *, drop_na=False):
123
125
  return np.diff(starts, append=data.nrow)
124
126
  aggregate.group_aware = True
125
127
  return aggregate
128
+ if not isinstance(x, (Vector, str)):
129
+ raise TypeError("Expected Vector or str")
126
130
  return dispatch(count_groups, x)
127
131
 
128
132
  @composite
@@ -131,21 +135,20 @@ def count_unique(x, *, drop_na=False):
131
135
  Return the amount of unique elements in `x`.
132
136
 
133
137
  If `x` is a string, return a function usable with
134
- :meth:`.DataFrame.aggregate` that operates group-wise on column `x`.
138
+ :meth:`.DataFrame.aggregate` and :meth:`.DataFrame.modify` that operates
139
+ group-wise on column `x`.
135
140
 
136
141
  >>> di.count_unique(di.Vector([1, 2, 2, 3, 3, 3]))
137
142
  >>> di.count_unique("x")
138
143
  """
139
- def count_unique_groups(x, starts):
140
- y, starts = handle_na_groups(x, starts, drop_na)
141
- n = np.diff(starts, append=len(y))
144
+ def count_unique_groups(codes, starts, take):
145
+ n = np.diff(starts, append=len(codes))
142
146
  group = np.repeat(np.arange(len(starts)), n)
143
- codes = value_codes(y)
144
147
  m = codes.max(initial=0) + 1
145
148
  # Count unique pairs of group and value.
146
149
  keys = util.unique_codes(group * m + codes)[0]
147
150
  return np.bincount(keys // m, minlength=len(starts))
148
- return dispatch(count_unique_groups, x)
151
+ return dispatch_codes(count_unique_groups, x, drop_na)
149
152
 
150
153
  def datetime_as_float(x):
151
154
  if not (x.is_datetime() or x.is_timedelta()): return x
@@ -171,6 +174,34 @@ def dispatch(function, x):
171
174
  return aggregate
172
175
  return item(function(x, np.array([0]))[0])
173
176
 
177
+ def dispatch_codes(function, x, drop_na):
178
+ # Like dispatch, but function takes value codes of x instead of x, starts
179
+ # of their groups and a function returning elements of x at indices of
180
+ # codes. For a column, codes are computed in source order and reordered
181
+ # to group order, which is faster than reordering e.g. strings first.
182
+ # With drop_na, missing values get code -1, hashing only the rest.
183
+ def get_codes(x):
184
+ na = x.is_na() if drop_na else None
185
+ if na is None or not na.any():
186
+ return value_codes(x)
187
+ codes = np.full(len(x), -1, np.int64)
188
+ codes[~na] = value_codes(x[~na])
189
+ return codes
190
+ def call(codes, take, starts):
191
+ valid = codes >= 0
192
+ if valid.all():
193
+ return function(codes, starts, take)
194
+ kept = np.flatnonzero(valid)
195
+ starts = (np.cumsum(valid) - valid)[starts]
196
+ return function(codes[kept], starts, lambda i: take(kept[i]))
197
+ if isinstance(x, str):
198
+ def aggregate(data, starts):
199
+ take = functools.partial(data._peek, x)
200
+ return call(data._map(x, get_codes), take, starts)
201
+ aggregate.group_aware = True
202
+ return aggregate
203
+ return item(call(get_codes(x), x.__getitem__, np.array([0]))[0])
204
+
174
205
  def fill_missing(values, found):
175
206
  if found.all(): return values
176
207
  # Upcast to have missing values, e.g. int to float.
@@ -183,7 +214,8 @@ def first(x, *, drop_na=False):
183
214
  Return the first element of `x`.
184
215
 
185
216
  If `x` is a string, return a function usable with
186
- :meth:`.DataFrame.aggregate` that operates group-wise on column `x`.
217
+ :meth:`.DataFrame.aggregate` and :meth:`.DataFrame.modify` that operates
218
+ group-wise on column `x`.
187
219
 
188
220
  >>> di.first(di.Vector([1, 2, 3]))
189
221
  >>> di.first("x")
@@ -214,7 +246,8 @@ def last(x, *, drop_na=False):
214
246
  Return the last element of `x`.
215
247
 
216
248
  If `x` is a string, return a function usable with
217
- :meth:`.DataFrame.aggregate` that operates group-wise on column `x`.
249
+ :meth:`.DataFrame.aggregate` and :meth:`.DataFrame.modify` that operates
250
+ group-wise on column `x`.
218
251
 
219
252
  >>> di.last(di.Vector([1, 2, 3]))
220
253
  >>> di.last("x")
@@ -227,7 +260,8 @@ def max(x, *, drop_na=True):
227
260
  Return the maximum of elements in `x`.
228
261
 
229
262
  If `x` is a string, return a function usable with
230
- :meth:`.DataFrame.aggregate` that operates group-wise on column `x`.
263
+ :meth:`.DataFrame.aggregate` and :meth:`.DataFrame.modify` that operates
264
+ group-wise on column `x`.
231
265
 
232
266
  >>> di.max(di.Vector([4, 5, 6]))
233
267
  >>> di.max("x")
@@ -240,9 +274,9 @@ def mean(x, *, weights=None, drop_na=True):
240
274
  Return the arithmetic mean of `x`, optionally weighted by `weights`.
241
275
 
242
276
  If `x` is a string, return a function usable with
243
- :meth:`.DataFrame.aggregate` that operates group-wise on column `x`,
244
- weighted by column `weights`, if given. If `x` is a vector, `weights`
245
- should be a vector of the same length.
277
+ :meth:`.DataFrame.aggregate` and :meth:`.DataFrame.modify` that operates
278
+ group-wise on column `x`, weighted by column `weights`, if given. If `x` is
279
+ a vector, `weights` should be a vector of the same length.
246
280
 
247
281
  With `drop_na`, rows where either `x` or `weights` is missing are
248
282
  dropped. If weights sum to zero, the result is NaN.
@@ -295,7 +329,8 @@ def median(x, *, drop_na=True):
295
329
  Return the median of `x`.
296
330
 
297
331
  If `x` is a string, return a function usable with
298
- :meth:`.DataFrame.aggregate` that operates group-wise on column `x`.
332
+ :meth:`.DataFrame.aggregate` and :meth:`.DataFrame.modify` that operates
333
+ group-wise on column `x`.
299
334
 
300
335
  Equivalent to ``numpy.median``, see the NumPy documentation for details:
301
336
  https://numpy.org/doc/stable/reference/generated/numpy.median.html
@@ -331,7 +366,8 @@ def min(x, *, drop_na=True):
331
366
  Return the minimum of elements in `x`.
332
367
 
333
368
  If `x` is a string, return a function usable with
334
- :meth:`.DataFrame.aggregate` that operates group-wise on column `x`.
369
+ :meth:`.DataFrame.aggregate` and :meth:`.DataFrame.modify` that operates
370
+ group-wise on column `x`.
335
371
 
336
372
  >>> di.min(di.Vector([4, 5, 6]))
337
373
  >>> di.min("x")
@@ -364,18 +400,17 @@ def mode(x, *, drop_na=True):
364
400
  Return the most common value in `x`, the first one if tied.
365
401
 
366
402
  If `x` is a string, return a function usable with
367
- :meth:`.DataFrame.aggregate` that operates group-wise on column `x`.
403
+ :meth:`.DataFrame.aggregate` and :meth:`.DataFrame.modify` that operates
404
+ group-wise on column `x`.
368
405
 
369
406
  >>> di.mode(di.Vector([1, 2, 2, 3, 3, 3]))
370
407
  >>> di.mode("x")
371
408
  """
372
- def mode_groups(x, starts):
373
- y, starts = handle_na_groups(x, starts, drop_na)
374
- n = np.diff(starts, append=len(y))
409
+ def mode_groups(codes, starts, take):
410
+ n = np.diff(starts, append=len(codes))
375
411
  found = n > 0
376
412
  if found.any():
377
413
  group = np.repeat(np.arange(len(starts)), n)
378
- codes = value_codes(y)
379
414
  m = codes.max() + 1
380
415
  keys, first, pairs = util.unique_codes(group * m + codes)
381
416
  # Unique pairs of group and value are in order of first appearance
@@ -388,11 +423,11 @@ def mode(x, *, drop_na=True):
388
423
  most = np.repeat(most, np.diff(pair_starts, append=len(keys)))
389
424
  candidates = np.flatnonzero(count == most)
390
425
  chosen = candidates[group_starts(pair_group[candidates])]
391
- values = y[first[chosen]]
426
+ values = take(first[chosen])
392
427
  else:
393
- values = y[:0]
428
+ values = take(np.array([], np.int64))
394
429
  return fill_missing(values, found)
395
- return dispatch(mode_groups, x)
430
+ return dispatch_codes(mode_groups, x, drop_na)
396
431
 
397
432
  @composite
398
433
  def nth(x, index, *, drop_na=False):
@@ -400,7 +435,8 @@ def nth(x, index, *, drop_na=False):
400
435
  Return the element of `x` at `index` (zero-based).
401
436
 
402
437
  If `x` is a string, return a function usable with
403
- :meth:`.DataFrame.aggregate` that operates group-wise on column `x`.
438
+ :meth:`.DataFrame.aggregate` and :meth:`.DataFrame.modify` that operates
439
+ group-wise on column `x`.
404
440
 
405
441
  >>> di.nth(di.Vector([1, 2, 3]), 1)
406
442
  >>> di.nth("x", 1)
@@ -429,7 +465,8 @@ def prod(x, *, drop_na=True):
429
465
  Return the product of `x`.
430
466
 
431
467
  If `x` is a string, return a function usable with
432
- :meth:`.DataFrame.aggregate` that operates group-wise on column `x`.
468
+ :meth:`.DataFrame.aggregate` and :meth:`.DataFrame.modify` that operates
469
+ group-wise on column `x`.
433
470
 
434
471
  >>> di.prod(di.Vector([1, 2, 3]))
435
472
  >>> di.prod("x")
@@ -442,7 +479,8 @@ def quantile(x, q, *, drop_na=True):
442
479
  Return the `qth` quantile of `x`.
443
480
 
444
481
  If `x` is a string, return a function usable with
445
- :meth:`.DataFrame.aggregate` that operates group-wise on column `x`.
482
+ :meth:`.DataFrame.aggregate` and :meth:`.DataFrame.modify` that operates
483
+ group-wise on column `x`.
446
484
 
447
485
  Equivalent to ``numpy.quantile``, see the NumPy documentation for details:
448
486
  https://numpy.org/doc/stable/reference/generated/numpy.quantile.html
@@ -502,7 +540,8 @@ def std(x, *, ddof=0, drop_na=True):
502
540
  Return the standard deviation of `x`.
503
541
 
504
542
  If `x` is a string, return a function usable with
505
- :meth:`.DataFrame.aggregate` that operates group-wise on column `x`.
543
+ :meth:`.DataFrame.aggregate` and :meth:`.DataFrame.modify` that operates
544
+ group-wise on column `x`.
506
545
 
507
546
  Equivalent to ``numpy.std``, see the NumPy documentation for details:
508
547
  https://numpy.org/doc/stable/reference/generated/numpy.std.html
@@ -526,7 +565,8 @@ def sum(x, *, drop_na=True):
526
565
  Return the sum of `x`.
527
566
 
528
567
  If `x` is a string, return a function usable with
529
- :meth:`.DataFrame.aggregate` that operates group-wise on column `x`.
568
+ :meth:`.DataFrame.aggregate` and :meth:`.DataFrame.modify` that operates
569
+ group-wise on column `x`.
530
570
 
531
571
  >>> di.sum(di.Vector([1, 2, 3]))
532
572
  >>> di.sum("x")
@@ -562,7 +602,8 @@ def var(x, *, ddof=0, drop_na=True):
562
602
  Return the variance of `x`.
563
603
 
564
604
  If `x` is a string, return a function usable with
565
- :meth:`.DataFrame.aggregate` that operates group-wise on column `x`.
605
+ :meth:`.DataFrame.aggregate` and :meth:`.DataFrame.modify` that operates
606
+ group-wise on column `x`.
566
607
 
567
608
  Equivalent to ``numpy.var``, see the NumPy documentation for details:
568
609
  https://numpy.org/doc/stable/reference/generated/numpy.var.html