Python 为什么np.array_equal对于这两个视觉上相等的稀疏数组返回False?
我有两种方法来计算数组的单位向量,这两种方法都可以处理稀疏数组。其中一个非常“手动”计算,而另一个更“正式”(来自gensim.matutils源代码) 这是手动方法:Python 为什么np.array_equal对于这两个视觉上相等的稀疏数组返回False?,python,numpy,sparse-matrix,Python,Numpy,Sparse Matrix,我有两种方法来计算数组的单位向量,这两种方法都可以处理稀疏数组。其中一个非常“手动”计算,而另一个更“正式”(来自gensim.matutils源代码) 这是手动方法: def manual_unitvec(vec): vec = vec.tocsr() if sparse.issparse(vec): vec_sum_of_squares = vec.multiply(vec) unit = 1. / np.sqr
def manual_unitvec(vec):
vec = vec.tocsr()
if sparse.issparse(vec):
vec_sum_of_squares = vec.multiply(vec)
unit = 1. / np.sqrt(vec_sum_of_squares.sum())
return vec.multiply(unit)
elif not sparse.issparse(vec):
sum_vec_squared = np.sum(vec ** 2)
vec /= np.sqrt(sum_vec_squared)
return vec
这是改进的gensim方法,其中显式计算单位向量的方法是unitvec
:
import numpy as np
from scipy import sparse
from gensim.matutils import ret_normalized_vec, blas
import scipy.sparse
blas_nrm2 = blas('nrm2', np.array([], dtype=float))
blas_scal = blas('scal', np.array([], dtype=float))
def unitvec(vec, norm='l2'):
"""Scale a vector to unit length.
Parameters
----------
vec : {numpy.ndarray, scipy.sparse, list of (int, float)}
Input vector in any format
norm : {'l1', 'l2'}, optional
Normalization that will be used.
Returns
-------
{numpy.ndarray, scipy.sparse, list of (int, float)}
Normalized vector in same format as `vec`.
Notes
-----
Zero-vector will be unchanged.
"""
if norm not in ('l1', 'l2'):
raise ValueError("'%s' is not a supported norm. Currently supported norms are 'l1' and 'l2'." % norm)
if scipy.sparse.issparse(vec):
print("INSIDE SPARSE HANDLING")
vec = vec.tocsr()
if norm == 'l1':
veclen = np.sum(np.abs(vec.data))
if norm == 'l2':
veclen = np.sqrt(np.sum(vec.data ** 2))
if veclen > 0.0:
if np.issubdtype(vec.dtype, np.int) == True:
vec = vec.astype(np.float)
return vec / veclen
else:
vec /= veclen
return vec.astype(vec.dtype)
else:
return vec
if isinstance(vec, np.ndarray):
print("INSIDE NORMAL VEC HANDLING")
vec = np.asarray(vec, dtype=vec.dtype)
if norm == 'l1':
veclen = np.sum(np.abs(vec))
if norm == 'l2':
veclen = blas_nrm2(vec)
if veclen > 0.0:
if np.issubdtype(vec.dtype, np.int) == True:
vec = vec.astype(np.float)
return blas_scal(1.0 / veclen, vec).astype(vec.dtype)
else:
return blas_scal(1.0 / veclen, vec).astype(vec.dtype)
else:
return vec
try:
first = next(iter(vec)) # is there at least one element?
except StopIteration:
return vec
if isinstance(first, (tuple, list)) and len(first) == 2: # gensim sparse format
print("INSIDE GENSIM SPARSE FORMAT HANDLING")
if norm == 'l1':
length = float(sum(abs(val) for _, val in vec))
if norm == 'l2':
length = 1.0 * math.sqrt(sum(val ** 2 for _, val in vec))
assert length > 0.0, "sparse documents must not contain any explicit zero entries"
return ret_normalized_vec(vec, length)
else:
raise ValueError("unknown input type")
在运行测试时,我想检查这些方法的输出是否相同。下面是一段示例代码:
vec = sparse.csr_matrix([[1, 2, 3], [4, 5, 6], [7, 8, 9]]).astype(np.float32)
output1 = manual_unitvec(vec)
output2 = unitvec(vec)
print(output1)
print(' ')
print(output2)
print(np.array_equal(output1, output2))
print(type(output1) == type(output2))
所以我要检查的是assertTrue(output1,output2)。您不能这样做,因为数组的真值不明确,所以我使用assertTrue(np.array_equal(output1,output2))
现在的问题是array_equal并不认为output1和output2是相同的,尽管我可以从打印出来的结果中看出它们是相同的
运行上面的所有代码会得到以下输出:
MacBook-Air:matutils.unitvec Olly$ python try.py
INSIDE SPARSE HANDLING
try.py:80: FutureWarning: Conversion of the second argument of issubdtype from `int` to `np.signedinteger` is deprecated. In future, it will be treated as `np.int64 == np.dtype(int).type`.
if np.issubdtype(vec.dtype, np.int) == True:
(0, 0) 0.059234887
(0, 1) 0.118469775
(0, 2) 0.17770466
(1, 0) 0.23693955
(1, 1) 0.29617444
(1, 2) 0.35540932
(2, 0) 0.4146442
(2, 1) 0.4738791
(2, 2) 0.53311396
(0, 0) 0.059234887
(0, 1) 0.118469775
(0, 2) 0.17770466
(1, 0) 0.23693955
(1, 1) 0.29617444
(1, 2) 0.35540932
(2, 0) 0.4146442
(2, 1) 0.4738791
(2, 2) 0.53311396
/Users/Olly/anaconda2/lib/python2.7/site-packages/scipy/sparse/compressed.py:226: SparseEfficiencyWarning: Comparing sparse matrices using == is inefficient, try using != instead.
" != instead.", SparseEfficiencyWarning)
False
True
我认为问题可能来自稀疏数组类型,但正如您所看到的,它们是相等的。您还可以直观地看到这些元素完全相同
那么为什么数组_等于返回false呢?如何更改它?在您的第一个函数中,您可以执行以下操作:
vec = vec.tocsr()
if sparse.issparse(vec):
我认为issparse
测试对您没有任何帮助。如果输入参数是稀疏矩阵,它有一个tocsr
方法,结果是稀疏矩阵。如果vec
是ndarray
is没有tocsr
方法,第一行将抛出错误
在该函数的其余部分中,稀疏矩阵具有乘法
方法和求和
方法。sum
的结果很密集,因此np.sqrt
可以很好地处理它。实际上np.sqrt(M)
也适用于稀疏矩阵,因为M.sqrt
存在
在第二个函数中,使用data
属性,该属性是1dndarray
np.sum(np.abs(vec.data))
那很好。但是请注意,稀疏矩阵的M.\uuuu abs\uuuu
是
self._with_data(abs(self._deduped_data()))
以更为全面的方式,函数/方法,如abs
,sqrt
也可以使用.data
属性。只有它们返回一个新的稀疏矩阵
至于测试,请看np.array_equal
return bool(asarray(a1 == a2).all())
如果我尝试在output1
上使用它(我不会尝试您的gensim
解决方案)
In [106]: np.array_equal(output1, output1)
/usr/local/lib/python3.5/dist-packages/scipy/sparse/compressed.py:226: SparseEfficiencyWarning: Comparing sparse matrices using == is inefficient, try using != instead.
" != instead.", SparseEfficiencyWarning)
Out[106]: False
它不喜欢在稀疏矩阵上取=
。通常这些矩阵很大,有许多0。这意味着所有这些矩阵的结果都是真的
,因此不再稀疏
您的output1
是一个稀疏矩阵,但至少对于这些输入而言,不是稀疏矩阵:
In [107]: output1.A
Out[107]:
array([[0.05923489, 0.11846977, 0.17770466],
[0.23693955, 0.29617444, 0.35540932],
[0.4146442 , 0.4738791 , 0.53311396]], dtype=float32)
但是,即使您绕过稀疏位,np.array_equal(output1.A,output2.A)
也可能由于浮点比较而失败
密集版本上的allclose
可能是最简单的测试:
In [113]: np.allclose(output1.A, output1.A)
Out[113]: True
您还可以比较数据
(假设稀疏度相同):
更全面的稀疏测试需要检查形状
、nnz
、和索引
属性
实际上,我不确定
np.array_equal
在哪里失败。请注意,它以a1=asarray(a1)开始
,它生成0d对象数据类型数组。这是坚持将其输入作为数组处理的numpy
函数之一。它不是稀疏感知的。您可能需要注意一些警告。另外,请尝试np.allclose(output1,output2)
相反。似乎np.array_equal
不是为处理稀疏数组而设计的。它可以完成您试图做的事情吗?np.allclose(output1,output2)
作为一般规则np。
函数不处理稀疏矩阵。您必须使用稀疏函数或方法(或委托给稀疏方法的numpy函数)。稀疏矩阵不是ndarray
的子类。
In [114]: np.allclose(output1.data, output1.data)
Out[114]: True