我有一个矩阵
A = np.array([[0.2, 0.4, 0.6],
[0.5, 0.5, 0.5],
[0.6, 0.4, 0.2]])
我想要一个新的矩阵,其中第i行和第j列中的条目的值是第i行的所有条目的乘积,除了第j列中该行的单元格。
array([[ 0.24, 0.12, 0.08],
[ 0.25, 0.25, 0.25],
[ 0.08, 0.12, 0.24]])
我首先遇到的解决方案是
np.repeat(np.prod(A, 1, keepdims = True), 3, axis = 1) / A
但只有当条目的值为零时,这才有效。
有什么想法?谢谢!
编辑:我已经开发了
B = np.zeros((3, 3))
for i in range(3):
for j in range(3):
B[i, j] = np.prod(i, A[[x for x in range(3) if x != j]])
但肯定有更优雅的方法来实现这一点,它使用numpy的高效C后端而不是低效的python循环?
答案 0 :(得分:4)
如果你愿意忍受一个循环:
B = np.empty_like(A)
for col in range(A.shape[1]):
B[:,col] = np.prod(np.delete(A, col, 1), 1)
计算你需要的东西,一次一列。它没有理论上那么有效,因为np.delete()创建了一个副本;如果您非常关心内存分配,请改用掩码:
B = np.empty_like(A)
mask = np.ones(A.shape[1], dtype=bool)
for col in range(A.shape[1]):
mask[col] = False
B[:,col] = np.prod(A[:,mask], 1)
mask[col] = True
答案 1 :(得分:3)
使用repeat
对您的解决方案进行修改,使用[:,None]
。
np.prod(A,axis=1)[:,None]/A
我对处理0s
的第一次尝试是:
In [21]: B
array([[ 0.2, 0.4, 0.6],
[ 0. , 0.5, 0.5],
[ 0.6, 0.4, 0.2]])
In [22]: np.prod(B,axis=1)[:,None]/(B+np.where(B==0,1,0))
array([[ 0.24, 0.12, 0.08],
[ 0. , 0. , 0. ],
[ 0.08, 0.12, 0.24]])
但正如评论指出的那样; [0,1]单元格应为0.25。
这可以解决这个问题,但是当连续存在多个0时会出现问题。
In [30]: I=B==0
In [31]: B1=B+np.where(I,1,0)
In [32]: B2=np.prod(B1,axis=1)[:,None]/B1
In [33]: B3=np.prod(B,axis=1)[:,None]/B1
In [34]: np.where(I,B2,B3)
Out[34]:
array([[ 0.24, 0.12, 0.08],
[ 0.25, 0. , 0. ],
[ 0.08, 0.12, 0.24]])
In [55]: C
array([[ 0.2, 0.4, 0.6],
[ 0. , 0.5, 0. ],
[ 0.6, 0.4, 0.2]])
In [64]: np.where(I,sum1[:,None],sum[:,None])/C1
array([[ 0.24, 0.12, 0.08],
[ 0.5 , 0. , 0.5 ],
[ 0.08, 0.12, 0.24]])
Blaz Bratanic的epsilon
方法是最好的非迭代解决方案(到目前为止):
In [74]: np.prod(C+eps,axis=1)[:,None]/(C+eps)
迭代列的不同解决方案:
def paulj(A):
P = np.ones_like(A)
for i in range(1,A.shape[1]):
P *= np.roll(A, i, axis=1)
return P
In [130]: paulj(A)
array([[ 0.24, 0.12, 0.08],
[ 0.25, 0.25, 0.25],
[ 0.08, 0.12, 0.24]])
In [131]: paulj(B)
array([[ 0.24, 0.12, 0.08],
[ 0.25, 0. , 0. ],
[ 0.08, 0.12, 0.24]])
In [132]: paulj(C)
array([[ 0.24, 0.12, 0.08],
[ 0. , 0. , 0. ],
[ 0.08, 0.12, 0.24]])
我在大矩阵上尝试了一些时间
In [13]: A=np.random.randint(0,100,(1000,1000))*0.01
In [14]: timeit paulj(A)
1 loops, best of 3: 23.2 s per loop
In [15]: timeit blaz(A)
10 loops, best of 3: 80.7 ms per loop
In [16]: timeit zwinck1(A)
1 loops, best of 3: 15.3 s per loop
In [17]: timeit zwinck2(A)
1 loops, best of 3: 65.3 s per loop
epsilon近似可能是我们可以预期的最佳速度,但有一些舍入问题。必须迭代许多列会损害速度。我不确定为什么np.prod(A[:,mask], 1)
方法最慢。
eeclo https://stackoverflow.com/a/22441825/901925建议使用as_strided
。以下是我认为他的想法(改编自重叠的块问题,https://stackoverflow.com/a/8070716/901925)
def strided(A):
h,w = A.shape
A2 = np.hstack([A,A])
x,y = A2.strides
strides = (y,x,y)
shape = (w, h, w-1)
blocks = np.lib.stride_tricks.as_strided(A2[:,1:], shape=shape, strides=strides)
P = blocks.prod(2).T # faster to prod on last dim
# alt: shape = (w-1, h, w), and P=blocks.prod(0)
return P
(1000,1000)数组的时间相对于列迭代而言是相当大的改进,但仍然比epsilon
方法慢得多。
In [153]: timeit strided(A)
1 loops, best of 3: 2.51 s per loop
另一种索引方法虽然相对简单,但速度较慢,并且会更快地产生内存错误。
def foo(A):
h,w = A.shape
I = (np.arange(w)[:,None]+np.arange(1,w))
I1 = np.array(I)%w
P = A[:,I1].prod(2)
return P
答案 2 :(得分:3)
我在奔跑,所以我没有时间研究这个解决方案;但是id做的是在最后一个轴上创建一个连续的圆形视图,通过沿着最后一个轴将数组连接到自身,然后使用np.lib.index_tricks.as_strided选择合适的元素来获取np.prod 。没有python循环,没有数值近似。
编辑:你走了:
import numpy as np
A = np.array([[0.2, 0.4, 0.6],
[0.5, 0.5, 0.5],
[0.5, 0.0, 0.5],
[0.6, 0.4, 0.2]])
B = np.concatenate((A,A),axis=1)
C = np.lib.index_tricks.as_strided(
B,
A.shape +A.shape[1:],
B.strides+B.strides[1:])
D = np.prod(C[...,1:], axis=-1)
print D
注意:这种方法并不理想,因为它是O(n ^ 3)。请参阅我的其他发布解决方案,即O(n ^ 2)
答案 3 :(得分:2)
如果您愿意容忍小错误,可以使用您最初提出的解决方案。
A += 1e-10
np.around(np.repeat(np.prod(A, 1, keepdims = True), 3, axis = 1) / A, 9)
答案 4 :(得分:1)
这是一个没有python循环或数值近似的O(n ^ 2)方法:
def double_cumprod(A):
B = np.empty((A.shape[0],A.shape[1]+1),A.dtype)
B[:,0] = 1
B[:,1:] = A
L = np.cumprod(B, axis=1)
B[:,1:] = A[:,::-1]
R = np.cumprod(B, axis=1)[:,::-1]
return L[:,:-1] * R[:,1:]
注意:它似乎是数值近似方法的两倍慢,这符合预期。