2012-10-25 5 views
17

Nehmen wir an, ich habe eine 2-dimensionale Matrix als numpy Array. Wenn ich Zeilen mit bestimmten Indizes in dieser Matrix löschen möchte, verwende ich numpy.delete(). Hier ist ein Beispiel dafür, was ich meine:Gibt es eine numpy.delete() Entsprechung für dünn besetzte Matrizen?

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

ich nach einer Möglichkeit, die oben mit Matrizen aus dem scipy.sparse Paket zu tun. Ich weiß, dass es möglich ist, dies zu tun, indem ich die gesamte Matrix in ein numpliges Array umwandele, aber das möchte ich nicht tun. Gibt es einen anderen Weg, das zu tun?

Vielen Dank!

Antwort

3

Sie können Reihe 0 < i < X.shape[0] - 1 aus einem CSR-Matrix mit X

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

löschen Sie die erste oder die letzte Reihe mit X[1:, :]X[:-1, :] oder löschen können, respectively. Das Löschen mehrerer Zeilen in einem Fall wird wahrscheinlich eine eigene Funktion erfordern.

Bei anderen Formaten als CSR funktioniert dies möglicherweise nicht, da nicht alle Formate Zeilen-Slicing unterstützen.

+0

Oh Liebes, das ist mühsam und scheint ziemlich kompliziert für das Löschen mehrerer Zeilen. Wenn dies der einzige Weg ist, es zu tun, ist es wahrscheinlich besser für meine Zwecke, die Matrix in ein numpy Array zu konvertieren, obwohl es ineffizient ist. – pemistahl

15

Für CSR, ist dies wahrscheinlich der effizienteste Weg, um es an Ort und Stelle zu tun:

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

In LIL-Format ist es noch einfacher:

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

Beachten Sie, dass dünn besetzte Matrizen unterstützen Phantasie Indizierung einem gewissen Grad. Also, was Sie tun können, ist dies:

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

Die Löschmethode tut auch nicht wirklich etwas anderes.

7

Pv.s Antwort ist eine gute und solide in-Place-Lösung, die

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 

für jede Array-Größe nimmt. Da boolean Indizierung für dünn besetzte Matrizen funktioniert, zumindest in scipy >= 0.14.0, würde ich vorschlagen, es zu benutzen, wenn mehrere Zeilen entfernt werden sollen:

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] 

Diese Lösung deutlich länger für eine einzelne Zeile Entfernung nimmt

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

Aber ist effizienter für die Entfernung von mehreren Zeilen, wie die Ausführungszeit kaum mit der Anzahl der Zeilen erhöht

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

die i-te Zeile aus einer einfach entfernen links Matrizenmultiplikation verwenden:

B = J*A 

wobei J eine spärliche Identitätsmatrix mit i-ten Zeile wird entfernt.
Linke Multiplikation durch die Transponierte von J wird einen Nullvektor zurück in die i-te Reihe von B einfügen, was diese Lösung etwas allgemeiner macht.

A0 = J.T * B 

konstruieren J selbst, ich pv verwendet. Die Lösung wird auf einer spärlichen Diagonalmatrix wie folgt (vielleicht eine einfachere Lösung für diesen speziellen Fall gibt es?)

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 

Sie können auch Spalten entfernen durch Rechts-Multiplikation mit JT der passenden Größe.
Schließlich ist die Multiplikation in diesem Fall effizient, weil J so spärlich ist.

+0

Wahrscheinlich wollte er löschen statt logisches Löschen durch Multiplizieren (d. H. Eine Zeile/Spalte Null machen). –

0

Neben @ loli-Version der Antwort des @ pv, erweiterte ich ihre Funktion für Reihe und/oder Spalte Löschen von Index CSR-Matrizen zu ermöglichen.

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 
Verwandte Themen