2011-10-28 36 views
11

C'è un modo per "vector" assegnare un array di struct.Matlab array of struct: assegnazione rapida

Attualmente posso

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; 

Ma questo è lento, ho voglia di fare qualcosa di più come

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

Tutte le idee/suggerimenti per vettorizzare questo compito, in modo che sarà più veloce.

Grazie in anticipo.

+2

questo post potrebbe essere d'aiuto: http://stackoverflow.com/questions/4166438/how-do-i-define-a-structure-in-matlab/4169216#4169216 – Amro

risposta

8

Puoi provare a utilizzare la funzione Matlab deal, ma ho scoperto che è necessario modificare leggermente l'input (utilizzando questa domanda: In Matlab, for a multiple input function, how to use a single input as multiple inputs?), forse c'è qualcosa di più semplice.

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

Inoltre ho trovato che questo non è quasi veloce come il ciclo for sul mio computer (~ 0.35s per affare rispetto a ~ 0.05s per il ciclo), presumibilmente a causa della chiamata a mat2cell. La differenza di velocità si riduce se la usi più di una volta, ma rimane a favore del ciclo for.

+0

È fantastico, grazie. – sumodds

+2

Questi sono i miei tempi. Su Octave: .17s per 100K e 1.57s per 1mil per questo metodo e richiede sempre se utilizzo loop, come 230s per 100K. MATLAB 2009B (diff machine/OS): 5s/49s usando sopra e .22s/2.2s usando for loop. – sumodds

2

C'è qualcosa che richiede di utilizzare in modo particolare una struttura in questo modo?

Considera di sostituire la matrice di strutture semplicemente con una matrice separata per ogni membro della struttura.

weights = rand(1, 1000); 

Se si dispone di un membro struct, che è un array, è possibile effettuare una dimensione extra:

matrices = rand(3, 3, 1000); 

Se si desidera solo per mantenere le cose pulite, si potrebbe mettere queste matrici in una struct:

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

Ma se avete bisogno di tenere una serie di struct, penso che si può fare

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

Entrambi fanno lo stesso. Ma penso di aver bisogno che sia una matrice di strutture (ovvero oggetti di array) e non una struttura di array (una singola grande struttura di un array di grandi dimensioni). Qual è la differenza tra i due in MATLAB, ce ne sono? Significato dell'assegnazione della memoria e, in caso affermativo, qual è la sua implicazione? – sumodds

+0

Grazie comunque. :) – sumodds

+1

La differenza è che in Matlab, una matrice di strutture ("struct-organizzata") è grossolanamente inefficiente perché ogni struttura memorizza ciascuno dei suoi campi in una matrice separata, quindi non è possibile eseguire operazioni vettorializzate su di essi. Una struttura di matrici ("organizzate planari") come quella di Brian memorizzerà ciascuno dei suoi campi in matrici primitive che sono contigue in memoria, e le funzioni di Matlab (veloce) vettorializzate funzioneranno. È una struttura molto migliore per Matlab e più idiomatica. –

7

Si potrebbe semplicemente scrivere:

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

Questo è molto più veloce di affare o un anello (almeno sul mio sistema):

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

Utilizzando le parentesi graffe sulla destra dà un separato da virgole la lista valori di tutti i valori in W (es. N output) e l'uso di parentesi quadre a destra assegna tali uscite N ai valori N in edge (:). weight.

+0

Bello! Sinteticamente e pragmaticamente elegante! Sarebbe bello se la sintassi di Matlab permettesse di espandere gli array in una sequenza di argomenti, qualcosa come "{values} {:}".Ho provato a fare una funzione per prendere una lista valori di celle, ma a quanto pare non gli piace assegnare a 'varargout' nello stesso modo in cui' deal() 'ha haha. – eacousineau

+0

Whoops, ho trovato che stavo usando 'mat2cell()' invece di 'num2cell()'. Ecco la funzione: ['cellexpand()'] (https://gist.github.com/eacousineau/9699289#file-cellexpand-m). – eacousineau

+0

Puoi anche usare maniglie anonime: 'cellexpand = @ (x) x {:}; numexpand = @ (x) cellexpand (num2cell (x)); '. Un esempio: '[a, b] = numexpand ([1, 2]);'. Esempio più specifico: '[edge.weight] = numexpand ([edge.weight] + 50);' – eacousineau

1

Il motivo per cui le strutture nell'esempio non vengono inizializzate correttamente è che la sintassi che si sta utilizzando si rivolge solo all'ultimo elemento nell'array struct. Per un array inesistente, il resto viene compilato implicitamente con le strutture che hanno il valore predefinito [] in tutti i loro campi.

per rendere questo comportamento chiaro, prova a fare una breve array con clear edges; edges(1:3) = struct('weight',1.0) e guardando a ciascuno dei edges(1), edges(2) e edges(3).L'elemento ha 1.0 nel suo peso come desideri; gli altri hanno [].

La sintassi per inizializzare in modo efficiente una serie di strutture è una di queste.

% 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 

Nota la 1:1000 invece di 1000 l'indicizzazione in all'array bordi inizializzato.

C'è un problema con il modulo edges(1:1000): se edges è già inizializzato, questa sintassi aggiornerà solo i valori degli elementi selezionati. Se i bordi hanno più di 1000 elementi, gli altri rimarranno invariati e il tuo codice sarà bacato. Oppure se edges è di un tipo diverso, potresti ottenere un errore o un comportamento strano a seconda del tipo di dati esistente. Per sicurezza, è necessario eseguire clear edges prima di inizializzare utilizzando la sintassi di indicizzazione. Quindi è meglio fare solo assegnamento completo con il modulo repmat.

MA: Indipendentemente da come si inizializza un array-di-struct come questo sta andando sempre essere intrinsecamente lento per lavorare con per il set di dati più grandi. Non si possono fare reali operazioni "vettorializzate" su di esso perché i vostri array primitivi sono tutti suddivisi in per separare mxArray all'interno di ogni elemento struct. Ciò include l'assegnazione del campo nella tua domanda - non è possibile vettorizzare ciò. Invece, dovresti cambiare una struttura di array come suggerisce la risposta di Brian L's.

0

È possibile utilizzare una struttura inversa e poi fare tutte le operazioni senza errori come questo

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

e quindi l'operazione come la seguente

x.E 

ans = 

    3  8  5 

o come questo

x.E(1:2)=2 

x = 

    E: [2 2 5] 

o forse questo

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

x = 

    E: [10 15 20] 

È molto più veloce di for_loop e non sono necessarie altre grandi funzioni per rallentare il programma.