Matlab结构体数组:快速赋值

12
有没有办法“向量”赋值结构体数组?
目前我可以:
edges(1000000) = struct('weight',1.0); //This really does not assign the value, I checked on 2009A.
for i=1:1000000; edges(i).weight=1.0; end; 

但是那样很慢,我想做一些更像

edges(:).weight=[rand(1000000,1)]; //with or without the square brackets. 

有什么想法/建议可以将这个赋值向量化,以使其更快。

提前致谢。


2
这篇帖子可能会有所帮助:https://dev59.com/V2855IYBdhLWcg3wvnKE#4169216 - Amro
6个回答

13

这比deal或循环快得多(至少在我的系统上):

N=10000;
edge(N) = struct('weight',1.0); % initialize the array
values = rand(1,N);  % set the values as a vector

W = mat2cell(values, 1,ones(1,N)); % convert values to a cell
[edge(:).weight] = W{:};

在右侧使用花括号会给出W中所有值的逗号分隔列表(即N个输出),而在右侧使用方括号则将这些N个输出分配给edge(:).weight中的N个值。


不错!在语法和实用性上都很优雅!如果Matlab语法允许将数组扩展为参数序列,比如'{values}{:}'这样的东西,那就太好了。我试着写一个函数来接受一个单元值列表,但显然它不喜欢以与deal()完全相同的方式分配给varargout哈哈。 - eacousineau
糟糕,我发现我使用了mat2cell()而不是num2cell()。这是函数:cellexpand() - eacousineau
你也可以使用匿名句柄:cellexpand = @(x) x {:}; numexpand = @(x) cellexpand(num2cell(x));。例如:[a, b] = numexpand([1, 2]);。更具体的例子:[edge.weight] = numexpand([edge.weight] + 50); - eacousineau

8
您可以尝试使用Matlab函数deal,但我发现需要稍微调整输入(使用这个问题:在Matlab中,对于多个输入函数,如何将单个输入用作多个输入?),也许有更简单的方法。
n=100000;
edges(n)=struct('weight',1.0);
m=mat2cell(rand(n,1),ones(n,1),1);
[edges(:).weight]=deal(m{:});

另外,我发现这种方式在我的电脑上并不像for循环那样快(deal需要约0.35秒,而循环只需要约0.05秒),这可能是因为调用了mat2cell函数。如果你使用这种方式多于一次,则速度差异会减小,但循环仍然更快。


2
这是我的时间记录。在Octave上,对于这种方法100K需要0.17秒,而100万需要1.57秒;如果我使用for循环,就算只是处理100K数据也需要230秒的时间。在MATLAB 2009B(不同的机器/操作系统):使用上述方法需要5秒/49秒,而使用for循环则需要0.22秒/2.2秒的时间。 - sumodds

7
您可以简单地写成以下内容:

您可以简单地编写:

edges = struct('weight', num2cell(rand(1000000,1)));

2

您是否有特别需要以这种方式使用结构体的原因?

考虑用一个单独的数组来替换您的结构体数组,每个数组对应结构体中的一个成员。

weights = rand(1, 1000);

如果你有一个结构体成员是数组,你可以添加一个额外的维度:
matrices = rand(3, 3, 1000);

如果您只是想保持事情的整洁,您可以将这些数组放入一个结构体中:

edges.weights = weights;
edges.matrices = matrices;

但是如果您需要保留一个结构体数组,我认为您可以这样做:

[edges.weight] = rand(1, 1000);

两者的功能是相同的,但我认为我需要它成为结构体数组(即数组对象),而不是数组结构体(大数组的单个大结构体)。在MATLAB中两者之间有什么区别,是否存在?指内存分配方面是否有所不同,如果是,则其含义是什么? - sumodds
1
不同之处在于,在Matlab中,结构体数组(“结构体组织”)非常低效,因为每个结构体将其各个字段存储在单独的数组中,因此无法对它们进行向量化操作。像Brian的那样的数组结构(“平面组织”)将其各个字段存储在原始数组中,这些数组在内存中是连续的,并且可以使用向量化(快速)的Matlab函数进行操作。这是Matlab更好的结构,也更符合惯用法。 - Andrew Janke

1
你的示例中结构体未能正确初始化的原因是,你使用的语法仅针对结构体数组中的最后一个元素。对于不存在的数组,其余部分将被隐式填充为所有字段都具有默认值[]的结构体。
为了清楚地说明这种行为,请尝试使用clear edges; edges(1:3) = struct('weight',1.0)创建一个短数组,并查看每个edges(1)edges(2)edges(3)edges(3)元素的权重为1.0,就像你想要的一样;其他元素的权重为[]
有效初始化结构体数组的语法之一如下。
% Using repmat and full assignment
edges = repmat(struct('weight', 1.0), [1 1000]);

% Using indexing
% NOTE: Only correct if variable is uninitialized!!!
edges(1:1000) = struct('weight', 1.0);  % QUESTIONABLE

注意,在访问未初始化的边缘数组时,使用1:1000而不是只有1000

edges(1:1000)形式存在问题:如果edges已经被初始化,此语法将仅更新所选元素的值。如果edges具有超过1000个元素,其他元素将保持不变,您的代码将出现错误。或者,如果edges是不同类型的,则根据其现有数据类型可能会收到错误或奇怪的行为。为了安全起见,在使用索引语法初始化之前,需要执行clear edges。因此,最好使用repmat表单进行全面分配。

BUT:无论如何初始化,类似这样的结构体数组在处理更大的数据集时始终会本质上变慢。您无法对其进行真正的“向量化”操作,因为每个结构体元素内部的原始数组都被拆分成单独的mxArrays。这包括您提问中的字段分配-无法向量化。相反,您应该像Brian L的答案建议的那样切换到数组结构。


0

您可以使用反向结构,然后执行所有操作而不会出现任何错误,就像这样

x.E(1)=1;
x.E(2)=3;
x.E(2)=8;
x.E(3)=5;

然后进行如下操作

x.E

ans =

    3     8     5

或者像这样

x.E(1:2)=2

x = 

    E: [2 2 5]

或者是这个

x.E(1:3)=[2,3,4]*5

x = 

    E: [10 15 20]

它比for循环快得多,而且您不需要其他大型函数来减慢程序。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接