MATLAB:如何在连续块中按列计算数字频率
MATLAB: How to count number frequency columnwise, in continuous blocks
我在 Matlab 中有一个矩阵 a
,如下所示:
a = zeros(10,3);
a(3:6,1)=2; a(5:9,3)=1; a(5:7,2)=3; a(8:10,1)=2;
a =
0 0 0
0 0 0
2 0 0
2 0 0
2 3 1
2 3 1
0 3 1
2 0 1
2 0 1
2 0 0
我想获取一个元胞数组,其中包含每个数字在一列中出现的次数。此外,无论列号如何,都应根据元素值对其进行排序。在上面的示例中,我想获取单元格:
b = {[5],[4,3],[3]}
因为数字1出现一次5次,数字2在4和3的块中出现两次,数字3出现一次3次。如您所见,重复是根据元素值而不是元素出现的列数排序的。
b = {}
for i = 1:ncolumns
for n = 1:nnumbers
b{i}(n) = sum(a(:,i) == n)
end
end
(请注意,这会将计数为 0 的数字置零,否则我看不出您还能如何识别正在计数的值)
困难的部分是找到并分离方块。 diff
会找到任意运行个数字的起点,也就是这个解法的起点:
b = [zeros(1,size(a,2)); a; zeros(1,size(a,2))];
idx = diff(b)~=0;
block_values = b(idx);
block_lengths = diff([0; find(idx)]);
现在我们有每个块的值的两个向量,以及它们有多长,只需要在元胞数组中捕获它们,忽略零块
c = accumarray(block_values(block_values~=0), block_lengths(block_values~=0), [], @(x) {x}).';
由于您不关心列,您可以将所有列串成一个列向量,在两端填充零以防止列的开头和结尾处的跨度 运行 :
v = reshape(padarray(a, [1 0]), [], 1);
% Or if you don't have the Image Processing Toolbox function padarray...
v = reshape([zeros(1, size(a, 2)); a; zeros(1, size(a, 2))], [], 1);
现在,假设跨度总是由 1 个或多个零分隔,您可以找到每个跨度的长度如下:
endPoints = find(diff(v) ~= 0); % Find where transitions to or from 0 occur
spans = endPoints(2:2:end)-endPoints(1:2:end); % Index of transitions to 0 minus
% index of transitions from 0
最后,您可以 accumulate 基于这些跨度中存在的值的跨度:
b = accumarray(v(endPoints(1:2:end)+1), spans, [], @(v) {v(:).'}).';
举个例子:
b =
1×3 cell array
[5] [1×2 double] [3]
注:
生成的元胞数组中值的排序不保证与 spans
中的顺序匹配(即上面的 b{2}
是 [3 4]
而不是 [4 3]
)。如果顺序很重要,您需要对下标进行排序 as per this section of the documentation。下面是如何更改 b
:
的计算
[vals, index] = sort(v(endPoints(1:2:end)+1));
b = accumarray(vals, spans(index), [], @(v) {v(:).'}).';
我在 Matlab 中有一个矩阵 a
,如下所示:
a = zeros(10,3);
a(3:6,1)=2; a(5:9,3)=1; a(5:7,2)=3; a(8:10,1)=2;
a =
0 0 0
0 0 0
2 0 0
2 0 0
2 3 1
2 3 1
0 3 1
2 0 1
2 0 1
2 0 0
我想获取一个元胞数组,其中包含每个数字在一列中出现的次数。此外,无论列号如何,都应根据元素值对其进行排序。在上面的示例中,我想获取单元格:
b = {[5],[4,3],[3]}
因为数字1出现一次5次,数字2在4和3的块中出现两次,数字3出现一次3次。如您所见,重复是根据元素值而不是元素出现的列数排序的。
b = {}
for i = 1:ncolumns
for n = 1:nnumbers
b{i}(n) = sum(a(:,i) == n)
end
end
(请注意,这会将计数为 0 的数字置零,否则我看不出您还能如何识别正在计数的值)
困难的部分是找到并分离方块。 diff
会找到任意运行个数字的起点,也就是这个解法的起点:
b = [zeros(1,size(a,2)); a; zeros(1,size(a,2))];
idx = diff(b)~=0;
block_values = b(idx);
block_lengths = diff([0; find(idx)]);
现在我们有每个块的值的两个向量,以及它们有多长,只需要在元胞数组中捕获它们,忽略零块
c = accumarray(block_values(block_values~=0), block_lengths(block_values~=0), [], @(x) {x}).';
由于您不关心列,您可以将所有列串成一个列向量,在两端填充零以防止列的开头和结尾处的跨度 运行 :
v = reshape(padarray(a, [1 0]), [], 1);
% Or if you don't have the Image Processing Toolbox function padarray...
v = reshape([zeros(1, size(a, 2)); a; zeros(1, size(a, 2))], [], 1);
现在,假设跨度总是由 1 个或多个零分隔,您可以找到每个跨度的长度如下:
endPoints = find(diff(v) ~= 0); % Find where transitions to or from 0 occur
spans = endPoints(2:2:end)-endPoints(1:2:end); % Index of transitions to 0 minus
% index of transitions from 0
最后,您可以 accumulate 基于这些跨度中存在的值的跨度:
b = accumarray(v(endPoints(1:2:end)+1), spans, [], @(v) {v(:).'}).';
举个例子:
b =
1×3 cell array
[5] [1×2 double] [3]
注:
生成的元胞数组中值的排序不保证与 spans
中的顺序匹配(即上面的 b{2}
是 [3 4]
而不是 [4 3]
)。如果顺序很重要,您需要对下标进行排序 as per this section of the documentation。下面是如何更改 b
:
[vals, index] = sort(v(endPoints(1:2:end)+1));
b = accumarray(vals, spans(index), [], @(v) {v(:).'}).';