15

If I have a list of numpy arrays, then using remove method returns a value error.

For example:

import numpy as np

l = [np.array([1,1,1]),np.array([2,2,2]),np.array([3,3,3])]

l.remove(np.array([2,2,2]))

Would give me

ValueError: The truth value of an array with more than one element is ambiguous. Use a.any() or a.all()

I can't seem to get the all() to work, is it just not possible?

2
  • Just so you know, it isn't a good idea to use list as a variable since it is a keyword in Python. It can come back to bite you later on. Commented Jul 1, 2010 at 17:40
  • Yes thanks, I was bitten whilst playing around trying to solve this problem, converting the arrays to lists using list() then using remove and so on. Commented Jul 2, 2010 at 10:24

4 Answers 4

15

The problem here is that when two numpy arrays are compared with ==, as in the remove() and index() methods, a numpy array of boolean values (the element by element comparisons) is returned which is interpretted as being ambiguous. A good way to compare two numpy arrays for equality is to use numpy's array_equal() function.

Since the remove() method of lists doesn't have a key argument (like sort() does), I think that you need to make your own function to do this. Here's one that I made:

def removearray(L,arr):
    ind = 0
    size = len(L)
    while ind != size and not np.array_equal(L[ind],arr):
        ind += 1
    if ind != size:
        L.pop(ind)
    else:
        raise ValueError('array not found in list.')

If you need it to be faster then you could Cython-ize it.

Sign up to request clarification or add additional context in comments.

Comments

3

Here you go:

list.pop(1)

Update:

list.pop(list.index(element))

I don't think you can get around traversing the list to find the position of the element. Don't worry about it. Python will, by default use a good searching algorithm to find it at least cost for you.

2 Comments

Thanks, I realise that works for my example but where I actually need to do this I don't know the position of the array I want to remove. Rather than using a loop I thought there might be a nicer way using the remove method.
Thanks for helping me with this. If I use list.index() for a numpy array I get the ambiguous truth error again, hmm.
2

Use Python Basic Functionalities

The following solution uses the list.index(element) method from the list of arrays.

Searching for a numpy.ndarray needs to be able to hash numpy.ndarray instances. Therefore, we need to implement a hashing algorithm. This is fairly simple, although the code presented looks a bit long, most of the lines are used for checking for edge cases or the addition of comments.

You can copy paste the code into a file and run it from the command line or a SDK as PyCharm.

You need to know about

  • [].index(element)
  • hash (hashing and hash collisions)
  • how to hash a numpy array

Note:

  • hash collisions can lead to wrong decisions, you need to decide yourself about the probability and impact
  • only the first occurence of the array is removed, should there be several with the same data.

import numpy as np


def remove(array, arrays):
    """
    Remove the `array` from the `list` of `arrays`
    Operates inplace on the `list` of `arrays` given

    :param array: `np.ndarray`
    :param arrays: `list:np.ndarray`
    :return: None
    """

    assert isinstance(arrays, list), f'Expected a list, got {type(arrays)} instead'
    assert isinstance(array, np.ndarray), f'Expected a numpy.ndarray, got {type(array)} instead'
    for a in arrays:
        assert isinstance(a, np.ndarray), f'Expected a numpy.ndarray instances in arrays, found {type(a)} instead'

    # Numpy ndarrays are not hashable by default, so we create
    # our own hashing algorithm. The following will do the job ...
    def _hash(a):
        return hash(a.tobytes())

    try:
        # We create a list of hashes and search for the index
        # of the hash of the array we want to remove.
        index = [_hash(a) for a in arrays].index(_hash(array))
    except ValueError as e:
        # It might be, that the array is not in the list at all.
        print(f'Array not in list. Leaving input unchanged.')
    else:
        # Only in the case of no exception we pop the array
        # with the same index/position from the original
        # arrays list
        arrays.pop(index)


if __name__ == '__main__':

    # Let's start with the following arrays as given in the question
    arrays = [np.array([1, 1, 1]), np.array([2, 2, 2]), np.array([3, 3, 3])]
    print(arrays)

    # And remove this array instance from it.
    # Note, this is a new instance, so the object id is
    # different. Structure and values coincide.
    remove(np.array([2, 2, 2]), arrays)

    # Let's check the result
    print(arrays)

    # Let's check, whether our edge case handling works.
    remove(np.array([1, 2, 3]), arrays)

Comments

1

Use Base Functionalities from Python and Numpy

You can run the following one-liner to get the result ...

import numpy as np

# Your inputs ...
l = [np.array([1, 1, 1]), np.array([2, 2, 2]), np.array([3, 3, 3])]
array_to_remove = np.array([2, 2, 2])

# My result ...
result = [a for a, skip in zip(l, [np.allclose(a, array_to_remove) for a in l]) if not skip]

print(result)

... or copy paste the following in a script and experiment a bit.

You need

  • numpy.allclose to compare numpy arrays up to floating point representation error
  • zip
  • list comprehension
  • the concept of a mask

Note, ...

  • this solution returns a list without all occurencies of the array we searched for
  • the returned list has references to the np.ndarray instances also referred from the initial list. There are no copies!

import numpy as np


def remove(array, arrays):
    """
    Remove the `array` from the `list` of `arrays`
    Returns list with remaining arrays by keeping the order.

    :param array: `np.ndarray`
    :param arrays: `list:np.ndarray`
    :return: `list:np.ndarray`
    """

    assert isinstance(arrays, list), f'Expected a list, got {type(arrays)} instead'
    assert isinstance(array, np.ndarray), f'Expected a numpy.ndarray, got {type(array)} instead'
    for a in arrays:
        assert isinstance(a, np.ndarray), f'Expected a numpy.ndarray instances in arrays, found {type(a)} instead'

    # We use np.allclose for comparing arrays, this will work even if there are
    # floating point representation differences.
    # The idea is to create a boolean mask of the same lenght as the input arrays.
    # Then we loop over the arrays-elements and the mask-elements and skip the
    # flagged elements
    mask = [np.allclose(a, array) for a in arrays]
    return [a for a, skip in zip(arrays, mask) if not skip]


if __name__ == '__main__':

    # Let's start with the following arrays as given in the question
    arrays = [np.array([1, 1, 1]), np.array([2, 2, 2]), np.array([3, 3, 3])]
    print(arrays)

    # And remove this array instance from it.
    # Note, this is a new instance, so the object id is
    # different. Structure and values coincide.
    _arrays = remove(np.array([2, 2, 2]), arrays)

    # Let's check the result
    print(_arrays)

    # Let's check, whether our edge case handling works.
    print(arrays)
    _arrays = remove(np.array([1, 2, 3]), arrays)
    print(_arrays)

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.