Trung Tran
Trung Tran

Reputation: 13771

Concat two arrays of different dimensions numpy

I am trying to concatenate two numpy arrays to add an extra column: array_1 is (569, 30) and array_2 is is (569, )

combined = np.concatenate((array_1, array_2), axis=1)

I thought this would work if I set axis=2 so it will concatenate vertically. The end should should be a 569 x 31 array.

The error I get is ValueError: all the input arrays must have same number of dimensions

Can someone help?

Thx!

Upvotes: 26

Views: 77741

Answers (5)

Sirrah
Sirrah

Reputation: 56

I have written a general stacking function. It is a bit more complicated but its inputs are just the arrays (tuple) and an axis along which you wish to stack the arrays. E.g.

A = np.random.random((569, 30))
B = np.random.random((569,))
C = stack((A, B), axis=1)  # C.shape == (569, 31)



def stack(arrays: tuple | list, axis: int | None = None, reduce: bool = False) -> np.ndarray:
    """
    concatenate arrays along the specific axis

    if reduce=True, the "arrays" tuple is processed in this way
    arrays = (A, B, C, D)
    stack((stack((stack((A, B), axis=axis), C), axis=axis), D), axis=axis)
    This is potentially slower but allows to concatenate e.g.
    A.shape = (2, 4, 4)
    B.shape = (3, 4)
    C.shape = (4,)
    res = stack((C, B, A), axis=0, reduce=True)
    res.shape = (3, 4, 4)
    res[0] == stack((C, B), axis=0)
    res[1:] == A
    """

    @reduce_like
    def _stack(arrays: tuple | list, axis: int | None = None) -> np.ndarray:
        ndim = np.array([np.ndim(array) for array in arrays])
        _check_dims(ndim, reduce)

        if np.all(ndim == 1):  # vector + vector + ...
            if axis is None:  # -> vector
                return np.concatenate(arrays, axis=axis)
            else:  # -> 2-D array
                return np.stack(arrays, axis=axis)

        elif np.var(ndim) != 0:  # N-D array + (N-1)-D array + ... -> N-D array
            max_dim = np.max(ndim)

            # longest array
            shape = list(np.shape(arrays[np.argmax(ndim)]))
            shape[axis] = -1

            arrays = [np.reshape(a, shape) if np.ndim(a) < max_dim else a for a in arrays]
            return np.concatenate(arrays, axis=axis)

        elif is_constant(ndim):  # N-D array + N-D array + -> N-D array or (N+1)-D array
            ndim = ndim[0]
            if axis < ndim:  # along existing dimensions
                return np.concatenate(arrays, axis=axis)
            else:  # along a new dimension
                return np.stack(arrays, axis=axis)

    def _check_dims(ndim: np.ndarray, reduce: bool = False) -> None:
        error_msg = "Maximum allowed difference in dimension of concatenated arrays is one."

        if np.max(ndim) - np.min(ndim) > 1:
            if reduce:
                raise ValueError(error_msg)
            else:
                raise ValueError(f'{error_msg}\nUse "reduce=True" to unlock more general (but slower) stacking.')

    # 0-D arrays to 1-D arrays (to e.g. add a number to a vector)
    arrays = tuple([np.reshape(array, (1,)) if np.ndim(array) == 0 else array for array in arrays])

    if reduce:
        return _stack(arrays, axis)
    else:
        return _stack.undecorated(arrays, axis)
        
        
def is_constant(array: np.ndarray, axis: int | bool = None, constant: float = None) -> bool | np.ndarray:
    if constant is None:  # return True if the array is constant along the axis
        return np.var(array, axis=axis) < _num_eps
    else:  # return True if the array is equal to "constant" along the axis
        return np.all(np.abs(array - constant) < _num_eps, axis=axis)
        
        
def reduce_like(func: Callable):
    @wraps(func)
    def _decorator(*args, **kw):
        args = list(args)
        arrays = args[0]
        result = arrays[0]

        for array in arrays[1:-1]:
            if args[1:]:
                new_args = [(result, array), *args[1:]]
            else:
                new_args = [(result, array)]
            result = func(*new_args, **kw)
        else:
            if args[1:]:
                new_args = [(result, arrays[-1]), *args[1:]]
            else:
                new_args = [(result, arrays[-1])]

            return func(*new_args, **kw)

    _decorator.undecorated = func

    return _decorator

Upvotes: 0

Shashwat Avi
Shashwat Avi

Reputation: 1

You can convert the 1-D array to 2-D array with the same number of rows using reshape function and concatenate the resulting array horizontally using numpy's append function.

Note: In numpy's append function, we have to mention axis along which we want to insert the values. If axis=0, arrays are appended vertically. If axis=1, arrays are appended horizontally. So, we can use axis=1, for current requirement

e.g.

a = np.arange(6).reshape(2,3)
b = np.arange(2)

a
#array([[0, 1, 2],
#       [3, 4, 5]])

b
#array([0, 1])

#First step, convert this 1-D array to 2-D (Number of rows should be same as array 'a' i.e. 2)
c = b.reshape(2,1)

c
#array([[0],
        [1]])


#Step 2, Using numpy's append function we can concatenate both arrays with same number of rows horizontally

requirement = np.append((a, c, axis=1))

requirement
#array([[0, 1, 2, 0],
#       [3, 4, 5, 1]])

Upvotes: 0

Forough Nasihati
Forough Nasihati

Reputation: 41

To stack them vertically try

np.vstack((array1,array2))

Upvotes: 0

Darshil Shah
Darshil Shah

Reputation: 11

You can simply use numpy's hstack function.

e.g.

import numpy as np

combined = np.hstack((array1,array2))

Upvotes: 0

akuiper
akuiper

Reputation: 215117

You can use numpy.column_stack:

np.column_stack((array_1, array_2))

Which converts the 1-d array to 2-d implicitly, and thus equivalent to np.concatenate((array_1, array_2[:,None]), axis=1) as commented by @umutto.


a = np.arange(6).reshape(2,3)
b = np.arange(2)

a
#array([[0, 1, 2],
#       [3, 4, 5]])

b
#array([0, 1])

np.column_stack((a, b))
#array([[0, 1, 2, 0],
#       [3, 4, 5, 1]])

Upvotes: 34

Related Questions