Matlab:如何比较两个长度和值不同的向量?

3
假设我有两个向量 A 和 B,长度不同 Length(A) 不等于 Length(B),向量 A 中的值与向量 B 中的值不相同。我想将 B 的每个值与 A 的值进行比较(比较意味着如果 Value B(i) 与 A(1:end) 中的某个值几乎相同,例如 B(i)-Tolerance<A(i)<B(i)+Tolerance)。
如何在不使用 for loop 的情况下完成此操作,因为数据量很大?
我知道有 ismember(F)、intersect、repmat、find 函数,但这些函数都不能真正帮助我。

那么你只是在比较 A(i)B(i) 吗?为什么不发布现有的 for 循环代码,人们可能会从那里提出改进建议。 - weston
这里提供了一个带有容差的ismember解决方案。它比@ondav发布的解决方案慢大约两倍,但可以更准确地处理容差。http://www.mathworks.com/matlabcentral/fileexchange/23294-ismemberf/content/ismemberf.m - Dennis Jaheruddin
5个回答

3
您可以尝试以下解决方案:
tol = 0.1; 

N = 1000000; 

a = randn(1, N)*1000; % create a randomly

b = a + tol*rand(1, N); % b is "tol" away from a

a_bin = floor(a/tol); 
b_bin = floor(b/tol); 

result = ismember(b_bin, a_bin) | ...
         ismember(b_bin, a_bin-1) | ...
         ismember(b_bin, a_bin+1); 

find(result==0) % should be empty matrix. 

这个方法的思路是将变量a和b离散化为大小为tol的区间。然后,您需要询问b是否在与a中任何元素相同的区间内,或者在其左侧或右侧的区间内。 优点: 我认为ismember内部很聪明,首先对a的元素进行排序,然后对每个元素b执行次线性(log(N))搜索。这与显式构造b中每个元素与a中元素之间的差异的方法不同,这意味着复杂度与a中的元素数量成线性关系。 比较: 对于N=100000,在我的机器上运行时间为0.04秒,而使用线性搜索则需要20秒(使用Alan的简洁漂亮的tf=arrayfun(@(bi) any(abs(a - bi) < tol), b);解决方案进行定时)。 缺点: 这会导致实际容限在tol和1.5*tol之间。取决于您的任务是否可以接受这一点(如果唯一关注浮点比较,则可以接受)。
注意:这种方法是否可行取决于a和b的范围以及tol的值。如果a和b非常大且tol非常小,则a_binb_bin将无法解决单个区间(然后您必须使用整数类型,再次仔细检查其范围是否足够)。循环的解决方案更为安全,但如果您真的需要速度,可以投资于优化所提出的想法。当然,另一个选择是编写mex扩展程序。

不确定这样做会对性能产生什么影响,但您是否考虑过将 a_bin +-1 结合起来,这样您只需要一个 ismember 函数? - Dennis Jaheruddin
是的,你可以这样做,我相信它会更快,因为只有一个内部排序。好建议! - ondrejdee
@DennisJaheruddin 试过了 - 是的,这将时间从0.05秒减少到0.04秒。 - ondrejdee
@Alan 是的,实际上就发生在这里:a_bin = floor(a/tol); - ondrejdee
@ondav,是的,我看到了。我的意思是,使用round并且只有一个ismember检查可以自动设置公差窗口。此外,乘法往往比除法更经济。 - Alan
显示剩余3条评论

2

看起来你想要的是一个针对实数数据使用的ismember函数。

也就是说,对于你的向量B中的每个值B(i),检查是否至少有一个值在你的向量A中与之在容差阈值T内。

大致上可以这样实现:

tf = false(1, length(b)); %//the result vector, true if that element of b is in a
t = 0.01; %// the tolerance threshold
for i = 1:length(b)
    %// is the absolute difference between the 
    %//element of a and b less that the threshold?
    matches = abs(a - b(i)) < t; 

    %// if b(i) matches any of the elements of a
    tf(i) = any(matches);
end

或者简单说:
t = 0.01;
tf = arrayfun(@(bi) any(abs(a - bi) < t), b);

关于避免使用for循环:虽然向量化可能有好处,但如果您的数据非常庞大,您还可以考虑并行化。在这种情况下,像我第一个示例中那样具有for循环可能很方便,因为您可以通过将for更改为parfor来轻松执行基本版本的并行处理。


我喜欢这个,它非常好而且直接解决了任务。唯一的问题是any中的线性搜索 - 请查看我的解决方案和执行时间比较。 - ondrejdee

1
这里是一个完全矢量化的解决方案。请注意,我实际上建议采用@Alan提供的解决方案,因为我的解决方案可能无法处理大数据集。
[X Y]=meshgrid(A,B)
M=abs(X-Y)<tolerance 

现在可以使用any(M)获取a中元素的逻辑索引,这些元素在容差范围内,any(M,2)可用于获取B的索引。

1

bsxfun 来拯救

 >> M = abs( bsxfun(@minus, A, B' ) ); %//' difference
 >> M < tolerance 

1
不错,但它明确地存储了元素对之间的差异-内存复杂度为M*N - ondrejdee

0

实现你想要的另一种方法是使用逻辑表达式。
由于A和B是不同大小的向量,因此不能简单地进行减法并查找小于公差的值,但可以执行以下操作:

Lmat = sparse((abs(repmat(A,[numel(B) 1])-repmat(B',[1 numel(A)])))<tolerance);

然后你将得到一个稀疏的逻辑矩阵,其中有许多相等的元素(在容差范围内)都是1。然后你可以通过编写以下代码来计算这些元素的数量:

Nequal = sum(sum(Lmat));

你也可以通过编写以下代码来获取相应元素的索引:

[r,c] = find(Lmat);

那么以下代码将为真(对于所有 numel(r) 中的 j):

B(r(j))==A(c(j))

最后,您应该注意,如果在A或B中存在重复的条目,则使用此方法会得到多个计数。建议首先使用unique函数。例如:

A_new = unique(A);

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接