【问题标题】:Rearrange structure arrays of uneven lengths to single 1d array将长度不均匀的结构数组重新排列为单个一维数组
【发布时间】:2012-10-18 07:50:52
【问题描述】:

我有一个结构数组,包含三个字段 - 一个数组、数组的长度和一个数字。

N = 5;
data = struct;
for i=1:N
    n = ceil(rand * 3);
    data(i).len = n;
    data(i).array = rand(1,n);
    data(i).number = i;
end

数据如下:

data = 
1x5 struct array with fields:
    len    = [ 1 3 3 1 1 ]
    array  = [[0.8]; [0.7 0.9 0.4]; [0.7 0 0.3]; [0.1]; [0.3]]
    number = [ 1 2 3 4 5 ]

我可以通过多种方式将数组返回为 1x9 数组:

>>> [data.array] 
>>> cat(2,data.array)
[0.8 | 0.7 0.9 0.4 | 0.7 0 0.3 | 0.1 | 0.3]     %  | shows array separation

我想重复数字 (data.number) len 次,以生成与串联数组相同长度的数组。

我目前正在使用arrayfun 然后cell2mat

>> x = arrayfun(@(x) repmat(x.number, 1, x.len), data, 'UniformOutput', false)
x = 
    [1]    [1x3 double]    [1x3 double]    [4]    [5]
>> cell2mat(x)
[ 1 2 2 2 3 3 3 4 5]

这使得数字与数组对齐。

arrays =  [ 0.8 | 0.7 0.9 0.4 | 0.7 0 0.3 | 0.1 | 0.3 ] 
numbers = [ 1   | 2   2   2   | 3   3   3 | 4   | 5   ]

这背后的想法是将数据提供给 GPU 进行处理 - 但重新排列数据所需的时间比实际处理时间长几个数量级。

Arrayfun 在 N=100,000 时需要约 5 秒,而 for 循环调用 repmat 需要约 4 秒。

有没有更快的方法将结构中不均匀数组中的数据重新排列成匹配长度的一维数组?我愿意使用不同的数据结构。


测试矢量化方法:

data = struct;
data(1).len = 1;
data(1).array = [1 2 3];
data(1).number = 11;
data(2).len = 0;
data(2).array = [];
data(2).number = 12;
data(3).len = 2;
data(3).array = [4 5 6; 7 8 9];
data(3).number = 13;

list_of_array = cat(1,data.array)

idx = zeros(1,size(list_of_array,1));
% Set start of each array to 1
len = cumsum([data.len])
idx(len) = 1
% Flat indices
idx = cumsum([1 idx(1:end-1)])

nf = [data.number]
repeated_num_faces = nf(idx)

给出输出:

list_of_array =
     1     2     3
     4     5     6
     7     8     9
len =
     1     1     3    % Cumulative lengths
idx =
     1     0     1    % Ones at start
idx =
     1     2     2    % Flat indexes - should be [1 3 3]
nf =
    11    12    13    % Numbers expanded
repeated_num_faces =
    11    12    12    % Wrong .numbers - should be [11 13 13]

【问题讨论】:

  • 好的。如果您确保当data(i).array 为空时,矢量化代码将适用于空data.arraydata(i).number 也为空。否则数字映射与数组长度不一致。

标签: arrays performance matlab structure


【解决方案1】:

好吧,struct 在这里并不是最容易处理的。当然,你不应该使用repmat。而不是这样,预分配data_number 数组并执行for 循环:

tic;
data_array  = [data(:).array];
data_number = zeros(size(data_array));
start = 1;
for i=1:N
    nel = data(i).len;
    data_number(start:start+nel-1) = data(i).number;
    start = start+nel;
end
toc;

这是另一个使用cumsum 标记“平面”向量中的索引的“向量化”解决方案

tic;
data_array  = [data.array];
data_number = zeros(size(data_array));

% cumulative sum of number of elements in every array
len = cumsum([data.len]);

% mark the end of every array in a 'flat' vector
data_number(len) = 1;

% compute 'flat' indices for every data(i).array
data_number = cumsum([1 data_number(1:end-1)]);

% extract the data.number field
data_num = [data.number];
data_number = data_num(data_number);
toc;

对于N=1e5 的数据集,时间为:

Elapsed time is 0.153539 seconds.
Elapsed time is 0.110694 seconds.

【讨论】:

  • 0.35s 在我的,使用 1e5 点!好多了 - 非常感谢!
  • @AlexL 您可能想要更正代码 - 缺少 -1。我还添加了一个类似的“矢量化”版本。
  • 在您的“矢量化”解决方案中,我认为您认为 .number 是数组中的索引是否正确?
  • @AlexL 是的!好点子。但在您的示例中,它实际上只是i,所以我想我忘记了data.number。你需要这个固定吗?可以做到的。
  • @AlexL 我已经更新了我的答案 - 因为这些只是索引,所以提取 data.number 值是微不足道的。但似乎循环更快。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2019-11-14
  • 2021-12-23
  • 2013-05-27
  • 2017-06-28
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多