返回带有容差的唯一元素

时间:2010-01-01 13:00:10

标签: matlab

在Matlab中,有一个unique command返回数组中的唯一行。这是一个非常方便的命令。

但问题是我不能给它赋予公差 - 在双精度中,我们总是需要比较一个精度内的两个元素。是否有内置命令在一定容差范围内返回唯一元素?

7 个答案:

答案 0 :(得分:12)

使用R2015a,这个问题最终有一个简单的答案(详见my other answer to this question)。对于R2015a之前的版本,有一个内置的(未记录的)函数:_mergesimpts。对名称组成的安全猜测是“合并相似点”。

使用以下语法调用该函数:

xMerged = builtin('_mergesimpts',x,tol,[type])

数据数组xN-by-D,其中N为点数,D为维度数。每个维度的容差由D - 元素行向量tol指定。可选的输入参数type是一个字符串('first'(默认)或'average'),表示如何合并相似的元素。

输出xMergedM-by-D,其中M<=N已排序

示例,1D数据

>> x = [1; 1.1; 1.05];             % elements need not be sorted
>> builtin('_mergesimpts',x,eps)   % but the output is sorted
ans =
    1.0000
    1.0500
    1.1000

合并类型:

>> builtin('_mergesimpts',x,0.1,'first')
ans =
    1.0000  % first of [1, 1.05] since abs(1 - 1.05) < 0.1
    1.1000
>> builtin('_mergesimpts',x,0.1,'average')
ans =
    1.0250  % average of [1, 1.05]
    1.1000
>> builtin('_mergesimpts',x,0.2,'average')
ans =
    1.0500  % average of [1, 1.1, 1.05]

示例,2D数据

>> x = [1 2; 1.06 2; 1.1 2; 1.1 2.03]
x =
    1.0000    2.0000
    1.0600    2.0000
    1.1000    2.0000
    1.1000    2.0300

机器精度所特有的所有2D点:

>> xMerged = builtin('_mergesimpts',x,[eps eps],'first')
xMerged =
    1.0000    2.0000
    1.0600    2.0000
    1.1000    2.0000
    1.1000    2.0300

基于二维容差进行合并:

>> xMerged = builtin('_mergesimpts',x,[eps 0.1],'first')
xMerged =
    1.0000    2.0000
    1.0600    2.0000
    1.1000    2.0000   % first of rows 3 and 4
>> xMerged = builtin('_mergesimpts',x,[eps 0.1],'average')
xMerged =
    1.0000    2.0000
    1.0600    2.0000
    1.1000    2.0150   % average of rows 3 and 4

基于第一维尺寸公差合并:

>> xMerged = builtin('_mergesimpts',x,[0.2 eps],'average')
xMerged =
    1.0533    2.0000   % average of rows 1 to 3
    1.1000    2.0300
>> xMerged = builtin('_mergesimpts',x,[0.05 eps],'average')
xMerged =
    1.0000    2.0000
    1.0800    2.0000   % average of rows 2 and 3
    1.1000    2.0300   % row 4 not merged because of second dimension

基于两个维度进行合并:

>> xMerged = builtin('_mergesimpts',x,[0.05 .1],'average')
xMerged =
    1.0000    2.0000
    1.0867    2.0100   % average of rows 2 to 4

答案 1 :(得分:11)

这是一个难题。我甚至声称它一般不可能解决,因为我称之为传递性问题。假设我们在集合中有三个元素{A,B,C}。我将定义一个简单的函数isSimilarTo,如果两个输入在彼此的指定容差范围内,则isSimilarTo(A,B)将返回真实结果。 (请注意,我在这里说的所有内容在一个维度和多维度上都是有意义的。)因此,如果已知两个数字彼此“相似”,那么我们将选择将它们组合在一起。

因此假设我们有值{A,B,C},使得isSimilarTo(A,B)为真,而且类似于(B,C)也是如此。我们是否应该决定将所有三个组合在一起,即使isSimilarTo(A,C)是假的?

更糟糕的是,转向两个维度。从围绕圆周等距间隔的k个点开始。假设选择容差使得任何点都在其直接邻居的指定容差内,但不在任何其他点上。您如何选择在设置中解决哪些点“唯一”?

我会声称这种不和谐的问题使分组问题无法解决,至少不是完美的,当然也不是以任何有效的方式解决。也许有人可能会尝试一种基于k-means聚合方式的方法。但这也是非常低效的,这种方法通常需要提前知道要查找的组的数量。

话虽如此,我仍然会提供妥协,有时可以在限制范围内工作。这个技巧可以在Consolidator中找到,可以在Matlab Central文件交换中找到。我的方法是有效地将输入舍入到指定的容差范围内。完成此操作后,独特和准确的组合可以有效地完成聚合,即使对于一维或多维的大型数据集也是如此。

当容差足够大以至于当多个数据属于一起时,这是一种合理的方法,它们将四舍五入到相同的值,并且舍入步骤偶尔会出错。

答案 2 :(得分:7)

自R2015a 起,最后有一项功能,uniquetol在R2015a之前,见my other answer):

  

uniquetol在公差范围内设置唯一。

  uniquetolunique类似。 unique执行精确比较,uniquetol使用容差执行比较。

语法很简单:

  

C = uniquetol(A,TOL)使用容差A返回TOL中的唯一值。

与语义一样:

  

C的每个值都在A的一个值的容差范围内,但C中的两个元素都不在彼此的容差范围内。 C按升序排序。如果出现以下情况,则uv两个值均在容差范围内:
  abs(u-v) <= TOL*max(A(:),[],1)

它也可以运行“ByRows”,并且可以通过输入“DataScale”而不是输入数据中的最大值来缩放容差。

但是有一个关于解决方案唯一性的重要说明:

  

可以有多个满足条件的有效C输出,“C中没有两个元素在彼此的容差范围内。”例如,交换A中的列可能会导致返回不同的解决方案,因为输入按列按字典顺序排序。另一个结果是uniquetol(-A,TOL)可能无法提供与-uniquetol(A,TOL)相同的结果。

还有一个新功能ismembertolismember有关,与上述相同。

答案 3 :(得分:5)

我知道没有这样的功能。一个棘手的方面是,如果你的容差是1e-10,并且你有一个数值等于9e-11的向量,第一个和第三个条目不一样,但第一个是相同的第二个,第二个和第三个相同 - 那么有多少“独特”?

解决问题的一种方法是将值舍入到所需的精度,然后在其上运行唯一。您可以使用round2(http://www.mathworks.com/matlabcentral/fileexchange/4261-round2)或使用以下简单方法执行此操作:

r = rand(100,1); % some random data
roundedData = round(r*1e6)/1e6; % round to 1e-6
uniqueValues = unique(roundedData);

你也可以使用hist命令,只要精度不是太高:

r = rand(100,1); % create 100 random values between 0 and 1
grid = 0:0.001:1; % creates a vector of uniquely spaced values 
counts = hist(r,grid); % now you know for each element in 'grid' how many values there are
uniqueValues = grid(counts>0); % and these are the uniques

答案 4 :(得分:4)

我之前遇到过这个问题。诀窍是首先对数据进行排序,然后使用diff函数查找每个项目之间的差异。然后比较那个差异小于你的容差。 这是我使用的代码:

tol = 0.001
[Y I] = sort(items(:));
uni_mask = diff([0; Y]) > tol;
%if you just want the unique items:
uni_items = Y(uni_mask); %in sorted order
uni_items = items(I(uni_mask));  % in the original order

这不会照顾“漂移”...所以0:0.00001:100之类的东西实际上会返回一个唯一值。

如果你想要能够处理“漂移”的东西,那么我会使用histc,但是你需要对你愿意拥有多少物品做一些粗略的猜测。

NUM = round(numel(items) / 10); % a rough guess
bins = linspace(min(items), max(items), NUM);
counts = histc(items, bins);
unit_items = bins(counts > 0);

顺便说一句:我是在远离matlab的文本编辑器中写的,所以可能会有一些愚蠢的错别字或一个错误。

希望有所帮助

答案 5 :(得分:0)

这很难定义,假设您的公差为1。 那么[1; 2; 3; 4]会有什么结果呢?

当您有多个列时,定义可能会变得更具挑战性。

但是,如果您主要担心舍入问题,可以通过以下两种方法之一解决大部分问题:

  1. 舍入所有数字(考虑您的容差),然后使用unique
  2. 从第一行开始作为您的唯一设置,使用ismemberf确定每个新行是否唯一,如果是,请将其添加到您的唯一设置。
  3. 第一种方法的缺点是0.499999999和0.500000000可能不会被视为重复。虽然第二种方法的缺点是输入的顺序很重要。

答案 6 :(得分:0)

前几天我被 MatLab 2010 困住了,所以,没有回合(X,n),没有_mergesimpts(至少我无法让它工作)所以,一个简单的解决方案这是有效的(至少对我的数据而言):

使用rat默认容差:

unique(cellstr(rat(x)))

其他宽容:

unique(cellstr(rat(x,tol)))