ValueError: При переходе на dtype большего размера его размер должен быть делителем общего размера в байтах последней оси массива - PullRequest
4 голосов
/ 06 марта 2019

Я сталкиваюсь со следующим исключением при работе с сторонним набором данных numpy:

ValueError: When changing to a larger dtype, its size must be a divisor of the total size in bytes of the last axis of the array

При каких обстоятельствах numpy вызывает это?Мой код применяет представление к массиву NumPy, где я пытаюсь применить структурированный dtype, который соответствует числу элементов в строке.

Я вижу эту ошибку, когда оператор X.view([('', X.dtype)] * X.shape[1]) вызывается внутри функции f - но не при каждом вызове этой функции f:

ipdb> X.view([('', X.dtype)] * X.shape[1])
*** ValueError: When changing to a larger dtype, its size must be a divisor of the total size in bytes of the last axis of the array.

X всегда является массивом с двумя осями (len(X.shape) всегда равно 2),так что вы ожидаете, что структурированный dtype длиной X.shape[1] будет соответствовать последней оси (X.shape[1]).

Исключение не происходит для всех наборов данных, поэтому, что приводит к тому, что numpy выбрасывает это для некоторые массивы, но не другие?Я даже не вижу, какой исходный код .py выдает эту ошибку.

Мне трудно изготовить для этого MCVE, но я сократил это до ноутбука Colab , который все еще немного велик для размещения здесь.

Здесь X должен быть подмножеством набора данных iris, который я получил из scikit learn.

from sklearn.datasets import load_iris
X = load_iris().data

Мой код выглядит так:

def f(X):
    X_rows = X.view([('', X.dtype)] * X.shape[1])

def g(X):
    f(X)

def h(X):
    f(X)

# call the functions
g(X) # this runs without a problem
f(X) # this returns the error

Ответы [ 2 ]

2 голосов
/ 06 марта 2019

Вы пытаетесь создать представление в массиве с несовместимым макетом памяти, где вывод dtype имеет itemsize , который не полностью соответствует количеству байтов, необходимых в памяти для покрытия полная длина «последней» оси исходного массива. Исключение также будет применяться, если вы просто устанавливаете атрибут .dtype для массива напрямую , а не просто для ndarray.view() (который создает новый ndarray с dtype, установленным для этого нового объекта) .

Здесь «последняя» ось является «самым внутренним» измерением с точки зрения структуры памяти ; для массивов C-порядка это shape[-1], для массивов Фортрана это shape[0]. Этот размерный размер, умноженный на исходный dtype.itemsize, должен делиться на новый dtype.itemsize, иначе вы не сможете просто «пройтись» по структуре внутренней памяти.

Например, для массива C-порядка (основного порядка строк) с формой (4, 3, 5) и dtype.itemsize из 8, «последняя» ось занимает 5 * 8 == 40 байт памяти и т. Д. Вы можете создать представление для этого с большими dtypes размеров 10, 20 и 40. Тот же массив, но в порядке Fortran (порядок столбцов), однако, использует 4 * 8 == 32 байта памяти , ограничивая ваши варианты только большими dtypes размеров 16 и 32.

Если X.view([('', X.dtype)] * X.shape[1]) терпит неудачу, то либо X.shape имеет больше измерений, чем просто 2, или , это массив с использованием Fortran-упорядочения. Вы можете исправить первое с помощью X.shape[-1], и вы можете проверить последние, просмотрев ndarray.flags['F_CONTIGUOUS']. Объединение их в одно выражение, подобное следующему, должно работать:

X_rows = X.view([('', X.dtype)] * X.shape[0 if X.flags['F_CONTIGUOUS'] else -1])

Однако , поскольку документация ndarray.view() предупреждает:

Представления, изменяющие размер dtype (байт на запись), обычно следует избегать для массивов, определяемых срезами, транспонированием, упорядочением по фортрану и т. Д. [.]

Когда вы пытаетесь изменить dtype массива порядка Фортрана, появляется предупреждение:

DeprecationWarning: Changing the shape of an F-contiguous array by descriptor assignment is deprecated. To maintain the Fortran contiguity of a multidimensional Fortran array, use 'a.T.view(...).T' instead

так что было бы лучше транспонировать массив, создать ваше представление, а затем снова транспонировать полученное представление:

if X.flags['F_CONTIGUOUS']:
    X_rows = X.T.view([('', X.dtype)] * X.shape[0]).T

Вам все еще нужно придерживаться X.shape[0] здесь, это shape[-1] транспонированного массива.

Тот факт, что поддержка изменения dtype в массивах порядка Фортрана устарела, также может объяснить ссылку исключения на «последнюю ось», что совершенно естественно с точки зрения массивов C-порядка, но кажется нелогичным, когда применяется к массивам фортрановских порядков.

Я даже не вижу, какой исходный код .py выдает эту ошибку.

Numpy в основном написан на C (с чертой на Fortran 77), поэтому вам нужно покопаться в исходном коде скомпилированных компонентов. Ошибка генерируется в функции установки дескриптора дескриптора dtype , которая здесь вызывается, когда функция PyArray_View() вызывает функцию PyObject_SetAttrString() для установки атрибута dtype, когда она вызывается из ndarray.view() метода .

Согласно исходному коду, не только изменение dtype массивов порядка Фортрана устарело, но и представления несмежных массивов вообще не поддерживаются (это означает, что если X.flags['C_CONTIGUOUS'] и X.flags['F_CONTIGUOUS'] равны False тогда вы вообще не сможете изменить dtype.

1 голос
/ 06 марта 2019

Итак, пытаясь воспроизвести вашу ситуацию:

In [129]: from sklearn import datasets                                          
In [131]: iris = datasets.load_iris()                                           

In [132]: X = iris.data                                                         
In [133]: X.shape                                                               
Out[133]: (150, 4)
In [134]: X.dtype                                                               
Out[134]: dtype('float64')

In [135]: X_rows = X.view([('',X.dtype)] * X.shape[1])                          
In [136]: X_rows.shape                                                          
Out[136]: (150, 1)
In [137]: X_rows.dtype                                                          
Out[137]: dtype([('f0', '<f8'), ('f1', '<f8'), ('f2', '<f8'), ('f3', '<f8')])

Пока выглядит хорошо.


Я собирался сдаться, так как не хотел отлаживать вашу записную книжку. Но я, возможно, натолкнулся на возможную причину.

В начале вашего бега появляется предупреждение:

/usr/local/lib/python3.6/dist-packages/ipykernel_launcher.py:14: DeprecationWarning: Changing the shape of an F-contiguous array by descriptor assignment is deprecated. To maintain the Fortran contiguity of a multidimensional Fortran array, use 'a.T.view(...).T' instead

Вы запускаете эту функцию с пандами apply, которыми я не особо пользовался. Но я знаю, что панды предпочитают порядок F, поскольку он ориентирован на Серию. Так что же произойдет, если я переключу X на этот порядок?

In [148]: X1 = X.copy(order='F')                                                
In [149]: X_rows = X1[:0].view([('',X1.dtype)] * X1.shape[1])                   
In [150]: X_rows                                                                
Out[150]: 
array([], shape=(0, 1),
      dtype=[('f0', '<f8'), ('f1', '<f8'), ('f2', '<f8'), ('f3', '<f8')])
In [151]: X_rows = X1[:6].view([('',X1.dtype)] * X1.shape[1])                   
---------------------------------------------------------------------------
ValueError                                Traceback (most recent call last)
<ipython-input-151-f3272035dc14> in <module>
----> 1 X_rows = X1[:6].view([('',X1.dtype)] * X1.shape[1])

ValueError: To change to a dtype of a different size, the array must be C-contiguous

ОК, это не та же ошибка, но она показывает, что порядок может повлиять на этот тип view.


Но давайте возьмем массив из вашего комментария и дадим ему порядок F:

In [153]: a = np.array([[4.7, 3.2, 1.3, 0.2],[4.6, 3.1, 1.5, 0.2],[4.6, 3.4, 1.4
     ...: , 0.3],[4.4, 3. , 1.3, 0.2],[4.4, 3.2, 1.3, 0.2],[4.6, 3.2, 1.4, 0.2]]
     ...: , dtype='float64', order='F')                                         

In [154]: a.view([('', a.dtype)] * a.shape[1])                                  
/usr/local/bin/ipython3:1: DeprecationWarning: Changing the shape of an F-contiguous array by descriptor assignment is deprecated. To maintain the Fortran contiguity of a multidimensional Fortran array, use 'a.T.view(...).T' instead
  #!/usr/bin/python3
---------------------------------------------------------------------------
ValueError                                Traceback (most recent call last)
<ipython-input-154-b804730eb70b> in <module>
----> 1 a.view([('', a.dtype)] * a.shape[1])

ValueError: When changing to a larger dtype, its size must be a divisor of the total size in bytes of the last axis of the array.

Вот и все - предупреждение и ошибка, как показано в блокноте.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...