Encontre linhas exclusivas em numpy.array

199

Preciso encontrar linhas exclusivas em um arquivo numpy.array.

Por exemplo:

>>> a # I have
array([[1, 1, 1, 0, 0, 0],
       [0, 1, 1, 1, 0, 0],
       [0, 1, 1, 1, 0, 0],
       [1, 1, 1, 0, 0, 0],
       [1, 1, 1, 1, 1, 0]])
>>> new_a # I want to get to
array([[1, 1, 1, 0, 0, 0],
       [0, 1, 1, 1, 0, 0],
       [1, 1, 1, 1, 1, 0]])

Eu sei que posso criar um conjunto e um loop sobre a matriz, mas estou procurando uma numpysolução pura e eficiente . Acredito que existe uma maneira de definir o tipo de dados como nulo e então eu poderia apenas usá-lo numpy.unique, mas não consegui descobrir como fazê-lo funcionar.

Akavall
fonte
11
O pandas possui um método dataframe.drop_duplicates (). Consulte stackoverflow.com/questions/12322779/pandas-unique-dataframe e pandas.pydata.org/pandas-docs/dev/generated/…
codeape
Obrigado, mas não posso usar pandas.
217 Akavall
2
possível duplicado de Remoção duplicados em cada linha de uma matriz numpy
Andy Hayden
1
@ Andy Hayden, apesar do título, não é uma duplicata para esta pergunta. O link do codeape é uma duplicata.
Wai Yip Tung #
5
Este recurso está chegando nativamente para 1,13: github.com/numpy/numpy/pull/7742
Eric

Respostas:

115

A partir do NumPy 1.13, pode-se simplesmente escolher o eixo para a seleção de valores únicos em qualquer matriz N-dim. Para obter linhas exclusivas, é possível:

unique_rows = np.unique(original_array, axis=0)

aiwabdn
fonte
12
Cuidado com esta função. np.unique(list_cor, axis=0)fornece a matriz com linhas duplicadas removidas ; ele não filtra a matriz para elementos exclusivos na matriz original . Veja aqui , por exemplo ..
Brad Solomon
Observe que, se você deseja que linhas únicas ignorem a ordem dos valores na linha, é possível classificar a matriz original diretamente nas colunas:original_array.sort(axis=1)
mangecoeur
140

Mais uma solução possível

np.vstack({tuple(row) for row in a})
Greg von Winckel
fonte
20
+1 Isso é claro, curto e pitônico. A menos que a velocidade seja um problema real, esse tipo de solução deve ter preferência sobre as respostas mais complexas e votadas a esta pergunta IMO.
Bill Cheatham
3
Excelente! Aparelhos curvos ou a função set () fazem o truque.
Tian He
2
@ Greg von Winckel Você pode sugerir algo que não faz algo que não muda de ordem.
Laschet Jain
Sim, mas não em um único comando: x = []; [x.append (tupla (r)) para r em a se tupla (r) não em x]; a_unique = matriz (x);
Greg von Winckel
1
Para evitar um FutureWarning, converta o conjunto em uma lista como: np.vstack(list({tuple(row) for row in AIPbiased[i, :, :]})) FutureWarning: as matrizes para empilhar devem ser passadas como um tipo de "sequência", como lista ou tupla. O suporte para iteráveis ​​sem sequência, como geradores, está obsoleto no NumPy 1.16 e gera um erro no futuro.
Leermeester
111

Outra opção para o uso de matrizes estruturadas é usar uma exibição de um voidtipo que une toda a linha em um único item:

a = np.array([[1, 1, 1, 0, 0, 0],
              [0, 1, 1, 1, 0, 0],
              [0, 1, 1, 1, 0, 0],
              [1, 1, 1, 0, 0, 0],
              [1, 1, 1, 1, 1, 0]])

b = np.ascontiguousarray(a).view(np.dtype((np.void, a.dtype.itemsize * a.shape[1])))
_, idx = np.unique(b, return_index=True)

unique_a = a[idx]

>>> unique_a
array([[0, 1, 1, 1, 0, 0],
       [1, 1, 1, 0, 0, 0],
       [1, 1, 1, 1, 1, 0]])

EDIT Adicionado np.ascontiguousarrayapós a recomendação de @ seberg. Isso reduzirá a velocidade do método se a matriz ainda não estiver contígua.

EDITAR A descrição acima pode ser levemente acelerada, talvez à custa da clareza, fazendo:

unique_a = np.unique(b).view(a.dtype).reshape(-1, a.shape[1])

Além disso, pelo menos no meu sistema, em termos de desempenho, é par ou melhor que o método lexsort:

a = np.random.randint(2, size=(10000, 6))

%timeit np.unique(a.view(np.dtype((np.void, a.dtype.itemsize*a.shape[1])))).view(a.dtype).reshape(-1, a.shape[1])
100 loops, best of 3: 3.17 ms per loop

%timeit ind = np.lexsort(a.T); a[np.concatenate(([True],np.any(a[ind[1:]]!=a[ind[:-1]],axis=1)))]
100 loops, best of 3: 5.93 ms per loop

a = np.random.randint(2, size=(10000, 100))

%timeit np.unique(a.view(np.dtype((np.void, a.dtype.itemsize*a.shape[1])))).view(a.dtype).reshape(-1, a.shape[1])
10 loops, best of 3: 29.9 ms per loop

%timeit ind = np.lexsort(a.T); a[np.concatenate(([True],np.any(a[ind[1:]]!=a[ind[:-1]],axis=1)))]
10 loops, best of 3: 116 ms per loop
Jaime
fonte
3
Muito obrigado. Esta é a resposta que eu estava procurando. Você pode explicar o que está acontecendo nesta etapa b = a.view(np.dtype((np.void, a.dtype.itemsize * a.shape[1]))):?
Akavall
3
@ Akavall Ele está criando uma visualização dos seus dados com um np.voidtipo de dados do tamanho do número de bytes em uma linha completa. São duas semelhantes ao que você obtém se tiver uma matriz de se np.uint8a visualizar como np.uint16s, que combina a cada duas colunas em uma única, mas mais flexível.
Jaime
3
@ Jaime, você pode adicionar um np.ascontiguousarrayou similar para ser geralmente seguro (eu sei que é um pouco mais restritivo do que o necessário, mas ...). As linhas devem ser contíguas para que a exibição funcione conforme o esperado.
Seberg
2
@ConstantineEvans É uma adição recente: no numpy 1.6, ao tentar executar np.uniqueem uma matriz de np.voidretornos, um erro relacionado ao mergesort não está sendo implementado para esse tipo. Mas funciona bem em 1.7.
Jaime
9
Vale a pena notar que, se esse método for usado para números de ponto flutuante, haverá uma captura que -0.não será comparada como igual a +0., enquanto uma comparação elemento a elemento teria -0.==+0.(conforme especificado pelo padrão ieee float). Veja stackoverflow.com/questions/26782038/…
tom10
29

Se você deseja evitar o gasto de memória com a conversão para uma série de tuplas ou outra estrutura de dados semelhante, pode explorar as matrizes estruturadas do numpy.

O truque é exibir sua matriz original como uma matriz estruturada, em que cada item corresponde a uma linha da matriz original. Isso não faz uma cópia e é bastante eficiente.

Como um exemplo rápido:

import numpy as np

data = np.array([[1, 1, 1, 0, 0, 0],
                 [0, 1, 1, 1, 0, 0],
                 [0, 1, 1, 1, 0, 0],
                 [1, 1, 1, 0, 0, 0],
                 [1, 1, 1, 1, 1, 0]])

ncols = data.shape[1]
dtype = data.dtype.descr * ncols
struct = data.view(dtype)

uniq = np.unique(struct)
uniq = uniq.view(data.dtype).reshape(-1, ncols)
print uniq

Para entender o que está acontecendo, dê uma olhada nos resultados intermediários.

Depois que visualizamos as coisas como uma matriz estruturada, cada elemento da matriz é uma linha na sua matriz original. (Basicamente, é uma estrutura de dados semelhante a uma lista de tuplas.)

In [71]: struct
Out[71]:
array([[(1, 1, 1, 0, 0, 0)],
       [(0, 1, 1, 1, 0, 0)],
       [(0, 1, 1, 1, 0, 0)],
       [(1, 1, 1, 0, 0, 0)],
       [(1, 1, 1, 1, 1, 0)]],
      dtype=[('f0', '<i8'), ('f1', '<i8'), ('f2', '<i8'), ('f3', '<i8'), ('f4', '<i8'), ('f5', '<i8')])

In [72]: struct[0]
Out[72]:
array([(1, 1, 1, 0, 0, 0)],
      dtype=[('f0', '<i8'), ('f1', '<i8'), ('f2', '<i8'), ('f3', '<i8'), ('f4', '<i8'), ('f5', '<i8')])

Uma vez executado numpy.unique, obteremos uma matriz estruturada de volta:

In [73]: np.unique(struct)
Out[73]:
array([(0, 1, 1, 1, 0, 0), (1, 1, 1, 0, 0, 0), (1, 1, 1, 1, 1, 0)],
      dtype=[('f0', '<i8'), ('f1', '<i8'), ('f2', '<i8'), ('f3', '<i8'), ('f4', '<i8'), ('f5', '<i8')])

Que precisamos ver como uma matriz "normal" ( _armazena o resultado do último cálculo ipython, e é por isso que você está vendo _.view...):

In [74]: _.view(data.dtype)
Out[74]: array([0, 1, 1, 1, 0, 0, 1, 1, 1, 0, 0, 0, 1, 1, 1, 1, 1, 0])

E, em seguida, remodelar novamente em uma matriz 2D ( -1é um espaço reservado que indica ao numpy para calcular o número correto de linhas, forneça o número de colunas):

In [75]: _.reshape(-1, ncols)
Out[75]:
array([[0, 1, 1, 1, 0, 0],
       [1, 1, 1, 0, 0, 0],
       [1, 1, 1, 1, 1, 0]])

Obviamente, se você quiser ser mais conciso, pode escrever como:

import numpy as np

def unique_rows(data):
    uniq = np.unique(data.view(data.dtype.descr * data.shape[1]))
    return uniq.view(data.dtype).reshape(-1, data.shape[1])

data = np.array([[1, 1, 1, 0, 0, 0],
                 [0, 1, 1, 1, 0, 0],
                 [0, 1, 1, 1, 0, 0],
                 [1, 1, 1, 0, 0, 0],
                 [1, 1, 1, 1, 1, 0]])
print unique_rows(data)

O que resulta em:

[[0 1 1 1 0 0]
 [1 1 1 0 0 0]
 [1 1 1 1 1 0]]
Joe Kington
fonte
Na verdade, isso parece muito lento, quase tão lento quanto usar tuplas. A classificação de uma matriz estruturada como essa é lenta, aparentemente.
cge
3
@ cge - Experimente com matrizes de tamanho maior. Sim, classificar uma matriz numpy é mais lento que classificar uma lista. A velocidade não é a principal consideração na maioria dos casos em que você usa ndarrays. É uso de memória. Uma lista de tuplas usará muito mais memória que esta solução. Mesmo que você tenha memória suficiente, com uma matriz razoavelmente grande, convertê-la em uma lista de tuplas tem uma sobrecarga maior do que a vantagem da velocidade.
precisa
@ cge - Ah, eu não percebi que você estava usando lexsort. Eu pensei que você estava se referindo ao uso de uma lista de tuplas. Sim, lexsortprovavelmente é a melhor opção neste caso. Eu tinha esquecido, e pulei para uma solução excessivamente complexa.
precisa
20

np.uniquequando eu o executo np.random.random(100).reshape(10,10)retorna todos os elementos individuais exclusivos, mas você deseja as linhas exclusivas; primeiro, você precisa colocá-las em tuplas:

array = #your numpy array of lists
new_array = [tuple(row) for row in array]
uniques = np.unique(new_array)

Essa é a única maneira que vejo você alterando os tipos para fazer o que deseja, e não tenho certeza se a iteração da lista a ser alterada para tuplas está de acordo com o seu "não repetir"

Ryan Saxe
fonte
5
+1 Isso é claro, curto e pitônico. A menos que a velocidade seja um problema real, esse tipo de solução deve ter preferência sobre as respostas mais complexas e votadas a esta pergunta IMO.
Bill Cheatham
Eu prefiro isso sobre a solução aceita. A velocidade não é um problema para mim, porque só tenho talvez < 100linhas por invocação. Isso descreve precisamente como é executado o desempenho de linhas únicas.
rayryeng
4
Na verdade, isso não funciona para meus dados, uniquescontém elementos exclusivos. Potencialmente, eu entendi mal a forma esperada de array- você poderia ser mais preciso aqui?
FooBar
@ ryan-saxe Eu gosto que isso é python, mas essa não é uma boa solução, porque a linha retornada uniquesé classificada (e, portanto, diferente das linhas array). B = np.array([[1,2],[2,1]]); A = np.unique([tuple(row) for row in B]); print(A) = array([[1, 2],[1, 2]])
jmlarson
16

O np.unique funciona classificando uma matriz nivelada e, em seguida, verificando se cada item é igual ao anterior. Isso pode ser feito manualmente, sem achatar:

ind = np.lexsort(a.T)
a[ind[np.concatenate(([True],np.any(a[ind[1:]]!=a[ind[:-1]],axis=1)))]]

Este método não usa tuplas e deve ser muito mais rápido e mais simples do que outros métodos fornecidos aqui.

NOTA: Uma versão anterior disso não tinha o ind logo após a [, o que significa que os índices incorretos foram usados. Joe Kington também argumenta que isso faz várias cópias intermediárias. O método a seguir economiza menos, fazendo uma cópia classificada e, em seguida, usando visualizações:

b = a[np.lexsort(a.T)]
b[np.concatenate(([True], np.any(b[1:] != b[:-1],axis=1)))]

Isso é mais rápido e usa menos memória.

Além disso, se você deseja encontrar linhas exclusivas em um ndarray, independentemente de quantas dimensões estão na matriz, o seguinte funcionará:

b = a[lexsort(a.reshape((a.shape[0],-1)).T)];
b[np.concatenate(([True], np.any(b[1:]!=b[:-1],axis=tuple(range(1,a.ndim)))))]

Uma questão interessante restante seria se você quisesse classificar / único ao longo de um eixo arbitrário de uma matriz de dimensões arbitrárias, algo que seria mais difícil.

Editar:

Para demonstrar as diferenças de velocidade, executei alguns testes no ipython dos três métodos diferentes descritos nas respostas. Com o seu a exato, não há muita diferença, embora esta versão seja um pouco mais rápida:

In [87]: %timeit unique(a.view(dtype)).view('<i8')
10000 loops, best of 3: 48.4 us per loop

In [88]: %timeit ind = np.lexsort(a.T); a[np.concatenate(([True], np.any(a[ind[1:]]!= a[ind[:-1]], axis=1)))]
10000 loops, best of 3: 37.6 us per loop

In [89]: %timeit b = [tuple(row) for row in a]; np.unique(b)
10000 loops, best of 3: 41.6 us per loop

Com um a maior, no entanto, essa versão acaba sendo muito, muito mais rápida:

In [96]: a = np.random.randint(0,2,size=(10000,6))

In [97]: %timeit unique(a.view(dtype)).view('<i8')
10 loops, best of 3: 24.4 ms per loop

In [98]: %timeit b = [tuple(row) for row in a]; np.unique(b)
10 loops, best of 3: 28.2 ms per loop

In [99]: %timeit ind = np.lexsort(a.T); a[np.concatenate(([True],np.any(a[ind[1:]]!= a[ind[:-1]],axis=1)))]
100 loops, best of 3: 3.25 ms per loop
cge
fonte
Muito agradável! Em uma nota lateral, porém, ele faz várias cópias intermediárias. (por exemplo, a[ind[1:]]é uma cópia, etc.) Por outro lado, sua solução geralmente é 2-3 vezes mais rápida que a minha até você ficar sem memória RAM.
9788 Joe
Bom ponto. Acontece que minha tentativa de tirar cópias intermediárias usando apenas os índices fez com que meu método usasse mais memória e acabasse mais lento do que apenas fazer uma cópia classificada da matriz, pois a_sorted [1:] não é uma cópia de a_sorted .
cge
O que há dtypenos seus horários? Eu acho que você entendeu errado. No meu sistema, chamar np.uniquecomo descrito na minha resposta é um pouco mais rápido do que usar um dos seus dois sabores np.lexsort. E é cerca de 5x mais rápido se a matriz para encontrar itens únicos tiver forma (10000, 100). Mesmo se você decidir reimplementar o que np.uniquefaz para reduzir algum tempo (menor) de execução, o recolhimento de cada linha em um único objeto executa comparações mais rápidas do que ter que recorrer np.anyà comparação das colunas, especialmente para contagens mais altas de colunas.
Jaime
@ cge: você provavelmente quis dizer 'np.any' em vez do padrão 'any', que não aceita argumentos de palavras-chave.
Toya
@ Jaime - Eu acredito que dtypeé justo a.dtype, ou seja, o tipo de dados que está sendo visualizado, como foi feito por Joe Kington em sua resposta. Se houver muitas colunas, outra maneira (imperfeita!) De manter as coisas rápidas usando lexsorté classificar apenas algumas colunas. Isso é específico dos dados, pois é necessário saber quais colunas fornecem variação suficiente para classificar perfeitamente. Por exemplo a.shape = (60000, 500)- uma espécie nas 3 primeiras colunas: ind = np.lexsort((a[:, 2], a[:, 1], a[:, 0])). A economia de tempo é bastante substancial, mas o aviso de isenção de novo: pode não pegar todos os casos - depende dos dados.
N1k31t4
9

Aqui está outra variação da resposta pitônica @Greg

np.vstack(set(map(tuple, a)))
divenex
fonte
9

Comparei a alternativa sugerida para a velocidade e descobri que, surpreendentemente, a uniquesolução de exibição nula é até um pouco mais rápida que a nativa de numpy uniquecom o axisargumento. Se você está procurando velocidade, vai querer

numpy.unique(
    a.view(numpy.dtype((numpy.void, a.dtype.itemsize*a.shape[1])))
    ).view(a.dtype).reshape(-1, a.shape[1])

insira a descrição da imagem aqui


Código para reproduzir o gráfico:

import numpy
import perfplot


def unique_void_view(a):
    return numpy.unique(
        a.view(numpy.dtype((numpy.void, a.dtype.itemsize*a.shape[1])))
        ).view(a.dtype).reshape(-1, a.shape[1])


def lexsort(a):
    ind = numpy.lexsort(a.T)
    return a[ind[
        numpy.concatenate((
            [True], numpy.any(a[ind[1:]] != a[ind[:-1]], axis=1)
            ))
        ]]


def vstack(a):
    return numpy.vstack({tuple(row) for row in a})


def unique_axis(a):
    return numpy.unique(a, axis=0)


perfplot.show(
    setup=lambda n: numpy.random.randint(2, size=(n, 20)),
    kernels=[unique_void_view, lexsort, vstack, unique_axis],
    n_range=[2**k for k in range(15)],
    logx=True,
    logy=True,
    xlabel='len(a)',
    equality_check=None
    )
Nico Schlömer
fonte
1
Resposta muito boa, um ponto menor vstack_dict:, nunca usa um ditado, chaves entre dentes é uma compreensão definida e, portanto, seu comportamento é quase idêntico a vstatck_set. Como vstack_dictfalta linha de desempenho para o gráfico, parece que ela está apenas sendo coberta pelo vstack_setgráfico de desempenho, uma vez que são muito semelhantes!
Akavall
Obrigado pela resposta. Eu melhorei o enredo para incluir apenas uma vstackvariante.
Nico Schlömer
8

Não gostei de nenhuma dessas respostas, porque nenhuma lida com matrizes de ponto flutuante em um sentido de álgebra linear ou espaço vetorial, onde duas linhas sendo "iguais" significam "dentro de alguns 𝜀". A única resposta que tem um limite de tolerância, https://stackoverflow.com/a/26867764/500207 , considerou o limite de precisão tanto em elementos quanto em decimal , o que funciona em alguns casos, mas não é matematicamente geral como um verdadeira distância do vetor.

Aqui está a minha versão:

from scipy.spatial.distance import squareform, pdist

def uniqueRows(arr, thresh=0.0, metric='euclidean'):
    "Returns subset of rows that are unique, in terms of Euclidean distance"
    distances = squareform(pdist(arr, metric=metric))
    idxset = {tuple(np.nonzero(v)[0]) for v in distances <= thresh}
    return arr[[x[0] for x in idxset]]

# With this, unique columns are super-easy:
def uniqueColumns(arr, *args, **kwargs):
    return uniqueRows(arr.T, *args, **kwargs)

A função de domínio público acima é usada scipy.spatial.distance.pdistpara encontrar a distância euclidiana (personalizável) entre cada par de linhas. Em seguida, ele compara cada distância a uma threshantiga para encontrar as linhas que estão uma dentro threshda outra e retorna apenas uma linha de cada threshcluster.

Como sugerido, a distância metricnão precisa ser euclidiana - pdistpode calcular diversas distâncias, incluindo cityblock(norma de Manhattan) ecosine (o ângulo entre vetores).

Se thresh=0(o padrão), as linhas precisam ser exatas para serem consideradas "únicas". Outros bons valores para threshuso de precisão de máquina em escala, ou seja thresh=np.spacing(1)*1e3,.

Ahmed Fasih
fonte
Melhor resposta. Obrigado. É a resposta mais (matematicamente) generalizada escrita até agora. Ele considera uma matriz como um conjunto de pontos de dados ou amostras no espaço N-dimensional e encontra uma coleção de pontos iguais ou semelhantes (semelhança sendo definida pela distância euclidiana ou por qualquer outro método). Esses pontos podem ser pontos de dados sobrepostos ou bairros muito próximos. No final, uma coleção de pontos iguais ou semelhantes é substituída por qualquer um dos pontos (na resposta acima por um primeiro ponto) pertencentes ao mesmo conjunto. Isso ajuda a reduzir a redundância de uma nuvem de pontos.
Sanchit 02/08/19
@Sanchit aha, esse é um bom ponto, em vez de escolher o "primeiro" ponto (na verdade, pode ser efetivamente aleatório, pois depende de como o Python armazena os pontos em a set) como representante de cada threshbairro de tamanho, a função pode permitir que o usuário especificar como escolher esse ponto, por exemplo, usar o “médio” ou o ponto mais próximo do centróide, etc.
Ahmed Fasih
Certo. Sem dúvida. Eu acabei de mencionar o primeiro ponto, já que é isso que seu programa está fazendo, o que é completamente bom.
Sanchit 02/08/19
Apenas uma correção - eu disse erroneamente acima que a linha que seria escolhida para cada threshcluster seria aleatória por causa da natureza desordenada de set. É claro que é um brainfart da minha parte, as setlojas tuplas de índices que estão no thresh-neighborhood, então isso findRows faz de fato retorno, para cada thresh-cluster, a primeira linha na mesma.
Ahmed Fasih
3

Por que não usar drop_duplicatesde pandas:

>>> timeit pd.DataFrame(image.reshape(-1,3)).drop_duplicates().values
1 loops, best of 3: 3.08 s per loop

>>> timeit np.vstack({tuple(r) for r in image.reshape(-1,3)})
1 loops, best of 3: 51 s per loop
kalu
fonte
Na verdade, eu amo essa resposta. Claro, ele não usa o numpy diretamente, mas para mim é o mais fácil de entender enquanto é rápido.
Noctilux 12/05
3

O pacote numpy_indexed (exoneração de responsabilidade: eu sou seu autor) envolve a solução postada por Jaime em uma interface agradável e testada, além de muitos outros recursos:

import numpy_indexed as npi
new_a = npi.unique(a)  # unique elements over axis=0 (rows) by default
Eelco Hoogendoorn
fonte
1

O np.unique trabalha com uma lista de tuplas:

>>> np.unique([(1, 1), (2, 2), (3, 3), (4, 4), (2, 2)])
Out[9]: 
array([[1, 1],
       [2, 2],
       [3, 3],
       [4, 4]])

Com uma lista de listas, gera uma TypeError: unhashable type: 'list'

codeape
fonte
parece não funcionar no meu. Cada tupla é composta por duas cadeias em vez de dois números flutuantes
mjp
não funciona, ele retorna uma lista de elementos e não tuplas
Mohanad Kaleia
1

Com base na resposta desta página, escrevi uma função que replica a capacidade da unique(input,'rows')função do MATLAB , com o recurso adicional de aceitar tolerância para verificar a exclusividade. Ele também retorna os índices tais que c = data[ia,:]e data = c[ic,:]. Informe se você vê alguma discrepância ou erro.

def unique_rows(data, prec=5):
    import numpy as np
    d_r = np.fix(data * 10 ** prec) / 10 ** prec + 0.0
    b = np.ascontiguousarray(d_r).view(np.dtype((np.void, d_r.dtype.itemsize * d_r.shape[1])))
    _, ia = np.unique(b, return_index=True)
    _, ic = np.unique(b, return_inverse=True)
    return np.unique(b).view(d_r.dtype).reshape(-1, d_r.shape[1]), ia, ic
Arash_D_B
fonte
1

Além da excelente resposta do @Jaime, outra maneira de reduzir uma linha é usar a.strides[0](supondo que aseja C-contíguo) que seja igual a a.dtype.itemsize*a.shape[0]. Além disso, void(n)é um atalho paradtype((void,n)) . chegamos finalmente a esta versão mais curta:

a[unique(a.view(void(a.strides[0])),1)[1]]

Para

[[0 1 1 1 0 0]
 [1 1 1 0 0 0]
 [1 1 1 1 1 0]]
BM
fonte
0

Para fins gerais, como matrizes aninhadas multidimensionais em 3D ou superiores, tente o seguinte:

import numpy as np

def unique_nested_arrays(ar):
    origin_shape = ar.shape
    origin_dtype = ar.dtype
    ar = ar.reshape(origin_shape[0], np.prod(origin_shape[1:]))
    ar = np.ascontiguousarray(ar)
    unique_ar = np.unique(ar.view([('', origin_dtype)]*np.prod(origin_shape[1:])))
    return unique_ar.view(origin_dtype).reshape((unique_ar.shape[0], ) + origin_shape[1:])

o que satisfaz o seu conjunto de dados 2D:

a = np.array([[1, 1, 1, 0, 0, 0],
       [0, 1, 1, 1, 0, 0],
       [0, 1, 1, 1, 0, 0],
       [1, 1, 1, 0, 0, 0],
       [1, 1, 1, 1, 1, 0]])
unique_nested_arrays(a)

dá:

array([[0, 1, 1, 1, 0, 0],
   [1, 1, 1, 0, 0, 0],
   [1, 1, 1, 1, 1, 0]])

Mas também matrizes 3D como:

b = np.array([[[1, 1, 1], [0, 1, 1]],
              [[0, 1, 1], [1, 1, 1]],
              [[1, 1, 1], [0, 1, 1]],
              [[1, 1, 1], [1, 1, 1]]])
unique_nested_arrays(b)

dá:

array([[[0, 1, 1], [1, 1, 1]],
   [[1, 1, 1], [0, 1, 1]],
   [[1, 1, 1], [1, 1, 1]]])
Tara
fonte
Usar o unique return_indexJaime como deve fazer essa última returnlinha mais simples. Apenas indexe o original arno eixo direito.
precisa saber é
0

Nenhuma dessas respostas funcionou para mim. Estou assumindo que minhas linhas únicas continham strings e não números. No entanto, esta resposta de outro segmento funcionou:

Fonte: https://stackoverflow.com/a/38461043/5402386

Você pode usar os métodos da lista .count () e .index ()

coor = np.array([[10, 10], [12, 9], [10, 5], [12, 9]])
coor_tuple = [tuple(x) for x in coor]
unique_coor = sorted(set(coor_tuple), key=lambda x: coor_tuple.index(x))
unique_count = [coor_tuple.count(x) for x in unique_coor]
unique_index = [coor_tuple.index(x) for x in unique_coor]
mjp
fonte
0

Na verdade, podemos transformar mxn numpy array numpy em mx 1 numpy string array, tente usar a seguinte função, que fornece count , inverse_idx e etc, assim como numpy.unique:

import numpy as np

def uniqueRow(a):
    #This function turn m x n numpy array into m x 1 numpy array storing 
    #string, and so the np.unique can be used

    #Input: an m x n numpy array (a)
    #Output unique m' x n numpy array (unique), inverse_indx, and counts 

    s = np.chararray((a.shape[0],1))
    s[:] = '-'

    b = (a).astype(np.str)

    s2 = np.expand_dims(b[:,0],axis=1) + s + np.expand_dims(b[:,1],axis=1)

    n = a.shape[1] - 2    

    for i in range(0,n):
         s2 = s2 + s + np.expand_dims(b[:,i+2],axis=1)

    s3, idx, inv_, c = np.unique(s2,return_index = True,  return_inverse = True, return_counts = True)

    return a[idx], inv_, c

Exemplo:

A = np.array([[ 3.17   9.502  3.291],
  [ 9.984  2.773  6.852],
  [ 1.172  8.885  4.258],
  [ 9.73   7.518  3.227],
  [ 8.113  9.563  9.117],
  [ 9.984  2.773  6.852],
  [ 9.73   7.518  3.227]])

B, inv_, c = uniqueRow(A)

Results:

B:
[[ 1.172  8.885  4.258]
[ 3.17   9.502  3.291]
[ 8.113  9.563  9.117]
[ 9.73   7.518  3.227]
[ 9.984  2.773  6.852]]

inv_:
[3 4 1 0 2 4 0]

c:
[2 1 1 1 2]
Ting On Chan
fonte
-1

Vamos obter a matriz numpy inteira como uma lista, soltar duplicados dessa lista e finalmente retornar nossa lista exclusiva de volta para uma matriz numpy:

matrix_as_list=data.tolist() 
matrix_as_list:
[[1, 1, 1, 0, 0, 0], [0, 1, 1, 1, 0, 0], [0, 1, 1, 1, 0, 0], [1, 1, 1, 0, 0, 0], [1, 1, 1, 1, 1, 0]]

uniq_list=list()
uniq_list.append(matrix_as_list[0])

[uniq_list.append(item) for item in matrix_as_list if item not in uniq_list]

unique_matrix=np.array(uniq_list)
unique_matrix:
array([[1, 1, 1, 0, 0, 0],
       [0, 1, 1, 1, 0, 0],
       [1, 1, 1, 1, 1, 0]])
Mahdi Ghelichi
fonte
-3

A solução mais direta é transformar as linhas em um único item, transformando-as em seqüências de caracteres. Cada linha pode ser comparada como um todo por sua exclusividade usando numpy. Essa solução é generalizada, você só precisa remodelar e transpor sua matriz para outras combinações. Aqui está a solução para o problema fornecido.

import numpy as np

original = np.array([[1, 1, 1, 0, 0, 0],
       [0, 1, 1, 1, 0, 0],
       [0, 1, 1, 1, 0, 0],
       [1, 1, 1, 0, 0, 0],
       [1, 1, 1, 1, 1, 0]])

uniques, index = np.unique([str(i) for i in original], return_index=True)
cleaned = original[index]
print(cleaned)    

Darei:

 array([[0, 1, 1, 1, 0, 0],
        [1, 1, 1, 0, 0, 0],
        [1, 1, 1, 1, 1, 0]])

Envie meu prêmio nobel pelo correio

Dave Pena
fonte
Muito ineficiente e propenso a erros, por exemplo, com diferentes opções de impressão. As outras opções são claramente preferíveis.
Michael
-3
import numpy as np
original = np.array([[1, 1, 1, 0, 0, 0],
                     [0, 1, 1, 1, 0, 0],
                     [0, 1, 1, 1, 0, 0],
                     [1, 1, 1, 0, 0, 0],
                     [1, 1, 1, 1, 1, 0]])
# create a view that the subarray as tuple and return unique indeies.
_, unique_index = np.unique(original.view(original.dtype.descr * original.shape[1]),
                            return_index=True)
# get unique set
print(original[unique_index])
YoungLearnsToCoding
fonte