Python 预测ufunc输出的内存布局
使用numpyPython 预测ufunc输出的内存布局,python,numpy,memory-layout,numpy-ufunc,Python,Numpy,Memory Layout,Numpy Ufunc,使用numpyndarrays大多数时候,我们不必担心内存布局,因为结果并不依赖于它 除非他们这样做。例如,考虑这种稍微过度设计的方式设置3x2矩阵的斜率。 >>> a = np.zeros((3,2)) >>> a.reshape(2,3)[:,0] = 1 >>> a array([[1., 0.], [0., 1.], [0., 0.]]) 只要我们控制a的内存布局就可以了。但如果我们不这样做,这是一个错误
ndarray
s大多数时候,我们不必担心内存布局,因为结果并不依赖于它
除非他们这样做。例如,考虑这种稍微过度设计的方式设置3x2矩阵的斜率。
>>> a = np.zeros((3,2))
>>> a.reshape(2,3)[:,0] = 1
>>> a
array([[1., 0.],
[0., 1.],
[0., 0.]])
只要我们控制a
的内存布局就可以了。但如果我们不这样做,这是一个错误,更糟糕的是,这是一个令人讨厌的沉默的错误:
>>> a = np.zeros((3,2),order='F')
>>> a.reshape(2,3)[:,0] = 1
>>> a
array([[0., 0.],
[0., 0.],
[0., 0.]])
这足以说明内存布局不仅仅是一个实现细节
要掌握阵列布局,人们可能会合理地问的第一件事是,新阵列是什么样子的?工厂为空
,为一
,为零
,标识
等。默认情况下返回C-连续布局
但是,这条规则并不适用于numpy分配的每个新数组。例如:
>>> a = np.arange(8).reshape(2,2,2).transpose(1,0,2)
>>> aa = a*a
产品aa
是由ufuncnp.multiply
分配的新数组。它是C-连续的吗?否:
>>> aa.strides
(16, 32, 8)
我的猜测是,这是一个优化的结果,该优化认识到该操作可以在平面线性阵列上完成,这将解释为什么输出与输入具有相同的内存布局
事实上,这甚至是有用的,不像下面的无意义函数。它展示了一种简便的习惯用法,可以在保持索引简单的同时实现axis参数
>>> def symmetrize_along_axis(a,axis=0):
... aux = a.swapaxes(0,axis)
... out = aux + aux[::-1]
... return out.swapaxes(0,axis)
稍微令人惊讶但显然令人满意的是,只要输入是连续的,就会产生连续的输出
>>> a = np.arange(8).reshape(2,2,2)
>>> symmetrize_along_axis(a,1).flags.contiguous
True
这足以说明了解UFUNC返回的布局是非常有用的。因此,我的问题是:
给定ufunc参数的布局,是否有任何关于输出布局的规则或保证?在a=np.zeros((3,2),order='F')
情况下,a.reformate(2,3)
创建副本,而不是视图。这就是分配失败的原因,而不是内存布局本身
请看相同形状的阵列:
In [123]: a = np.arange(6).reshape(3,2)
In [124]: a
Out[124]:
array([[0, 1],
[2, 3],
[4, 5]])
In [125]: a.reshape(2,3)
Out[125]:
array([[0, 1, 2],
[3, 4, 5]])
In [127]: a.reshape(2,3)[:,0]
Out[127]: array([0, 3])
在[125]中,值仍按顺序C流动
和一个F阶数组:
In [128]: b = np.arange(6).reshape(3,2, order='F')
In [129]: b
Out[129]:
array([[0, 3], # values flow in order F
[1, 4],
[2, 5]])
In [130]: b.reshape(2,3)
Out[130]:
array([[0, 3, 1], # values are jumbled
[4, 2, 5]])
In [131]: b.reshape(2,3)[:,0]
Out[131]: array([0, 4])
如果我在形状中保持顺序F:
In [132]: b.reshape(2,3, order='F')
Out[132]:
array([[0, 2, 4], # values still flow in order F
[1, 3, 5]])
In [133]: b.reshape(2,3, order='F')[:,0]
Out[133]: array([0, 1])
与任务确认:
In [135]: a.reshape(2,3)[:,0]=10
In [136]: a
Out[136]:
array([[10, 1],
[ 2, 10],
[ 4, 5]])
In [137]: b.reshape(2,3)[:,0]=10
In [138]: b
Out[138]:
array([[0, 3],
[1, 4],
[2, 5]])
非转让:
In [135]: a.reshape(2,3)[:,0]=10
In [136]: a
Out[136]:
array([[10, 1],
[ 2, 10],
[ 4, 5]])
In [137]: b.reshape(2,3)[:,0]=10
In [138]: b
Out[138]:
array([[0, 3],
[1, 4],
[2, 5]])
但在这里,任务是有效的:
In [139]: b.reshape(2,3, order='F')[:,0]=10
In [140]: b
Out[140]:
array([[10, 3],
[10, 4],
[ 2, 5]])
或者我们可以使用orderA
来维持秩序:
In [143]: b.reshape(2,3, order='A')[:,0]
Out[143]: array([10, 10])
In [144]: b.reshape(2,3, order='A')[:,0] = 20
In [145]: b
Out[145]:
array([[20, 3],
[20, 4],
[ 2, 5]])
ufunc命令
我怀疑ufunc
主要是用nditer
(C版本)实现的,所以我检查了np.nditer`docs-可以在几个地方指定顺序。本教程演示了迭代的顺序效应
我没有看到ufunc
的order
文档,但是kwargs
接受它
In [171]: c = np.arange(8).reshape(2,2,2)
In [172]: d = c.transpose(1,0,2)
In [173]: d.strides
Out[173]: (16, 32, 8)
In [174]: np.multiply(d,d,order='K').strides
Out[174]: (16, 32, 8)
In [175]: np.multiply(d,d,order='C').strides
Out[175]: (32, 16, 8)
In [176]: np.multiply(d,d,order='F').strides
Out[176]: (8, 16, 32)
“…创建副本,而不是视图。这就是分配失败的原因,…”以及它创建副本的原因?因为内存布局不允许查看。@Panzer在“注释”部分对此进行了解释。但不确定它是否回答了你的问题。