2012-10-25 18 views
17

Załóżmy, że mam macierz 2-wymiarową jako tablicę numpy. Jeśli chcę usunąć wiersze z określonymi indeksami w tej macierzy, używam numpy.delete(). Oto przykład tego, co mam na myśli:Czy istnieje odpowiednik numpy.delete() dla macierzy rzadkich?

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]]) 

szukam sposób do powyższych z matrycami z pakietu scipy.sparse. Wiem, że można to zrobić, przekształcając całą macierz w tablicę numpy, ale nie chcę tego robić. Czy istnieje inny sposób robienia tego?

Wielkie dzięki!

Odpowiedz

3

Można usunąć wiersz 0 < i < X.shape[0] - 1 z CSR matrycy X z

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

można usunąć pierwszy lub ostatni wiersz z X[1:, :] lub X[:-1, :] odpowiednio. Usunięcie wielu wierszy z jednego z nich prawdopodobnie wymaga przetasowania własnej funkcji.

Dla innych formatów niż CSR może to nie działać, ponieważ nie wszystkie formaty obsługują cięcie wierszy.

+0

Och, to jest pracochłonne i wydaje się być dość skomplikowane do usuwania wielu wierszy. Jeśli jest to jedyny sposób, aby to zrobić, prawdopodobnie lepiej dla moich celów przekształcić matrycę w tablicę numpy, nawet jeśli jest nieefektywna. – pemistahl

15

Dla CSR, jest to prawdopodobnie najbardziej skuteczny sposób to zrobić na miejscu:

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]) 

W formacie LIL to jeszcze prostsze:

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]) 
1

Należy pamiętać, że rzadkie macierze obsługują fantazyjne indeksowanie do pewien stopień. To, co możesz zrobić, to:

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,:] 

Metoda usuwania tak naprawdę nie robi nic innego.

7

Pv.s rozwiązanie jest dobre i trwałe rozwiązanie, w miejscu, które zajmuje

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 

każdego rozmiaru tablicy. Od logiczna indeksujący działa w nielicznych matryce, co najmniej scipy >= 0.14.0, sugeruję go używać, gdy wielokrotne rzędy mają być usunięte:

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] 

Roztwór ten trwa znacznie dłużej pojedynczego usuwania rzędu

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

ale jest bardziej skuteczny do usuwania wielu wierszy, a czas realizacji ledwo rośnie wraz z liczbą wierszy

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

Aby usunąć i'th wierszy od A wystarczy użyć w lewo mnożenia macierzy:

B = J*A 

gdzie J oznacza macierz rzadką identyczności z i'th rzędu usunięte.
Mnożenie w lewo przez transpozycję J wstawi wektor zerowy z powrotem do I rzędu B, co sprawia, że ​​rozwiązanie to jest nieco bardziej ogólne.

A0 = J.T * B 

Aby skonstruować sam J, użyłem pv. Na rozwiązanie na rozrzedzony przekątnej matrycy następująco (może jest prostsze rozwiązanie dla tego szczególnego przypadku?)

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 

Można również usunąć kolumny przez rozmnożenie w prawo przez JT o odpowiedniej wielkości.
Wreszcie, mnożenie jest w tym przypadku skuteczne, ponieważ J jest tak rzadkie.

+0

Prawdopodobnie chciał usunąć zamiast logicznego wymazywania przez pomnożenie (czyli utworzenie zera/kolumny zero). –

0

Oprócz użytkownika @ LOLI wersja odpowiedzi @ Pv jest, że ich funkcje rozszerzone, aby umożliwić rzędu i/lub kolumnowej wymazanie indeksu CSR macierzy.

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 
Powiązane problemy