测试 Numpy 操作
Testing Numpy operations
每当我需要测试一个中等复杂的 numpy 表达式时,比如说,
c = np.multiply.outer(a, b)
d = np.einsum('kjij->ijk', c)
我最终做了一些黑客攻击,例如设置 a
和 b
因此
a = np.arange(9).reshape(3,3)
b = a / 10
这样我就可以跟踪 d
包含的内容。
这很丑而且不太方便。理想情况下,我可以执行以下操作:
a = np.array(list("abcdefghi")).reshape(3,3)
b = np.array(list("ABCDEFGHI")).reshape(3,3)
c = np.add.outer(a, b)
d = np.einsum('kjij->ijk', c)
这样,例如,d[0, 1, 2]
可以被视为对应于 'hB',这比 .7 更清晰(这是对 a
和 [= 的其他赋值14=] 会给出。)这是做不到的,因为 ufunc add
不接受字符。
总而言之,一旦我开始链接一些转换(外积、einsum、广播或切片等),我就会迷失方向,需要自己看看我的转换实际上在做什么。那是我需要 运行 几个例子的时候,这就是我目前这样做的方法让我觉得不理想的地方。有没有标准或更好的方法来做到这一点?
In [454]: a = np.array(list("abcdefghi")).reshape(3,3)
...: b = np.array(list("ABCDEFGHI")).reshape(3,3)
np.add
无法使用,因为尚未为字符串 dtype
:
定义 add
In [455]: c = np.add.outer(a,b)
....
TypeError: ufunc 'add' did not contain a loop with signature matching types dtype('<U1') dtype('<U1') dtype('<U1')
但是 np.char
具有将 Python string
方法应用于 ndarray
元素的函数(这些并不快,只是方便):
Signature: np.char.add(x1, x2)
Docstring:
Return element-wise string concatenation for two arrays of str or unicode.
使用广播我可以执行你的 outer
字符串连接:
In [457]: c = np.char.add(a[:,:,None,None], b[None,None,:,:])
In [458]: c.shape
Out[458]: (3, 3, 3, 3)
In [459]: c
Out[459]:
array([[[['aA', 'aB', 'aC'],
['aD', 'aE', 'aF'],
['aG', 'aH', 'aI']],
[['bA', 'bB', 'bC'],
['bD', 'bE', 'bF'],
['bG', 'bH', 'bI']],
....
[['iA', 'iB', 'iC'],
['iD', 'iE', 'iF'],
['iG', 'iH', 'iI']]]], dtype='<U2')
我怀疑 einsum
能否处理这个数组,因为通常 einsum
用于 np.dot
之类的乘积计算。但是有了这个索引,它只是选择一条对角线并重新排列轴,所以它确实有效:
In [460]: np.einsum('kjij->ijk', c)
Out[460]:
array([[['aA', 'dA', 'gA'],
['bB', 'eB', 'hB'],
['cC', 'fC', 'iC']],
[['aD', 'dD', 'gD'],
['bE', 'eE', 'hE'],
['cF', 'fF', 'iF']],
[['aG', 'dG', 'gG'],
['bH', 'eH', 'hH'],
['cI', 'fI', 'iI']]], dtype='<U2')
来自数字测试用例的d
:
array([[[0. , 3. , 6. ],
[1.1, 4.1, 7.1],
[2.2, 5.2, 8.2]],
[[0.3, 3.3, 6.3],
[1.4, 4.4, 7.4],
[2.5, 5.5, 8.5]],
[[0.6, 3.6, 6.6],
[1.7, 4.7, 7.7],
[2.8, 5.8, 8.8]]])
这些数值的模式与字符串一样清晰。
我喜欢尽可能使用不同的数组形状,因为它可以更轻松地跟踪更改的维度:
In [496]: a3 = np.arange(1,13).reshape(4,3)
...: b3 = np.arange(1,7).reshape(2,3) / 10
In [497]: c3 = np.add.outer(a3,b3)
In [498]: d3 = np.einsum('kjij->ijk', c3)
In [499]: c3.shape
Out[499]: (4, 3, 2, 3)
In [500]: d3.shape
Out[500]: (2, 3, 4)
In [501]: d3
Out[501]:
array([[[ 1.1, 4.1, 7.1, 10.1],
[ 2.2, 5.2, 8.2, 11.2],
[ 3.3, 6.3, 9.3, 12.3]],
[[ 1.4, 4.4, 7.4, 10.4],
[ 2.5, 5.5, 8.5, 11.5],
[ 3.6, 6.6, 9.6, 12.6]]])
例如,如果我尝试 ''kjik->ijk'.
会引发错误
使用数值我可以用 einsum
:
执行 multiply.outer
In [502]: c4 = np.multiply.outer(a3,b3)
In [503]: np.allclose(c4,np.einsum('ij,kl',a3,b3))
Out[503]: True
In [504]: d4 = np.einsum('kjij->ijk', c4)
In [505]: np.allclose(d4,np.einsum('kj,ij->ijk',a3,b3))
Out[505]: True
In [506]: d4
Out[506]:
array([[[0.1, 0.4, 0.7, 1. ],
[0.4, 1. , 1.6, 2.2],
[0.9, 1.8, 2.7, 3.6]],
[[0.4, 1.6, 2.8, 4. ],
[1. , 2.5, 4. , 5.5],
[1.8, 3.6, 5.4, 7.2]]])
与 d
显示相比,'kj,ij->ijk'
让我更了解正在发生的事情。
另一种表达方式:
(4,3) + (2,3) => (2,3,4)
每当我需要测试一个中等复杂的 numpy 表达式时,比如说,
c = np.multiply.outer(a, b)
d = np.einsum('kjij->ijk', c)
我最终做了一些黑客攻击,例如设置 a
和 b
因此
a = np.arange(9).reshape(3,3)
b = a / 10
这样我就可以跟踪 d
包含的内容。
这很丑而且不太方便。理想情况下,我可以执行以下操作:
a = np.array(list("abcdefghi")).reshape(3,3)
b = np.array(list("ABCDEFGHI")).reshape(3,3)
c = np.add.outer(a, b)
d = np.einsum('kjij->ijk', c)
这样,例如,d[0, 1, 2]
可以被视为对应于 'hB',这比 .7 更清晰(这是对 a
和 [= 的其他赋值14=] 会给出。)这是做不到的,因为 ufunc add
不接受字符。
总而言之,一旦我开始链接一些转换(外积、einsum、广播或切片等),我就会迷失方向,需要自己看看我的转换实际上在做什么。那是我需要 运行 几个例子的时候,这就是我目前这样做的方法让我觉得不理想的地方。有没有标准或更好的方法来做到这一点?
In [454]: a = np.array(list("abcdefghi")).reshape(3,3)
...: b = np.array(list("ABCDEFGHI")).reshape(3,3)
np.add
无法使用,因为尚未为字符串 dtype
:
add
In [455]: c = np.add.outer(a,b)
....
TypeError: ufunc 'add' did not contain a loop with signature matching types dtype('<U1') dtype('<U1') dtype('<U1')
但是 np.char
具有将 Python string
方法应用于 ndarray
元素的函数(这些并不快,只是方便):
Signature: np.char.add(x1, x2)
Docstring:
Return element-wise string concatenation for two arrays of str or unicode.
使用广播我可以执行你的 outer
字符串连接:
In [457]: c = np.char.add(a[:,:,None,None], b[None,None,:,:])
In [458]: c.shape
Out[458]: (3, 3, 3, 3)
In [459]: c
Out[459]:
array([[[['aA', 'aB', 'aC'],
['aD', 'aE', 'aF'],
['aG', 'aH', 'aI']],
[['bA', 'bB', 'bC'],
['bD', 'bE', 'bF'],
['bG', 'bH', 'bI']],
....
[['iA', 'iB', 'iC'],
['iD', 'iE', 'iF'],
['iG', 'iH', 'iI']]]], dtype='<U2')
我怀疑 einsum
能否处理这个数组,因为通常 einsum
用于 np.dot
之类的乘积计算。但是有了这个索引,它只是选择一条对角线并重新排列轴,所以它确实有效:
In [460]: np.einsum('kjij->ijk', c)
Out[460]:
array([[['aA', 'dA', 'gA'],
['bB', 'eB', 'hB'],
['cC', 'fC', 'iC']],
[['aD', 'dD', 'gD'],
['bE', 'eE', 'hE'],
['cF', 'fF', 'iF']],
[['aG', 'dG', 'gG'],
['bH', 'eH', 'hH'],
['cI', 'fI', 'iI']]], dtype='<U2')
来自数字测试用例的d
:
array([[[0. , 3. , 6. ],
[1.1, 4.1, 7.1],
[2.2, 5.2, 8.2]],
[[0.3, 3.3, 6.3],
[1.4, 4.4, 7.4],
[2.5, 5.5, 8.5]],
[[0.6, 3.6, 6.6],
[1.7, 4.7, 7.7],
[2.8, 5.8, 8.8]]])
这些数值的模式与字符串一样清晰。
我喜欢尽可能使用不同的数组形状,因为它可以更轻松地跟踪更改的维度:
In [496]: a3 = np.arange(1,13).reshape(4,3)
...: b3 = np.arange(1,7).reshape(2,3) / 10
In [497]: c3 = np.add.outer(a3,b3)
In [498]: d3 = np.einsum('kjij->ijk', c3)
In [499]: c3.shape
Out[499]: (4, 3, 2, 3)
In [500]: d3.shape
Out[500]: (2, 3, 4)
In [501]: d3
Out[501]:
array([[[ 1.1, 4.1, 7.1, 10.1],
[ 2.2, 5.2, 8.2, 11.2],
[ 3.3, 6.3, 9.3, 12.3]],
[[ 1.4, 4.4, 7.4, 10.4],
[ 2.5, 5.5, 8.5, 11.5],
[ 3.6, 6.6, 9.6, 12.6]]])
例如,如果我尝试 ''kjik->ijk'.
会引发错误使用数值我可以用 einsum
:
multiply.outer
In [502]: c4 = np.multiply.outer(a3,b3)
In [503]: np.allclose(c4,np.einsum('ij,kl',a3,b3))
Out[503]: True
In [504]: d4 = np.einsum('kjij->ijk', c4)
In [505]: np.allclose(d4,np.einsum('kj,ij->ijk',a3,b3))
Out[505]: True
In [506]: d4
Out[506]:
array([[[0.1, 0.4, 0.7, 1. ],
[0.4, 1. , 1.6, 2.2],
[0.9, 1.8, 2.7, 3.6]],
[[0.4, 1.6, 2.8, 4. ],
[1. , 2.5, 4. , 5.5],
[1.8, 3.6, 5.4, 7.2]]])
与 d
显示相比,'kj,ij->ijk'
让我更了解正在发生的事情。
另一种表达方式:
(4,3) + (2,3) => (2,3,4)