2011-09-15 32 views
11

Mam (N,3) tablicę wartości NumPy:Usuwanie wierszy z duplikatów w tablicy numpy

>>> vals = numpy.array([[1,2,3],[4,5,6],[7,8,7],[0,4,5],[2,2,1],[0,0,0],[5,4,3]]) 
>>> vals 
array([[1, 2, 3], 
     [4, 5, 6], 
     [7, 8, 7], 
     [0, 4, 5], 
     [2, 2, 1], 
     [0, 0, 0], 
     [5, 4, 3]]) 

chciałbym usunąć wiersze z tablicy, które mają zduplikowane wartości. Na przykład, wynik dla powyższej tablicy powinno być:

>>> duplicates_removed 
array([[1, 2, 3], 
     [4, 5, 6], 
     [0, 4, 5], 
     [5, 4, 3]]) 

nie jestem pewien jak to zrobić skutecznie z numpy bez pętli (tablica może być dość duże). Czy ktoś wie, jak to zrobić?

+0

Przez „bez pętli” Co masz na myśli? Musisz sprawdzić każdy element w tablicy, więc jest to O (m * n) bez względu na to, jakich sztuczek używasz do ukrycia pętli. – agf

+1

Myślę, że on oznacza pętlę w Numpy zamiast pętli w Pythonie. O (m * n) wewnątrz skompilowanej funkcji Numpy jest znacznie szybsze niż O (m * n) w pętli 'for' Pythona. Gdy opcje są skompilowane z kodem i zinterpretowanym kodem, stałe mają znaczenie. –

+0

['From your comments'] (https://stackoverflow.com/questions/7438438/removing-duplicates-in-each-row-of-a-numpy-array/45136720#comment8994361_7438505), ponieważ starałeś się uogólnij to, aby poradzić sobie z generycznym nie. kolumn, możesz znaleźć ["to rozwiązanie"] (https://stackoverflow.com/a/45136720/) na to pytanie warte przeczytania. – Divakar

Odpowiedz

9

Jest to opcja:

import numpy 
vals = numpy.array([[1,2,3],[4,5,6],[7,8,7],[0,4,5],[2,2,1],[0,0,0],[5,4,3]]) 
a = (vals[:,0] == vals[:,1]) | (vals[:,1] == vals[:,2]) | (vals[:,0] == vals[:,2]) 
vals = numpy.delete(vals, numpy.where(a), axis=0) 
+0

Próbowałem to wypracować, dobra robota. Ale nie potrzebujesz | nie ^? –

+1

To jest znacznie szybsze niż metody ze zrozumieniem listy, więc prawdopodobnie zaakceptuję. Zastanawiasz się, czy istnieje jakiś sposób uogólnienia na NxM? – jterrace

+0

^działa, ale ciekawe dlaczego nie używać | ? – jterrace

2
numpy.array([v for v in vals if len(set(v)) == len(v)]) 

Pamiętaj, że nadal pętle za kulisami. Nie możesz tego uniknąć. Ale powinno działać dobrze nawet dla milionów wierszy.

+0

Wymyśliłem '[element dla elementu w vals, jeśli Counter (element) .most_common (1) [0] [1] to 1]' ale jest ładniej, zwłaszcza, że ​​już znasz 'len (v)'. Nadal "zapętlasz" się w tym, że robisz iterację po tablicy. – agf

+0

To jest rzeczywiście zaskakująco szybkie dla dużej tablicy, chociaż potrzebuję lokalizacji indeksu duplikatów, więc lubię @ Benjamin's rozwiązanie – jterrace

1

Identyczne Marcelo, ale myślę, że za pomocą numpy.unique() zamiast set() może przedostać się dokładnie to, czego strzelają do.

numpy.array([v for v in vals if len(numpy.unique(v)) == len(v)]) 
+1

Myślę, że to numpy.unique – jterrace

+0

Cóż, 'set' również ma tę samą intencję, ale jest" numpy.unique "szybsze, być może? –

+0

Wydaje się, że jest znacznie wolniej - 23 sekundy dla numpy.unique() vs. 3 sekundy dla set() na moim komputerze z 1 milionem wierszy – jterrace

2

Oto podejście do obsługi numeru rodzajowe kolumn i nadal być Vectorized metody -

def rows_uniq_elems(a): 
    idx = a.argsort(1) 
    a_sorted = a[np.arange(idx.shape[0])[:,None], idx] 
    return a[(a_sorted[:,1:] != a_sorted[:,:-1]).all(-1)] 

kroki:

  • Get argsort indeksy wzdłuż każdego rzędu.

  • Użyj advanced-indexing, aby posortować każdy wiersz, dając nam tablicę posortowaną według rzędów.

  • Poszukaj różnic między kolejnymi elementami w każdym wierszu. Zatem dowolny rząd z co najmniej jednym zróżniczkowaniem zerowym wskazuje na duplikat elementu. Wykorzystamy to, aby uzyskać maskę prawidłowych wierszy. Tak więc, ostatnim krokiem jest po prostu wybranie prawidłowych wierszy poza macierzą wejściową, używając maski.

Sample Run -

In [90]: a 
Out[90]: 
array([[1, 2, 3, 7], 
     [4, 5, 6, 7], 
     [7, 8, 7, 8], 
     [0, 4, 5, 6], 
     [2, 2, 1, 1], 
     [0, 0, 0, 3], 
     [5, 4, 3, 2]]) 

In [91]: rows_uniq_elems(a) 
Out[91]: 
array([[1, 2, 3, 7], 
     [4, 5, 6, 7], 
     [0, 4, 5, 6], 
     [5, 4, 3, 2]]) 
2

Sześć lat później, ale to pytanie pomógł mi, więc pobiegłem do porównania dla prędkości do odpowiedzi udzielonych przez Divakar, Benjamin, Marcelo Cantos i Curtis Patrick.

import numpy as np 
vals = np.array([[1,2,3],[4,5,6],[7,8,7],[0,4,5],[2,2,1],[0,0,0],[5,4,3]]) 

def rows_uniq_elems1(a): 
    idx = a.argsort(1) 
    a_sorted = a[np.arange(idx.shape[0])[:,None], idx] 
    return a[(a_sorted[:,1:] != a_sorted[:,:-1]).all(-1)] 

def rows_uniq_elems2(a): 
    a = (a[:,0] == a[:,1]) | (a[:,1] == a[:,2]) | (a[:,0] == a[:,2]) 
    return np.delete(a, np.where(a), axis=0) 

def rows_uniq_elems3(a): 
    return np.array([v for v in a if len(set(v)) == len(v)]) 

def rows_uniq_elems4(a): 
    return np.array([v for v in a if len(np.unique(v)) == len(v)]) 

Wyniki:

%timeit rows_uniq_elems1(vals) 
10000 loops, best of 3: 67.9 µs per loop 

%timeit rows_uniq_elems2(vals) 
10000 loops, best of 3: 156 µs per loop 

%timeit rows_uniq_elems3(vals) 
1000 loops, best of 3: 59.5 µs per loop 

%timeit rows_uniq_elems(vals) 
10000 loops, best of 3: 268 µs per loop 

Wydaje się, że za pomocą set uderzeń numpy.unique. W moim przypadku musiałem to zrobić na znacznie większej tablicy:

bigvals = np.random.randint(0,10,3000).reshape([3,1000]) 

%timeit rows_uniq_elems1(bigvals) 
10000 loops, best of 3: 276 µs per loop 

%timeit rows_uniq_elems2(bigvals) 
10000 loops, best of 3: 192 µs per loop 

%timeit rows_uniq_elems3(bigvals) 
10000 loops, best of 3: 6.5 ms per loop 

%timeit rows_uniq_elems4(bigvals) 
10000 loops, best of 3: 35.7 ms per loop 

Metody bez rozumienia list są znacznie szybsze. Jednak liczba wierszy jest zakodowana na sztywno i trudno jest ją rozszerzyć do więcej niż trzech kolumn, więc w moim przypadku najlepszym rozwiązaniem jest zrozumienie listy w zestawie.

EDITED bo mylić wierszy i kolumn w bigvals

Powiązane problemy