diff --git a/arkouda/numpy/numeric.py b/arkouda/numpy/numeric.py index 94c7c2d0ecd..2206ad4059a 100644 --- a/arkouda/numpy/numeric.py +++ b/arkouda/numpy/numeric.py @@ -3767,7 +3767,7 @@ def minimum(x1: Union[pdarray, numeric_scalars], x2: Union[pdarray, numeric_scal if np.isscalar(tx1) and isinstance(tx2, pdarray): return ( - full(nan, tx2.size) if np.isnan(tx1) else where(isnan(tx2), tx2, where(tx1 < tx2, tx1, tx2)) + full(tx2.size, nan) if np.isnan(tx1) else where(isnan(tx2), tx2, where(tx1 < tx2, tx1, tx2)) ) # if tx2 was a scalar, then tx1 isn't (they can't both be, at this point). @@ -3855,7 +3855,7 @@ def maximum(x1: Union[pdarray, numeric_scalars], x2: Union[pdarray, numeric_scal if np.isscalar(tx1) and isinstance(tx2, pdarray): return ( - full(nan, tx2.size) if np.isnan(tx1) else where(isnan(tx2), tx2, where(tx1 > tx2, tx1, tx2)) + full(tx2.size, nan) if np.isnan(tx1) else where(isnan(tx2), tx2, where(tx1 > tx2, tx1, tx2)) ) # if tx2 was a scalar, then tx1 isn't (they can't both be, at this point). diff --git a/arkouda/numpy/pdarraycreation.py b/arkouda/numpy/pdarraycreation.py index fa7e409fae2..8ee9208c506 100644 --- a/arkouda/numpy/pdarraycreation.py +++ b/arkouda/numpy/pdarraycreation.py @@ -34,8 +34,10 @@ is_supported_int, is_supported_number, numeric_scalars, + numeric_and_bool_scalars, resolve_scalar_dtype, str_, + str_scalars, ) from arkouda.numpy.dtypes import dtype as akdtype from arkouda.numpy.dtypes import int64 as akint64 @@ -702,7 +704,7 @@ def ones( size: Union[int_scalars, Tuple[int_scalars, ...], str], dtype: Union[np.dtype, type, str, bigint] = float64, max_bits: Optional[int] = None, -) -> pdarray: +) -> Union[pdarray, Strings]: """ Create a pdarray filled with ones. @@ -757,11 +759,47 @@ def ones( return full(size=size, fill_value=1, dtype=dtype, max_bits=max_bits) +@overload +def full( + size: Union[int_scalars, Tuple[int_scalars, ...], str], + fill_value: str_scalars, + dtype: None = ..., + max_bits: Optional[int] = ..., +) -> Strings: ... + + +@overload +def full( + size: Union[int_scalars, Tuple[int_scalars, ...], str], + fill_value: Union[numeric_and_bool_scalars, str_scalars], + dtype: str, + max_bits: Optional[int] = ..., +) -> Strings: ... + + +@overload +def full( + size: Union[int_scalars, Tuple[int_scalars, ...], str], + fill_value: Union[numeric_and_bool_scalars], + dtype: None = ..., + max_bits: Optional[int] = ..., +) -> pdarray: ... + + +@overload +def full( + size: Union[int_scalars, Tuple[int_scalars, ...], str], + fill_value: Union[numeric_and_bool_scalars, str_scalars], + dtype: Union[np.dtype, type, bigint], + max_bits: Optional[int] = ..., +) -> pdarray: ... + + @typechecked def full( size: Union[int_scalars, Tuple[int_scalars, ...], str], - fill_value: Union[numeric_scalars, np.bool, str], - dtype: Union[np.dtype, type, str, bigint] = float64, + fill_value: Union[numeric_and_bool_scalars, str_scalars], + dtype: Union[None, np.dtype, type, str, bigint] = None, max_bits: Optional[int] = None, ) -> Union[pdarray, Strings]: """ @@ -771,10 +809,10 @@ def full( ---------- size: int_scalars or tuple of int_scalars Size or shape of the array - fill_value: int_scalars or str + fill_value: numeric_scalars, bool_scalars or str_scalars Value with which the array will be filled dtype: all_scalars - Resulting array type, default float64 + Resulting array type. If None, it will be inferred from fill_value max_bits: int Specifies the maximum number of bits; only used for bigint pdarrays @@ -793,6 +831,7 @@ def full( ValueError Raised if the rank of the given shape is not in get_array_ranks() or is empty + Raised if a multi-dim Strings array was requested Raised if max_bits is not NONE and ndim does not equal 1 See Also @@ -813,32 +852,44 @@ def full( array([True True True True True]) """ from arkouda.client import generic_msg, get_array_ranks - from arkouda.numpy.dtypes import dtype as ak_dtype from arkouda.numpy.pdarrayclass import create_pdarray + from arkouda.numpy.util import _infer_shape_from_size # placed here to avoid circ import - if isinstance(fill_value, str): - return _full_string(size, fill_value) - elif ak_dtype(dtype) == str_ or dtype == Strings: - return _full_string(size, str_(fill_value)) + # Process the arguments (size, fill_value, dtype) before choosing an action. - dtype = dtype if dtype is not None else resolve_scalar_dtype(fill_value) + shape, ndim, full_size = _infer_shape_from_size(size) + fv_dtype = akdtype(resolve_scalar_dtype(fill_value)) # derived from fill_value + dtype = dtype if dtype is not None else fv_dtype # must allow it to be None + + if isinstance(dtype, bigint) or isinstance(dtype, np.dtype): + dtype_name = dtype.name + elif isinstance(dtype, type) or isinstance(dtype, str): + dtype_name = akdtype(dtype).name + else: + raise TypeError(f"Unsupported dtype {dtype!r}") + + # The only string cases. + + if dtype == Strings or akdtype(dtype) == str_: + if ndim != 1: + raise ValueError(f"Size parameter {size} incompatible with Strings.") + else: + if isinstance(fill_value, float): # needed to match numpy, which + fill_value = int(fill_value) # truncates floats to ints before "str" + return _full_string(full_size, str(fill_value)) + + # Remaining cases are all numeric. Check dtype for error - dtype = akdtype(dtype) # normalize dtype - dtype_name = dtype.name if isinstance(dtype, bigint) else cast(np.dtype, dtype).name - # check dtype for error if dtype_name not in NumericDTypes: - raise TypeError(f"unsupported dtype {dtype}") - from arkouda.numpy.util import ( - _infer_shape_from_size, # placed here to avoid circ import - ) + raise TypeError(f"Unsupported dtype {dtype} in ak.full") - shape, ndim, full_size = _infer_shape_from_size(size) + # dtype is supported. Two more checks. if ndim not in get_array_ranks(): - raise ValueError(f"array rank {ndim} not in compiled ranks {get_array_ranks()}") + raise ValueError(f"Array rank {ndim} not in compiled ranks {get_array_ranks()}") if isinstance(shape, tuple) and len(shape) == 0: - raise ValueError("size () not currently supported in ak.full.") + raise ValueError("Empty shape () not currently supported in ak.full.") rep_msg = generic_msg(cmd=f"create<{dtype_name},{ndim}>", args={"shape": shape}) @@ -1408,11 +1459,11 @@ def linspace( if endpoint is None: endpoint = True + # First make sure everything's a float. + start_ = start stop_ = stop - # First make sure everything's a float. - if isinstance(start_, pdarray): start_ = start_.astype(float64) elif isinstance(start_, int): @@ -1471,7 +1522,10 @@ def linspace( else: if axis == 0: delta = (stop_ - start_) / divisor - result = full(num, start_) + arange(num).astype(float64) * delta + if isinstance(start_, (int, float, np.number)): # required for mypy + result = full(num, start_) + arange(num).astype(float64) * delta + else: # this should be impossible to reach + raise TypeError("Non-numeric start value found in linspace.") else: raise ValueError("axis should not be supplied when start and stop are scalars.") diff --git a/arkouda/numpy/util.py b/arkouda/numpy/util.py index 4bd36d95162..d2a12f3b50f 100644 --- a/arkouda/numpy/util.py +++ b/arkouda/numpy/util.py @@ -698,6 +698,7 @@ def broadcast_to(x: Union[numeric_scalars, pdarray], shape: Union[int, Tuple[int from arkouda.numpy.pdarraycreation import full as akfull if _val_isinstance_of_union(x, numeric_scalars): + assert not isinstance(x, pdarray) # Required for mypy return akfull(shape, x, dtype=type(x)) elif isinstance(x, pdarray) and isinstance(shape, int): if x.ndim == 1 and x.size == shape: @@ -1012,6 +1013,7 @@ def map( xtra_keys = gb_keys[in1d(gb_keys, mapping.index.values, invert=True)] if xtra_keys.size > 0: + nans: Union[pdarray, Strings] # without this, mypy complains if not isinstance(mapping.values, (Strings, Categorical)): nans = full(xtra_keys.size, np.nan, mapping.values.dtype) else: diff --git a/arkouda/pandas/extension/_arkouda_array.py b/arkouda/pandas/extension/_arkouda_array.py index e422519341d..a043953ca1d 100644 --- a/arkouda/pandas/extension/_arkouda_array.py +++ b/arkouda/pandas/extension/_arkouda_array.py @@ -194,7 +194,7 @@ def isna(self) -> ExtensionArray | ndarray[Any, Any]: from arkouda.numpy.util import is_float if not is_float(self._data): - return ak_full(self._data.size, False, dtype=bool) + return ArkoudaArray(ak_full(self._data.size, False, dtype=bool)) return isnan(self._data) diff --git a/tests/pandas/extension/arkouda_array_extension.py b/tests/pandas/extension/arkouda_array_extension.py index 0de5df06d12..68d689aeb1a 100644 --- a/tests/pandas/extension/arkouda_array_extension.py +++ b/tests/pandas/extension/arkouda_array_extension.py @@ -168,7 +168,7 @@ def test_isna(self): ak_data = ak.arange(10) arr = ArkoudaArray(ak_data) na = arr.isna() - assert ak.all(na == False) + assert (na == False).all() def test_isna_with_nan(self): from arkouda.testing import assert_equal