在 matlab 中使用列零填充将向量重塑为矩阵

Reshape vector to matrix with column-wise zero padding in matlab

对于输入矩阵

in = [1 1;
      1 2;
      1 3;
      1 4;
      2 5;
      2 6;
      2 7;
      3 8;
      3 9;
      3 10;
      3 11];

我想得到输出矩阵

out = [1 5 8;
       2 6 9;
       3 7 10;
       4 0 11];

意思是我想将第二个输入列重塑为输出矩阵,其中与第一个输入列中的一个值对应的所有值都写入输出矩阵的一列。

由于第一个输入列中的每个值可以有不同数量的条目(这里“1”和“3”有 4 个值,但“2”只有 3 个值),正常的重塑功能不适用.我需要将所有列填充到最大行数。

你知道如何做这个 matlab-ish 吗?

第二个输入列只能包含正数,因此填充值可以是0-xNaN、...

我能想到的最好的是这个(基于循环):

maxNumElem = 0;
for i=in(1,1):in(end,1)
    maxNumElem = max(maxNumElem,numel(find(in(:,1)==i)));
end

out = zeros(maxNumElem,in(end,1)-in(1,1));
for i=in(1,1):in(end,1)
    tmp = in(in(:,1)==i,2);
    out(1:length(tmp),i) = tmp;
end

稍微好一点,但仍然使用循环:(

out=zeros(4,3);%set to zero matrix
for i = 1:max(in(:,1)); %find max in column 1, and loop for that number
ind = find(in(:,1)==i); % 
out(1: size(in(ind,2),1),i)= in(ind,2);
end

不知道能不能避免循环...

以下任一方法都假设 in 的第 1 列 已排序 ,如示例中所示。如果不是这种情况,请最初应用它根据该标准对 in 进行排序:

in = sortrows(in,1);

方法 1(使用 accumarray

  1. 使用mode;
  2. 计算所需的行数
  3. 使用accumarray收集每列对应的值,最后用零填充。结果是一个单元格;
  4. 横向连接所有单元格的内容。

代码:

[~, n] = mode(in(:,1));                                                 %//step 1
out = accumarray(in(:,1), in(:,2), [], @(x){[x; zeros(n-numel(x),1)]}); %//step 2
out = [out{:}];                                                         %//step 3

或者,第 1 步可以用 histc

完成
n = max(histc(in(:,1), unique(in(:,1))));                               %//step 1

accumarray:

n = max(accumarray(in(:,1), in(:,2), [], @(x) numel(x)));               %//step 1

方法 2(使用 sparse

使用 , and then build your matrix with sparse:

生成行索引向量
a = arrayfun(@(x)(1:x), diff(find([1,diff(in(:,1).'),1])), 'uni', 0); %//'
out = full(sparse([a{:}], in(:,1), in(:,2)));

提议的解决方案和代码介绍

这里提出的是一种基于 bsxfun 的屏蔽方法,它使用可作为内置函数与 bsxfun 一起使用的二元运算符,因此我认为这非常适合解决问题像这样。当然,您还必须知道 bsxfun 是一个占用大量内存的工具。因此,如果您正在处理 billions of elements,这可能会构成威胁,这也取决于 MATLAB 使用的可用内存。

深入了解所提出方法的细节,我们从输入的第 1 列获得每个 ID 的 counts histc。然后,神奇的是 bsxfun + @le 在输出数组(由 zeros 初始化)中创建一个位置掩码,这些位置将由输入中的第 2 列元素填充。这就是您使用此方法解决问题所需的全部内容。

解决方案代码

counts = histc(in(:,1),1:max(in(:,1)))'; %//' counts of each ID from column1

max_counts = max(counts); %// Maximum counts for each ID
mask = bsxfun(@le,[1:max_counts]',counts); %//'# mask of locations where
                                           %//   column2 elements are to be placed
out = zeros(max_counts,numel(counts)); %// Initialize the output array
out(mask) = in(:,2); %// place the column2 elements in the output array

基准测试(性能)

此处介绍的基准测试将 post 中提出的解决方案与 中介绍的各种方法进行了比较。这跳过了问题中出现的原始循环方法,因为它对于基准测试代码中生成的输入来说似乎非常慢。

基准代码

num_ids = 5000;
counts_each_id = randi([10 100],num_ids,1);
num_runs = 20;  %// number of iterations each approach is run for

%// Generate random input array
in = [];
for k = 1:num_ids
    in = [in ; [repmat(k,counts_each_id(k),1) rand(counts_each_id(k),1)]];
end

%// Warm up tic/toc.
for k = 1:50000
    tic(); elapsed = toc();
end

disp('------------- With HISTC + BSXFUN Masking approach')
tic
for iter = 1:num_runs
    counts = histc(in(:,1),1:max(in(:,1)))';
    max_counts = max(counts);
    out = zeros(max_counts,numel(counts));
    out(bsxfun(@le,[1:max_counts]',counts)) = in(:,2);
end
toc
clear counts max_counts out

disp('------------- With MODE + ACCUMARRAY approach')
tic
for iter = 1:num_runs
    [~, n] = mode(in(:,1));                                                 %//step 1
    out = accumarray(in(:,1), in(:,2), [], @(x){[x; zeros(n-numel(x),1)]}); %//step 2
    out = [out{:}];
end
toc
clear n out

disp('------------- With HISTC + ACCUMARRAY approach')
tic
for iter = 1:num_runs
    n = max(histc(in(:,1), unique(in(:,1))));
    out = accumarray(in(:,1), in(:,2), [], @(x){[x; zeros(n-numel(x),1)]}); %//step 2
    out = [out{:}];
end
toc
clear n out

disp('------------- With ARRAYFUN + Sparse approach')
tic
for iter = 1:num_runs
    a = arrayfun(@(x)(1:x), diff(find([1,diff(in(:,1).'),1])), 'uni', 0); %//'
    out = full(sparse([a{:}], in(:,1), in(:,2)));
end
toc
clear a out

结果

------------- With HISTC + BSXFUN Masking approach
Elapsed time is 0.598359 seconds.
------------- With MODE + ACCUMARRAY approach
Elapsed time is 2.452778 seconds.
------------- With HISTC + ACCUMARRAY approach
Elapsed time is 2.579482 seconds.
------------- With ARRAYFUN + Sparse approach
Elapsed time is 1.455362 seconds.