arraykit 0.6.3__tar.gz → 0.7.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 (27) hide show
  1. {arraykit-0.6.3/arraykit.egg-info → arraykit-0.7.0}/PKG-INFO +1 -1
  2. {arraykit-0.6.3 → arraykit-0.7.0}/README.rst +8 -0
  3. {arraykit-0.6.3 → arraykit-0.7.0/arraykit.egg-info}/PKG-INFO +1 -1
  4. {arraykit-0.6.3 → arraykit-0.7.0}/setup.py +1 -1
  5. {arraykit-0.6.3 → arraykit-0.7.0}/src/__init__.py +2 -0
  6. {arraykit-0.6.3 → arraykit-0.7.0}/src/__init__.pyi +2 -0
  7. {arraykit-0.6.3 → arraykit-0.7.0}/src/_arraykit.c +175 -1
  8. {arraykit-0.6.3 → arraykit-0.7.0}/test/test_util.py +89 -0
  9. {arraykit-0.6.3 → arraykit-0.7.0}/LICENSE.txt +0 -0
  10. {arraykit-0.6.3 → arraykit-0.7.0}/MANIFEST.in +0 -0
  11. {arraykit-0.6.3 → arraykit-0.7.0}/arraykit.egg-info/SOURCES.txt +0 -0
  12. {arraykit-0.6.3 → arraykit-0.7.0}/arraykit.egg-info/dependency_links.txt +0 -0
  13. {arraykit-0.6.3 → arraykit-0.7.0}/arraykit.egg-info/requires.txt +0 -0
  14. {arraykit-0.6.3 → arraykit-0.7.0}/arraykit.egg-info/top_level.txt +0 -0
  15. {arraykit-0.6.3 → arraykit-0.7.0}/setup.cfg +0 -0
  16. {arraykit-0.6.3 → arraykit-0.7.0}/src/py.typed +0 -0
  17. {arraykit-0.6.3 → arraykit-0.7.0}/test/test_array_go.py +0 -0
  18. {arraykit-0.6.3 → arraykit-0.7.0}/test/test_block_index.py +0 -0
  19. {arraykit-0.6.3 → arraykit-0.7.0}/test/test_delimited_to_arrays.py +0 -0
  20. {arraykit-0.6.3 → arraykit-0.7.0}/test/test_delimited_to_arrays_integration.py +0 -0
  21. {arraykit-0.6.3 → arraykit-0.7.0}/test/test_delimited_to_arrays_property.py +0 -0
  22. {arraykit-0.6.3 → arraykit-0.7.0}/test/test_nonzero_1d.py +0 -0
  23. {arraykit-0.6.3 → arraykit-0.7.0}/test/test_nonzero_1d_property.py +0 -0
  24. {arraykit-0.6.3 → arraykit-0.7.0}/test/test_pyi.py +0 -0
  25. {arraykit-0.6.3 → arraykit-0.7.0}/test/test_split_after_count.py +0 -0
  26. {arraykit-0.6.3 → arraykit-0.7.0}/test/test_tri_map.py +0 -0
  27. {arraykit-0.6.3 → arraykit-0.7.0}/test/test_type_discovery.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: arraykit
3
- Version: 0.6.3
3
+ Version: 0.7.0
4
4
  Summary: Array utilities for StaticFrame
5
5
  Home-page: https://github.com/static-frame/arraykit
6
6
  Author: Christopher Ariza, Brandt Bucher, Charles Burkland
@@ -37,6 +37,14 @@ ArrayKit requires the following:
37
37
  What is New in ArrayKit
38
38
  -------------------------
39
39
 
40
+ 0.7.0
41
+ ............
42
+
43
+ Added ``array2d_to_array1d()``.
44
+
45
+ Added ``array2d_tuple_iter()``.
46
+
47
+
40
48
  0.6.3
41
49
  ............
42
50
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: arraykit
3
- Version: 0.6.3
3
+ Version: 0.7.0
4
4
  Summary: Array utilities for StaticFrame
5
5
  Home-page: https://github.com/static-frame/arraykit
6
6
  Author: Christopher Ariza, Brandt Bucher, Charles Burkland
@@ -5,7 +5,7 @@ from setuptools import Extension # type: ignore
5
5
  from setuptools import setup
6
6
  from pathlib import Path
7
7
 
8
- AK_VERSION = '0.6.3'
8
+ AK_VERSION = '0.7.0'
9
9
 
10
10
  def get_long_description() -> str:
11
11
  return '''The ArrayKit library provides utilities for creating and transforming NumPy arrays, implementing performance-critical StaticFrame operations as Python C extensions.
@@ -28,4 +28,6 @@ from ._arraykit import count_iteration as count_iteration
28
28
  from ._arraykit import first_true_1d as first_true_1d
29
29
  from ._arraykit import first_true_2d as first_true_2d
30
30
  from ._arraykit import slice_to_ascending_slice as slice_to_ascending_slice
31
+ from ._arraykit import array2d_to_array1d as array2d_to_array1d
32
+ from ._arraykit import array2d_tuple_iter as array2d_tuple_iter
31
33
  from ._arraykit import nonzero_1d as nonzero_1d
@@ -161,3 +161,5 @@ def first_true_1d(__array: np.ndarray, *, forward: bool) -> int: ...
161
161
  def first_true_2d(__array: np.ndarray, *, forward: bool, axis: int) -> np.ndarray: ...
162
162
  def nonzero_1d(__array: np.ndarray, /) -> np.ndarray: ...
163
163
  def slice_to_ascending_slice(__slice: slice, __size: int) -> slice: ...
164
+ def array2d_to_array1d(__array: np.ndarray) -> np.ndarray: ...
165
+ def array2d_tuple_iter(__array: np.ndarray) -> tp.Iterator[tp.Tuple[tp.Any, ...]]: ...
@@ -37,6 +37,18 @@ const static size_t UCS4_SIZE = sizeof(Py_UCS4);
37
37
  } \
38
38
  } while (0)
39
39
 
40
+ // Given a PyObject, raise if not an array or is not two dimensional.
41
+ # define AK_CHECK_NUMPY_ARRAY_2D(O) \
42
+ do { \
43
+ AK_CHECK_NUMPY_ARRAY(O) \
44
+ int ndim = PyArray_NDIM((PyArrayObject *)O); \
45
+ if (ndim != 2) { \
46
+ return PyErr_Format(PyExc_NotImplementedError,\
47
+ "Expected a 2D array, not %i.", \
48
+ ndim); \
49
+ } \
50
+ } while (0)
51
+
40
52
  // Placeholder of not implemented pathways / debugging.
41
53
  # define AK_NOT_IMPLEMENTED(msg) \
42
54
  do { \
@@ -3511,6 +3523,167 @@ array_deepcopy(PyObject *m, PyObject *args, PyObject *kwargs)
3511
3523
  return AK_ArrayDeepCopy(m, (PyArrayObject*)array, memo);
3512
3524
  }
3513
3525
 
3526
+ //------------------------------------------------------------------------------
3527
+ // Given a 2D array, return a 1D object array of tuples.
3528
+ static PyObject *
3529
+ array2d_to_array1d(PyObject *Py_UNUSED(m), PyObject *a)
3530
+ {
3531
+ AK_CHECK_NUMPY_ARRAY_2D(a);
3532
+ PyArrayObject *input_array = (PyArrayObject *)a;
3533
+
3534
+ npy_intp num_rows = PyArray_DIM(input_array, 0);
3535
+ npy_intp num_cols = PyArray_DIM(input_array, 1);
3536
+
3537
+ npy_intp dims[] = {num_rows};
3538
+ // NOTE: this initializes values to NULL, not None
3539
+ PyObject* output = PyArray_SimpleNew(1, dims, NPY_OBJECT);
3540
+ if (output == NULL) {
3541
+ return NULL;
3542
+ }
3543
+
3544
+ PyObject** output_data = (PyObject**)PyArray_DATA((PyArrayObject*)output);
3545
+ PyObject** p = output_data;
3546
+ PyObject** p_end = p + num_rows;
3547
+ npy_intp i = 0;
3548
+ npy_intp j;
3549
+ PyObject* tuple;
3550
+ PyObject* item;
3551
+
3552
+ while (p < p_end) {
3553
+ tuple = PyTuple_New(num_cols);
3554
+ if (tuple == NULL) {
3555
+ goto error;
3556
+ }
3557
+ for (j = 0; j < num_cols; ++j) {
3558
+ // cannot assume input_array is contiguous
3559
+ item = PyArray_ToScalar(PyArray_GETPTR2(input_array, i, j), input_array);
3560
+ if (item == NULL) {
3561
+ Py_DECREF(tuple);
3562
+ goto error;
3563
+ }
3564
+ PyTuple_SET_ITEM(tuple, j, item); // steals reference to item
3565
+ }
3566
+ *p++ = tuple; // assign with new ref, no incr needed
3567
+ i++;
3568
+ }
3569
+ PyArray_CLEARFLAGS((PyArrayObject *)output, NPY_ARRAY_WRITEABLE);
3570
+ return output;
3571
+ error:
3572
+ p = output_data;
3573
+ p_end = p + num_rows;
3574
+ while (p < p_end) { // decref all tuples within array
3575
+ Py_XDECREF(*p++); // xdec as might be NULL
3576
+ }
3577
+ Py_DECREF(output);
3578
+ return NULL;
3579
+ }
3580
+
3581
+ //------------------------------------------------------------------------------
3582
+ // Array2DTuple Iterator
3583
+
3584
+ static PyTypeObject A2DTupleType;
3585
+
3586
+ typedef struct A2DTupleObject {
3587
+ PyObject_HEAD
3588
+ PyArrayObject* array;
3589
+ npy_intp num_rows;
3590
+ npy_intp num_cols;
3591
+ Py_ssize_t pos; // current index state, mutated in-place
3592
+ } A2DTupleObject;
3593
+
3594
+ static PyObject *
3595
+ A2DTuple_new(PyArrayObject* array,
3596
+ npy_intp num_rows,
3597
+ npy_intp num_cols) {
3598
+ A2DTupleObject* a2dt = PyObject_New(A2DTupleObject, &A2DTupleType);
3599
+ if (!a2dt) {
3600
+ return NULL;
3601
+ }
3602
+ Py_INCREF((PyObject*)array);
3603
+ a2dt->array = array;
3604
+ a2dt->num_rows = num_rows;
3605
+ a2dt->num_cols = num_cols;
3606
+ a2dt->pos = 0;
3607
+ return (PyObject *)a2dt;
3608
+ }
3609
+
3610
+ static void
3611
+ A2DTuple_dealloc(A2DTupleObject *self) {
3612
+ Py_DECREF((PyObject*)self->array);
3613
+ PyObject_Del((PyObject*)self);
3614
+ }
3615
+
3616
+ static PyObject*
3617
+ A2DTuple_iter(A2DTupleObject *self) {
3618
+ Py_INCREF(self);
3619
+ return (PyObject*)self;
3620
+ }
3621
+
3622
+ static PyObject *
3623
+ A2DTuple_iternext(A2DTupleObject *self) {
3624
+ Py_ssize_t i = self->pos;
3625
+ if (i < self->num_rows) {
3626
+ npy_intp num_cols = self->num_cols;
3627
+ PyArrayObject* array = self->array;
3628
+ PyObject* tuple = PyTuple_New(num_cols);
3629
+ PyObject* item;
3630
+ if (tuple == NULL) {
3631
+ return NULL;
3632
+ }
3633
+ for (npy_intp j = 0; j < num_cols; ++j) {
3634
+ // cannot assume array is contiguous
3635
+ item = PyArray_ToScalar(PyArray_GETPTR2(array, i, j), array);
3636
+ if (item == NULL) {
3637
+ Py_DECREF(tuple);
3638
+ return NULL;
3639
+ }
3640
+ PyTuple_SET_ITEM(tuple, j, item); // steals reference to item
3641
+ }
3642
+ self->pos++;
3643
+ return tuple;
3644
+ }
3645
+ return NULL;
3646
+ }
3647
+
3648
+ // static PyObject *
3649
+ // A2DTuple_reversed(A2DTupleObject *self) {
3650
+ // return A2DTuple_new(self->bi, !self->reversed);
3651
+ // }
3652
+
3653
+ static PyObject *
3654
+ A2DTuple_length_hint(A2DTupleObject *self) {
3655
+ Py_ssize_t len = Py_MAX(0, self->num_rows - self->pos);
3656
+ return PyLong_FromSsize_t(len);
3657
+ }
3658
+
3659
+ static PyMethodDef A2DTuple_methods[] = {
3660
+ {"__length_hint__", (PyCFunction)A2DTuple_length_hint, METH_NOARGS, NULL},
3661
+ // {"__reversed__", (PyCFunction)A2DTuple_reversed, METH_NOARGS, NULL},
3662
+ {NULL},
3663
+ };
3664
+
3665
+ static PyTypeObject A2DTupleType = {
3666
+ PyVarObject_HEAD_INIT(NULL, 0)
3667
+ .tp_basicsize = sizeof(A2DTupleObject),
3668
+ .tp_dealloc = (destructor) A2DTuple_dealloc,
3669
+ .tp_iter = (getiterfunc) A2DTuple_iter,
3670
+ .tp_iternext = (iternextfunc) A2DTuple_iternext,
3671
+ .tp_methods = A2DTuple_methods,
3672
+ .tp_name = "arraykit.A2DTupleIterator",
3673
+ };
3674
+
3675
+ // Given a 2D array, return an iterator of row tuples.
3676
+ static PyObject *
3677
+ array2d_tuple_iter(PyObject *Py_UNUSED(m), PyObject *a)
3678
+ {
3679
+ AK_CHECK_NUMPY_ARRAY_2D(a);
3680
+ PyArrayObject* array = (PyArrayObject *)a;
3681
+ npy_intp num_rows = PyArray_DIM(array, 0);
3682
+ npy_intp num_cols = PyArray_DIM(array, 1);
3683
+ return A2DTuple_new(array, num_rows, num_cols);
3684
+ }
3685
+
3686
+
3514
3687
  //------------------------------------------------------------------------------
3515
3688
  // type resolution
3516
3689
 
@@ -5944,7 +6117,6 @@ TriMap_dealloc(TriMapObject *self) {
5944
6117
  if (self->many_from != NULL) {
5945
6118
  // decref all arrays before freeing
5946
6119
  for (Py_ssize_t i = 0; i < self->many_count; i++) {
5947
- // NOTE: using dot to get to pointer?
5948
6120
  Py_DECREF((PyObject*)self->many_from[i].dst);
5949
6121
  }
5950
6122
  PyMem_Free(self->many_from);
@@ -7259,6 +7431,8 @@ static PyMethodDef arraykit_methods[] = {
7259
7431
  (PyCFunction)array_deepcopy,
7260
7432
  METH_VARARGS | METH_KEYWORDS,
7261
7433
  NULL},
7434
+ {"array2d_to_array1d", array2d_to_array1d, METH_O, NULL},
7435
+ {"array2d_tuple_iter", array2d_tuple_iter, METH_O, NULL},
7262
7436
  {"resolve_dtype", resolve_dtype, METH_VARARGS, NULL},
7263
7437
  {"resolve_dtype_iter", resolve_dtype_iter, METH_O, NULL},
7264
7438
  {"first_true_1d",
@@ -22,6 +22,8 @@ from arraykit import count_iteration
22
22
  from arraykit import first_true_1d
23
23
  from arraykit import first_true_2d
24
24
  from arraykit import slice_to_ascending_slice
25
+ from arraykit import array2d_to_array1d
26
+ from arraykit import array2d_tuple_iter
25
27
 
26
28
  from performance.reference.util import get_new_indexers_and_screen_ak as get_new_indexers_and_screen_full
27
29
  from arraykit import get_new_indexers_and_screen
@@ -283,6 +285,93 @@ class TestUnit(unittest.TestCase):
283
285
  with self.assertRaises(TypeError):
284
286
  a2 = array_deepcopy(a1, ())
285
287
 
288
+ #---------------------------------------------------------------------------
289
+ def test_array2d_to_array1d_dummy(self) -> None:
290
+ a1 = np.arange(10)
291
+ with self.assertRaises(NotImplementedError):
292
+ # 1 dimensional
293
+ _ = array2d_to_array1d(a1)
294
+
295
+ def test_array2d_to_array1d_b(self) -> None:
296
+ a1 = np.arange(10, dtype=np.int64).reshape(5, 2)
297
+ result = array2d_to_array1d(a1)
298
+ assert isinstance(result[0], tuple)
299
+ assert result[0] == (0, 1)
300
+ self.assertIs(type(result[0][0]), np.int64)
301
+ self.assertFalse(result.flags.writeable)
302
+ self.assertEqual(tuple(result), ((0, 1), (2, 3), (4, 5), (6, 7), (8, 9)))
303
+
304
+
305
+ def test_array2d_to_array1d_c(self) -> None:
306
+ a1 = np.array([["a", "b"], ["ccc", "ddd"], ["ee", "ff"]])
307
+ a2 = array2d_to_array1d(a1)
308
+ self.assertEqual(a2.tolist(), [('a', 'b'), ('ccc', 'ddd'), ('ee', 'ff')])
309
+
310
+ def test_array2d_to_array1d_d(self) -> None:
311
+ a1 = np.array([[3, 5], [10, 20], [7, 2]], dtype=np.uint8)
312
+ a2 = array2d_to_array1d(a1)
313
+ self.assertEqual(a2.tolist(), [(3, 5), (10, 20), (7, 2)])
314
+ self.assertIs(type(a2[0][0]), np.uint8)
315
+
316
+ def test_array2d_to_array1d_e(self) -> None:
317
+ a1 = np.arange(20, dtype=np.int64).reshape(4, 5)
318
+ result = array2d_to_array1d(a1)
319
+ self.assertEqual(result.tolist(), [(0, 1, 2, 3, 4), (5, 6, 7, 8, 9), (10, 11, 12, 13, 14), (15, 16, 17, 18, 19)])
320
+
321
+ #---------------------------------------------------------------------------
322
+ def test_array2d_tuple_iter_a(self) -> None:
323
+ a1 = np.arange(20, dtype=np.int64).reshape(4, 5)
324
+ result = list(array2d_tuple_iter(a1))
325
+ self.assertEqual(len(result), 4)
326
+ self.assertEqual(result, [(0, 1, 2, 3, 4), (5, 6, 7, 8, 9), (10, 11, 12, 13, 14), (15, 16, 17, 18, 19)])
327
+
328
+ def test_array2d_tuple_iter_b(self) -> None:
329
+ a1 = np.arange(20, dtype=np.int64).reshape(10, 2)
330
+ result = list(array2d_tuple_iter(a1))
331
+ self.assertEqual(result, [(0, 1), (2, 3), (4, 5), (6, 7), (8, 9), (10, 11), (12, 13), (14, 15), (16, 17), (18, 19)])
332
+
333
+ def test_array2d_tuple_iter_c(self) -> None:
334
+ a1 = np.array([['aaa', 'bb'], ['c', 'dd'], ['ee', 'fffff']])
335
+ it = array2d_tuple_iter(a1)
336
+ self.assertEqual(it.__length_hint__(), 3)
337
+ self.assertEqual(next(it), ('aaa', 'bb'))
338
+ self.assertEqual(it.__length_hint__(), 2)
339
+ self.assertEqual(next(it), ('c', 'dd'))
340
+ self.assertEqual(it.__length_hint__(), 1)
341
+ self.assertEqual(next(it), ('ee', 'fffff'))
342
+ self.assertEqual(it.__length_hint__(), 0)
343
+ with self.assertRaises(StopIteration):
344
+ next(it)
345
+
346
+ def test_array2d_tuple_iter_d(self) -> None:
347
+ a1 = np.array([['aaa', 'bb'], ['c', 'dd'], ['ee', 'fffff']])
348
+ it = array2d_tuple_iter(a1)
349
+ # __reversed__ not implemented
350
+ with self.assertRaises(TypeError):
351
+ reversed(it)
352
+
353
+ def test_array2d_tuple_iter_e(self) -> None:
354
+ a1 = np.array([[None, 'bb'], [None, 'dd'], [3, None]])
355
+ it = array2d_tuple_iter(a1)
356
+ del a1
357
+ self.assertEqual(list(it), [(None, 'bb'), (None, 'dd'), (3, None)])
358
+
359
+ def test_array2d_tuple_iter_f(self) -> None:
360
+ a1 = np.array([[None, 'bb'], [None, 'dd'], [3, None]])
361
+ it1 = array2d_tuple_iter(a1)
362
+ del a1
363
+ it2 = iter(it1)
364
+ self.assertEqual(list(it1), [(None, 'bb'), (None, 'dd'), (3, None)])
365
+ self.assertEqual(list(it2), []) # expected behavior
366
+
367
+ def test_array2d_tuple_iter_g(self) -> None:
368
+ a1 = np.array([[None, 'bb'], [None, 'dd'], [3, None]])
369
+ it1 = array2d_tuple_iter(a1)
370
+ it2 = array2d_tuple_iter(a1)
371
+ del a1
372
+ self.assertEqual(list(it1), [(None, 'bb'), (None, 'dd'), (3, None)])
373
+ self.assertEqual(list(it2), [(None, 'bb'), (None, 'dd'), (3, None)])
374
+
286
375
  #---------------------------------------------------------------------------
287
376
 
288
377
  def test_isna_element_a(self) -> None:
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes