arraykit 1.9.0__tar.gz → 1.10.0__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 (53) hide show
  1. {arraykit-1.9.0/arraykit.egg-info → arraykit-1.10.0}/PKG-INFO +7 -1
  2. {arraykit-1.9.0 → arraykit-1.10.0}/README.rst +6 -0
  3. arraykit-1.10.0/VERSION +2 -0
  4. {arraykit-1.9.0 → arraykit-1.10.0/arraykit.egg-info}/PKG-INFO +7 -1
  5. {arraykit-1.9.0 → arraykit-1.10.0}/src/__init__.pyi +1 -0
  6. {arraykit-1.9.0 → arraykit-1.10.0}/src/tri_map.c +55 -0
  7. {arraykit-1.9.0 → arraykit-1.10.0}/test/test_tri_map.py +154 -0
  8. arraykit-1.9.0/VERSION +0 -2
  9. {arraykit-1.9.0 → arraykit-1.10.0}/LICENSE.txt +0 -0
  10. {arraykit-1.9.0 → arraykit-1.10.0}/MANIFEST.in +0 -0
  11. {arraykit-1.9.0 → arraykit-1.10.0}/arraykit.egg-info/SOURCES.txt +0 -0
  12. {arraykit-1.9.0 → arraykit-1.10.0}/arraykit.egg-info/dependency_links.txt +0 -0
  13. {arraykit-1.9.0 → arraykit-1.10.0}/arraykit.egg-info/requires.txt +0 -0
  14. {arraykit-1.9.0 → arraykit-1.10.0}/arraykit.egg-info/top_level.txt +0 -0
  15. {arraykit-1.9.0 → arraykit-1.10.0}/pyproject.toml +0 -0
  16. {arraykit-1.9.0 → arraykit-1.10.0}/setup.cfg +0 -0
  17. {arraykit-1.9.0 → arraykit-1.10.0}/setup.py +0 -0
  18. {arraykit-1.9.0 → arraykit-1.10.0}/src/__init__.py +0 -0
  19. {arraykit-1.9.0 → arraykit-1.10.0}/src/_arraykit.c +0 -0
  20. {arraykit-1.9.0 → arraykit-1.10.0}/src/array_go.c +0 -0
  21. {arraykit-1.9.0 → arraykit-1.10.0}/src/array_go.h +0 -0
  22. {arraykit-1.9.0 → arraykit-1.10.0}/src/array_to_tuple.c +0 -0
  23. {arraykit-1.9.0 → arraykit-1.10.0}/src/array_to_tuple.h +0 -0
  24. {arraykit-1.9.0 → arraykit-1.10.0}/src/auto_map.c +0 -0
  25. {arraykit-1.9.0 → arraykit-1.10.0}/src/auto_map.h +0 -0
  26. {arraykit-1.9.0 → arraykit-1.10.0}/src/block_index.c +0 -0
  27. {arraykit-1.9.0 → arraykit-1.10.0}/src/block_index.h +0 -0
  28. {arraykit-1.9.0 → arraykit-1.10.0}/src/delimited_to_arrays.c +0 -0
  29. {arraykit-1.9.0 → arraykit-1.10.0}/src/delimited_to_arrays.h +0 -0
  30. {arraykit-1.9.0 → arraykit-1.10.0}/src/methods.c +0 -0
  31. {arraykit-1.9.0 → arraykit-1.10.0}/src/methods.h +0 -0
  32. {arraykit-1.9.0 → arraykit-1.10.0}/src/py.typed +0 -0
  33. {arraykit-1.9.0 → arraykit-1.10.0}/src/tri_map.h +0 -0
  34. {arraykit-1.9.0 → arraykit-1.10.0}/src/utilities.h +0 -0
  35. {arraykit-1.9.0 → arraykit-1.10.0}/test/test_array_go.py +0 -0
  36. {arraykit-1.9.0 → arraykit-1.10.0}/test/test_astype_array.py +0 -0
  37. {arraykit-1.9.0 → arraykit-1.10.0}/test/test_auto_map.py +0 -0
  38. {arraykit-1.9.0 → arraykit-1.10.0}/test/test_auto_map_property.py +0 -0
  39. {arraykit-1.9.0 → arraykit-1.10.0}/test/test_block_index.py +0 -0
  40. {arraykit-1.9.0 → arraykit-1.10.0}/test/test_delimited_to_arrays.py +0 -0
  41. {arraykit-1.9.0 → arraykit-1.10.0}/test/test_delimited_to_arrays_integration.py +0 -0
  42. {arraykit-1.9.0 → arraykit-1.10.0}/test/test_delimited_to_arrays_property.py +0 -0
  43. {arraykit-1.9.0 → arraykit-1.10.0}/test/test_factorize.py +0 -0
  44. {arraykit-1.9.0 → arraykit-1.10.0}/test/test_fill_directional.py +0 -0
  45. {arraykit-1.9.0 → arraykit-1.10.0}/test/test_group_ordering.py +0 -0
  46. {arraykit-1.9.0 → arraykit-1.10.0}/test/test_nonzero_1d.py +0 -0
  47. {arraykit-1.9.0 → arraykit-1.10.0}/test/test_nonzero_1d_property.py +0 -0
  48. {arraykit-1.9.0 → arraykit-1.10.0}/test/test_objectable.py +0 -0
  49. {arraykit-1.9.0 → arraykit-1.10.0}/test/test_pyi.py +0 -0
  50. {arraykit-1.9.0 → arraykit-1.10.0}/test/test_split_after_count.py +0 -0
  51. {arraykit-1.9.0 → arraykit-1.10.0}/test/test_transition_slices_from_group.py +0 -0
  52. {arraykit-1.9.0 → arraykit-1.10.0}/test/test_type_discovery.py +0 -0
  53. {arraykit-1.9.0 → arraykit-1.10.0}/test/test_util.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: arraykit
3
- Version: 1.9.0
3
+ Version: 1.10.0
4
4
  Summary: Array utilities for StaticFrame
5
5
  Author: Christopher Ariza, Brandt Bucher, Charles Burkland
6
6
  License: MIT
@@ -64,6 +64,12 @@ ArrayKit requires the following:
64
64
  What is New in ArrayKit
65
65
  -------------------------
66
66
 
67
+ 1.10.0
68
+ ............
69
+
70
+ Added ``TriMap.register_pairs()``.
71
+
72
+
67
73
  1.9.0
68
74
  ............
69
75
 
@@ -35,6 +35,12 @@ ArrayKit requires the following:
35
35
  What is New in ArrayKit
36
36
  -------------------------
37
37
 
38
+ 1.10.0
39
+ ............
40
+
41
+ Added ``TriMap.register_pairs()``.
42
+
43
+
38
44
  1.9.0
39
45
  ............
40
46
 
@@ -0,0 +1,2 @@
1
+ 1.10.0
2
+
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: arraykit
3
- Version: 1.9.0
3
+ Version: 1.10.0
4
4
  Summary: Array utilities for StaticFrame
5
5
  Author: Christopher Ariza, Brandt Bucher, Charles Burkland
6
6
  License: MIT
@@ -64,6 +64,12 @@ ArrayKit requires the following:
64
64
  What is New in ArrayKit
65
65
  -------------------------
66
66
 
67
+ 1.10.0
68
+ ............
69
+
70
+ Added ``TriMap.register_pairs()``.
71
+
72
+
67
73
  1.9.0
68
74
  ............
69
75
 
@@ -54,6 +54,7 @@ class TriMap:
54
54
  def __repr__(self) -> str: ...
55
55
  def register_one(self, /, src_from: int, dst_from: int) -> None: ...
56
56
  def register_many_from_one(self, __dst_pos: np.ndarray) -> None: ...
57
+ def register_pairs(self, __src_pos: np.ndarray, __dst_pos: np.ndarray) -> None: ...
57
58
  def register_unmatched_dst(self) -> None: ...
58
59
  def register_many(self, /, src_from: int, dst_from: np.ndarray) -> None: ...
59
60
  def finalize(self) -> None: ...
@@ -310,6 +310,60 @@ TriMap_register_many_from_one(TriMapObject *self, PyObject *arg) {
310
310
  Py_RETURN_NONE;
311
311
  }
312
312
 
313
+ // Bulk pair registration: given two equal-length int64 arrays `src_pos` and `dst_pos`,
314
+ // register the pair (src_pos[i], dst_pos[i]) for each i in a single C loop -- equivalent
315
+ // to calling register_one(src_pos[i], dst_pos[i]) for each i, but without per-element
316
+ // Python overhead. Either position may be -1 to indicate an unmatched (fill) side. This
317
+ // generalizes register_many_from_one (where src is the implicit range 0..src_len) to
318
+ // arbitrary src positions, as needed for many-to-many joins.
319
+ PyObject *
320
+ TriMap_register_pairs(TriMapObject *self, PyObject *args) {
321
+ PyObject* src_arg;
322
+ PyObject* dst_arg;
323
+ if (!PyArg_ParseTuple(args,
324
+ "OO:register_pairs",
325
+ &src_arg,
326
+ &dst_arg)) {
327
+ return NULL;
328
+ }
329
+ if (self->finalized) {
330
+ PyErr_SetString(PyExc_RuntimeError, "Cannot register post finalization");
331
+ return NULL;
332
+ }
333
+ if (!PyArray_Check(src_arg) || !PyArray_Check(dst_arg)) {
334
+ PyErr_SetString(PyExc_TypeError, "Must provide arrays");
335
+ return NULL;
336
+ }
337
+ PyArrayObject* src_a = (PyArrayObject*)src_arg;
338
+ PyArrayObject* dst_a = (PyArrayObject*)dst_arg;
339
+ if (PyArray_TYPE(src_a) != NPY_INT64 || PyArray_TYPE(dst_a) != NPY_INT64) {
340
+ PyErr_SetString(PyExc_ValueError, "Arrays must be of type int64");
341
+ return NULL;
342
+ }
343
+ if (PyArray_NDIM(src_a) != 1 || PyArray_NDIM(dst_a) != 1) {
344
+ PyErr_SetString(PyExc_ValueError, "Arrays must be 1-dimensional");
345
+ return NULL;
346
+ }
347
+ if (!PyArray_IS_C_CONTIGUOUS(src_a) || !PyArray_ISALIGNED(src_a)
348
+ || !PyArray_IS_C_CONTIGUOUS(dst_a) || !PyArray_ISALIGNED(dst_a)) {
349
+ PyErr_SetString(PyExc_ValueError, "Arrays must be contiguous");
350
+ return NULL;
351
+ }
352
+ npy_intp n = PyArray_SIZE(src_a);
353
+ if (n != PyArray_SIZE(dst_a)) {
354
+ PyErr_SetString(PyExc_ValueError, "Arrays must be the same length");
355
+ return NULL;
356
+ }
357
+ const npy_int64* s = (npy_int64*)PyArray_DATA(src_a);
358
+ const npy_int64* d = (npy_int64*)PyArray_DATA(dst_a);
359
+ for (npy_intp i = 0; i < n; i++) {
360
+ if (AK_TM_register_one(self, (Py_ssize_t)s[i], (Py_ssize_t)d[i])) {
361
+ return NULL;
362
+ }
363
+ }
364
+ Py_RETURN_NONE;
365
+ }
366
+
313
367
  PyObject *
314
368
  TriMap_register_unmatched_dst(TriMapObject *self) {
315
369
  if (self->finalized) {
@@ -1400,6 +1454,7 @@ TriMap_map_dst_fill(TriMapObject *self, PyObject *args) {
1400
1454
  static PyMethodDef TriMap_methods[] = {
1401
1455
  {"register_one", (PyCFunction)TriMap_register_one, METH_VARARGS, NULL},
1402
1456
  {"register_many_from_one", (PyCFunction)TriMap_register_many_from_one, METH_O, NULL},
1457
+ {"register_pairs", (PyCFunction)TriMap_register_pairs, METH_VARARGS, NULL},
1403
1458
  {"register_unmatched_dst", (PyCFunction)TriMap_register_unmatched_dst, METH_NOARGS, NULL},
1404
1459
  {"register_many", (PyCFunction)TriMap_register_many, METH_VARARGS, NULL},
1405
1460
  {"finalize", (PyCFunction)TriMap_finalize, METH_NOARGS, NULL},
@@ -1508,3 +1508,157 @@ class TestUnit(unittest.TestCase):
1508
1508
  tm.finalize()
1509
1509
  with self.assertRaises(RuntimeError): # post-finalize
1510
1510
  tm.register_many_from_one(np.array([0, 1], dtype=np.int64))
1511
+
1512
+ # ---------------------------------------------------------------------------
1513
+ # register_pairs (bulk arbitrary (src, dst) pairs)
1514
+
1515
+ def test_tri_map_register_pairs_a(self) -> None:
1516
+ # a many-to-many mapping: src 0 -> dst 0 and 1; src 1 -> dst 1
1517
+ tm = TriMap(2, 2)
1518
+ tm.register_pairs(
1519
+ np.array([0, 0, 1], dtype=np.int64),
1520
+ np.array([0, 1, 1], dtype=np.int64),
1521
+ )
1522
+ tm.finalize()
1523
+ self.assertTrue(tm.is_many())
1524
+ self.assertTrue(tm.src_no_fill())
1525
+ self.assertTrue(tm.dst_no_fill())
1526
+ src = np.array([10, 20])
1527
+ dst = np.array([100, 200])
1528
+ self.assertEqual(tm.map_src_no_fill(src).tolist(), [10, 10, 20])
1529
+ self.assertEqual(tm.map_dst_no_fill(dst).tolist(), [100, 200, 200])
1530
+
1531
+ def test_tri_map_register_pairs_unmatched(self) -> None:
1532
+ # -1 on either side marks that side as a fill row
1533
+ tm = TriMap(3, 2)
1534
+ tm.register_pairs(
1535
+ np.array([0, 1, 2], dtype=np.int64),
1536
+ np.array([0, 1, -1], dtype=np.int64),
1537
+ )
1538
+ tm.finalize()
1539
+ self.assertTrue(tm.src_no_fill()) # every row has a src
1540
+ self.assertFalse(tm.dst_no_fill()) # row 2 has no dst
1541
+ dst = np.array([100, 200])
1542
+ self.assertEqual(
1543
+ tm.map_dst_fill(dst, -9, np.dtype(np.int64)).tolist(), [100, 200, -9]
1544
+ )
1545
+
1546
+ def test_tri_map_register_pairs_unmatched_src(self) -> None:
1547
+ # -1 on the src side (as register_unmatched_dst would produce for OUTER)
1548
+ tm = TriMap(2, 3)
1549
+ tm.register_pairs(
1550
+ np.array([0, 1, -1], dtype=np.int64),
1551
+ np.array([0, 1, 2], dtype=np.int64),
1552
+ )
1553
+ tm.finalize()
1554
+ self.assertFalse(tm.src_no_fill()) # row 2 has no src
1555
+ self.assertTrue(tm.dst_no_fill()) # every row has a dst
1556
+ src = np.array([10, 20])
1557
+ self.assertEqual(
1558
+ tm.map_src_fill(src, -9, np.dtype(np.int64)).tolist(), [10, 20, -9]
1559
+ )
1560
+
1561
+ def test_tri_map_register_pairs_is_many(self) -> None:
1562
+ # a single output row per src/dst -> not many
1563
+ tm = TriMap(2, 2)
1564
+ tm.register_pairs(
1565
+ np.array([0, 1], dtype=np.int64),
1566
+ np.array([1, 0], dtype=np.int64),
1567
+ )
1568
+ tm.finalize()
1569
+ self.assertFalse(tm.is_many())
1570
+
1571
+ def test_tri_map_register_pairs_equivalence_loop(self) -> None:
1572
+ # register_pairs == a register_one loop over the same pairs
1573
+ rng = np.random.RandomState(0)
1574
+ for _ in range(50):
1575
+ src_len = int(rng.randint(1, 12))
1576
+ dst_len = int(rng.randint(1, 10))
1577
+ n = int(rng.randint(1, 20))
1578
+ src_pos = rng.randint(-1, src_len, size=n).astype(np.int64)
1579
+ dst_pos = rng.randint(-1, dst_len, size=n).astype(np.int64)
1580
+ tb = TriMap(src_len, dst_len)
1581
+ tb.register_pairs(src_pos, dst_pos)
1582
+ tb.finalize()
1583
+ tl = TriMap(src_len, dst_len)
1584
+ for i in range(n):
1585
+ tl.register_one(int(src_pos[i]), int(dst_pos[i]))
1586
+ tl.finalize()
1587
+ src = np.arange(100, 100 + src_len)
1588
+ dst = np.arange(200, 200 + dst_len)
1589
+ self.assertEqual(tb.is_many(), tl.is_many())
1590
+ self.assertEqual(
1591
+ tb.map_src_fill(src, -1, np.dtype(np.int64)).tolist(),
1592
+ tl.map_src_fill(src, -1, np.dtype(np.int64)).tolist(),
1593
+ )
1594
+ self.assertEqual(
1595
+ tb.map_dst_fill(dst, -1, np.dtype(np.int64)).tolist(),
1596
+ tl.map_dst_fill(dst, -1, np.dtype(np.int64)).tolist(),
1597
+ )
1598
+
1599
+ def test_tri_map_register_pairs_equivalence_many_from_one(self) -> None:
1600
+ # with src = arange(src_len), register_pairs matches register_many_from_one
1601
+ rng = np.random.RandomState(1)
1602
+ for _ in range(20):
1603
+ src_len = int(rng.randint(1, 12))
1604
+ dst_len = int(rng.randint(1, 10))
1605
+ dst_pos = rng.randint(-1, dst_len, size=src_len).astype(np.int64)
1606
+ src_pos = np.arange(src_len, dtype=np.int64)
1607
+ tp = TriMap(src_len, dst_len)
1608
+ tp.register_pairs(src_pos, dst_pos)
1609
+ tp.finalize()
1610
+ tm = TriMap(src_len, dst_len)
1611
+ tm.register_many_from_one(dst_pos)
1612
+ tm.finalize()
1613
+ src = np.arange(100, 100 + src_len)
1614
+ dst = np.arange(200, 200 + dst_len)
1615
+ self.assertEqual(tp.is_many(), tm.is_many())
1616
+ self.assertEqual(
1617
+ tp.map_src_fill(src, -1, np.dtype(np.int64)).tolist(),
1618
+ tm.map_src_fill(src, -1, np.dtype(np.int64)).tolist(),
1619
+ )
1620
+ self.assertEqual(
1621
+ tp.map_dst_fill(dst, -1, np.dtype(np.int64)).tolist(),
1622
+ tm.map_dst_fill(dst, -1, np.dtype(np.int64)).tolist(),
1623
+ )
1624
+
1625
+ def test_tri_map_register_pairs_empty(self) -> None:
1626
+ tm = TriMap(3, 3)
1627
+ tm.register_pairs(
1628
+ np.array([], dtype=np.int64), np.array([], dtype=np.int64)
1629
+ )
1630
+ tm.finalize()
1631
+ self.assertEqual(tm.map_src_no_fill(np.arange(3)).tolist(), [])
1632
+
1633
+ def test_tri_map_register_pairs_errors(self) -> None:
1634
+ with self.assertRaises(ValueError): # length mismatch
1635
+ TriMap(3, 3).register_pairs(
1636
+ np.array([0, 1], dtype=np.int64), np.array([0], dtype=np.int64)
1637
+ )
1638
+ with self.assertRaises(ValueError): # wrong dtype
1639
+ TriMap(3, 3).register_pairs(
1640
+ np.array([0, 1], dtype=np.int32), np.array([0, 1], dtype=np.int64)
1641
+ )
1642
+ with self.assertRaises(ValueError): # 2d
1643
+ TriMap(3, 3).register_pairs(
1644
+ np.array([[0, 1]], dtype=np.int64), np.array([[0, 1]], dtype=np.int64)
1645
+ )
1646
+ with self.assertRaises(ValueError): # out of bounds src
1647
+ TriMap(3, 3).register_pairs(
1648
+ np.array([9], dtype=np.int64), np.array([0], dtype=np.int64)
1649
+ )
1650
+ with self.assertRaises(ValueError): # out of bounds dst
1651
+ TriMap(3, 3).register_pairs(
1652
+ np.array([0], dtype=np.int64), np.array([9], dtype=np.int64)
1653
+ )
1654
+ with self.assertRaises(TypeError): # not an array
1655
+ TriMap(3, 3).register_pairs([0, 1], np.array([0, 1], dtype=np.int64))
1656
+ tm = TriMap(2, 2)
1657
+ tm.register_pairs(
1658
+ np.array([0, 1], dtype=np.int64), np.array([0, 1], dtype=np.int64)
1659
+ )
1660
+ tm.finalize()
1661
+ with self.assertRaises(RuntimeError): # post-finalize
1662
+ tm.register_pairs(
1663
+ np.array([0], dtype=np.int64), np.array([0], dtype=np.int64)
1664
+ )
arraykit-1.9.0/VERSION DELETED
@@ -1,2 +0,0 @@
1
- 1.9.0
2
-
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
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes