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

时间:2015-01-14 15:30:48

标签: performance matlab matrix reshape

表示输入矩阵

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];

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

因为第一个输入栏中的每个值可以有不同数量的条目(这里有4个值为" 1"和" 3"但是#34只有3个; 2"),正常的重塑功能不适用。我需要将所有列填充到最大行数。

你知道怎么做这个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

3 个答案:

答案 0 :(得分:4)

以下任一方法均假定 in的第1列已排序,如示例中所示。如果情况并非如此,请首先应用此选项,根据该标准对in进行排序:

in = sortrows(in,1);

方法1(使用accumarray

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

    [~, 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
    

    或者,可以使用histc

    完成步骤1
    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

    使用this answer by @Dan生成行索引向量,然后使用sparse构建矩阵:

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

答案 1 :(得分:2)

建议的解决方案和代码

简介

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

详细了解提议的方法,我们从 histc 的输入的第1列获得每个ID的counts。然后,魔术发生在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

基准测试(性能)

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

基准代码

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.

答案 2 :(得分:0)

略好,但仍使用循环:(

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

不知道你是否可以避免循环...