Repository: mars-project/mars

Issue:
Add support for `mars.tensor.append`.
<!--
Thank you for your contribution!

Please review https://github.com/mars-project/mars/blob/master/CONTRIBUTING.rst before opening an issue.
-->

**Is your feature request related to a problem? Please describe.**

`mars.tensor.append` can be added support.



Discussion:

Resolving patch:
diff --git a/mars/tensor/__init__.py b/mars/tensor/__init__.py
--- a/mars/tensor/__init__.py
+++ b/mars/tensor/__init__.py
@@ -53,7 +53,8 @@
     argmax, nanargmax, argmin, nanargmin, cumsum, cumprod, \
     var, std, nanvar, nanstd, nancumsum, nancumprod, count_nonzero, allclose, array_equal
 from .reshape import reshape
-from .merge import concatenate, stack, hstack, vstack, dstack, column_stack, union1d, block
+from .merge import concatenate, stack, hstack, vstack, dstack, \
+    column_stack, union1d, block, append
 from .indexing import take, compress, extract, choose, unravel_index, \
     nonzero, flatnonzero, fill_diagonal
 from .rechunk import rechunk
diff --git a/mars/tensor/merge/__init__.py b/mars/tensor/merge/__init__.py
--- a/mars/tensor/merge/__init__.py
+++ b/mars/tensor/merge/__init__.py
@@ -20,3 +20,4 @@
 from .column_stack import column_stack
 from .union1d import union1d
 from .block import block
+from .append import append
diff --git a/mars/tensor/merge/append.py b/mars/tensor/merge/append.py
new file mode 100644
--- /dev/null
+++ b/mars/tensor/merge/append.py
@@ -0,0 +1,74 @@
+# Copyright 1999-2021 Alibaba Group Holding Ltd.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+from ..base.ravel import ravel
+from ..datasource.array import asarray
+from .concatenate import concatenate
+
+
+def append(arr, values, axis=None):
+    """
+    Append values to the end of an array.
+
+    Parameters
+    ----------
+    arr : array_like
+        Values are appended to a copy of this array.
+    values : array_like
+        These values are appended to a copy of `arr`.  It must be of the
+        correct shape (the same shape as `arr`, excluding `axis`).  If
+        `axis` is not specified, `values` can be any shape and will be
+        flattened before use.
+    axis : int, optional
+        The axis along which `values` are appended.  If `axis` is not
+        given, both `arr` and `values` are flattened before use.
+
+    Returns
+    -------
+    append : Tensor
+        A copy of `arr` with `values` appended to `axis`.  Note that
+        `append` does not occur in-place: a new array is allocated and
+        filled.  If `axis` is None, `out` is a flattened array.
+
+    See Also
+    --------
+    insert : Insert elements into an array.
+    delete : Delete elements from an array.
+
+    Examples
+    --------
+    >>> import mars.tensor as mt
+
+    >>> mt.append([1, 2, 3], [[4, 5, 6], [7, 8, 9]]).execute()
+    array([1, 2, 3, ..., 7, 8, 9])
+
+    When `axis` is specified, `values` must have the correct shape.
+
+    >>> mt.append([[1, 2, 3], [4, 5, 6]], [[7, 8, 9]], axis=0).execute()
+    array([[1, 2, 3],
+           [4, 5, 6],
+           [7, 8, 9]])
+    >>> mt.append([[1, 2, 3], [4, 5, 6]], [7, 8, 9], axis=0)
+    Traceback (most recent call last):
+        ...
+    ValueError: all the input tensors must have same number of dimensions
+
+    """
+    arr = asarray(arr)
+    if axis is None:
+        if arr.ndim != 1:
+            arr = arr.ravel()
+        values = ravel(values)
+        axis = arr.ndim - 1
+    return concatenate((arr, values), axis=axis)


Test patch:
diff --git a/mars/tensor/merge/tests/test_merge_execution.py b/mars/tensor/merge/tests/test_merge_execution.py
--- a/mars/tensor/merge/tests/test_merge_execution.py
+++ b/mars/tensor/merge/tests/test_merge_execution.py
@@ -20,7 +20,7 @@
 
 from ...datasource import tensor, empty, eye, ones, zeros
 from ... import concatenate, stack, hstack, vstack, dstack, column_stack, \
-    union1d, array, block
+    union1d, array, block, append
 
 
 def test_concatenate_execution(setup):
@@ -352,3 +352,16 @@ def test_block_execution(setup):
     r = c.execute().fetch()
     expected = array([[1]])
     np.testing.assert_array_equal(r, expected)
+
+
+@pytest.mark.parametrize('axis', [0, None])
+def test_append_execution(setup, axis):
+    raw1 = np.random.rand(10, 3)
+    raw2 = np.random.rand(6, 3)
+
+    a1 = tensor(raw1, chunk_size=3)
+    a2 = tensor(raw2, chunk_size=4)
+    r = append(a1, a2, axis=axis)
+    result = r.execute().fetch()
+    expected = np.append(raw1, raw2, axis=axis)
+    np.testing.assert_array_equal(result, expected)