arraykit 1.8.0__tar.gz → 1.9.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.
- {arraykit-1.8.0/arraykit.egg-info → arraykit-1.9.0}/PKG-INFO +7 -1
- {arraykit-1.8.0 → arraykit-1.9.0}/README.rst +6 -0
- arraykit-1.9.0/VERSION +2 -0
- {arraykit-1.8.0 → arraykit-1.9.0/arraykit.egg-info}/PKG-INFO +7 -1
- {arraykit-1.8.0 → arraykit-1.9.0}/src/__init__.pyi +2 -0
- {arraykit-1.8.0 → arraykit-1.9.0}/src/auto_map.c +204 -0
- {arraykit-1.8.0 → arraykit-1.9.0}/src/tri_map.c +42 -0
- {arraykit-1.8.0 → arraykit-1.9.0}/test/test_auto_map.py +103 -0
- {arraykit-1.8.0 → arraykit-1.9.0}/test/test_tri_map.py +69 -0
- arraykit-1.8.0/VERSION +0 -2
- {arraykit-1.8.0 → arraykit-1.9.0}/LICENSE.txt +0 -0
- {arraykit-1.8.0 → arraykit-1.9.0}/MANIFEST.in +0 -0
- {arraykit-1.8.0 → arraykit-1.9.0}/arraykit.egg-info/SOURCES.txt +0 -0
- {arraykit-1.8.0 → arraykit-1.9.0}/arraykit.egg-info/dependency_links.txt +0 -0
- {arraykit-1.8.0 → arraykit-1.9.0}/arraykit.egg-info/requires.txt +0 -0
- {arraykit-1.8.0 → arraykit-1.9.0}/arraykit.egg-info/top_level.txt +0 -0
- {arraykit-1.8.0 → arraykit-1.9.0}/pyproject.toml +0 -0
- {arraykit-1.8.0 → arraykit-1.9.0}/setup.cfg +0 -0
- {arraykit-1.8.0 → arraykit-1.9.0}/setup.py +0 -0
- {arraykit-1.8.0 → arraykit-1.9.0}/src/__init__.py +0 -0
- {arraykit-1.8.0 → arraykit-1.9.0}/src/_arraykit.c +0 -0
- {arraykit-1.8.0 → arraykit-1.9.0}/src/array_go.c +0 -0
- {arraykit-1.8.0 → arraykit-1.9.0}/src/array_go.h +0 -0
- {arraykit-1.8.0 → arraykit-1.9.0}/src/array_to_tuple.c +0 -0
- {arraykit-1.8.0 → arraykit-1.9.0}/src/array_to_tuple.h +0 -0
- {arraykit-1.8.0 → arraykit-1.9.0}/src/auto_map.h +0 -0
- {arraykit-1.8.0 → arraykit-1.9.0}/src/block_index.c +0 -0
- {arraykit-1.8.0 → arraykit-1.9.0}/src/block_index.h +0 -0
- {arraykit-1.8.0 → arraykit-1.9.0}/src/delimited_to_arrays.c +0 -0
- {arraykit-1.8.0 → arraykit-1.9.0}/src/delimited_to_arrays.h +0 -0
- {arraykit-1.8.0 → arraykit-1.9.0}/src/methods.c +0 -0
- {arraykit-1.8.0 → arraykit-1.9.0}/src/methods.h +0 -0
- {arraykit-1.8.0 → arraykit-1.9.0}/src/py.typed +0 -0
- {arraykit-1.8.0 → arraykit-1.9.0}/src/tri_map.h +0 -0
- {arraykit-1.8.0 → arraykit-1.9.0}/src/utilities.h +0 -0
- {arraykit-1.8.0 → arraykit-1.9.0}/test/test_array_go.py +0 -0
- {arraykit-1.8.0 → arraykit-1.9.0}/test/test_astype_array.py +0 -0
- {arraykit-1.8.0 → arraykit-1.9.0}/test/test_auto_map_property.py +0 -0
- {arraykit-1.8.0 → arraykit-1.9.0}/test/test_block_index.py +0 -0
- {arraykit-1.8.0 → arraykit-1.9.0}/test/test_delimited_to_arrays.py +0 -0
- {arraykit-1.8.0 → arraykit-1.9.0}/test/test_delimited_to_arrays_integration.py +0 -0
- {arraykit-1.8.0 → arraykit-1.9.0}/test/test_delimited_to_arrays_property.py +0 -0
- {arraykit-1.8.0 → arraykit-1.9.0}/test/test_factorize.py +0 -0
- {arraykit-1.8.0 → arraykit-1.9.0}/test/test_fill_directional.py +0 -0
- {arraykit-1.8.0 → arraykit-1.9.0}/test/test_group_ordering.py +0 -0
- {arraykit-1.8.0 → arraykit-1.9.0}/test/test_nonzero_1d.py +0 -0
- {arraykit-1.8.0 → arraykit-1.9.0}/test/test_nonzero_1d_property.py +0 -0
- {arraykit-1.8.0 → arraykit-1.9.0}/test/test_objectable.py +0 -0
- {arraykit-1.8.0 → arraykit-1.9.0}/test/test_pyi.py +0 -0
- {arraykit-1.8.0 → arraykit-1.9.0}/test/test_split_after_count.py +0 -0
- {arraykit-1.8.0 → arraykit-1.9.0}/test/test_transition_slices_from_group.py +0 -0
- {arraykit-1.8.0 → arraykit-1.9.0}/test/test_type_discovery.py +0 -0
- {arraykit-1.8.0 → arraykit-1.9.0}/test/test_util.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: arraykit
|
|
3
|
-
Version: 1.
|
|
3
|
+
Version: 1.9.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.9.0
|
|
68
|
+
............
|
|
69
|
+
|
|
70
|
+
Added ``FrozenAutoMap.get_all_fill()`` and ``TriMap.register_many_from_one()``.
|
|
71
|
+
|
|
72
|
+
|
|
67
73
|
1.8.0
|
|
68
74
|
............
|
|
69
75
|
|
arraykit-1.9.0/VERSION
ADDED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: arraykit
|
|
3
|
-
Version: 1.
|
|
3
|
+
Version: 1.9.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.9.0
|
|
68
|
+
............
|
|
69
|
+
|
|
70
|
+
Added ``FrozenAutoMap.get_all_fill()`` and ``TriMap.register_many_from_one()``.
|
|
71
|
+
|
|
72
|
+
|
|
67
73
|
1.8.0
|
|
68
74
|
............
|
|
69
75
|
|
|
@@ -53,6 +53,7 @@ class TriMap:
|
|
|
53
53
|
def __init__(self, /, src_len: int, dst_len: int) -> None: ...
|
|
54
54
|
def __repr__(self) -> str: ...
|
|
55
55
|
def register_one(self, /, src_from: int, dst_from: int) -> None: ...
|
|
56
|
+
def register_many_from_one(self, __dst_pos: np.ndarray) -> None: ...
|
|
56
57
|
def register_unmatched_dst(self) -> None: ...
|
|
57
58
|
def register_many(self, /, src_from: int, dst_from: np.ndarray) -> None: ...
|
|
58
59
|
def finalize(self) -> None: ...
|
|
@@ -142,6 +143,7 @@ class FrozenAutoMap:
|
|
|
142
143
|
def items(self) -> tp.Iterator[tuple[_TLabel, int]]: ...
|
|
143
144
|
def values(self) -> tp.Iterator[int]: ...
|
|
144
145
|
def get_all(self, __key: list[_TLabel] | np.ndarray) -> np.ndarray: ...
|
|
146
|
+
def get_all_fill(self, __key: list[_TLabel] | np.ndarray) -> np.ndarray: ...
|
|
145
147
|
def get_any(self, __key: list[_TLabel] | np.ndarray) -> list[int]: ...
|
|
146
148
|
def __iter__(self) -> tp.Iterator[_TLabel]: ...
|
|
147
149
|
def __getitem__(self, __key: tp.Any) -> int: ...
|
|
@@ -1919,6 +1919,209 @@ fam_get_all(FAMObject *self, PyObject *key) {
|
|
|
1919
1919
|
# undef GET_ALL_FLEXIBLE
|
|
1920
1920
|
|
|
1921
1921
|
|
|
1922
|
+
// Fill variants of the GET_ALL_* macros: on a miss, write -1 into the output slot and
|
|
1923
|
+
// continue (instead of raising KeyError). Depend on self, key_size, key_array, i, b, array.
|
|
1924
|
+
# define GET_ALL_FILL_SCALARS(npy_type_src, npy_type_dst, kat, lookup_func, hash_func, post_deref) \
|
|
1925
|
+
{ \
|
|
1926
|
+
npy_type_dst v; \
|
|
1927
|
+
Py_ssize_t table_pos; \
|
|
1928
|
+
for (; i < key_size; i++) { \
|
|
1929
|
+
v = post_deref(*(npy_type_src*)PyArray_GETPTR1(key_array, i)); \
|
|
1930
|
+
table_pos = lookup_func(self, v, hash_func(v), kat); \
|
|
1931
|
+
if (table_pos < 0 || (self->table[table_pos].hash == -1)) { \
|
|
1932
|
+
if (PyErr_Occurred()) { \
|
|
1933
|
+
Py_DECREF(array); \
|
|
1934
|
+
return NULL; \
|
|
1935
|
+
} \
|
|
1936
|
+
b[i] = -1; \
|
|
1937
|
+
continue; \
|
|
1938
|
+
} \
|
|
1939
|
+
b[i] = (npy_int64)self->table[table_pos].keys_pos; \
|
|
1940
|
+
} \
|
|
1941
|
+
} \
|
|
1942
|
+
|
|
1943
|
+
# define GET_ALL_FILL_DT64(npy_type_src, npy_type_dst, kat, lookup_func, hash_func) \
|
|
1944
|
+
{ \
|
|
1945
|
+
npy_type_dst v; \
|
|
1946
|
+
Py_ssize_t table_pos; \
|
|
1947
|
+
for (; i < key_size; i++) { \
|
|
1948
|
+
v = *(npy_type_src*)PyArray_GETPTR1(key_array, i); \
|
|
1949
|
+
table_pos = lookup_func(self, v, hash_func(v), kat); \
|
|
1950
|
+
if (table_pos < 0 || (self->table[table_pos].hash == -1)) { \
|
|
1951
|
+
if (PyErr_Occurred()) { \
|
|
1952
|
+
Py_DECREF(array); \
|
|
1953
|
+
return NULL; \
|
|
1954
|
+
} \
|
|
1955
|
+
b[i] = -1; \
|
|
1956
|
+
continue; \
|
|
1957
|
+
} \
|
|
1958
|
+
b[i] = (npy_int64)self->table[table_pos].keys_pos; \
|
|
1959
|
+
} \
|
|
1960
|
+
} \
|
|
1961
|
+
|
|
1962
|
+
# define GET_ALL_FILL_FLEXIBLE(char_type, get_end_func, lookup_func, hash_func) \
|
|
1963
|
+
{ \
|
|
1964
|
+
char_type* v; \
|
|
1965
|
+
Py_ssize_t dt_size = PyArray_ITEMSIZE(key_array) / sizeof(char_type); \
|
|
1966
|
+
Py_ssize_t k_size; \
|
|
1967
|
+
Py_ssize_t table_pos; \
|
|
1968
|
+
for (; i < key_size; i++) { \
|
|
1969
|
+
v = (char_type*)PyArray_GETPTR1(key_array, i); \
|
|
1970
|
+
k_size = get_end_func(v, dt_size) - v; \
|
|
1971
|
+
table_pos = lookup_func(self, v, k_size, hash_func(v, k_size)); \
|
|
1972
|
+
if (table_pos < 0 || (self->table[table_pos].hash == -1)) { \
|
|
1973
|
+
if (PyErr_Occurred()) { \
|
|
1974
|
+
Py_DECREF(array); \
|
|
1975
|
+
return NULL; \
|
|
1976
|
+
} \
|
|
1977
|
+
b[i] = -1; \
|
|
1978
|
+
continue; \
|
|
1979
|
+
} \
|
|
1980
|
+
b[i] = (npy_int64)self->table[table_pos].keys_pos; \
|
|
1981
|
+
} \
|
|
1982
|
+
} \
|
|
1983
|
+
|
|
1984
|
+
// Given a list or array of keys, return an input-aligned int64 array of looked-up
|
|
1985
|
+
// positions, with -1 for any key not found (never raises KeyError). This is the
|
|
1986
|
+
// "vectorized get with default -1" needed by left/outer joins. Immutable output.
|
|
1987
|
+
static PyObject *
|
|
1988
|
+
fam_get_all_fill(FAMObject *self, PyObject *key) {
|
|
1989
|
+
Py_ssize_t key_size = 0;
|
|
1990
|
+
Py_ssize_t keys_pos = -1;
|
|
1991
|
+
PyObject* k = NULL;
|
|
1992
|
+
PyObject *array = NULL;
|
|
1993
|
+
Py_ssize_t i = 0;
|
|
1994
|
+
|
|
1995
|
+
int key_is_list;
|
|
1996
|
+
if (PyList_CheckExact(key)) {
|
|
1997
|
+
key_is_list = 1;
|
|
1998
|
+
key_size = PyList_GET_SIZE(key);
|
|
1999
|
+
}
|
|
2000
|
+
else if (PyArray_Check(key)) {
|
|
2001
|
+
key_is_list = 0;
|
|
2002
|
+
key_size = PyArray_SIZE((PyArrayObject *)key);
|
|
2003
|
+
}
|
|
2004
|
+
else {
|
|
2005
|
+
PyErr_SetString(PyExc_TypeError, "Must provide a list or array.");
|
|
2006
|
+
return NULL;
|
|
2007
|
+
}
|
|
2008
|
+
|
|
2009
|
+
npy_intp dims[] = {key_size};
|
|
2010
|
+
array = PyArray_EMPTY(1, dims, NPY_INT64, 0);
|
|
2011
|
+
if (array == NULL) {
|
|
2012
|
+
return NULL;
|
|
2013
|
+
}
|
|
2014
|
+
npy_int64* b = (npy_int64*)PyArray_DATA((PyArrayObject*)array);
|
|
2015
|
+
|
|
2016
|
+
if (key_is_list) {
|
|
2017
|
+
for (; i < key_size; i++) {
|
|
2018
|
+
k = PyList_GET_ITEM(key, i); // borrow
|
|
2019
|
+
keys_pos = lookup(self, k);
|
|
2020
|
+
if (keys_pos < 0) {
|
|
2021
|
+
if (PyErr_Occurred()) {
|
|
2022
|
+
Py_DECREF(array);
|
|
2023
|
+
return NULL;
|
|
2024
|
+
}
|
|
2025
|
+
b[i] = -1;
|
|
2026
|
+
continue;
|
|
2027
|
+
}
|
|
2028
|
+
b[i] = (npy_int64)keys_pos;
|
|
2029
|
+
}
|
|
2030
|
+
}
|
|
2031
|
+
else { // key is an array
|
|
2032
|
+
PyArrayObject* key_array = (PyArrayObject *)key;
|
|
2033
|
+
int key_array_t = PyArray_TYPE(key_array);
|
|
2034
|
+
int use_typed = kat_is_kind(self->keys_array_type, PyArray_DESCR(key_array)->kind);
|
|
2035
|
+
if (use_typed && key_array_t == NPY_DATETIME) {
|
|
2036
|
+
NPY_DATETIMEUNIT key_unit = AK_dt_unit_from_array(key_array);
|
|
2037
|
+
if (!kat_is_datetime_unit(self->keys_array_type, key_unit)) {
|
|
2038
|
+
// mismatched units: use the coercing scalar path (below) rather than
|
|
2039
|
+
// raising, so equal instants still resolve (and true misses give -1)
|
|
2040
|
+
use_typed = 0;
|
|
2041
|
+
}
|
|
2042
|
+
}
|
|
2043
|
+
if (use_typed) {
|
|
2044
|
+
switch (key_array_t) {
|
|
2045
|
+
case NPY_INT64:
|
|
2046
|
+
GET_ALL_FILL_SCALARS(npy_int64, npy_int64, KAT_INT64, lookup_hash_int, int_to_hash,);
|
|
2047
|
+
break;
|
|
2048
|
+
case NPY_INT32:
|
|
2049
|
+
GET_ALL_FILL_SCALARS(npy_int32, npy_int64, KAT_INT32, lookup_hash_int, int_to_hash,);
|
|
2050
|
+
break;
|
|
2051
|
+
case NPY_INT16:
|
|
2052
|
+
GET_ALL_FILL_SCALARS(npy_int16, npy_int64, KAT_INT16, lookup_hash_int, int_to_hash,);
|
|
2053
|
+
break;
|
|
2054
|
+
case NPY_INT8:
|
|
2055
|
+
GET_ALL_FILL_SCALARS(npy_int8, npy_int64, KAT_INT8, lookup_hash_int, int_to_hash,);
|
|
2056
|
+
break;
|
|
2057
|
+
case NPY_UINT64:
|
|
2058
|
+
GET_ALL_FILL_SCALARS(npy_uint64, npy_uint64, KAT_UINT64, lookup_hash_uint, uint_to_hash,);
|
|
2059
|
+
break;
|
|
2060
|
+
case NPY_UINT32:
|
|
2061
|
+
GET_ALL_FILL_SCALARS(npy_uint32, npy_uint64, KAT_UINT32, lookup_hash_uint, uint_to_hash,);
|
|
2062
|
+
break;
|
|
2063
|
+
case NPY_UINT16:
|
|
2064
|
+
GET_ALL_FILL_SCALARS(npy_uint16, npy_uint64, KAT_UINT16, lookup_hash_uint, uint_to_hash,);
|
|
2065
|
+
break;
|
|
2066
|
+
case NPY_UINT8:
|
|
2067
|
+
GET_ALL_FILL_SCALARS(npy_uint8, npy_uint64, KAT_UINT8, lookup_hash_uint, uint_to_hash,);
|
|
2068
|
+
break;
|
|
2069
|
+
case NPY_FLOAT64:
|
|
2070
|
+
GET_ALL_FILL_SCALARS(npy_double, npy_double, KAT_FLOAT64, lookup_hash_double, double_to_hash,);
|
|
2071
|
+
break;
|
|
2072
|
+
case NPY_FLOAT32:
|
|
2073
|
+
GET_ALL_FILL_SCALARS(npy_float, npy_double, KAT_FLOAT32, lookup_hash_double, double_to_hash,);
|
|
2074
|
+
break;
|
|
2075
|
+
case NPY_FLOAT16:
|
|
2076
|
+
GET_ALL_FILL_SCALARS(npy_half, npy_double, KAT_FLOAT16, lookup_hash_double, double_to_hash, npy_half_to_double);
|
|
2077
|
+
break;
|
|
2078
|
+
case NPY_UNICODE:
|
|
2079
|
+
GET_ALL_FILL_FLEXIBLE(Py_UCS4, ucs4_get_end_p, lookup_hash_unicode, unicode_to_hash);
|
|
2080
|
+
break;
|
|
2081
|
+
case NPY_STRING:
|
|
2082
|
+
GET_ALL_FILL_FLEXIBLE(char, char_get_end_p, lookup_hash_string, string_to_hash);
|
|
2083
|
+
break;
|
|
2084
|
+
case NPY_DATETIME:
|
|
2085
|
+
GET_ALL_FILL_DT64(npy_int64, npy_int64, KAT_INT64, lookup_hash_int, int_to_hash);
|
|
2086
|
+
break;
|
|
2087
|
+
default:
|
|
2088
|
+
use_typed = 0; // unhandled kind: fall to the scalar path below
|
|
2089
|
+
break;
|
|
2090
|
+
}
|
|
2091
|
+
}
|
|
2092
|
+
if (!use_typed) {
|
|
2093
|
+
for (; i < key_size; i++) {
|
|
2094
|
+
k = PyArray_ToScalar(PyArray_GETPTR1(key_array, i), key_array);
|
|
2095
|
+
if (k == NULL) {
|
|
2096
|
+
Py_DECREF(array);
|
|
2097
|
+
return NULL;
|
|
2098
|
+
}
|
|
2099
|
+
keys_pos = lookup(self, k);
|
|
2100
|
+
if (keys_pos < 0) {
|
|
2101
|
+
if (PyErr_Occurred()) {
|
|
2102
|
+
Py_DECREF(k);
|
|
2103
|
+
Py_DECREF(array);
|
|
2104
|
+
return NULL;
|
|
2105
|
+
}
|
|
2106
|
+
Py_DECREF(k);
|
|
2107
|
+
b[i] = -1;
|
|
2108
|
+
continue;
|
|
2109
|
+
}
|
|
2110
|
+
Py_DECREF(k);
|
|
2111
|
+
b[i] = (npy_int64)keys_pos;
|
|
2112
|
+
}
|
|
2113
|
+
}
|
|
2114
|
+
}
|
|
2115
|
+
|
|
2116
|
+
PyArray_CLEARFLAGS((PyArrayObject *)array, NPY_ARRAY_WRITEABLE);
|
|
2117
|
+
return array;
|
|
2118
|
+
}
|
|
2119
|
+
|
|
2120
|
+
# undef GET_ALL_FILL_SCALARS
|
|
2121
|
+
# undef GET_ALL_FILL_DT64
|
|
2122
|
+
# undef GET_ALL_FILL_FLEXIBLE
|
|
2123
|
+
|
|
2124
|
+
|
|
1922
2125
|
static inline int
|
|
1923
2126
|
append_ssize_t(
|
|
1924
2127
|
PyObject* list,
|
|
@@ -2974,6 +3177,7 @@ static PyMethodDef fam_methods[] = {
|
|
|
2974
3177
|
{"keys", (PyCFunction) fam_keys, METH_NOARGS, NULL},
|
|
2975
3178
|
{"values", (PyCFunction) fam_values, METH_NOARGS, NULL},
|
|
2976
3179
|
{"get_all", (PyCFunction) fam_get_all, METH_O, NULL},
|
|
3180
|
+
{"get_all_fill", (PyCFunction) fam_get_all_fill, METH_O, NULL},
|
|
2977
3181
|
{"get_any", (PyCFunction) fam_get_any, METH_O, NULL},
|
|
2978
3182
|
{NULL},
|
|
2979
3183
|
};
|
|
@@ -269,6 +269,47 @@ TriMap_register_one(TriMapObject *self, PyObject *args) {
|
|
|
269
269
|
Py_RETURN_NONE;
|
|
270
270
|
}
|
|
271
271
|
|
|
272
|
+
// Bulk one-to-one registration: given an int64 array `dst_pos` of length `src_len`,
|
|
273
|
+
// register src position i to dst position dst_pos[i] (or -1 for an unmatched src) in a
|
|
274
|
+
// single C loop -- equivalent to calling register_one(i, dst_pos[i]) for each i, but
|
|
275
|
+
// without per-element Python overhead. Used by the hash-join fast path.
|
|
276
|
+
PyObject *
|
|
277
|
+
TriMap_register_many_from_one(TriMapObject *self, PyObject *arg) {
|
|
278
|
+
if (self->finalized) {
|
|
279
|
+
PyErr_SetString(PyExc_RuntimeError, "Cannot register post finalization");
|
|
280
|
+
return NULL;
|
|
281
|
+
}
|
|
282
|
+
if (!PyArray_Check(arg)) {
|
|
283
|
+
PyErr_SetString(PyExc_TypeError, "Must provide an array");
|
|
284
|
+
return NULL;
|
|
285
|
+
}
|
|
286
|
+
PyArrayObject* a = (PyArrayObject*)arg;
|
|
287
|
+
if (PyArray_TYPE(a) != NPY_INT64) {
|
|
288
|
+
PyErr_SetString(PyExc_ValueError, "Array must be of type int64");
|
|
289
|
+
return NULL;
|
|
290
|
+
}
|
|
291
|
+
if (PyArray_NDIM(a) != 1) {
|
|
292
|
+
PyErr_SetString(PyExc_ValueError, "Array must be 1-dimensional");
|
|
293
|
+
return NULL;
|
|
294
|
+
}
|
|
295
|
+
if (!PyArray_IS_C_CONTIGUOUS(a)) {
|
|
296
|
+
PyErr_SetString(PyExc_ValueError, "Array must be contiguous");
|
|
297
|
+
return NULL;
|
|
298
|
+
}
|
|
299
|
+
npy_intp n = PyArray_SIZE(a);
|
|
300
|
+
if (n != self->src_len) {
|
|
301
|
+
PyErr_SetString(PyExc_ValueError, "Array length must equal src_len");
|
|
302
|
+
return NULL;
|
|
303
|
+
}
|
|
304
|
+
const npy_int64* d = (npy_int64*)PyArray_DATA(a);
|
|
305
|
+
for (npy_intp i = 0; i < n; i++) {
|
|
306
|
+
if (AK_TM_register_one(self, (Py_ssize_t)i, (Py_ssize_t)d[i])) {
|
|
307
|
+
return NULL;
|
|
308
|
+
}
|
|
309
|
+
}
|
|
310
|
+
Py_RETURN_NONE;
|
|
311
|
+
}
|
|
312
|
+
|
|
272
313
|
PyObject *
|
|
273
314
|
TriMap_register_unmatched_dst(TriMapObject *self) {
|
|
274
315
|
if (self->finalized) {
|
|
@@ -1358,6 +1399,7 @@ TriMap_map_dst_fill(TriMapObject *self, PyObject *args) {
|
|
|
1358
1399
|
|
|
1359
1400
|
static PyMethodDef TriMap_methods[] = {
|
|
1360
1401
|
{"register_one", (PyCFunction)TriMap_register_one, METH_VARARGS, NULL},
|
|
1402
|
+
{"register_many_from_one", (PyCFunction)TriMap_register_many_from_one, METH_O, NULL},
|
|
1361
1403
|
{"register_unmatched_dst", (PyCFunction)TriMap_register_unmatched_dst, METH_NOARGS, NULL},
|
|
1362
1404
|
{"register_many", (PyCFunction)TriMap_register_many, METH_VARARGS, NULL},
|
|
1363
1405
|
{"finalize", (PyCFunction)TriMap_finalize, METH_NOARGS, NULL},
|
|
@@ -916,6 +916,109 @@ def test_fam_array_get_all_m3():
|
|
|
916
916
|
assert post2.tolist() == [2, 1]
|
|
917
917
|
|
|
918
918
|
|
|
919
|
+
# -------------------------------------------------------------------------------
|
|
920
|
+
# get_all_fill: input-aligned lookup, -1 for misses (never raises KeyError)
|
|
921
|
+
|
|
922
|
+
|
|
923
|
+
def test_fam_array_get_all_fill_int_a():
|
|
924
|
+
a1 = np.array((10, 20, 30, 40), dtype=np.int64)
|
|
925
|
+
a1.flags.writeable = False
|
|
926
|
+
fam = FrozenAutoMap(a1)
|
|
927
|
+
# all present -> identical to get_all
|
|
928
|
+
keys = np.array([30, 10, 40], dtype=np.int64)
|
|
929
|
+
keys.flags.writeable = False
|
|
930
|
+
assert fam.get_all_fill(keys).tolist() == [2, 0, 3]
|
|
931
|
+
assert fam.get_all_fill(keys).tolist() == fam.get_all(keys).tolist()
|
|
932
|
+
|
|
933
|
+
|
|
934
|
+
def test_fam_array_get_all_fill_int_missing():
|
|
935
|
+
a1 = np.array((10, 20, 30, 40), dtype=np.int64)
|
|
936
|
+
a1.flags.writeable = False
|
|
937
|
+
fam = FrozenAutoMap(a1)
|
|
938
|
+
keys = np.array([30, 99, 10, 77], dtype=np.int64)
|
|
939
|
+
keys.flags.writeable = False
|
|
940
|
+
assert fam.get_all_fill(keys).tolist() == [2, -1, 0, -1]
|
|
941
|
+
|
|
942
|
+
|
|
943
|
+
def test_fam_array_get_all_fill_immutable_and_dtype():
|
|
944
|
+
a1 = np.array((10, 20, 30), dtype=np.int64)
|
|
945
|
+
a1.flags.writeable = False
|
|
946
|
+
fam = FrozenAutoMap(a1)
|
|
947
|
+
keys = np.array([10], dtype=np.int64)
|
|
948
|
+
keys.flags.writeable = False
|
|
949
|
+
post = fam.get_all_fill(keys)
|
|
950
|
+
assert not post.flags.writeable
|
|
951
|
+
assert post.dtype == np.dtype(np.int64)
|
|
952
|
+
|
|
953
|
+
|
|
954
|
+
def test_fam_array_get_all_fill_empty():
|
|
955
|
+
a1 = np.array((10, 20), dtype=np.int64)
|
|
956
|
+
a1.flags.writeable = False
|
|
957
|
+
fam = FrozenAutoMap(a1)
|
|
958
|
+
keys = np.array([], dtype=np.int64)
|
|
959
|
+
keys.flags.writeable = False
|
|
960
|
+
assert fam.get_all_fill(keys).tolist() == []
|
|
961
|
+
|
|
962
|
+
|
|
963
|
+
def test_fam_array_get_all_fill_all_missing():
|
|
964
|
+
a1 = np.array((10, 20), dtype=np.int64)
|
|
965
|
+
a1.flags.writeable = False
|
|
966
|
+
fam = FrozenAutoMap(a1)
|
|
967
|
+
keys = np.array([1, 2, 3], dtype=np.int64)
|
|
968
|
+
keys.flags.writeable = False
|
|
969
|
+
assert fam.get_all_fill(keys).tolist() == [-1, -1, -1]
|
|
970
|
+
|
|
971
|
+
|
|
972
|
+
def test_fam_array_get_all_fill_kind_mismatch():
|
|
973
|
+
# int map queried with a float array -> scalar fallback, -1 for misses
|
|
974
|
+
a1 = np.array((10, 20, 30), dtype=np.int64)
|
|
975
|
+
a1.flags.writeable = False
|
|
976
|
+
fam = FrozenAutoMap(a1)
|
|
977
|
+
keys = np.array([10.0, 15.0, 30.0], dtype=np.float64)
|
|
978
|
+
keys.flags.writeable = False
|
|
979
|
+
assert fam.get_all_fill(keys).tolist() == [0, -1, 2]
|
|
980
|
+
|
|
981
|
+
|
|
982
|
+
def test_fam_array_get_all_fill_unicode():
|
|
983
|
+
a1 = np.array(['a', 'bb', 'c'])
|
|
984
|
+
a1.flags.writeable = False
|
|
985
|
+
fam = FrozenAutoMap(a1)
|
|
986
|
+
keys = np.array(['c', 'zz', 'a'])
|
|
987
|
+
keys.flags.writeable = False
|
|
988
|
+
assert fam.get_all_fill(keys).tolist() == [2, -1, 0]
|
|
989
|
+
|
|
990
|
+
|
|
991
|
+
def test_fam_array_get_all_fill_bytes():
|
|
992
|
+
a1 = np.array([b'a', b'bb', b'c'], dtype='S2')
|
|
993
|
+
a1.flags.writeable = False
|
|
994
|
+
fam = FrozenAutoMap(a1)
|
|
995
|
+
keys = np.array([b'c', b'zz', b'a'], dtype='S2')
|
|
996
|
+
keys.flags.writeable = False
|
|
997
|
+
assert fam.get_all_fill(keys).tolist() == [2, -1, 0]
|
|
998
|
+
|
|
999
|
+
|
|
1000
|
+
def test_fam_array_get_all_fill_datetime():
|
|
1001
|
+
a1 = np.array(['2020-01', '2021-06', '2022-03'], dtype='datetime64[M]')
|
|
1002
|
+
a1.flags.writeable = False
|
|
1003
|
+
fam = FrozenAutoMap(a1)
|
|
1004
|
+
keys = np.array(['2022-03', '1999-12', '2020-01'], dtype='datetime64[M]')
|
|
1005
|
+
keys.flags.writeable = False
|
|
1006
|
+
assert fam.get_all_fill(keys).tolist() == [2, -1, 0]
|
|
1007
|
+
|
|
1008
|
+
|
|
1009
|
+
def test_fam_array_get_all_fill_object_list():
|
|
1010
|
+
fam = FrozenAutoMap([('x',), ('y',)])
|
|
1011
|
+
assert fam.get_all_fill([('y',), ('z',), ('x',)]).tolist() == [1, -1, 0]
|
|
1012
|
+
|
|
1013
|
+
|
|
1014
|
+
def test_fam_array_get_all_fill_bad_type():
|
|
1015
|
+
a1 = np.array((10, 20), dtype=np.int64)
|
|
1016
|
+
a1.flags.writeable = False
|
|
1017
|
+
fam = FrozenAutoMap(a1)
|
|
1018
|
+
with pytest.raises(TypeError):
|
|
1019
|
+
fam.get_all_fill('a')
|
|
1020
|
+
|
|
1021
|
+
|
|
919
1022
|
# -------------------------------------------------------------------------------
|
|
920
1023
|
|
|
921
1024
|
|
|
@@ -1439,3 +1439,72 @@ class TestUnit(unittest.TestCase):
|
|
|
1439
1439
|
np.datetime64('2005-11'),
|
|
1440
1440
|
],
|
|
1441
1441
|
)
|
|
1442
|
+
|
|
1443
|
+
# ------------------------------------------------------------------
|
|
1444
|
+
# register_many_from_one (bulk one-to-one)
|
|
1445
|
+
|
|
1446
|
+
def test_tri_map_register_many_from_one_a(self) -> None:
|
|
1447
|
+
tm = TriMap(3, 4)
|
|
1448
|
+
tm.register_many_from_one(np.array([2, 0, 3], dtype=np.int64))
|
|
1449
|
+
tm.finalize()
|
|
1450
|
+
self.assertFalse(tm.is_many())
|
|
1451
|
+
src = np.array([10, 20, 30])
|
|
1452
|
+
dst = np.array([100, 200, 300, 400])
|
|
1453
|
+
self.assertEqual(tm.map_src_no_fill(src).tolist(), [10, 20, 30])
|
|
1454
|
+
self.assertEqual(tm.map_dst_no_fill(dst).tolist(), [300, 100, 400])
|
|
1455
|
+
|
|
1456
|
+
def test_tri_map_register_many_from_one_unmatched(self) -> None:
|
|
1457
|
+
# -1 marks an unmatched src -> a src-only row (dst side gets a fill there)
|
|
1458
|
+
tm = TriMap(3, 4)
|
|
1459
|
+
tm.register_many_from_one(np.array([2, -1, 0], dtype=np.int64))
|
|
1460
|
+
tm.finalize()
|
|
1461
|
+
self.assertTrue(tm.src_no_fill()) # every output row has a src value
|
|
1462
|
+
self.assertFalse(tm.dst_no_fill()) # row 1 has no dst -> needs fill
|
|
1463
|
+
dst = np.array([100, 200, 300, 400])
|
|
1464
|
+
self.assertEqual(tm.map_dst_fill(dst, -9, np.dtype(np.int64)).tolist(), [300, -9, 100])
|
|
1465
|
+
|
|
1466
|
+
def test_tri_map_register_many_from_one_is_many(self) -> None:
|
|
1467
|
+
# two src rows matching the same dst -> is_many
|
|
1468
|
+
tm = TriMap(3, 3)
|
|
1469
|
+
tm.register_many_from_one(np.array([0, 0, 1], dtype=np.int64))
|
|
1470
|
+
tm.finalize()
|
|
1471
|
+
self.assertTrue(tm.is_many())
|
|
1472
|
+
|
|
1473
|
+
def test_tri_map_register_many_from_one_equivalence(self) -> None:
|
|
1474
|
+
rng = np.random.RandomState(0)
|
|
1475
|
+
for _ in range(50):
|
|
1476
|
+
src_len = int(rng.randint(1, 12))
|
|
1477
|
+
dst_len = int(rng.randint(1, 10))
|
|
1478
|
+
dst_pos = rng.randint(-1, dst_len, size=src_len).astype(np.int64)
|
|
1479
|
+
tb = TriMap(src_len, dst_len)
|
|
1480
|
+
tb.register_many_from_one(dst_pos)
|
|
1481
|
+
tb.finalize()
|
|
1482
|
+
tl = TriMap(src_len, dst_len)
|
|
1483
|
+
for i in range(src_len):
|
|
1484
|
+
tl.register_one(i, int(dst_pos[i]))
|
|
1485
|
+
tl.finalize()
|
|
1486
|
+
src = np.arange(100, 100 + src_len)
|
|
1487
|
+
dst = np.arange(200, 200 + dst_len)
|
|
1488
|
+
self.assertEqual(tb.is_many(), tl.is_many())
|
|
1489
|
+
self.assertEqual(
|
|
1490
|
+
tb.map_src_no_fill(src).tolist(), tl.map_src_no_fill(src).tolist()
|
|
1491
|
+
)
|
|
1492
|
+
self.assertEqual(
|
|
1493
|
+
tb.map_dst_fill(dst, -1, np.dtype(np.int64)).tolist(),
|
|
1494
|
+
tl.map_dst_fill(dst, -1, np.dtype(np.int64)).tolist(),
|
|
1495
|
+
)
|
|
1496
|
+
|
|
1497
|
+
def test_tri_map_register_many_from_one_errors(self) -> None:
|
|
1498
|
+
with self.assertRaises(ValueError): # wrong length
|
|
1499
|
+
TriMap(3, 3).register_many_from_one(np.array([0, 1], dtype=np.int64))
|
|
1500
|
+
with self.assertRaises(ValueError): # wrong dtype
|
|
1501
|
+
TriMap(3, 3).register_many_from_one(np.array([0, 1, 2], dtype=np.int32))
|
|
1502
|
+
with self.assertRaises(ValueError): # out of bounds dst
|
|
1503
|
+
TriMap(3, 3).register_many_from_one(np.array([0, 1, 9], dtype=np.int64))
|
|
1504
|
+
with self.assertRaises(TypeError): # not an array
|
|
1505
|
+
TriMap(3, 3).register_many_from_one([0, 1, 2])
|
|
1506
|
+
tm = TriMap(2, 2)
|
|
1507
|
+
tm.register_many_from_one(np.array([0, 1], dtype=np.int64))
|
|
1508
|
+
tm.finalize()
|
|
1509
|
+
with self.assertRaises(RuntimeError): # post-finalize
|
|
1510
|
+
tm.register_many_from_one(np.array([0, 1], dtype=np.int64))
|
arraykit-1.8.0/VERSION
DELETED
|
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
|
|
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
|