Cuente los elementos únicos en una fila en un ndarray

Una extensión a esta pregunta. Además de tener los elementos únicos en la fila, quiero tener una matriz de forma similar que me permita contar valores únicos. Por ejemplo, si la matriz inicial se ve así:

a = np.array([[1, 2, 2, 3, 4, 5], [1, 2, 3, 3, 4, 5], [1, 2, 3, 4, 4, 5], [1, 2, 3, 4, 5, 5], [1, 2, 3, 4, 5, 6]]) 

Me gustaría obtener esto como resultado de la función:

 np.array([[1, 2, 0, 1, 1, 1], [1, 1, 2, 0, 1, 1], [1, 1, 1, 2, 0, 1], [1, 1, 1, 1, 2, 0], [1, 1, 1, 1, 1, 1]]) 

En numpy v.1.9 parece haber un argumento adicional return_counts que puede devolver los conteos en una matriz aplanada. ¿Hay alguna manera de que esto pueda reconstruirse en las dimensiones de la matriz original con ceros donde se duplicaron los valores?

La idea detrás de esta respuesta es muy similar a la que se usa aquí . Estoy agregando un número imaginario único a cada fila. Por lo tanto, no hay dos números de filas diferentes que puedan ser iguales. Por lo tanto, puede encontrar todos los valores únicos en una matriz 2D por fila con solo una llamada a np.unique .

El índice, ind , devuelto cuando return_index=True le da la ubicación de la primera aparición de cada valor único.

El conteo, cnt , devuelto cuando return_counts=True le da el conteo.

np.put(b, ind, cnt) coloca el recuento en la ubicación de la primera aparición de cada valor único.

Una limitación obvia del truco utilizado aquí es que la matriz original debe tener int o float dtype. Para empezar, no puede tener un tipo de dty complejo, ya que multiplicar cada fila por un número imaginario único puede producir pares duplicados de diferentes filas.


 import numpy as np a = np.array([[1, 2, 2, 3, 4, 5], [1, 2, 3, 3, 4, 5], [1, 2, 3, 4, 4, 5], [1, 2, 3, 4, 5, 5], [1, 2, 3, 4, 5, 6]]) def count_unique_by_row(a): weight = 1j*np.linspace(0, a.shape[1], a.shape[0], endpoint=False) b = a + weight[:, np.newaxis] u, ind, cnt = np.unique(b, return_index=True, return_counts=True) b = np.zeros_like(a) np.put(b, ind, cnt) return b 

rendimientos

 In [79]: count_unique_by_row(a) Out[79]: array([[1, 2, 0, 1, 1, 1], [1, 1, 2, 0, 1, 1], [1, 1, 1, 2, 0, 1], [1, 1, 1, 1, 2, 0], [1, 1, 1, 1, 1, 1]]) 

Este método hace lo mismo que np.unique para cada fila, al ordenar cada fila y obtener la longitud de valores iguales consecutivos. Esto tiene una complejidad O (NMlog (M)) que es mejor que la ejecución única en toda la matriz, ya que tiene una complejidad O (NM (log (NM))

 def row_unique_count(a): args = np.argsort(a) unique = a[np.indices(a.shape)[0], args] changes = np.pad(unique[:, 1:] != unique[:, :-1], ((0, 0), (1, 0)), mode="constant", constant_values=1) idxs = np.nonzero(changes) tmp = np.hstack((idxs[-1], 0)) counts = np.where(tmp[1:], np.diff(tmp), a.shape[-1]-tmp[:-1]) count_array = np.zeros(a.shape, dtype="int") count_array[(idxs[0], args[idxs])] = counts return count_array 

Tiempos de ejecución:

 In [162]: b = np.random.random(size=100000).reshape((100, 1000)) In [163]: %timeit row_unique_count(b) 100 loops, best of 3: 10.4 ms per loop In [164]: %timeit count_unique_by_row(b) 100 loops, best of 3: 19.4 ms per loop In [165]: assert np.all(row_unique_count(b) == count_unique_by_row(b))