Есть ли способ заставить функцию Python all () работать с многомерными массивами? - PullRequest
0 голосов
/ 02 июня 2019

Я пытаюсь реализовать универсальный и гибкий метод __eq__ для базового класса, который будет работать с максимально возможным количеством типов объектов, включая итерируемые и пустые массивы.

Вот что у меня есть:

class Environment:

    def __init__(self, state):
        self.state = state

    def __eq__(self, other):
        """Compare two environments based on their states.
        """
        if isinstance(other, self.__class__):
            try:
                return all(self.state == other.state)
            except TypeError:
                return self.state == other.state
        return False

Это прекрасно работает для большинства типов объектов, включая одномерные массивы:

s = 'abcdef'
e1 = Environment(s)
e2 = Environment(s)

e1 == e2  # True

s = [[1, 2, 3], [4, 5, 6]]
e1 = Environment(s)
e2 = Environment(s)

e1 == e2  # True

s = np.array(range(6))
e1 = Environment(s)
e2 = Environment(s)

e1 == e2  # True

Проблема в том, что он возвращает ошибку ValueError, когда self.state - это многомерный массив, который является многомерным.

s = np.array(range(6)).reshape((2, 3))
e1 = Environment(s)
e2 = Environment(s)

e1 == e2

Выдает:

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

Очевидно, я мог бы проверить isinstance(other, np.ndarray), а затем сделать (return self.state == other.state).all(), но подумал, что может быть более общий способ обработки всех итераций, коллекций и массивов любого типа с помощью одного оператора.

Я также немного сбит с толку, почему all() не выполняет итерацию по всем элементам массива, как array.all(). Есть ли способ вызвать np.nditer и сделать это, может быть?

Ответы [ 2 ]

0 голосов
/ 02 июня 2019

Это не краткое решение, на которое я надеялся, и, вероятно, неэффективное, но я думаю, что оно работает с любой n-мерной итерацией:

def nd_true(nd_object):
    try:
        iterator = iter(nd_object)
    except TypeError:
        return nd_object
    else:
        return all([nd_true(x) for x in iterator])

class Environment:

    def __init__(self, state):
        self.state = state

    def __eq__(self, other):
        """Compare two environments based on their states.
        """
        if isinstance(other, self.__class__):
            return nd_true(self.state == other.state)
        return False

# Tests    
s = 'abcdef'
e1 = Environment(s)
e2 = Environment(s)

e1 == e2  # True

s = [[1, 2, 3], [4, 5, 6]]
e1 = Environment(s)
e2 = Environment(s)

e1 == e2  # True

s = np.array(range(6))
e1 = Environment(s)
e2 = Environment(s)

e1 == e2  # True

s = np.array(range(6)).reshape((2, 3))
e1 = Environment(s)
e2 = Environment(s)

e1 == e2  # True

s = np.array(range(27)).reshape((3, 3, 3))
e1 = Environment(s)
e2 = Environment(s)

e1 == e2  # True
0 голосов
/ 02 июня 2019
Signature: all(iterable, /)
Docstring:
Return True if bool(x) is True for all values x in the iterable.

Для массива 1d:

In [200]: x=np.ones(3)                                                               
In [201]: x                                                                          
Out[201]: array([1., 1., 1.])
In [202]: y = x==x                                                                   
In [203]: y          # 1d array of booleans                                                                      
Out[203]: array([ True,  True,  True])
In [204]: bool(y[0])                                                                 
Out[204]: True
In [205]: all(y)                                                                     
Out[205]: True

Для массива 2d:

In [206]: x=np.ones((2,3))                                                           
In [207]: x                                                                          
Out[207]: 
array([[1., 1., 1.],
       [1., 1., 1.]])
In [208]: y = x==x                                                                   
In [209]: y                                                                          
Out[209]: 
array([[ True,  True,  True],
       [ True,  True,  True]])
In [210]: y[0]                                                                       
Out[210]: array([ True,  True,  True])
In [211]: bool(y[0])                                                                 
---------------------------------------------------------------------------
ValueError                                Traceback (most recent call last)
<ipython-input-211-d0ce0868392c> in <module>
----> 1 bool(y[0])

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

Но для другого массива 2d:

In [212]: x=np.ones((3,1))                                                           
In [213]: y = x==x                                                                   
In [214]: y                                                                          
Out[214]: 
array([[ True],
       [ True],
       [ True]])
In [215]: y[0]                                                                       
Out[215]: array([ True])
In [216]: bool(y[0])                                                                 
Out[216]: True
In [217]: all(y)                                                                     
Out[217]: True

Итерация на массиве NumPy происходит по первому измерению.[i for i in x]

Эта неоднозначность ValueError возникает каждый раз, когда многозначный логический массив используется в контексте, который ожидает скалярное логическое значение.Выражения if и or/and являются общими.

In [223]: x=np.ones((2,3))                                                           
In [224]: y = x==x                                                                   
In [225]: np.all(y)                                                                  
Out[225]: True

np.all отличается от Python all тем, что он «знает» о размерах.В этом случае ravel обрабатывает массив как 1d:

По умолчанию (axis = None) выполняется логическое И для всех измерений входного массива.,

...