python - library - ¿Existe un equivalente numpy.delete() para matrices dispersas?
scipy python (7)
Digamos que tengo una matriz bidimensional como una matriz numpy. Si quiero eliminar filas con índices específicos en esta matriz, uso numpy.delete()
. Aquí hay un ejemplo de lo que quiero decir:
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]])
Estoy buscando una manera de hacer lo anterior con matrices del paquete scipy.sparse
. Sé que es posible hacer esto convirtiendo toda la matriz en una matriz numpy pero no quiero hacer eso. ¿Hay alguna otra manera de hacer eso?
¡Muchas gracias!
Además de la versión de @ loli de la respuesta de @pv, amplié su función para permitir la eliminación de filas y / o columnas por índice en matrices de 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``) from 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
La respuesta de Pv.s es una solución buena y sólida en el lugar que lleva
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
para cualquier tamaño de matriz. Dado que la indexación booleana funciona con matrices dispersas, al menos en scipy >= 0.14.0
, sugeriría usarla siempre que se eliminen varias filas:
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]
Esta solución lleva mucho más tiempo para una eliminación de una sola fila
%timeit delete_rows_csr(a.copy(), [50])
1000 loops, best of 3: 509 us per loop
Pero es más eficiente para la eliminación de varias filas, ya que el tiempo de ejecución apenas aumenta con el número de filas
%timeit delete_rows_csr(a.copy(), numpy.random.randint(0, 100, 30))
1000 loops, best of 3: 523 us per loop
Para CSR, esta es probablemente la forma más eficiente de hacerlo en el lugar:
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])
En formato LIL es aún más simple:
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])
Para eliminar la fila i de A simplemente use la multiplicación de la matriz izquierda:
B = J*A
donde J es una matriz de identidad dispersa con i''th fila eliminada.
La multiplicación a la izquierda por la transposición de J insertará un vector cero en la fila i de B, lo que hace que esta solución sea un poco más general.
A0 = J.T * B
Para construir J en sí, utilicé la solución de pv. En una matriz diagonal dispersa de la siguiente manera (tal vez haya una solución más simple para este caso especial)
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
También puede eliminar columnas multiplicando a la derecha por JT del tamaño apropiado.
Finalmente, la multiplicación es eficiente en este caso porque J es muy escaso.
Puede eliminar la fila 0 < i < X.shape[0] - 1
de una matriz CSR X
con
scipy.sparse.vstack([X[:i, :], X[i:, :]])
Puede eliminar la primera o la última fila con X[1:, :]
o X[:-1, :]
, respectivamente. Eliminar varias filas en una pasada probablemente requerirá rodar su propia función.
Para otros formatos que no sean CSR, esto no necesariamente funciona, ya que no todos los formatos admiten el corte de filas.
Tenga en cuenta que las matrices dispersas admiten la indexación elegante hasta cierto punto. Entonces lo que puedes hacer es esto:
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,:]
El método de borrado tampoco hace nada más.
Usando la implementación de @loli, aquí les dejo una función para eliminar columnas:
def delete_cols_csr(mat, indices):
"""
Remove the cols denoted by ``indices`` form the CSR sparse matrix ``mat``.
"""
if not isinstance(mat, csr_matrix):
raise ValueError("works only for CSR format -- use .tocsr() first")
indices = list(indices)
mask = np.ones(mat.shape[1], dtype=bool)
mask[indices] = False
return mat[:,mask]