Вопрос
Предположим, нам дан простой массив arr
с двойными числами и небольшим положительным целым числом n
.Я ищу эффективный способ установить n
наименее значимые записи каждого элемента от arr
до 0
или 1
.Есть ли ufunc
для этого?Если нет, то есть ли подходящие функции C, которые я мог бы применить к элементам из Cython?
Мотивация
Ниже я приведу мотивацию для вопроса.Если вы обнаружите, что ответ на поставленный выше вопрос не нужен для достижения конечной цели, я с удовольствием получу соответствующие комментарии.Затем я создам отдельный вопрос для сортировки.
Мотивация для этого вопроса состоит в том, чтобы реализовать версию np.unique(arr, True)
, которая принимает параметр относительной толерантности.Таким образом, второй аргумент np.unique
важен: мне нужно знать индексы уникальных элементов (первое вхождение!) В исходном массиве.Таким образом, не важно, что элементы отсортированы.
Мне известны вопросы и решения по np.unique с допуском .Однако я не нашел решения, которое также возвращает индексы первых появлений уникальных элементов в исходном массиве.Кроме того, решения, которые я видел, были основаны на сортировке, которая выполняется в O (arr.size log (arr.size)) .Тем не менее, решение с постоянным временем возможно с хэш-картой.
Идея состоит в том, чтобы округлить каждый элемент в arr
вверх и вниз и поместить эти элементы в хэш-карту.Если любое из значений уже есть в хэш-карте, запись игнорируется.В противном случае элемент включается в результат.Поскольку вставка и поиск выполняются с постоянным средним временем для хеш-карт, этот метод должен быть быстрее, чем метод, основанный на сортировке в теории.
Ниже найдите мою реализацию на Cython:
import numpy as np
cimport numpy as np
import cython
from libcpp.unordered_map cimport unordered_map
@cython.boundscheck(False)
@cython.wraparound(False)
def unique_tol(np.ndarray[DOUBLE_t, ndim=1] lower,
np.ndarray[DOUBLE_t, ndim=1] higher):
cdef long i, count
cdef long endIndex = lower.size
cdef unordered_map[double, short] vals = unordered_map[double, short]()
cdef np.ndarray[DOUBLE_t, ndim=1] result_vals = np.empty_like(lower)
cdef np.ndarray[INT_t, ndim=1] result_indices = np.empty_like(lower,
dtype=int)
count = 0
for i in range(endIndex):
if not vals.count(lower[i]) and not vals.count(higher[i]):
# insert in result
result_vals[count] = lower[i]
result_indices[count] = i
# put lowerVal and higherVal in the hashMap
vals[lower[i]]
vals[higher[i]]
# update the index in the result
count += 1
return result_vals[:count], result_indices[:count]
Этот метод называетсяс соответствующим округлением делает работу.Например, если различия менее 10 ^ -6 будут игнорироваться, мы напишем
unique_tol(np.round(a, 6), np.round(a+1e-6, 6))
Теперь я хотел бы заменить np.round
процедурой относительного округления, основанной на манипулировании мантиссой.Мне известны альтернативные способы относительного округления , но я думаю, что манипулирование мантиссой напрямую должно быть более эффективным и элегантным.(Правда, я не думаю, что увеличение производительности является значительным. Но я был бы заинтересован в решении.)
EDIT
Решение Уоррена Векессера работает какочарование.Однако результат не применим, как я надеялся, поскольку два числа с очень малой разницей могут иметь разные показатели.Объединение мантиссы не приведет к аналогичным числам.Я думаю, что я должен придерживаться относительных решений округления, которые существуют.