【发布时间】:2019-07-10 00:06:47
【问题描述】:
问题
假设我们得到一个双精度数的 numpy 数组 arr 和一个小的正整数 n。我正在寻找一种有效的方法来将arr 的每个元素的n 最低有效条目设置为0 或1。有ufunc 吗?如果没有,是否有合适的 C 函数可以应用于 Cython 的元素?
动机
下面我将提供问题的动机。如果您发现不需要回答上述问题来实现最终目标,我很高兴收到相应的 cmets。然后,我将创建一个单独的问题以保持排序。
这个问题的动机是实现一个接受相对容差参数的np.unique(arr, True) 版本。因此,np.unique 的第二个参数很重要:我需要知道原始数组中唯一元素(第一次出现!)的索引。因此,对元素进行排序并不重要。
我知道questions and solutions on np.unique with tolerance。但是,我还没有找到一个解决方案,它也返回原始数组中唯一元素的第一次出现的索引。此外,我看到的解决方案是基于排序的,它在 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。我知道alternative ways of relative rounding,但我认为直接操作尾数应该更高效、更优雅。 (诚然,我认为性能提升并不显着。但我会对解决方案感兴趣。)
编辑
Warren Weckesser 的解决方案很有魅力。然而,结果并不像我希望的那样适用,因为两个相差很小的数字可能有不同的指数。统一尾数将不会导致相似的数字。我想我必须坚持使用现有的相对舍入解决方案。
【问题讨论】:
标签: python numpy floating-point rounding