代码之家  ›  专栏  ›  技术社区  ›  Graviton

返回具有公差的唯一元素

  •  16
  • Graviton  · 技术社区  · 15 年前

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

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

    7 回复  |  直到 8 年前
        1
  •  12
  •   Community CDub    8 年前

    对于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 . 它是排序的。 .

    示例,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]
    

    示例,二维数据 :

    >> 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
    
        2
  •  11
  •   user85109    15 年前

    这是个难题。我甚至会说这是不可能解决的,因为我称之为及物性问题。假设我们在一个集合中有三个元素{a,b,c}。我将定义一个简单的函数issimilarto,这样issimilarto(a,b)将返回一个真实的结果,如果这两个输入在彼此指定的公差范围内。(注意,我在这里所说的每件事在一维和多维中都是有意义的。)因此,如果已知两个数字彼此“相似”,那么我们将选择将它们组合在一起。

    所以假设我们有值{a,b,c},这样issimilarto(a,b)是真的,issimilarto(b,c)也是真的。即使Issimilarto(A,C)是错误的,我们是否应该决定将这三者组合在一起?

    更糟的是,移动到二维。从围绕圆周长等距分布的K点开始。假设选择公差时,任何点都在其相邻点的指定公差范围内,但不在任何其他点的指定公差范围内。您将如何选择解决设置中哪些点是“唯一的”?

    我认为这个不及物性的问题使得分组问题不可能解决,至少不能完美地解决,当然也不能以任何有效的方式解决。或许可以尝试基于k-means聚合风格的方法。但这将是相当低效的,同样,这样的方法通常需要事先知道要查找的组的数量。

    话虽如此,我还是会提出一个折衷的方案,有时可以在一定范围内奏效。诀窍就在 Consolidator ,在Matlab中央文件交换中找到。我的方法是有效地将输入舍入到指定的公差内。这样做之后,unique和accumarray的结合允许高效地进行聚合,即使是对于一个或多个维度中的大型数据集也是如此。

    当公差足够大时,这是一种合理的方法,当多个数据块属于一起时,它们将被舍入到相同的值,舍入步骤偶尔会产生错误。

        3
  •  7
  •   Community CDub    8 年前

    截至R2015A ,最后有一个函数来执行此操作, uniquetol ( r2015a之前 my other answer ):

    独活醇 在公差范围内设置唯一。

    独活醇 类似于 unique . 反之 独特的 执行精确比较, 独活醇 使用公差执行比较。

    语法很简单:

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

    正如语义一样:

    每个值 C 在一个值的公差范围内 ,但没有两个元素 C 彼此都能容忍。 C 按升序排序。两个值 u v 在以下情况下在公差范围内:
    abs(u-v) <= TOL*max(A(:),[],1)

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

    但是关于解的唯一性有一个重要的注意事项:

    可以有多个有效的 C 满足条件的输出 C 在彼此的容忍范围内。 可能导致返回不同的解决方案,因为输入按列按字典顺序排序。另一个结果是 uniquetol(-A,TOL) 可能不会得到与 -uniquetol(A,TOL) .

    还有一个新功能 ismembertol ismember 和上面一样。

        4
  •  5
  •   Jonas    15 年前

    我不知道有这样的功能。一个棘手的问题是,如果你的公差是,比如1e-10,并且你有一个值在9e-11处等距分布的向量,那么第一个和第三个条目是不一样的,但是第一个和第二个是一样的,第二个和第三个是一样的,那么有多少个“uniques”?

    解决这个问题的一种方法是将值舍入到所需的精度,然后在该精度上运行unique。你可以用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
    
        5
  •  4
  •   JudoWill    15 年前

    我以前遇到过这个问题。诀窍是首先对数据进行排序,然后使用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的文本编辑器中写的,所以可能会有一些愚蠢的错别字或者一个错误就错了。

    希望能帮上忙

        6
  •  0
  •   Dennis Jaheruddin    11 年前

    这很难定义,假设你有一个1的公差。 那么结果会是什么 [1; 2; 3; 4] ?

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

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

    1. 把所有数字四舍五入(考虑到你的容忍度),然后使用 unique
    2. 从最上面一行开始作为您的唯一集合,使用 ismemberf 要确定每个新行是否唯一,如果是,请将其添加到唯一集。

    第一种方法的弱点是0.499999999和0.500000000可能不被视为重复。而第二种方法的缺点是输入顺序很重要。

        7
  •  0
  •   cladelpino    8 年前

    前几天我被一个 MATLAB 2010 ,所以,没有round(x,n),没有mergesimpts(至少我不能让它工作),所以,一个简单的解决方案(至少对我的数据有效):

    使用 rat 默认公差:

    unique(cellstr(rat(x)))
    

    其他公差:

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