在使用第三方提供的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为 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
答案 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,而且完全不支持非连续数组的视图(这意味着如果同时dtype
和X.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])
使用这种方法,我不再出现错误。