Проверка на однородность многомерного списка - PullRequest
0 голосов
/ 05 ноября 2018

После моего другого вопроса Я был удивлен, узнав, что Numpy довольно слабо относится к определению объектов типа array_. По сути, np.array(1) - это действительный numy ndarray формы () и размера 0! np.array([[1,2],[3]]) также является допустимым массивом формы (2,) и размером 1. В основном np.array копает столько измерений, сколько возможно, пока не достигнет неоднородности или нулевых значений. Эта реализация может быть довольно быстрой, но не обязательно безопасной. На самом деле это очень подвержено ошибкам. если кто-то забывает один элемент из списка ввода, функция не возвращает ошибок, что приводит к другим наиболее вероятным ошибкам в коде.

Я думал о том, можно ли написать функцию checkArr для проверки однородности и однородности многомерного списка с наименьшими возможными издержками. Изучив пару других SO-сообщений, я получил рекурсивное решение:

def checkArr(A):
    assert isinstance(A, (list,tuple,range)), 
             "input must be iterable (list, tuple, range)"
    assert all(isinstance(a, type(A[0])) for a in A[1:]), 
             "elements of the input must of a the same type, input must be homogeneous"
    if isinstance(A[0], (list,tuple,range)):
        assert all(len(a)==len(A[0]) for a in A[1:]),
             "elements of the input must of a the same size, input must be uniform"
        [checkArr(a) for a in A]

Теперь мой вопрос: возможно ли это самое быстрое решение или возможны более производительные / Pythonic реализации?

Ответы [ 2 ]

0 голосов
/ 05 ноября 2018

Питон говорит, что проще просить прощения, чем разрешения. Так что может быть меньше накладных расходов, если вы просто позвоните np.array, а затем проверите для object dtype.

Еще одна вещь, на которую нужно обратить внимание, это когда выдается ошибка. Например:

In [273]: np.array((np.zeros((2,3)), np.ones((2,4))))
---------------------------------------------------------------------------
ValueError                                Traceback (most recent call last)
<ipython-input-273-70f6273e3371> in <module>()
----> 1 np.array((np.zeros((2,3)), np.ones((2,4))))

ValueError: could not broadcast input array from shape (2,3) into shape (2)

Если неоднородность находится в первом измерении, она создает массив dtype объекта, например, np.array((np.zeros((2,3)), np.ones((1,4)))). Но когда он находится на более глубоком уровне, кажется, что он выделяет массив результатов, а затем возникают проблемы при копировании в него одного или нескольких массивов компонентов. Это сложный случай для диагностики.

Или рассмотрим:

In [277]: np.array([[1,2,3],[4,5,'6']])
Out[277]: 
array([['1', '2', '3'],
       ['4', '5', '6']], dtype='<U21')

Последний элемент во вложенном списке вызывает строку dtype. И если этот последний элемент является каким-то другим объектом PYthon, мы можем быть объектом dtype:

In [279]: np.array([[1,2,3],[4,5,{}]])
Out[279]: 
array([[1, 2, 3],
       [4, 5, {}]], dtype=object)

Но если объект является списком, мы получаем вариант с ошибкой трансляции:

In [280]: np.array([[1,2,3],[4,5,['6']]])
ValueError: setting an array element with a sequence

Но если вы хотите сначала проверить, np.stack может быть хорошей моделью. При оси = 0 он ведет себя так же, как np.array, если заданы массивы или списки.

0 голосов
/ 05 ноября 2018

Указание dtype аргумента при создании массива позволяет избежать непреднамеренного создания массивов объектов из зубчатых матриц без написания дополнительного кода.

np.array([[1, 2], [3, 4]], dtype=int)  # okay
np.array([[1, 2], [3]], dtype=int)     # ValueError
np.array([[1, "b"]], dtype=int)        # ValueError

(Что касается последнего, np.array([1, "b"]) будет молча преобразовывать «1» в строку, если тип данных не был установлен.)

...