dataiter 1.1__tar.gz → 1.2__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 (28) hide show
  1. {dataiter-1.1 → dataiter-1.2}/.gitignore +1 -0
  2. {dataiter-1.1 → dataiter-1.2}/PKG-INFO +1 -1
  3. {dataiter-1.1 → dataiter-1.2}/dataiter/__init__.py +1 -1
  4. {dataiter-1.1 → dataiter-1.2}/dataiter/aggregate.py +9 -16
  5. {dataiter-1.1 → dataiter-1.2}/dataiter/data_frame.py +8 -3
  6. {dataiter-1.1 → dataiter-1.2}/dataiter/dt.py +13 -0
  7. {dataiter-1.1 → dataiter-1.2}/dataiter/test/test_aggregate.py +2 -0
  8. {dataiter-1.1 → dataiter-1.2}/dataiter/test/test_data_frame.py +9 -0
  9. {dataiter-1.1 → dataiter-1.2}/dataiter/test/test_dt.py +16 -0
  10. {dataiter-1.1 → dataiter-1.2}/AUTHORS.md +0 -0
  11. {dataiter-1.1 → dataiter-1.2}/COPYING +0 -0
  12. {dataiter-1.1 → dataiter-1.2}/README.md +0 -0
  13. {dataiter-1.1 → dataiter-1.2}/dataiter/deco.py +0 -0
  14. {dataiter-1.1 → dataiter-1.2}/dataiter/dtypes.py +0 -0
  15. {dataiter-1.1 → dataiter-1.2}/dataiter/geojson.py +0 -0
  16. {dataiter-1.1 → dataiter-1.2}/dataiter/io.py +0 -0
  17. {dataiter-1.1 → dataiter-1.2}/dataiter/list_of_dicts.py +0 -0
  18. {dataiter-1.1 → dataiter-1.2}/dataiter/regex.py +0 -0
  19. {dataiter-1.1 → dataiter-1.2}/dataiter/test/__init__.py +0 -0
  20. {dataiter-1.1 → dataiter-1.2}/dataiter/test/test_geojson.py +0 -0
  21. {dataiter-1.1 → dataiter-1.2}/dataiter/test/test_io.py +0 -0
  22. {dataiter-1.1 → dataiter-1.2}/dataiter/test/test_list_of_dicts.py +0 -0
  23. {dataiter-1.1 → dataiter-1.2}/dataiter/test/test_regex.py +0 -0
  24. {dataiter-1.1 → dataiter-1.2}/dataiter/test/test_util.py +0 -0
  25. {dataiter-1.1 → dataiter-1.2}/dataiter/test/test_vector.py +0 -0
  26. {dataiter-1.1 → dataiter-1.2}/dataiter/util.py +0 -0
  27. {dataiter-1.1 → dataiter-1.2}/dataiter/vector.py +0 -0
  28. {dataiter-1.1 → dataiter-1.2}/pyproject.toml +0 -0
@@ -1,5 +1,6 @@
1
1
  *.lprof
2
2
  *.prof
3
+ .claude
3
4
  .env
4
5
  .envrc
5
6
  .pytest_cache
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: dataiter
3
- Version: 1.1
3
+ Version: 1.2
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>
@@ -25,7 +25,7 @@ import numpy as np
25
25
 
26
26
  from dataiter import util
27
27
 
28
- __version__ = "1.1"
28
+ __version__ = "1.2"
29
29
 
30
30
  DEFAULT_PEEK_ELEMENTS = 10
31
31
  DEFAULT_PEEK_ITEMS = 3
@@ -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 mode1(x) if len(x) >= 1 else x.na_value
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 mode1(xg) if len(xg) >= 1 else None
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
- ng = np.full(len(xg), 0)
417
+ best_found = xg[0]
418
+ best_count = 1
419
419
  for i in range(len(xg)):
420
- for j in range(len(xg)):
421
- if xg[j] == xg[i]:
422
- ng[i] += 1
423
- out.append(xg[np.argmax(ng)])
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
- return self.copy().group_by(*colnames).aggregate(n=dataiter.count())
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
  """
@@ -1258,9 +1263,9 @@ class DataFrame(dict):
1258
1263
  batch_rows[i] += " "
1259
1264
  batch_rows[i] += column[i]
1260
1265
  del columns[colname]
1261
- rows_to_print.append("" if rows_to_print else ".")
1266
+ rows_to_print.append("" if rows_to_print else "")
1262
1267
  rows_to_print += batch_rows
1263
- rows_to_print.append(".")
1268
+ rows_to_print.append("")
1264
1269
  if max_rows < self.nrow:
1265
1270
  rows_to_print.append(f"... {self.nrow} rows total")
1266
1271
  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`.
@@ -171,6 +171,8 @@ class TestAggregate:
171
171
  assert stat.a.equal(expected)
172
172
  except AssertionError:
173
173
  print("")
174
+ print("Function:", function.__name__)
175
+ print("Numba:", use_numba)
174
176
  print(data)
175
177
  print("Expected:")
176
178
  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]
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