在numpy中查找浮点数组的唯一元素(使用delta值进行比较)

rob*_*ntw 18 python floating-point numpy

ndarray在numpy中有一个浮点值,我想找到这个数组的唯一值.当然,这有问题,因为浮点精度......所以我希望能够在确定哪些元素是唯一的时候设置一个delta值用于比较.

有没有办法做到这一点?目前我只是在做:

unique(array)
Run Code Online (Sandbox Code Playgroud)

这给了我类似的东西:

array([       -Inf,  0.62962963,  0.62962963,  0.62962963,  0.62962963,
    0.62962963])
Run Code Online (Sandbox Code Playgroud)

其中看起来相同的值(显示的小数位数)显然略有不同.

Jos*_*del 15

另一种可能性是四舍五入到最接近的理想公差:

np.unique(a.round(decimals=4))
Run Code Online (Sandbox Code Playgroud)

a你的原始数组在哪里.

编辑:只是要注意我的解决方案和@ unutbu的速度几乎相同(我的时间可能快了5%),所以要么是一个很好的解决方案.

编辑#2:这是为了解决保罗的担忧.它肯定是慢的,可能会有一些优化,但我发布它是为了证明stratgey:

def eclose(a,b,rtol=1.0000000000000001e-05, atol=1e-08):
    return np.abs(a - b) <= (atol + rtol * np.abs(b))

x = np.array([6.4,6.500000001, 6.5,6.51])
y = x.flat.copy()
y.sort()
ci = 0

U = np.empty((0,),dtype=y.dtype)

while ci < y.size:
    ii = eclose(y[ci],y)
    mi = np.max(ii.nonzero())
    U = np.concatenate((U,[y[mi]])) 
    ci = mi + 1

print U
Run Code Online (Sandbox Code Playgroud)

如果在精度范围内有许多重复值,这应该是相当快的,但如果许多值是唯一的,那么这将是缓慢的.此外,最好设置U为列表并通过while循环追加,但这属于"进一步优化".


Pau*_*aul 6

在某些情况下,不会floorround两者都不符合OP的要求吗?

np.floor([5.99999999, 6.0]) # array([ 5.,  6.])
np.round([6.50000001, 6.5], 0) #array([ 7.,  6.])
Run Code Online (Sandbox Code Playgroud)

我这样做的方式是(这可能不是最佳的(并且肯定比其他答案慢))这样的事情:

import numpy as np
TOL = 1.0e-3
a = np.random.random((10,10))
i = np.argsort(a.flat)
d = np.append(True, np.diff(a.flat[i]))
result = a.flat[i[d>TOL]]
Run Code Online (Sandbox Code Playgroud)

当然,此方法将排除除了任何其他值的容差范围内的一组值的最大成员之外的所有值,这意味着如果所有值都非常接近,即使max-min,您也可能在数组中找不到任何唯一值大于公差.

这里基本上是相同的算法,但更容易理解,并且应该更快,因为它避免了索引步骤:

a = np.random.random((10,))
b = a.copy()
b.sort()
d = np.append(True, np.diff(b))
result = b[d>TOL]
Run Code Online (Sandbox Code Playgroud)

OP也可能想要研究scipy.cluster(对于这种方法的花哨版本)或numpy.digitize(对于其他两种方法的花哨版本)


Gui*_*e S 5

我刚刚注意到接受的答案不起作用。例如这种情况:

a = 1-np.random.random(20)*0.05
<20 uniformly chosen values between 0.95 and 1.0>
np.sort(a)
>>>> array([ 0.9514548 ,  0.95172218,  0.95454535,  0.95482343,  0.95599525,
             0.95997008,  0.96385762,  0.96679186,  0.96873524,  0.97016127,
             0.97377579,  0.98407259,  0.98490461,  0.98964753,  0.9896733 ,
             0.99199411,  0.99261766,  0.99317258,  0.99420183,  0.99730928])
TOL = 0.01
Run Code Online (Sandbox Code Playgroud)

结果变成:

a.flat[i[d>TOL]]
>>>> array([], dtype=float64)
Run Code Online (Sandbox Code Playgroud)

仅仅因为排序的输入数组的值都没有足够的间隔来至少“TOL”appart,而正确的结果应该是:

>>>> array([ 0.9514548,  0.96385762,  0.97016127,  0.98407259,
             0.99199411])
Run Code Online (Sandbox Code Playgroud)

(尽管这取决于您如何决定在“TOL”中取哪个值)

您应该使用整数不会受到这种机器精度影响的事实:

np.unique(np.floor(a/TOL).astype(int))*TOL
>>>> array([ 0.95,  0.96,  0.97,  0.98,  0.99])
Run Code Online (Sandbox Code Playgroud)

它的执行速度比建议的解决方案快 5 倍(根据 %timeit)。

请注意,“.astype(int)”是可选的,尽管删除它会使性能降低 1.5 倍,因为从 int 数组中提取唯一值要快得多。

您可能希望将“TOL”的一半添加到唯一性的结果中,以补偿地板效果:

(np.unique(np.floor(a/TOL).astype(int))+0.5)*TOL
>>>> array([ 0.955,  0.965,  0.975,  0.985,  0.995])
Run Code Online (Sandbox Code Playgroud)