例如......
import numpy as np
from scipy.sparse import csr_matrix
X = csr_matrix([[1,2,3], [4,5,6], [7,8,9]])
Y = csr_matrix([[1,2,3], [4,5,6], [7,8,9], [11,12,13]])
# Print matrices
X.toarray()
[[1, 2, 3],
[4, 5, 6],
[7, 8, 9]]
Y.toarray()
[[ 1, 2, 3],
[ 4, 5, 6],
[ 7, 8, 9],
[11, 12, 13]]
我有一组索引(x,y)表示来自X
的行和来自Y
的行。我想采取相应行的点积,但我无法弄清楚如何有效地做到这一点。
这是我尝试过的事情
# build arbitrary combinations of row from X and row from Y. Need to calculate dot product of each pair
x_idxs = np.array([2,2,1,0])
y_idxs = np.arange(Y.shape[0])
# current method (slow)
def get_dot_product(x_idx, y_idx):
return np.dot(X[x_idx].toarray()[0], Y[y_idx].toarray()[0])
func_args = np.transpose(np.array([x_idxs, y_idxs]))
np.apply_along_axis(func1d=lambda x: get_dot_product(x[0], x[1]), axis=1, arr=func_args)
虽然有效,但在X
和Y
变大时效果很慢。有更有效的方法吗?
遵循Warren优雅但缓慢的解决方案,这是一个更好的测试示例(以及基准测试)
X = csr_matrix(np.tile(np.repeat(1, 50000),(10000,1)))
Y = X
y_idxs = np.arange(Y.shape[0])
x_idxs = y_idxs
import time
start_time = time.time()
func_args = np.transpose(np.array([x_idxs, y_idxs]))
bg = np.apply_along_axis(func1d=lambda x: get_dot_product(x[0], x[1]), axis=1, arr=func_args)
print("--- %s seconds ---" % (time.time() - start_time)) # 15.48 seconds
start_time = time.time()
ww = X[x_idxs].multiply(Y[y_idxs]).sum(axis=1)
print("--- %s seconds ---" % (time.time() - start_time)) # 38.29 seconds
答案 0 :(得分:4)
使用X
,Y
,x_idxs
和y_idxs
,即可:
In [160]: X[x_idxs].multiply(Y[y_idxs]).sum(axis=1)
Out[160]:
matrix([[ 50],
[122],
[122],
[ 74]])
那使用"花式"索引(即用任意序列索引以拉出所需的行集),然后是逐点乘法和沿轴1的和来计算点积。
结果是一个numpy matrix
,您可以将其转换为常规numpy数组并根据需要展平。您甚至可以使用有点神秘的A1
属性(getA1
方法的快捷方式):
In [178]: p = X[x_idxs].multiply(Y[y_idxs]).sum(axis=1)
In [179]: p
Out[179]:
matrix([[ 50],
[122],
[122],
[ 74]])
In [180]: p.A1
Out[180]: array([ 50, 122, 122, 74])
更新,有时间......
这是一个完整的脚本,用于比较我的版本与原版的性能,使用实际稀疏的数组X
和Y
(密度约为0.001,即大约0.1%非零元素) )。
import numpy as np
from scipy import sparse
def get_dot_product(x_idx, y_idx):
return np.dot(X[x_idx].toarray()[0], Y[y_idx].toarray()[0])
print("Generating random sparse integer matrix X...")
X = (100000*sparse.rand(50000, 120000, density=0.001, format='csr')).astype(np.int64)
X.eliminate_zeros()
print("X has shape %s with %s nonzero elements." % (X.shape, X.nnz))
Y = X
y_idxs = np.arange(Y.shape[0])
x_idxs = y_idxs
import time
start_time = time.time()
func_args = np.transpose(np.array([x_idxs, y_idxs]))
bg = np.apply_along_axis(func1d=lambda x: get_dot_product(x[0], x[1]), axis=1, arr=func_args)
print("--- %8.5f seconds ---" % (time.time() - start_time))
start_time = time.time()
ww = X[x_idxs].multiply(Y[y_idxs]).sum(axis=1)
print("--- %8.5f seconds ---" % (time.time() - start_time))
输出:
Generating random sparse integer matrix X...
X has shape (50000, 120000) with 5999934 nonzero elements.
--- 18.29916 seconds ---
--- 0.32749 seconds ---
对于稀疏度较小的矩阵,速度差异不是很大,对于足够密集的矩阵,原始版本更快。