从sparse.coo_matrix采样n个零

时间:2018-12-02 02:52:59

标签: python numpy scipy sparse-matrix

如何从scipy.sparse.coo_matrix中有效采样零值?

count_static_assign

效率在这里很重要,因为我将在提供神经网络的迭代器中进行此操作。

2 个答案:

答案 0 :(得分:3)

由于您知道X的形状,因此可以使用np.random.choice生成 (row, col)中的随机X位置:

h, w = X.shape
rows = np.random.choice(h, size=n)
cols = np.random.choice(w, size=n)

主要困难在于如何检查(row, col)中的X是否为非零位置。 执行此操作的方法是:在X非零的地方,使新的稀疏X等于1。 接下来,创建一个新的稀疏矩阵Y,在上面生成的随机位置上使用非零值。然后减去

Y = Y - X.multiply(Y)

无论Y非零,此稀疏矩阵X都将为零。 因此,如果我们设法在Y中生成足够的非零值,则可以将它们的(row, col)位置用作sample_negs的返回值:

import unittest
import sys
import numpy as np
import scipy.sparse as sparse

def sample_negs(X, n=3, replace=False):
    N = np.prod(X.shape)
    m = N - X.size
    if n == 0:
        result = []
    elif (n < 0) or (not replace and m < n) or (replace and m == 0):
        raise ValueError("{n} samples from {m} locations do not exist"
                         .format(n=n, m=m))
    elif n/m > 0.5:
        # Y (in the else clause, below) would be pretty dense so there would be no point 
        # trying to use sparse techniques. So let's use hpaulj's idea 
        # (https://stackoverflow.com/a/53577267/190597) instead.
        import warnings
        warnings.filterwarnings("ignore", category=sparse.SparseEfficiencyWarning)

        Y = sparse.coo_matrix(X == 0)
        rows = Y.row
        cols = Y.col
        idx = np.random.choice(len(rows), size=n, replace=replace)
        result = list(zip(rows[idx], cols[idx]))

    else:
        X_row, X_col = X.row, X.col
        X_data = np.ones(X.size)
        X = sparse.coo_matrix((X_data, (X_row, X_col)), shape=X.shape)

        h, w = X.shape
        Y = sparse.coo_matrix(X.shape)
        Y_size = 0
        while Y_size < n:
            m = n - Y.size
            Y_data = np.concatenate([Y.data, np.ones(m)])
            Y_row = np.concatenate([Y.row, np.random.choice(h, size=m)])
            Y_col = np.concatenate([Y.col, np.random.choice(w, size=m)])
            Y = sparse.coo_matrix((Y_data, (Y_row, Y_col)), shape=X.shape)
            # Remove values in Y where X is nonzero
            # This also consolidates (row, col) duplicates
            Y = sparse.coo_matrix(Y - X.multiply(Y))
            if replace:
                Y_size = Y.data.sum()
            else:
                Y_size = Y.size
        if replace:
            rows = np.repeat(Y.row, Y.data.astype(int))        
            cols = np.repeat(Y.col, Y.data.astype(int))
            idx = np.random.choice(rows.size, size=n, replace=False)
            result = list(zip(rows[idx], cols[idx]))
        else:
            rows = Y.row
            cols = Y.col
            idx = np.random.choice(rows.size, size=n, replace=False)
            result = list(zip(rows[idx], cols[idx]))
    return result


class Test(unittest.TestCase):
    def setUp(self): 
        import warnings
        warnings.filterwarnings("ignore", category=sparse.SparseEfficiencyWarning)

        self.ncols, self.nrows = 100, 100
        self.X = sparse.random(self.ncols, self.nrows, density=0.05, format='coo')
        Y = sparse.coo_matrix(self.X == 0)
        self.expected = set(zip(Y.row, Y.col))

    def test_n_too_large(self):
        self.assertRaises(ValueError, sample_negs, self.X, n=100*100+1, replace=False)

        X_dense = sparse.coo_matrix(np.ones((4,2)))
        self.assertRaises(ValueError, sample_negs, X_dense, n=1, replace=True)

    def test_no_replacement(self):
        for m in range(100):
            negative_list = sample_negs(self.X, n=m, replace=False)
            negative_set = set(negative_list)
            self.assertEqual(len(negative_list), m)
            self.assertLessEqual(negative_set, self.expected)

    def test_no_repeats_when_replace_is_false(self):
        negative_list = sample_negs(self.X, n=10, replace=False)
        self.assertEqual(len(negative_list), len(set(negative_list)))

    def test_dense_replacement(self):
        N = self.ncols * self.nrows
        m = N - self.X.size
        for i in [-1, 0, 1]:
            negative_list = sample_negs(self.X, n=m+i, replace=True)
            negative_set = set(negative_list)
            self.assertEqual(len(negative_list), m+i)
            self.assertLessEqual(negative_set, self.expected)

    def test_sparse_replacement(self):
        for m in range(100):
            negative_list = sample_negs(self.X, n=m, replace=True)
            negative_set = set(negative_list)
            self.assertEqual(len(negative_list), m)
            self.assertLessEqual(negative_set, self.expected)


if __name__ == '__main__':
    sys.argv.insert(1,'--verbose')
    unittest.main(argv = sys.argv)

由于sample_negs相当复杂,因此我包含了一些单元测试 希望验证合理的行为。

答案 1 :(得分:1)

我认为没有一种有效的方法可以利用稀疏矩阵结构:

In [197]: >>> X = np.array([[1., 0.], [2., 1.], [0., 0.]])
     ...: >>> X_sparse = sparse.coo_matrix(X)
In [198]: X_sparse
Out[198]: 
<3x2 sparse matrix of type '<class 'numpy.float64'>'
    with 3 stored elements in COOrdinate format>
In [199]: print(X_sparse)
  (0, 0)    1.0
  (1, 0)    2.0
  (1, 1)    1.0

使用密集数组,您可以执行以下操作:

In [204]: zeros = np.argwhere(X==0)
In [205]: zeros
Out[205]: 
array([[0, 1],
       [2, 0],
       [2, 1]])
In [206]: idx=np.random.choice(3,3, replace=False)
In [207]: idx
Out[207]: array([0, 2, 1])
In [208]: zeros[idx,:]
Out[208]: 
array([[0, 1],
       [2, 1],
       [2, 0]])

我们可以要求稀疏矩阵的全0:

In [209]: X_sparse==0
/usr/local/lib/python3.6/dist-packages/scipy/sparse/compressed.py:214: SparseEfficiencyWarning: Comparing a sparse matrix with 0 using == is inefficient, try using != instead.
  ", try using != instead.", SparseEfficiencyWarning)
Out[209]: 
<3x2 sparse matrix of type '<class 'numpy.bool_'>'
    with 3 stored elements in Compressed Sparse Row format>
In [210]: print(_)
  (0, 1)    True
  (2, 0)    True
  (2, 1)    True
相关问题