Подтвердить что ты не робот

Существует ли эквивалент numpy.delete() для разреженных матриц?

Скажем, у меня есть двумерная матрица в виде массива numpy. Если я хочу удалить строки с конкретными индексами в этой матрице, я использую numpy.delete(). Вот пример того, что я имею в виду:

In [1]: my_matrix = numpy.array([
   ...:     [10, 20, 30, 40, 50],
   ...:     [15, 25, 35, 45, 55],
   ...:     [95, 96, 97, 98, 99]
   ...: ])
In [2]: numpy.delete(my_matrix, [0, 2], axis=0)
Out[2]: array([[15, 25, 35, 45, 55]])

Я ищу способ сделать это с помощью матриц из пакета scipy.sparse. Я знаю, что это можно сделать, преобразовывая всю матрицу в массив numpy, но я не хочу этого делать. Есть ли другой способ сделать это?

Спасибо большое!

4b9b3361

Ответ 1

Для CSR это, вероятно, самый эффективный способ сделать это на месте:

def delete_row_csr(mat, i):
    if not isinstance(mat, scipy.sparse.csr_matrix):
        raise ValueError("works only for CSR format -- use .tocsr() first")
    n = mat.indptr[i+1] - mat.indptr[i]
    if n > 0:
        mat.data[mat.indptr[i]:-n] = mat.data[mat.indptr[i+1]:]
        mat.data = mat.data[:-n]
        mat.indices[mat.indptr[i]:-n] = mat.indices[mat.indptr[i+1]:]
        mat.indices = mat.indices[:-n]
    mat.indptr[i:-1] = mat.indptr[i+1:]
    mat.indptr[i:] -= n
    mat.indptr = mat.indptr[:-1]
    mat._shape = (mat._shape[0]-1, mat._shape[1])

В формате LIL это еще проще:

def delete_row_lil(mat, i):
    if not isinstance(mat, scipy.sparse.lil_matrix):
        raise ValueError("works only for LIL format -- use .tolil() first")
    mat.rows = np.delete(mat.rows, i)
    mat.data = np.delete(mat.data, i)
    mat._shape = (mat._shape[0] - 1, mat._shape[1])

Ответ 2

Ответ Pv.s - хорошее и твердое решение на месте, которое принимает

a = scipy.sparse.csr_matrix((100,100), dtype=numpy.int8)
%timeit delete_row_csr(a.copy(), 0)
10000 loops, best of 3: 80.3 us per loop

для любого размера массива. Поскольку булевская индексация работает для разреженных матриц, по крайней мере в scipy >= 0.14.0, я бы предложил использовать ее всякий раз, когда нужно удалить несколько строк:

def delete_rows_csr(mat, indices):
    """
    Remove the rows denoted by ``indices`` form the CSR sparse matrix ``mat``.
    """
    if not isinstance(mat, scipy.sparse.csr_matrix):
        raise ValueError("works only for CSR format -- use .tocsr() first")
    indices = list(indices)
    mask = numpy.ones(mat.shape[0], dtype=bool)
    mask[indices] = False
    return mat[mask]

Это решение занимает значительно больше времени для удаления одной строки

%timeit delete_rows_csr(a.copy(), [50])
1000 loops, best of 3: 509 us per loop

Но более эффективен для удаления нескольких строк, так как время выполнения едва увеличивается с количеством строк

%timeit delete_rows_csr(a.copy(), numpy.random.randint(0, 100, 30))
1000 loops, best of 3: 523 us per loop

Ответ 3

Вы можете удалить строку 0 < i < X.shape[0] - 1 из матрицы CSR X с помощью

scipy.sparse.vstack([X[:i, :], X[i:, :]])

Вы можете удалить первую или последнюю строку с помощью X[1:, :] или X[:-1, :], соответственно. Удаление нескольких строк за один проход, вероятно, потребует перемотки вашей собственной функции.

Для других форматов, кроме CSR, это может не обязательно работать, поскольку не все форматы поддерживают разделение строк.

Ответ 4

Обратите внимание, что разреженные матрицы в некоторой степени поддерживают причудливое индексирование. Итак, что вы можете сделать, это следующее:

mask = np.ones(len(mat), dtype=bool)
mask[rows_to_delete] = False
# unfortunatly I think boolean indexing does not work:
w = np.flatnonzero(mask)
result = s[w,:]

Метод удаления тоже ничего не делает.

Ответ 5

Чтобы удалить i-й ряд из A, просто используйте умножение на левую матрицу:

B = J*A

где J - разреженная единичная матрица с удаленной i-й строкой.
Левое умножение на транспонирование J введет нулевой вектор обратно в i-ю строку B, что делает это решение немного более общим.

A0 = J.T * B

Чтобы построить сам J, я использовал pv. решение на разреженной диагональной матрице следующим образом (может быть, есть более простое решение для этого частного случая?)

def identity_minus_rows(N, rows):
    if np.isscalar(rows):
        rows = [rows]
    J = sps.diags(np.ones(N), 0).tocsr()  # make a diag matrix
    for r in sorted(rows):
        J = delete_row_csr(J, r)
    return J

Вы также можете удалить столбцы, умножив их на J.T соответствующего размера.
Наконец, умножение эффективно в этом случае, потому что J настолько разрежен.

Ответ 6

В дополнение к @loli версии ответа @pv я расширил их функцию, чтобы разрешить удаление строки и/или столбца по индексу на CSR-матрицах.

import numpy as np
from scipy.sparse import csr_matrix

def delete_from_csr(mat, row_indices=[], col_indices=[]):
    """
    Remove the rows (denoted by ``row_indices``) and columns (denoted by ``col_indices``) form the CSR sparse matrix ``mat``.
    WARNING: Indices of altered axes are reset in the returned matrix
    """
    if not isinstance(mat, csr_matrix):
        raise ValueError("works only for CSR format -- use .tocsr() first")

    rows = []
    cols = []
    if row_indices:
        rows = list(row_indices)
    if col_indices:
        cols = list(col_indices)

    if len(rows) > 0 and len(cols) > 0:
        row_mask = np.ones(mat.shape[0], dtype=bool)
        row_mask[rows] = False
        col_mask = np.ones(mat.shape[1], dtype=bool)
        col_mask[cols] = False
        return mat[row_mask][:,col_mask]
    elif len(rows) > 0:
        mask = np.ones(mat.shape[0], dtype=bool)
        mask[rows] = False
        return mat[mask]
    elif len(cols) > 0:
        mask = np.ones(mat.shape[1], dtype=bool)
        mask[cols] = False
        return mat[:,mask]
    else:
        return mat