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

ioekq8ef  于 2022-11-10  发布在  其他
关注(0)|答案(6)|浏览(196)

我在NumPy中有一个ndarray的浮点值,我想要找到这个数组的唯一值。当然,这有问题,因为浮点精度…所以我希望能够设置一个增量值用于比较时,计算出哪些元素是唯一的。
有办法做到这一点吗?目前,我只是在做以下事情:

unique(array)

这给了我一些类似的东西:

array([       -Inf,  0.62962963,  0.62962963,  0.62962963,  0.62962963,
    0.62962963])

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

nimxete2

nimxete21#

另一种可能是只四舍五入到最接近理想的容忍度:

np.unique(a.round(decimals=4))

其中a是原始数组。

**编辑:**只是要注意,根据我的计时,我的解决方案和@unutbu的解决方案在速度上几乎相同(我的可能快5%),所以这两个解决方案都是很好的解决方案。
**编辑#2:**这是为了解决保罗的担忧。它绝对更慢,而且可能会有一些优化,但我原封不动地发布它是为了演示这一策略:

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

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

f0ofjuux

f0ofjuux2#

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

np.floor([5.99999999, 6.0]) # array([ 5.,  6.])
np.round([6.50000001, 6.5], 0) #array([ 7.,  6.])

我会这样做(这可能不是最优的(而且肯定比其他答案慢))如下:

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]]

当然,此方法将排除在任何其他值的容差范围内的一系列值中除最大成员之外的所有成员,这意味着如果所有值都非常接近,即使max-min大于容差,也可能找不到数组中的任何唯一值。
以下是本质上相同的算法,但更容易理解,而且应该更快,因为它避免了索引步骤:

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

OP可能还想查看scipy.cluster(查找此方法的特殊版本)或numpy.digitize(查找其他两个方法的特殊版本)

nkoocmlb

nkoocmlb3#

我刚刚注意到,公认的答案并不奏效。例如,本例:

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

结果为:

a.flat[i[d>TOL]]
>>>> array([], dtype=float64)

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

>>>> array([ 0.9514548,  0.96385762,  0.97016127,  0.98407259,
             0.99199411])

(尽管这取决于您如何决定在“tol”中取哪个值)
您应该利用这样一个事实,即整数不会受到这样的机器精度影响:

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

它的执行速度比建议的解决方案快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])
2g32fytz

2g32fytz4#

像这样的怎么样?

np.unique1d(np.floor(1e7*x)/1e7)

其中x是原始数组。

yx2lnoni

yx2lnoni5#

我刚刚在npx(我的一个很小的扩展包)中添加了对此的支持。

import npx

a = [0.1, 0.15, 0.7]
a_unique = npx.unique(a, tol=2.0e-1)

assert all(a_unique == [0.1, 0.7])
w6mmgewl

w6mmgewl6#

在当前版本的NumPy(1.23)中,numpy.unique有一个可选参数return_index,用于返回每个唯一值第一次出现的索引。因此,您只需在四舍五入数组上使用numpy.uniquereturn_index=True,并为原始数组编制索引,即可获得原始的非四舍五入的值。就像这样:

decimals = 3
X_unique_with_tolerance = X[np.unique(X.round(decimals), return_index=True)[1]].shape

相关问题