Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Find unique elements of floating point array in numpy (with comparison using a delta value)

I've got a ndarray of floating point values in numpy and I want to find the unique values of this array. Of course, this has problems because of floating point accuracy...so I want to be able to set a delta value to use for the comparisons when working out which elements are unique.

Is there a way to do this? At the moment I am simply doing:

unique(array) 

Which gives me something like:

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

where the values that look the same (to the number of decimal places being displayed) are obviously slightly different.

like image 968
robintw Avatar asked Mar 24 '11 23:03

robintw


People also ask

How do you find the number of unique elements in a numpy array?

To count each unique element's number of occurrences in the numpy array, we can use the numpy. unique() function. It takes the array as an input argument and returns all the unique elements inside the array in ascending order.

What does numpy function NP unique My_array do?

unique() function. The unique() function is used to find the unique elements of an array. Returns the sorted unique elements of an array.

How do I find unique rows in numpy?

To find unique rows in a NumPy array we are using numpy. unique() function of NumPy library.

What does NP unique () do in Python?

This function returns an array of unique elements in the input array. The function can be able to return a tuple of array of unique vales and an array of associated indices.


2 Answers

Another possibility is to just round to the nearest desirable tolerance:

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

where a is your original array.

Edit: Just to note that my solution and @unutbu's are nearly identical speed-wise (mine is maybe 5% faster) according to my timings, so either is a good solution.

Edit #2: This is meant to address Paul's concern. It is definitely slower and there may be some optimizations one can make, but I'm posting it as-is to demonstrate the 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 

This should be decently fast if there are many repeated values within the precision range, but if many of the values are unique, then this is going to be slow. Also, it may be better to set U up as a list and append through the while loop, but that falls under 'further optimization'.

like image 191
JoshAdel Avatar answered Sep 20 '22 11:09

JoshAdel


Doesn't floor and round both fail the OP's requirement in some cases?

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

The way I would do it is (and this may not be optimal (and is surely slower than other answers)) something like this:

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

Of course this method will exclude all but the largest member of a run of values that come within the tolerance of any other value, which means you may not find any unique values in an array if all values are significantly close even though the max-min is larger than the tolerance.

Here is essentially the same algorithm, but easier to understand and should be faster as it avoids an indexing step:

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

The OP may also want to look into scipy.cluster (for a fancy version of this method) or numpy.digitize (for a fancy version of the other two methods)

like image 36
Paul Avatar answered Sep 22 '22 11:09

Paul