如何在 Matlab 中按列计算模式?

How to count patterns columnwise in Matlab?

我在 Matlab 中有一个矩阵 S,如下所示:

2   2   1   2
2   3   1   1
3   3   1   1
3   4   1   1
3   1   2   1
4   1   3   1
1   1   3   1

我想按列计算值的模式。我对任何列中 紧跟在 数字 3 之后的数字的频率感兴趣。例如,数字 3 在第一列中出现了三次。我们第一次观察它时,它后面跟着 3,第二次它后面跟着 3,第三次它后面跟着 4。因此,在第一列中观察到的模式的频率看起来像:

3-3: 66.66%
3-4: 33.33% 
3-1: 0%
3-2: 0%

这是一种方法,找到 3,然后查看后面的数字

[i,j]=find(S==3);
k=i+1<=size(S,1);
T=S(sub2ind(size(S),i(k)+1,j(k))) %// the elements of S that are just below a 3
R=arrayfun(@(x) sum(T==x)./sum(k),1:max(S(:))).' %// get the number of probability of each digit

我将以我能理解的方式重述你的问题陈述,我的解决方案将反映这个新的问题陈述。

  1. 对于特定列,找到包含数字 3 的位置。
  2. 查看这些位置正下方的行并查看这些位置的值
  3. 取这些值并计算找到的总出现次数。
  4. 对所有列重复这些操作并更新计数,然后确定值出现的百分比。

我们可以通过以下方式做到这一点:

A = [2   2   1   2
2   3   1   1
3   3   1   1
3   4   1   1
3   1   2   1
4   1   3   1
1   1   3   1]; %// Define your matrix
[row,col] = find(A(1:end-1,:) == 3);
vals = A(sub2ind(size(A), row+1, col));
h = 100*accumarray(vals, 1) / numel(vals)

h =

          0
          0
    66.6667
    33.3333

我们慢慢看上面的代码。前几行定义示例矩阵 A。接下来,我们查看除矩阵的最后一行 之外的所有行 ,并查看数字 3 在 find 中的位置。我们跳过最后一行,因为我们想确保我们在您的矩阵范围内。如果最后一行有一个数字 3,如果我们试图检查最后一行下面的值,就会出现未定义的行为,因为那里什么也没有!

完成此操作后,我们会查看矩阵中位于数字 3 下方 1 行的那些值。我们使用 sub2ind to help us facilitate this. Next, we use these values and tally them up using accumarray 然后通过计数的总和将它们归一化为百分比。

结果将是一个 4 元素数组,显示每个数字遇到的百分比。

为了仔细检查,如果我们查看矩阵,我们会看到 3 的值跟随其他 3 的值总共出现 4 次 - 第一列、第 3 行、第 4 行、第二列、第 2 行和第三列,第 6 行。4 的值跟随 3 的值两次:第一列,第 6 行,第二列,第 3 行。

我们总共计算了 6 个数字,因此除以 6 得出数字 3 为 4/6 或 66.67%,数字 4 为 2/6 或 33.33%。

如果我正确地得到了问题陈述,您可以使用 MATLAB's logical indexing 和基本上由两行组成的方法有效地实现它 -

%// Input 2D matrix
S = [
    2   2   1   2
    2   3   1   1
    3   3   1   1
    3   4   1   1
    3   1   2   1
    4   1   3   1
    1   1   3   1]

Labels = [1:4]'; %//'# Label array

counts = histc(S([false(1,size(S,2)) ; S(1:end-1,:) == 3]),Labels)
Percentages = 100*counts./sum(counts)

Verify/Present 结果

接下来列出的显示输出结果的样式使用 MATLAB's table人类可读 数据格式。

样式 #1

>> table(Labels,Percentages)
ans = 
    Labels    Percentages
    ______    ___________
    1              0     
    2              0     
    3         66.667     
    4         33.333     

样式 #2

你可以做一些花哨的字符串操作来以更具“代表性”的方式呈现结果-

>> Labels_3 = strcat('3-',cellstr(num2str(Labels','%1d')'));
>> table(Labels_3,Percentages)
ans = 
    Labels_3    Percentages
    ________    ___________
    '3-1'            0     
    '3-2'            0     
    '3-3'       66.667     
    '3-4'       33.333    

样式 #3

如果您想根据问题的预期输出部分中列出的百分比以降序排序的方式呈现它们,您可以使用 sort -

执行额外的步骤
>> [Percentages,idx] = sort(Percentages,'descend');
>> Labels_3 = strcat('3-',cellstr(num2str(Labels(idx)','%1d')'));
>> table(Labels_3,Percentages)
ans = 
    Labels_3    Percentages
    ________    ___________
    '3-3'       66.667     
    '3-4'       33.333     
    '3-1'            0     
    '3-2'            0   

奖励内容:查找所有案例的频率(计数)

现在,假设您还想对 124 重复此过程,即查找 1、[=20 之后的匹配项=] 和 4 分别。在这种情况下,您可以针对所有情况重复上述步骤,同样可以使用 arrayfun -

%// Get counts
C = cell2mat(arrayfun(@(n) histc(S([false(1,size(S,2)) ; S(1:end-1,:) == n]),...
    1:4),1:4,'Uni',0))

%// Get percentages
Percentages = 100*bsxfun(@rdivide, C, sum(C,1))

给我们-

Percentages =
   90.9091   20.0000         0  100.0000
    9.0909   20.0000         0         0
         0   60.0000   66.6667         0
         0         0   33.3333         0

     

因此,在 Percentages 中,第一列是 [1,2,3,4] 在输入矩阵中某处出现 1 之后的计数。例如,可以看到 Percentagescolumn -3 是在输入矩阵中 3 之后查找元素时样本输出中的内容。

要生成输出,您可以使用方便的 tabulate

S = [
    2   2   1   2
    2   3   1   1
    3   3   1   1
    3   4   1   1
    3   1   2   1
    4   1   3   1
    1   1   3   1];

idx = find(S(1:end-1,:)==3);
S2 = S(2:end,:);

tabulate(S2(idx))
  Value    Count   Percent
      1        0      0.00%
      2        0      0.00%
      3        4     66.67%
      4        2     33.33%

如果你想为每一列独立计算频率

S = [2   2   1   2
     2   3   1   1
     3   3   1   1
     3   4   1   1
     3   1   2   1
     4   1   3   1
     1   1   3   1];                                    %// data: matrix
N = 3;                                                  %// data: number
r = max(S(:));
[R, C] = size(S);
[ii, jj] = find(S(1:end-1,:)==N);                       %// step 1
count = full(sparse(S(ii+1+(jj-1)*R), jj, 1, r, C));    %// step 2
result = bsxfun(@rdivide, count, sum(S(1:end-1,:)==N)); %// step 3

其工作原理如下:

  1. find 首先用于确定 S 中出现 N 的行和列索引,但最后一行除外。
  2. 第 1 步索引正下方 条目中的值在变量 count 中为每一列累积。很方便的把sparse function is used for this purpose. Note that this uses linear indexing改成S.
  3. 要获得每列的频率,count 除以(bsxfun)每列中 N 的出现次数。

本例中的结果是

result =
         0         0         0       NaN
         0         0         0       NaN
    0.6667    0.5000    1.0000       NaN
    0.3333    0.5000         0       NaN

请注意,最后一列正确包含 NaNs,因为该列未定义所寻找模式的频率。