Есть довольно много проблем с тем, что вы делаете.
Во-первых, обратите внимание, что np.unique
не работает хорошо для арифметики с плавающей запятой и не будет в целом отфильтровать "уникальные" массивы с плавающей точкой:
In [16]: a = np.array([[2.1*3, .123], [6.3, 2.05*.06]])
In [17]: a
Out[17]:
array([[6.3 , 0.123],
[6.3 , 0.123]])
In [18]: np.unique(a, axis=0)
Out[18]:
array([[6.3 , 0.123],
[6.3 , 0.123]])
Обратите внимание, что дубликаты все еще находятся в результате после вызова np.unique
.Причина этого в том, что np.unique
сравнивается с равенство , что означает, что числа с плавающей запятой должны совпадать бит за битом.Однако арифметика с плавающей запятой не является точной, поэтому вы не гарантированно правильно отфильтруете дубликаты.
Во-вторых, с точки зрения производительности вы можете добиться большего успеха, чем np.unique
с типом hashable. np.unique
всегда будет работать в O (n log n), так как он выполняет сортировку.Вы можете проверить это в исходном коде:
if optional_indices:
perm = ar.argsort(kind='mergesort' if return_index else 'quicksort')
aux = ar[perm]
else:
ar.sort()
aux = ar
Таким образом, независимо от того, как условная оценка оценивается, сортировка выполняется по ar
(который является входным массивом, см. Здесь для получения более подробной информации: https://github.com/numpy/numpy/blob/v1.15.0/numpy/lib/arraysetops.py#L277). Причина этого в том, что np.unique
поддерживает богатый набор функций (например, получение индексов дуплей, возвращение количества дуплей и т. Д.).
Вам не нужно сортировать пополучить уникальные элементы. Если вы превратите свой тип в тип с хэшем (например, tuple
), то вы можете отфильтровать дубликаты по O (n), линейному времени. Вот пример:
In [37]: b
Out[37]:
[(0, 1, 2, 3, 4, 5, 6, 7, 8, 9),
(0, 1, 2, 3, 4, 5, 6, 7, 8, 9),
(0, 1, 2, 3, 4, 5, 6, 7, 8, 9),
(0, 1, 2, 3, 4, 5, 6, 7, 8, 9),
(0, 1, 2, 3, 4, 5, 6, 7, 8, 9),
(0, 1, 2, 3, 4, 5, 6, 7, 8, 9),
(0, 1, 2, 3, 4, 5, 6, 7, 8, 9),
(0, 1, 2, 3, 4, 5, 6, 7, 8, 9),
(0, 1, 2, 3, 4, 5, 6, 7, 8, 9),
(0, 1, 2, 3, 4, 5, 6, 7, 8, 9)]
In [39]: np.unique(b, axis=0)
Out[39]: array([[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]])
In [40]: set(b)
Out[40]: {(0, 1, 2, 3, 4, 5, 6, 7, 8, 9)}
In [41]: %timeit np.unique(b, axis=0)
21.9 µs ± 132 ns per loop (mean ± std. dev. of 7 runs, 10000 loops each)
In [42]: %timeit set(b)
627 ns ± 5.09 ns per loop (mean ± std. dev. of 7 runs, 1000000 loops each)
Итак,Как вы можете видеть, просто использование встроенного set
работает примерно в 30 раз быстрее, чем np.unique
. Обратите внимание, что это не будет работать правильно для массивов с плавающей запятой, но я просто хотел показать, что np.unique
не особенно эффективен изАлгоритмическая перспектива.
Наконец, 3500x60 не так уж и велик. Вы можете легко пройти через это, даже с алгоритмом низкого уровня, и он не должен зависать на любом современном оборудовании. Этоsон должен работать довольно быстро:
In [43]: np.random.seed(0)
In [46]: x = np.random.random((3500, 60))
In [49]: %timeit np.unique(x, axis=0)
2.57 ms ± 17.2 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)
Так что на моем MacBook Pro уходит 2,57 миллисекунды, что не совсем удобно с точки зрения аппаратного обеспечения (2,3 ГГц i5, 8 ГБ ОЗУ). Убедитесь, что вы профилируете свой код, и убедитесь, что строка в этом вопросе на самом деле строка с ошибкой.
HTH.