关于Matlab:实现自定义距离功能

Implementing a custom distance function

AB是由二进制元素组成的矩阵。 A表示为基础数据矩阵,B表示为查询矩阵。 A由长度为10的75个数据点组成,而B由长度为10的50个数据点组成。我想计算A中所有数据点与B中每个查询数据点之间的距离为了应用最近的邻居搜索。因此,我使用了另一个指标:

,而不是使用欧几里得距离或汉明距离。

metric

N = 2k = length of data sampless = A(1,:)t = B(1,:)
该代码适用于A中的一个数据样本和B中的另一个数据样本。如何缩放以使其适用于所有基础数??据点和所有查询数据点?

代码适用的示例

A(1,:) = [1,0,1,1,0,0,0,1,1,0]是A矩阵中的第一个样本。令B(1,:) = [1,1,0,0,1,1,1,1,0,0]为第一个查询点。

如果从A和B获取的样本中的元素相同,则每个相似元素记录为0,否则记录为1。最终距离为1的总和。因此程序检查两个序列是否相同,如果B设置为1,则
因此,否则为零。有人可以告诉我如何将其应用于矩阵吗?

代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
l = length(A);

D=zeros(1,l);
for i=1:l,
    if A(1,i)==B(1,i),
        D(1,i)=0;
    else
        D(1,i)=1;
    end
end

sum=0;
for j=1:l,
    sum=sum+D(1,j);
end

if sum==0,
    b = 1;
else
    b = 0;
end


一线解决方案

此计算可以在一行代码中完成:

1
D = A*B'+(1-A)*(1-B)' < size(A,2)

说明

对A和B是二进制的事实,A处的每个样本与B处的每个样本之间的距离函数基本上检查每个坐标的匹配量是否等于样本的长度。
您可以使用矩阵乘法来实现此目的。

更多描述性代码示例

将A和B定义为答案中提到的两个二进制矩阵:

1
2
3
4
%initializes A and B randomly
A = double(rand(75,10) > 0.5);
B = double(rand(50,10) > 0.5);
[m,n] = size(A);

A中的每个样本与B中的每个样本之间的距离可以计算如下:

首先,定义大小为75x50的矩阵D,s.t D(i,j)包含A中的样本i和B中的样本j之间的匹配数。

可以如下计算:

1
D = A*B' + (1-A)*(1-B)';

如果每对(i,j)的匹配量小于n(n是每个样本的长度),则可以通过测试每对(i,j)来完成最终距离测量。如果较小,则样本将不同,结果应为1。否则应为零。可以按照以下步骤进行操作:

1
finalDist = D < n ;


如果要使代码按循环原样工作,只需分配size(A,1) x size(B,1)大的空间,以便每个空间位置(i,j)为您提供行ij之间的"距离"。

因此,请执行以下操作。假定AM x d矩阵,BN x d矩阵,其中d是特征点的总数,并且MN是表示正数的任意正数每行中都有元素的行。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
b = zeros(size(A,1), size(B,1)); % Change
l = size(A,2); % Change - Total number of feature points

for ii = 1 : size(A,1) % Change
    for jj = 1 : size(B,1)
        D=zeros(1,l);
        for i=1:l,
            if A(ii,i)==B(jj,i) % Change
                D(i)=0;
            else
                D(i)=1;
            end
        end

        sum=0;
        for j=1:l,
            sum=sum+D(j);
        end

        if sum==0,
            b(ii,jj) = 1; % Change
        end
    end
end

这将遍历所有行组合。但是,在这里使用任何先前的答案可以将其向量化。我只是想向您展示如果您最满意的话如何修改当前代码。


您的距离指标实际上只是L1范数,即sum(abs(x-y)),因此在Octave中,您可以像这样使用pdist2

1
pdist2(A,B,'L1')

在MATLAB中,您可以使用街区距离:

1
pdist2(A,B,'cityblock')

注意,要定义自己的距离度量标准(但最好使用'cityblock'):

1
pdist2(A,B,@(x,y)sum(abs(bsxfun(@minus,x,y)),2))

1
pdist2(A,B,@(x,y)sum(bsxfun(@xor,x,y),2))

1
pdist2(A,B,@(x,y)sum(bsxfun(@ne,x,y),2))

一个向量与另一个向量的距离可以这样找到:

1
distance = @(x,y)sum(x~=y)

但是,您想将A的所有行与B的所有行进行比较。 bsxfun在这里将很有用,我们只需要使用permute使其中一个矩阵进入第三维:

1
D = squeeze(sum(bsxfun(@ne, permute(B, [3,2,1]),A),2))'

例如,如果

1
2
3
4
5
6
7
8
A = [1,1,0;
     0,0,1;
     1,1,1];

B = [1,1,1;
     0,0,0;
     1,1,1;
     0,1,0]

然后

1
2
3
4
5
6
7
8
> D = squeeze(sum(bsxfun(@ne, permute(B, [3,2,1]),A),2))'

D =

   1   2   0
   2   1   3
   1   2   0
   1   2   2

所以这些列现在是A的行,而他的行是B的行,因此D(2,3)表示比较B(2,:)(它是[0,0,0]A(3,:),它是[1,1,1]),以及因此,由于所有元素都不相同,因此它们之间的距离为3

如果有统计信息工具箱,则可以将我的distance函数与pdist2结合使用。


这就是我从您的描述中了解的信息(如果我错了,请纠正我)
设A为矩阵

1
2
3
4
5
6
7
8
9
10
11
12
A =

     0     1     0     0     1     0     0     1     0     1
     1     1     0     0     1     1     1     0     0     1
     0     1     1     0     0     1     1     0     1     1
     0     1     1     1     1     1     1     0     0     0
     1     1     0     0     0     1     0     0     1     1
     0     0     0     0     0     1     0     0     1     0
     1     0     1     0     1     0     0     1     1     0
     1     1     0     0     1     0     1     0     0     0
     1     0     1     0     0     1     0     0     1     0
     0     0     1     0     1     0     1     1     0     0

而B为

1
2
3
4
5
6
7
8
9
10
11
12
13
B =

     0     1     1     1     0     1     0     0     1     0
     0     1     0     1     0     0     1     1     1     1
     1     0     0     1     1     0     0     0     1     1
     0     1     0     1     1     0     0     0     1     0
     1     1     0     0     0     0     0     0     0     1
     1     1     0     1     1     1     1     1     1     0
     1     1     0     0     1     0     0     1     0     1
     0     0     1     1     0     1     1     1     0     1
     0     0     0     1     1     0     0     0     1     0
     0     1     1     0     0     0     1     1     0     1
>> C = A.*B

如果A的行数更多,可以说

给您它们之间的共同点,那么您可以做的是A(1:size(B,1),:).*B而不是

1
2
3
4
5
6
7
8
9
10
11
12
C =

     0     1     0     0     0     0     0     0     0     0
     0     1     0     0     0     0     1     0     0     1
     0     0     0     0     0     0     0     0     1     1
     0     1     0     1     1     0     0     0     0     0
     1     1     0     0     0     0     0     0     0     1
     0     0     0     0     0     1     0     0     1     0
     1     0     0     0     1     0     0     1     0     0
     0     0     0     0     0     0     1     0     0     0
     0     0     0     0     0     0     0     0     1     0
     0     0     1     0     0     0     1     1     0     0

在查找中是否没有匹配点b = 1否则为0

1
b = ~find(sum(sum(C)))

更新:如果D像您说的那样是75x50,那么C应该是

1
  C = A*(B.')

而不是

1
C = A.*B

因为首先我认为它与您的代码中的点比较