ValueError:更改为较大的dtype时,其大小必须是数组最后一个轴的总大小(以字节为单位)的除数

时间:2019-03-06 13:27:58

标签: python python-3.x numpy

在使用第三方提供的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),因此您希望结构化的dtypeX.shape[1]以适合最后一个轴(X.shape[1])。

并非所有数据集都会发生该异常,那么是什么导致numpy为 some 数组而不是其他数组抛出此异常?我什至看不到哪个.py numpy源代码引发此错误。

我发现很难为此生成MCVE,但是我已经将其缩小到colab notebook了,仍然可以在此处发布。

这里X应该是iris数据集的子集,该数据集是我从scikit学习中获得的。

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

4 个答案:

答案 0 :(得分:1)

您正在尝试使用不兼容的内存布局在数组上创建视图,其中输出dtype的{​​{3}}并不完全适合内存中用于覆盖内存的字节数源数组“最后”轴的全长。如果您只是直接在数组 上设置.dtype属性,而不仅是ndarray.view()(这会用{{创建一个新的ndarray 1}}设置在该新对象上。

此处的“最后”轴是“最内”的尺寸,根据内存布局;对于dtype的C阶数组,对于shape[-1]的Fortran阶数组。该尺寸大小乘以原始shape[0]的大小必须被新的dtype.itemsize整除,否则您将无法“遍历”内部存储器结构。

例如,对于形状为dtype.itemsize(4, 3, 5)为8的C阶(行优先级)数组,“最后”轴占用5 * 8 == 40个字节内存,因此您可以使用大小为10、20和40的较大dtypes在此视图上创建一个视图。相同的数组但是按 Fortran 顺序(列大顺序)使用4 * 8 = = 32字节的内存,将您的选择限制为仅大小16和32的较大dtype。

如果dtype.itemsize失败,则X.view([('', X.dtype)] * X.shape[1])的维数不只是2,,这是使用Fortran排序的数组。您可以使用X.shape来校正第一个,也可以通过查看itemsize来检查纬度。将它们组合成一个表达式,如下所示:

X.shape[-1]

但是,如ndarray.flags['F_CONTIGUOUS']警告:

  

通常应避免在由切片,转置,fortran顺序等定义的数组上避免更改X_rows = X.view([('', X.dtype)] * X.shape[0 if X.flags['F_CONTIGUOUS'] else -1]) 大小(每个条目的字节数)的视图。[。]

当您尝试更改Fortran顺序数组的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]

不赞成在Fortran顺序数组上更改shape[-1]的事实也可以解释该异常对“最后一个轴”的引用,这在C顺序数组方面是很自然的,但感觉与应用于Fortran顺序数组时直观。

  

我什至看不到哪个.py numpy源代码引发此错误。

Numpy主要用C语言编写(带有Fortran 77的破折号),因此您需要深入研究已编译组件的源代码。在ndarray.view() documentation中引发错误,当dtype descriptor setter function调用dtype函数以设置PyObject_SetAttrString()属性时,从{{3 }}。

根据源代码,不仅不建议更改Fortran顺序数组的dtype,而且完全不支持非连续数组的视图(这意味着如果同时dtypeX.flags['C_CONTIGUOUS']X.flags['F_CONTIGUOUS'],则根本无法更改False

答案 1 :(得分:1)

因此,尝试重现您的情况:

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.

就这样-警告和错误,如笔记本中所示。

答案 2 :(得分:0)

不是答案,而只是对@martijn答案的评论。对于大型数据集,在类似的转换为视图的操作中遇到了此错误。似乎每当numpy决定创建F订单数组时,都会出现错误。我正在使用的数据集是非常统一的,不确定为什么numpy有时决定创建另一个F阶和C阶数组。希望有人可以解释。
无论如何,我的解决方法是将F订单转换为C订单。

    if arr2.flags['F_CONTIGUOUS']:  # ‘C_CONTIGUOUS’ (‘C’) - ensure a C-contiguous array
        arr2 = np.require(arr1, requirements= ["C"])
        logging.critical("Switching from Fortran to Cstyle ")

答案 3 :(得分:-1)

请注意:

M=np.ascontiguousarray(X, dtype=np.int64) 
# **int64** is type of my array. 
# please be careful about the right type of your array!
m_rows = M.view([('', M.dtype)] * M.shape[1])

使用这种方法,我不再出现错误。