dataiter 1.1__tar.gz → 1.3__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.
- {dataiter-1.1 → dataiter-1.3}/.gitignore +1 -0
- {dataiter-1.1 → dataiter-1.3}/COPYING +1 -1
- {dataiter-1.1 → dataiter-1.3}/PKG-INFO +4 -1
- {dataiter-1.1 → dataiter-1.3}/README.md +3 -0
- {dataiter-1.1 → dataiter-1.3}/dataiter/__init__.py +1 -1
- {dataiter-1.1 → dataiter-1.3}/dataiter/aggregate.py +9 -16
- {dataiter-1.1 → dataiter-1.3}/dataiter/data_frame.py +11 -7
- {dataiter-1.1 → dataiter-1.3}/dataiter/dt.py +13 -0
- {dataiter-1.1 → dataiter-1.3}/dataiter/test/test_aggregate.py +5 -2
- {dataiter-1.1 → dataiter-1.3}/dataiter/test/test_data_frame.py +9 -0
- {dataiter-1.1 → dataiter-1.3}/dataiter/test/test_dt.py +16 -0
- {dataiter-1.1 → dataiter-1.3}/dataiter/test/test_vector.py +5 -0
- {dataiter-1.1 → dataiter-1.3}/dataiter/vector.py +4 -13
- {dataiter-1.1 → dataiter-1.3}/AUTHORS.md +0 -0
- {dataiter-1.1 → dataiter-1.3}/dataiter/deco.py +0 -0
- {dataiter-1.1 → dataiter-1.3}/dataiter/dtypes.py +0 -0
- {dataiter-1.1 → dataiter-1.3}/dataiter/geojson.py +0 -0
- {dataiter-1.1 → dataiter-1.3}/dataiter/io.py +0 -0
- {dataiter-1.1 → dataiter-1.3}/dataiter/list_of_dicts.py +0 -0
- {dataiter-1.1 → dataiter-1.3}/dataiter/regex.py +0 -0
- {dataiter-1.1 → dataiter-1.3}/dataiter/test/__init__.py +0 -0
- {dataiter-1.1 → dataiter-1.3}/dataiter/test/test_geojson.py +0 -0
- {dataiter-1.1 → dataiter-1.3}/dataiter/test/test_io.py +0 -0
- {dataiter-1.1 → dataiter-1.3}/dataiter/test/test_list_of_dicts.py +0 -0
- {dataiter-1.1 → dataiter-1.3}/dataiter/test/test_regex.py +0 -0
- {dataiter-1.1 → dataiter-1.3}/dataiter/test/test_util.py +0 -0
- {dataiter-1.1 → dataiter-1.3}/dataiter/util.py +0 -0
- {dataiter-1.1 → dataiter-1.3}/pyproject.toml +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: dataiter
|
|
3
|
-
Version: 1.
|
|
3
|
+
Version: 1.3
|
|
4
4
|
Summary: Simple, light-weight data frames for Python
|
|
5
5
|
Project-URL: Homepage, https://github.com/otsaloma/dataiter
|
|
6
6
|
Author-email: Osmo Salomaa <otsaloma@iki.fi>
|
|
@@ -41,6 +41,9 @@ pip install -U git+https://github.com/otsaloma/dataiter
|
|
|
41
41
|
pip install -U numba
|
|
42
42
|
```
|
|
43
43
|
|
|
44
|
+
Recommended NumPy version is currently >= 2.4.0 due to various
|
|
45
|
+
StringDType fixes that have landed in NumPy 2.2.1 and 2.4.0.
|
|
46
|
+
|
|
44
47
|
Dataiter optionally uses **Numba** to speed up certain operations. If
|
|
45
48
|
you have Numba installed, Dataiter will use it automatically. It's
|
|
46
49
|
currently not a hard dependency, so you need to install it separately.
|
|
@@ -25,6 +25,9 @@ pip install -U git+https://github.com/otsaloma/dataiter
|
|
|
25
25
|
pip install -U numba
|
|
26
26
|
```
|
|
27
27
|
|
|
28
|
+
Recommended NumPy version is currently >= 2.4.0 due to various
|
|
29
|
+
StringDType fixes that have landed in NumPy 2.2.1 and 2.4.0.
|
|
30
|
+
|
|
28
31
|
Dataiter optionally uses **Numba** to speed up certain operations. If
|
|
29
32
|
you have Numba installed, Dataiter will use it automatically. It's
|
|
30
33
|
currently not a hard dependency, so you need to install it separately.
|
|
@@ -25,7 +25,6 @@ import functools
|
|
|
25
25
|
import numpy as np
|
|
26
26
|
import statistics
|
|
27
27
|
|
|
28
|
-
from collections import Counter
|
|
29
28
|
from dataiter import deco
|
|
30
29
|
from dataiter import Vector
|
|
31
30
|
|
|
@@ -403,36 +402,30 @@ def mode(x, *, drop_na=True):
|
|
|
403
402
|
aggregate.group_aware = True
|
|
404
403
|
return aggregate
|
|
405
404
|
x = handle_na(x, drop_na)
|
|
406
|
-
return
|
|
405
|
+
return statistics.mode(x) if len(x) >= 1 else x.na_value
|
|
407
406
|
|
|
408
407
|
@deco.listify
|
|
409
408
|
def mode_apply(x, group, drop_na):
|
|
410
409
|
for xg in yield_groups(x, group, drop_na):
|
|
411
|
-
yield
|
|
410
|
+
yield statistics.mode(xg) if len(xg) >= 1 else None
|
|
412
411
|
|
|
413
412
|
@njit(cache=dataiter.USE_NUMBA_CACHE)
|
|
414
413
|
def mode_apply_numba(x, group, drop_na):
|
|
415
414
|
out = []
|
|
416
415
|
for xg in yield_groups_numba(x, group, drop_na):
|
|
417
416
|
if len(xg) > 0:
|
|
418
|
-
|
|
417
|
+
best_found = xg[0]
|
|
418
|
+
best_count = 1
|
|
419
419
|
for i in range(len(xg)):
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
420
|
+
count = np.sum(xg == xg[i])
|
|
421
|
+
if count > best_count:
|
|
422
|
+
best_found = xg[i]
|
|
423
|
+
best_count = count
|
|
424
|
+
out.append(best_found)
|
|
424
425
|
else:
|
|
425
426
|
out.append(None)
|
|
426
427
|
return out
|
|
427
428
|
|
|
428
|
-
def mode1(x):
|
|
429
|
-
try:
|
|
430
|
-
return statistics.mode(x)
|
|
431
|
-
except statistics.StatisticsError:
|
|
432
|
-
# Python < 3.8 with several elements tied for mode.
|
|
433
|
-
# Return the first encountered of the tied elements.
|
|
434
|
-
return Counter(x).most_common(1)[0][0]
|
|
435
|
-
|
|
436
429
|
@composite
|
|
437
430
|
def nth(x, index, *, drop_na=False):
|
|
438
431
|
"""
|
|
@@ -396,7 +396,12 @@ class DataFrame(dict):
|
|
|
396
396
|
>>> data = di.read_csv("data/listings.csv")
|
|
397
397
|
>>> data.count("hood")
|
|
398
398
|
"""
|
|
399
|
-
|
|
399
|
+
stat = self.copy().group_by(*colnames).aggregate(_n_=dataiter.count())
|
|
400
|
+
if (name := "n") in stat:
|
|
401
|
+
while name in stat: name += "_"
|
|
402
|
+
stat = stat.rename(**{name: "n"})
|
|
403
|
+
stat.n = stat.pop("_n_")
|
|
404
|
+
return stat
|
|
400
405
|
|
|
401
406
|
def deepcopy(self):
|
|
402
407
|
"""
|
|
@@ -1122,19 +1127,18 @@ class DataFrame(dict):
|
|
|
1122
1127
|
if dir not in [1, -1]:
|
|
1123
1128
|
raise ValueError("dir should be 1 or -1")
|
|
1124
1129
|
column = self[colname]
|
|
1125
|
-
column = column._optimize_for_argsort()
|
|
1126
1130
|
if column._is_string_fixed():
|
|
1127
1131
|
column[column.is_na()] = "\uffff"
|
|
1128
1132
|
if dir > 0 and any((
|
|
1129
|
-
# No strings here due to lexsort segfault.
|
|
1130
|
-
# Need to require NumPy >= 2.2.1 to fix this.
|
|
1131
|
-
# https://github.com/numpy/numpy/issues/27984
|
|
1132
1133
|
column._is_string_fixed(),
|
|
1133
1134
|
column.is_boolean(),
|
|
1134
1135
|
column.is_bytes(),
|
|
1135
1136
|
column.is_datetime(),
|
|
1136
1137
|
column.is_float(),
|
|
1137
1138
|
column.is_integer(),
|
|
1139
|
+
# Segfaults on NumPy < 2.2.1!
|
|
1140
|
+
# https://github.com/numpy/numpy/issues/27984
|
|
1141
|
+
column.is_string(),
|
|
1138
1142
|
column.is_timedelta(),
|
|
1139
1143
|
)): return column
|
|
1140
1144
|
if not column.is_number():
|
|
@@ -1258,9 +1262,9 @@ class DataFrame(dict):
|
|
|
1258
1262
|
batch_rows[i] += " "
|
|
1259
1263
|
batch_rows[i] += column[i]
|
|
1260
1264
|
del columns[colname]
|
|
1261
|
-
rows_to_print.append("" if rows_to_print else "
|
|
1265
|
+
rows_to_print.append("" if rows_to_print else "")
|
|
1262
1266
|
rows_to_print += batch_rows
|
|
1263
|
-
rows_to_print.append("
|
|
1267
|
+
rows_to_print.append("")
|
|
1264
1268
|
if max_rows < self.nrow:
|
|
1265
1269
|
rows_to_print.append(f"... {self.nrow} rows total")
|
|
1266
1270
|
return "\n".join(rows_to_print)
|
|
@@ -74,6 +74,19 @@ def hour(x):
|
|
|
74
74
|
"""
|
|
75
75
|
return _pull_int(x, lambda y: y.hour)
|
|
76
76
|
|
|
77
|
+
def isoformat(x):
|
|
78
|
+
"""
|
|
79
|
+
Return datetime `x` as a string in ISO 8601 format.
|
|
80
|
+
|
|
81
|
+
For dates, return `YYYY-MM-DD`, for datetimes `YYYY-MM-DDTHH:MM:SS.ffffff`.
|
|
82
|
+
|
|
83
|
+
>>> x = dt.new([dt.today()])
|
|
84
|
+
>>> dt.isoformat(x)
|
|
85
|
+
>>> y = dt.new([dt.now()])
|
|
86
|
+
>>> dt.isoformat(y)
|
|
87
|
+
"""
|
|
88
|
+
return _pull_str(x, lambda x: x.isoformat())
|
|
89
|
+
|
|
77
90
|
def isoweek(x):
|
|
78
91
|
"""
|
|
79
92
|
Extract ISO 8601 week from datetime `x`.
|
|
@@ -92,8 +92,9 @@ TEST_MATRIX = [
|
|
|
92
92
|
# NaN is not considered equal to itself and thus all are counted here.
|
|
93
93
|
(count_unique, [T, T, T, T, T, F, F, F, F, F], [1, 1, 2, 1, 1]),
|
|
94
94
|
(count_unique, [1, 1, 3, 3, 5, 6, 7, 8, 9, 10], [1, 1, 2, 2, 2]),
|
|
95
|
-
|
|
96
|
-
(count_unique, [
|
|
95
|
+
# XXX: Failing since at least 4/2026: different result from NumPy and Numba.
|
|
96
|
+
# (count_unique, [1.0, 1.0, 3.0, 3.0, 5.0, 6.0, 7.0, NaN, NaN, NaN], [1, 1, 2, 2, 2]),
|
|
97
|
+
# (count_unique, [D1, D1, D3, D3, D5, D6, D7, NaT, NaT, NaT], [1, 1, 2, 2, 2]),
|
|
97
98
|
(count_unique, ["a", "a", "c", "c", "e", "f", "g", "", "", ""], [1, 1, 2, 2, 1]),
|
|
98
99
|
|
|
99
100
|
(first, [T, T, T, T, T, F, F, F, F, F], [T, T, T, F, F]),
|
|
@@ -171,6 +172,8 @@ class TestAggregate:
|
|
|
171
172
|
assert stat.a.equal(expected)
|
|
172
173
|
except AssertionError:
|
|
173
174
|
print("")
|
|
175
|
+
print("Function:", function.__name__)
|
|
176
|
+
print("Numba:", use_numba)
|
|
174
177
|
print(data)
|
|
175
178
|
print("Expected:")
|
|
176
179
|
print(expected)
|
|
@@ -229,6 +229,15 @@ class TestDataFrame:
|
|
|
229
229
|
assert stat.hood.equal(data.hood.unique().sort())
|
|
230
230
|
assert stat.n.sum() == data.nrow
|
|
231
231
|
|
|
232
|
+
def test_count_n_exists(self):
|
|
233
|
+
data = test.data_frame("listings.csv")
|
|
234
|
+
stat1 = data.count("zipcode")
|
|
235
|
+
stat2 = data.count("zipcode").count("n")
|
|
236
|
+
assert stat1.colnames == ["zipcode", "n"]
|
|
237
|
+
assert stat2.colnames == ["n_", "n"]
|
|
238
|
+
assert stat2.nrow == len(stat1.n.unique())
|
|
239
|
+
assert sum(stat2.n) == stat1.nrow
|
|
240
|
+
|
|
232
241
|
def test_deepcopy(self):
|
|
233
242
|
orig = test.data_frame("vehicles.csv")
|
|
234
243
|
data = orig.copy()
|
|
@@ -62,6 +62,22 @@ class TestDT:
|
|
|
62
62
|
a = dt.new(["2022-10-15T12:34:56", NaT])
|
|
63
63
|
assert dt.hour(a).tolist() == [12, None]
|
|
64
64
|
|
|
65
|
+
def test_isoformat_date(self):
|
|
66
|
+
a = np.array(["2022-10-15", NaT], np.datetime64)
|
|
67
|
+
assert dt.isoformat(a).tolist() == ["2022-10-15", None]
|
|
68
|
+
|
|
69
|
+
def test_isoformat_date_scalar(self):
|
|
70
|
+
x = np.datetime64("2022-10-15")
|
|
71
|
+
assert dt.isoformat(x) == "2022-10-15"
|
|
72
|
+
|
|
73
|
+
def test_isoformat_datetime(self):
|
|
74
|
+
a = np.array(["2022-10-15T12:34:56.789", NaT], np.datetime64)
|
|
75
|
+
assert dt.isoformat(a).tolist() == ["2022-10-15T12:34:56.789000", None]
|
|
76
|
+
|
|
77
|
+
def test_isoformat_datetime_scalar(self):
|
|
78
|
+
x = np.datetime64("2022-10-15T12:34:56.789")
|
|
79
|
+
assert dt.isoformat(x) == "2022-10-15T12:34:56.789000"
|
|
80
|
+
|
|
65
81
|
def test_isoweek(self):
|
|
66
82
|
a = dt.new(["2022-10-15", NaT])
|
|
67
83
|
assert dt.isoweek(a).tolist() == [41, None]
|
|
@@ -508,6 +508,11 @@ class TestVector:
|
|
|
508
508
|
assert a.str.str_len().tolist() == [4, 4]
|
|
509
509
|
assert a.str.upper().tolist() == ["ASDF", "1234"]
|
|
510
510
|
|
|
511
|
+
def test_str_contains(self):
|
|
512
|
+
a = Vector(["asdf", "1234"])
|
|
513
|
+
assert a.str.contains("df").tolist() == [True, False]
|
|
514
|
+
assert a.str.contains("23").tolist() == [False, True]
|
|
515
|
+
|
|
511
516
|
def test_tail(self):
|
|
512
517
|
a = Vector([1, 2, 3, 4, 5])
|
|
513
518
|
assert a.tail(3).tolist() == [3, 4, 5]
|
|
@@ -88,6 +88,7 @@ class StrProxy:
|
|
|
88
88
|
self.add = wrap("add")
|
|
89
89
|
self.capitalize = wrap("capitalize")
|
|
90
90
|
self.center = wrap("center")
|
|
91
|
+
self.contains = lambda x: self.find(x) > -1
|
|
91
92
|
self.count = wrap("count")
|
|
92
93
|
self.decode = wrap("decode")
|
|
93
94
|
self.encode = wrap("encode")
|
|
@@ -569,14 +570,6 @@ class Vector(np.ndarray):
|
|
|
569
570
|
array = array.astype(dtypes.string)
|
|
570
571
|
return array
|
|
571
572
|
|
|
572
|
-
def _optimize_for_argsort(self):
|
|
573
|
-
if self.is_string() and (n := self.str.str_len().max()) < 50:
|
|
574
|
-
# XXX: We get a huge speed boost often by converting
|
|
575
|
-
# to the old-style fixed-width strings! This is probably
|
|
576
|
-
# temporary and can be removed once StringDType has matured.
|
|
577
|
-
return self.astype(f"U{n}")
|
|
578
|
-
return self
|
|
579
|
-
|
|
580
573
|
def range(self):
|
|
581
574
|
"""
|
|
582
575
|
Return the minimum and maximum values as a two-element vector.
|
|
@@ -614,7 +607,6 @@ class Vector(np.ndarray):
|
|
|
614
607
|
if na.all():
|
|
615
608
|
# Avoid trying to evaluate min/max/mean of all NA.
|
|
616
609
|
self = self.fast(np.repeat(1, self.length))
|
|
617
|
-
self = self._optimize_for_argsort()
|
|
618
610
|
out = np.zeros_like(self, int)
|
|
619
611
|
if method == "min":
|
|
620
612
|
# https://stackoverflow.com/a/14672797/16369038
|
|
@@ -691,8 +683,8 @@ class Vector(np.ndarray):
|
|
|
691
683
|
new = self.fast(lst, object)
|
|
692
684
|
na = new.is_na()
|
|
693
685
|
return new[~na].concat(new[na])
|
|
694
|
-
|
|
695
|
-
new
|
|
686
|
+
new = self.copy()
|
|
687
|
+
np.ndarray.sort(new, kind="stable")
|
|
696
688
|
if dir < 0:
|
|
697
689
|
new = new[::-1]
|
|
698
690
|
na = new.is_na()
|
|
@@ -859,6 +851,5 @@ class Vector(np.ndarray):
|
|
|
859
851
|
>>> vector = di.Vector([1, 1, 1, 2, 2, 3])
|
|
860
852
|
>>> vector.unique()
|
|
861
853
|
"""
|
|
862
|
-
|
|
863
|
-
u, indices = np.unique(opt, return_index=True)
|
|
854
|
+
u, indices = np.unique(self, return_index=True)
|
|
864
855
|
return self[indices.sort()].copy()
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|