由于您使用的是矩阵与其转置的乘积,因此[m, n]
的值基本上将会是原始矩阵中的列m
和n
的点积。
我将使用下面的矩阵作为一个玩具示例
a = np.array([[0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1],
[0, 1, 0, 0, 0, 0, 0, 1, 0, 0, 1, 0],
[0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 1]])
>>> np.dot(a.T, a)
array([[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 1, 0, 0, 0, 0, 0, 1, 0, 0, 1, 0],
[0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 1],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 1, 0, 0, 0, 0, 0, 1, 0, 0, 1, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 1, 0, 0, 0, 0, 0, 1, 0, 0, 1, 0],
[0, 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 2]])
它是形状(3, 12)
,并具有7个非零项。其转置产品当然是(12, 12)
的形状,并有16个非零项,其中6个在对角线,因此它只需要存储11个元素。
你能得到你的输出矩阵的大小将是在以下两种方式之一的一个好主意:
CSR FORMAT
如果你原来的矩阵具有C
非零列,您的新矩阵将至多有C**2
非零条目,其中C
在对角线中,并且确保不为零,其余条目中您只需保留一半,这样至多是(C**2 + C)/2
非零值,零元素。当然,其中很多也是零,所以这可能是一个高估。
如果矩阵被存储在csr
格式,则相应的scipy
对象的indices
属性具有与所有非零元素的列索引的阵列,所以可以很容易地计算上述估计为:
>>> a_csr = scipy.sparse.csr_matrix(a)
>>> a_csr.indices
array([ 2, 11, 1, 7, 10, 4, 11])
>>> np.unique(a_csr.indices).shape[0]
6
因此,有6列非零项,因此估计将是至多36个非零项,比真正的16
CSC数据的方式更
如果不是非零元素的列索引我们有行索引,我们实际上可以做一个更好的估计。对于两列的点积是非零的,它们必须在同一行中有一个非零元素。如果给定行中有非零元素,则它们将为产品贡献R**2
非零元素。当你对所有行进行总结时,你必然会多次计算一些元素,所以这也是一个上限。
你的矩阵的非零元素的行索引是稀疏CSC矩阵indices
属性,所以这个估计可以计算如下:
>>> a_csc = scipy.sparse.csc_matrix(a)
>>> a_csc.indices
array([1, 0, 2, 1, 1, 0, 2])
>>> rows, where = np.unique(a_csc.indices, return_inverse=True)
>>> where = np.bincount(where)
>>> rows
array([0, 1, 2])
>>> where
array([2, 3, 2])
>>> np.sum(where**2)
17
这是混账接近真实16!它其实并不是一个巧合,这个估计实际上是一样的:
>>> np.sum(np.dot(a.T,a),axis=None)
17
在任何情况下,下面的代码应该让你看到,估计是相当不错的:
def estimate(a) :
a_csc = scipy.sparse.csc_matrix(a)
_, where = np.unique(a_csc.indices, return_inverse=True)
where = np.bincount(where)
return np.sum(where**2)
def test(shape=(10,1000), count=100) :
a = np.zeros(np.prod(shape), dtype=int)
a[np.random.randint(np.prod(shape), size=count)] = 1
print 'a non-zero = {0}'.format(np.sum(a))
a = a.reshape(shape)
print 'a.T * a non-zero = {0}'.format(np.flatnonzero(np.dot(a.T,
a)).shape[0])
print 'csc estimate = {0}'.format(estimate(a))
>>> test(count=100)
a non-zero = 100
a.T * a non-zero = 1065
csc estimate = 1072
>>> test(count=200)
a non-zero = 199
a.T * a non-zero = 4056
csc estimate = 4079
>>> test(count=50)
a non-zero = 50
a.T * a non-zero = 293
csc estimate = 294
道歉延时。感谢您的帮助!我担心“存储要求”这个短语含糊不清。您发送的估算代码完全是*我希望学习的内容。你的方法是否受到sedgewick和flajolet一直在做的分析组合/渐近性工作(关于近似计数)的启发?参考文献: https://en.wikipedia.org/wiki/Analytic_combinatorics http://algo.inria.fr/flajolet/Publications/AnaCombi/ https://en.wikipedia.org/wiki/Asymptotic_theory https: //en.wikipedia.org/wiki/Approximate_counting_algorithm –
@ct。不幸的是,我住在远离学术界的遥远的土地上,所以我从来没有听说过任何你们联系过的东西。我的灵感只是对[CSR]的描述(http://en.wikipedia.org/wiki/Sparse_matrix#Compressed_sparse_row_.28CSR_or_CRS.29)和[CSC](http://en.wikipedia.org/wiki/Sparse_matrix #Compressed_sparse_column_.28CSC_or_CCS.29)格式。 – Jaime