diff --git a/ServerModules.cfg b/ServerModules.cfg index b3947ac77b..efec67b62b 100644 --- a/ServerModules.cfg +++ b/ServerModules.cfg @@ -3,6 +3,7 @@ ArgSortMsg ArraySetopsMsg +AryUtil BroadcastMsg CastMsg ConcatenateMsg diff --git a/arkouda/numpy/_manipulation_functions.py b/arkouda/numpy/_manipulation_functions.py index 51472d92dd..9a90781cbb 100644 --- a/arkouda/numpy/_manipulation_functions.py +++ b/arkouda/numpy/_manipulation_functions.py @@ -81,8 +81,8 @@ def flip( elif isinstance(x, Strings): rep_msg = generic_msg( - cmd="flipString", args={"objType": x.objType, "obj": x.entry, "size": x.size} - ) + cmd="flipString", args={"objType": x.objType, "obj": x.entry, "size": x.size} + ) return Strings.from_return_msg(cast(str, rep_msg)) else: raise TypeError("flip only accepts type pdarray, Strings, or Categorical.") diff --git a/arkouda/pdarrayclass.py b/arkouda/pdarrayclass.py index fe9d239d40..67d5a955f6 100644 --- a/arkouda/pdarrayclass.py +++ b/arkouda/pdarrayclass.py @@ -533,8 +533,7 @@ def _binop(self, other: pdarray, op: str) -> pdarray: except ValueError: raise ValueError(f"shape mismatch {self.shape} {other.shape}") repMsg = generic_msg( - cmd=f"binopvv<{self.dtype},{other.dtype},{x1.ndim}>", - args={"op": op, "a": x1, "b": x2} + cmd=f"binopvv<{self.dtype},{other.dtype},{x1.ndim}>", args={"op": op, "a": x1, "b": x2} ) if tmp_x1: del x1 @@ -791,7 +790,7 @@ def opeq(self, other, op): raise ValueError(f"shape mismatch {self.shape} {other.shape}") generic_msg( cmd=f"opeqvv<{self.dtype},{other.dtype},{self.ndim}>", - args={"op": op, "a": self, "b": other} + args={"op": op, "a": self, "b": other}, ) return self # pdarray binop scalar @@ -1769,6 +1768,21 @@ def reshape(self, *shape): ), ) + def flatten(self): + """ + Return a copy of the array collapsed into one dimension. + + Returns + ------- + A copy of the input array, flattened to one dimension. + """ + return create_pdarray( + generic_msg( + cmd=f"flatten<{self.dtype.name},{self.ndim}>", + args={"a": self}, + ) + ) + def to_ndarray(self) -> np.ndarray: """ Convert the array to a np.ndarray, transferring array data from the diff --git a/arkouda/strings.py b/arkouda/strings.py index add03eb0e8..805272a61e 100644 --- a/arkouda/strings.py +++ b/arkouda/strings.py @@ -2082,6 +2082,21 @@ def _get_grouping_keys(self) -> List[Strings]: """ return [self] + def flatten(self): + """ + Return a copy of the array collapsed into one dimension. + + Returns + ------- + A copy of the input array, flattened to one dimension. + + Note + ---- + As multidimensional Strings are currently supported, + flatten on a Strings object will always return itself. + """ + return self + def to_ndarray(self) -> np.ndarray: """ Convert the array to a np.ndarray, transferring array data from the diff --git a/src/AryUtil.chpl b/src/AryUtil.chpl index da573e77c2..71fa35100a 100644 --- a/src/AryUtil.chpl +++ b/src/AryUtil.chpl @@ -944,6 +944,7 @@ module AryUtil /* flatten a multi-dimensional array into a 1D array */ + @arkouda.registerCommand proc flatten(const ref a: [?d] ?t): [] t throws where a.rank > 1 { @@ -1003,6 +1004,12 @@ module AryUtil return flat; } + proc flatten(const ref a: [?d] ?t): [] t throws + where a.rank == 1 + { + return a; + } + // helper for computing an array element's index from its order record orderer { param rank: int; diff --git a/tests/pdarrayclass_test.py b/tests/pdarrayclass_test.py index 00418da328..33682ebb8c 100644 --- a/tests/pdarrayclass_test.py +++ b/tests/pdarrayclass_test.py @@ -1,7 +1,8 @@ +import numpy as np import pytest import arkouda as ak -import numpy as np +from arkouda.testing import assert_equal as ak_assert_equal class TestPdarrayClass: @@ -21,7 +22,20 @@ def test_shape(self): @pytest.mark.skip_if_max_rank_less_than(2) def test_shape_multidim(self): - a = ak.arange(4).reshape((2,2)) - np_a = np.arange(4).reshape((2,2)) + a = ak.arange(4).reshape((2, 2)) + np_a = np.arange(4).reshape((2, 2)) assert isinstance(a.shape, tuple) assert a.shape == np_a.shape + + @pytest.mark.parametrize("size", pytest.prob_size) + def test_flatten(self, size): + a = ak.arange(size) + ak_assert_equal(a.flatten(), a) + + @pytest.mark.skip_if_max_rank_less_than(3) + @pytest.mark.parametrize("size", pytest.prob_size) + def test_flatten(self, size): + size = size - (size % 4) + a = ak.arange(size) + b = a.reshape((2, 2, size / 4)) + ak_assert_equal(b.flatten(), a) diff --git a/tests/string_test.py b/tests/string_test.py index 6aca11f174..05bd1c151e 100644 --- a/tests/string_test.py +++ b/tests/string_test.py @@ -6,6 +6,7 @@ import pytest import arkouda as ak +from arkouda.testing import assert_equal as ak_assert_equal ak.verbose = False N = 100 @@ -910,3 +911,10 @@ def test_in1d(self, size): for word in more_words: inds |= strings == word assert (inds == matches).all() + + @pytest.mark.parametrize("size", pytest.prob_size) + def test_flatten(self, size): + base_words, _ = self.base_words(size) + strings = self.get_strings(size, base_words) + + ak_assert_equal(strings.flatten(), strings)