【发布时间】:2010-01-01 13:00:10
【问题描述】:
在 Matlab 中,有一个 unique command,它返回数组中的唯一行。这是一个非常方便的命令。
但问题是我无法为其分配容差——在双精度中,我们总是必须在一个精度范围内比较两个元素。是否有内置命令在一定的容差范围内返回唯一元素?
【问题讨论】:
-
让您的问题更进一步,也许您对集群感兴趣?
标签: matlab
在 Matlab 中,有一个 unique command,它返回数组中的唯一行。这是一个非常方便的命令。
但问题是我无法为其分配容差——在双精度中,我们总是必须在一个精度范围内比较两个元素。是否有内置命令在一定的容差范围内返回唯一元素?
【问题讨论】:
标签: matlab
有了R2015a,这个问题终于有了一个简单的答案(详见my other answer to this question)。对于 R2015a 之前的版本,有这样一个内置(未记录的)函数:_mergesimpts。对名称组成的一个安全猜测是“合并相似点”。
使用以下语法调用该函数:
xMerged = builtin('_mergesimpts',x,tol,[type])
数据数组x为N-by-D,其中N为点数,D为维数。每个尺寸的公差由D-元素行向量tol 指定。可选的输入参数type 是一个字符串('first'(默认)或'average'),指示如何合并相似的元素。
输出xMerged 将是M-by-D,其中M<=N。 已排序。
示例,一维数据:
>> 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
机器精度特有的所有二维点:
>> 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
【讨论】:
griddata 如何识别“重复”点时发现了它。太糟糕了,这个功能没有适当的文档。请随时编辑这篇文章以纠正我的错误。我记得有一些不准确之处,或者至少缺少一些细节,但从来没有时间深入研究并确保 100% 正确。
这是一个难题。我什至声称它一般是不可能解决的,因为我称之为传递性问题。假设我们在一个集合中有三个元素,{A,B,C}。我将定义一个简单的函数 isSimilarTo,这样如果两个输入在彼此指定的容差范围内,isSimilarTo(A,B) 将返回一个真结果。 (请注意,我将在这里说的所有内容在一维和多维中都有意义。)因此,如果已知两个数字彼此“相似”,那么我们将选择将它们组合在一起。
所以假设我们有值 {A,B,C} 使得 isSimilarTo(A,B) 为真,并且 isSimilarTo(B,C) 也为真。即使 isSimilarTo(A,C) 为假,我们是否应该决定将这三个组合在一起?
更糟糕的是,移动到二维。从围绕圆周等距分布的 k 个点开始。假设选择的容差使得任何点都在其直接邻居的指定容差范围内,但不在任何其他点的范围内。您将如何选择解决设置中哪些点是“独特的”?
我会声称这个不传递性问题使得分组问题无法解决,至少不能完美解决,当然也不能以任何有效的方式解决。也许有人可以尝试一种基于 k-means 聚合风格的方法。但这也会非常低效,而且这种方法通常需要提前知道要查找的组数。
话虽如此,我仍然会提出妥协,有时可以在一定范围内发挥作用。该技巧可在 Consolidator 中找到,可在 Matlab Central 文件交换中找到。我的方法是有效地将输入四舍五入到指定的容差范围内。完成此操作后,unique 和 accumarray 的组合可以有效地完成聚合,即使对于一维或多维的大型数据集也是如此。
当容差足够大时,这是一种合理的方法,当多条数据属于一起时,它们将被舍入到相同的值,舍入步骤偶尔会出现错误。
【讨论】:
从 R2015a 开始,终于有一个函数可以做到这一点,uniquetol(在 R2015a 之前,见 my other answer):
uniquetol在容差范围内设置唯一性。uniquetol类似于unique。unique执行精确比较,uniquetol使用容差执行比较。
语法很简单:
C = uniquetol(A,TOL)使用容差TOL返回A中的唯一值。
语义也是如此:
C的每个值都在A的一个值的容差范围内,但C中没有两个元素彼此处于容差范围内。C按升序排列。两个值u和v在公差范围内,如果:abs(u-v) <= TOL*max(A(:),[],1)
它还可以操作“ByRows”,并且可以通过输入“DataScale”而不是输入数据中的最大值来缩放容差。
但是关于解决方案的独特性有一个重要说明:
可以有多个满足条件的有效
C输出,“C中没有两个元素在彼此的容差范围内。”例如,交换A中的列可能会导致返回不同的解决方案,因为输入按列按字典顺序排序。另一个结果是uniquetol(-A,TOL)可能不会给出与-uniquetol(A,TOL)相同的结果。
还有一个新函数ismembertol与ismember相关,方法同上。
【讨论】:
据我所知,没有这样的功能。一个棘手的方面是,如果您的容差是 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
【讨论】:
我以前遇到过这个问题。诀窍是首先对数据进行排序,然后使用 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 的文本编辑器中编写的,因此可能存在一些愚蠢的拼写错误或一个错误。
希望有帮助
【讨论】:
这很难很好地定义,假设您的容差为 1。
那么[1; 2; 3; 4] 的结果是什么?
当您有多个列时,定义可能会变得更具挑战性。
但是,如果您最担心的是舍入问题,您可以通过以下两种方法之一解决大部分问题:
unique
ismemberf 确定每个新行是否唯一,如果是,将其添加到您的唯一集。第一种方法的缺点是 0.499999999 和 0.500000000 可能不会被视为重复项。而第二种方法的弱点是输入的顺序很重要。
【讨论】:
前几天我被 MatLab 2010 卡住了,所以,没有 round(X,n),没有 _mergesimpts(至少我无法让它工作)所以,一个简单的解决方案有效(至少对我的数据而言):
使用rat默认容差:
unique(cellstr(rat(x)))
其他公差:
unique(cellstr(rat(x,tol)))
【讨论】: