2011-10-28 9 views
11

Y at-il un moyen de "vectoriser" affecter un tableau de struct.Matlab tableau de struct: affectation rapide

Actuellement je peux

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; 

Mais c'est lent, je veux faire quelque chose comme

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

Toutes les idées/suggestions pour vectoriser cette mission, de sorte qu'il sera plus rapide.

Merci d'avance.

+2

cet article peut être utile: http://stackoverflow.com/questions/4166438/how-do-i-define-a-structure-in-matlab/4169216#4169216 – Amro

Répondre

8

Vous pouvez essayer d'utiliser la fonction Matlab deal, mais j'ai trouvé qu'il nécessite de modifier un peu l'entrée (en utilisant cette question: In Matlab, for a multiple input function, how to use a single input as multiple inputs?), peut-être qu'il y a quelque chose de plus simple.

n=100000; 
edges(n)=struct('weight',1.0); 
m=mat2cell(rand(n,1),ones(n,1),1); 
[edges(:).weight]=deal(m{:}); 

Aussi je trouve que ce n'est pas presque aussi rapide que la boucle sur mon ordinateur (~ 0.35s pour beaucoup par rapport à ~ 0.05s pour la boucle), probablement en raison de l'appel à mat2cell. La différence de vitesse est réduite si vous l'utilisez plus d'une fois mais elle reste en faveur de la boucle for.

+0

C'est génial, merci. – sumodds

+2

Ce sont mes temps. Sur Octave: .17s pour 100K et 1.57s pour 1mil pour cette méthode et cela prend pour toujours si j'utilise pour la boucle, comme 230s pour 100K. MATLAB 2009B (diff machine/OS): 5s/49s en utilisant ci-dessus et .22s/2.2s en utilisant pour la boucle. – sumodds

2

Y at-il quelque chose qui vous oblige à utiliser particulièrement une structure de cette façon? Envisagez de remplacer votre tableau de structures par un tableau distinct pour chaque membre de la structure.

weights = rand(1, 1000); 

Si vous avez un membre struct qui est un tableau, vous pouvez faire une dimension supplémentaire:

matrices = rand(3, 3, 1000); 

Si vous voulez juste garder les choses propres, vous pouvez placer ces tableaux dans un struct:

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

Mais si vous avez besoin de garder un tableau de struct, je pense que vous pouvez faire

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

Les deux font la même chose. Mais, je pense que j'ai besoin d'être un tableau de structures (signifiant des objets de tableau) et non une structure de tableaux (une seule grande structure d'un grand tableau). Quelle est la différence entre les deux dans MATLAB, y en a-t-il? Signification w.r.t allocation de la mémoire et si oui, quelle est son implication? – sumodds

+0

Merci quand même. :) – sumodds

+1

La différence est que dans Matlab, un tableau de structures ("struct-organisé") est très inefficace car chaque structure stocke chacun de ses champs dans un tableau séparé, donc vous ne pouvez pas faire d'opérations vectorisées sur eux. Une structure de tableaux ("planar-organisés") comme celle de Brian stockera chacun de ses champs dans des tableaux primitifs contigus en mémoire, et les fonctions matlab vectorisées (rapides) fonctionneront. C'est une bien meilleure structure pour Matlab, et plus idiomatique. –

7

Vous pouvez simplement écrire:

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

Ceci est beaucoup plus rapide que beaucoup ou une boucle (au moins sur mon système):

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{:}; 

avec des accolades à droite donne séparées par une virgule liste de valeurs de toutes les valeurs de W (c.-à-d. N sorties) et en utilisant des accolades carrées sur la droite affecte ces N sorties aux N valeurs dans le bord (:).

+0

Nice! Syntatiquement et pragmatiquement élégant! Ce serait bien si la syntaxe de Matlab permettait d'étendre les tableaux dans une séquence d'arguments, quelque chose comme '{values} {:}'.J'ai essayé de faire une fonction pour prendre une liste de valeurs de cellules, mais apparemment, il n'aime pas assigner 'varargout' de la même manière que' deal() 'haha. – eacousineau

+0

Oups, j'ai trouvé que j'utilisais 'mat2cell()' au lieu de 'num2cell()'. Voici la fonction: ['cellexpand()'] (https://gist.github.com/eacousineau/9699289#file-cellexpand-m). – eacousineau

+0

Vous pouvez également utiliser des handles anonymes: 'cellexpand = @ (x) x {:}; numexpand = @ (x) cellexpand (num2cell (x)); Un exemple: '[a, b] = numexpand ([1, 2]);'. Exemple plus spécifique: '[edge.weight] = numexpand ([edge.weight] + 50);' – eacousineau

1

La raison pour laquelle les structures de votre exemple ne sont pas initialisées correctement est que la syntaxe que vous utilisez n'adresse que le dernier élément du tableau struct. Pour un tableau inexistant, le reste d'entre eux est implicitement rempli avec des structs qui ont la valeur par défaut [] dans tous leurs champs.

Pour rendre ce comportement clair, essayez de faire un tableau court avec clear edges; edges(1:3) = struct('weight',1.0) et regardant chacun des edges(1), edges(2) et edges(3).L'élément edges(3) a 1.0 dans son poids comme vous voulez; les autres ont [].

La syntaxe pour initialiser efficacement un tableau de structures est l'une d'entre elles.

% 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 

Notez le 1:1000 au lieu de simplement 1000 lors de l'indexation dans le tableau des bords non initialisée.

Il y a un problème avec le formulaire edges(1:1000): si edges est déjà initialisé, cette syntaxe mettra simplement à jour les valeurs des éléments sélectionnés. Si les bords ont plus de 1000 éléments, les autres seront laissés inchangés, et votre code sera bogué. Ou si edges est un type différent, vous pourriez obtenir une erreur ou un comportement étrange en fonction de son type de données existant. Pour être sûr, vous devez faire clear edges avant d'initialiser en utilisant la syntaxe d'indexation. Il est donc préférable de faire une affectation complète avec le formulaire repmat. Quelle que soit la façon dont vous l'initialisez, un tableau de structures comme celui-ci sera toujours lent à travailler avec des ensembles de données plus volumineux. Vous ne pouvez pas effectuer de véritables opérations "vectorisées" car vos tableaux primitifs sont tous fragmentés pour séparer mxArrays dans chaque élément struct. Cela inclut l'assignation de champ dans votre question - il n'est pas possible de le vectoriser. Au lieu de cela, vous devriez changer une structure de tableaux comme suggère la réponse de Brian L.

0

Vous pouvez utiliser une struct inverse puis effectuez toutes les opérations sans erreur comme celui-ci

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

puis l'opération comme suit

x.E 

ans = 

    3  8  5 

ou comme celui-ci

x.E(1:2)=2 

x = 

    E: [2 2 5] 

ou peut-être cela

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

x = 

    E: [10 15 20] 

Il est vraiment plus rapide que for_loop et vous n'avez pas besoin d'autres grandes fonctions pour ralentir votre programme.