2011-10-28 15 views
11

¿Hay alguna forma de "vector" asignar una matriz de struct.Matlab matriz de struct: Asignación rápida

Actualmente puedo

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; 

Pero eso es lento, quiero hacer algo más como

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

Cualquier ideas/sugerencias para vectorizar esta asignación, por lo que será más rápido.

Gracias de antemano.

+2

esta publicación puede ser de ayuda: http://stackoverflow.com/questions/4166438/how-do-i-define-a-structure-in-matlab/4169216#4169216 – Amro

Respuesta

8

Puede intentar usar la función Matlab deal, pero creo que es necesario ajustar un poco la entrada (usando esta pregunta: In Matlab, for a multiple input function, how to use a single input as multiple inputs?), tal vez haya algo más simple.

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

También he encontrado que esto no es tan rápido como el bucle en mi equipo (~ 0.35s de acuerdo frente a ~ 0.05s para el bucle), presumiblemente debido a la llamada a mat2cell. La diferencia de velocidad se reduce si usa esto más de una vez, pero se mantiene a favor del bucle for.

+0

Eso es increíble, gracias. – sumodds

+2

Estos son mis tiempos. En Octave: .17s para 100K y 1.57s para 1mil para este método y se necesita para siempre si uso for loop, como 230s para 100K. MATLAB 2009B (máquina/OS de diferencia): 5s/49s usando arriba y .22s/2.2s usando for loop. – sumodds

2

¿Hay algo que requiera que usted use particularmente una estructura de esta manera?

Considere reemplazar su conjunto de estructuras simplemente con una matriz separada para cada miembro de la estructura.

weights = rand(1, 1000); 

Si usted tiene un miembro de estructura que es una matriz, se puede hacer una nueva dimensión:

matrices = rand(3, 3, 1000); 

Si lo que desea es mantener las cosas ordenadas, podría poner estas matrices en una estructura:

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

Pero si usted necesita para mantener una serie de estructuras, creo que se puede hacer

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

Ambos hacen lo mismo. Pero, creo que necesito que sea una matriz de estructuras (es decir, objetos de una matriz) y no una estructura de matrices (estructura grande única de una matriz grande). ¿Cuál es la diferencia entre los dos en MATLAB? ¿Hay alguno? Significa w.r.t asignación de memoria y, de ser así, ¿cuál es su implicación? – sumodds

+0

Gracias de todos modos. :) – sumodds

+1

La diferencia es que en Matlab, una matriz de estructuras ("struct-organized") es extremadamente ineficiente porque cada estructura almacena cada uno de sus campos en una matriz separada, por lo que no puede hacer operaciones vectorizadas sobre ellos. Una estructura de matrices ("planar-organized") como la de Brian almacenará cada uno de sus campos en matrices primitivas que son contiguas en la memoria, y las funciones vectorizadas (rápidas) de Matlab funcionarán. Es una estructura mucho mejor para Matlab, y más idiomática. –

7

Usted simplemente puede escribir:

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

Esto es mucho más rápido que el trato o un bucle (al menos en mi 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{:}; 

El uso de llaves a la derecha da una separada por comas lista de valores de todos los valores en W (es decir, N salidas) y utilizando llaves cuadradas a la derecha asigna esas N salidas a los valores N en el borde (:). peso.

+0

¡Agradable! Sintética y pragmáticamente elegante! Sería bueno si la sintaxis de Matlab permitiera expandir las matrices en una secuencia de argumentos, algo así como '{values} {:}'.Intentó hacer una función para tomar una lista de valores de celda, pero aparentemente no le gusta asignar a 'varargout' de la misma manera que' deal() 'hace jaja. – eacousineau

+0

Vaya, encontré que estaba usando 'mat2cell()' en lugar de 'num2cell()'. Aquí está la función: ['cellexpand()'] (https://gist.github.com/eacousineau/9699289#file-cellexpand-m). – eacousineau

+0

También puede usar identificadores anónimos: 'cellexpand = @ (x) x {:}; numexpand = @ (x) cellexpand (num2cell (x)); '. Un ejemplo: '[a, b] = numexpand ([1, 2]);'. Ejemplo más específico: '[edge.weight] = numexpand ([edge.weight] + 50);' – eacousineau

1

La razón por la que las estructuras de su ejemplo no se inicializan correctamente es porque la sintaxis que está utilizando solo se dirige al último elemento de la matriz de estructuras. Para una matriz inexistente, el resto de ellos se completan implícitamente con estructuras que tienen el valor predeterminado [] en todos sus campos.

Para que este comportamiento clara, trate de hacer un corto matriz con clear edges; edges(1:3) = struct('weight',1.0) y mirando a cada uno de edges(1), edges(2) y edges(3).El elemento edges(3) tiene 1.0 en su peso como desee; los otros tienen [].

La sintaxis para inicializar eficientemente una matriz de estructuras es una de ellas.

% 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 del 1:1000 en lugar de sólo 1000 cuando se indexa a la matriz sin inicializar bordes.

Existe un problema con el formulario edges(1:1000): si edges ya está inicializado, esta sintaxis simplemente actualizará los valores de los elementos seleccionados. Si los bordes tienen más de 1000 elementos, los otros se mantendrán sin cambios, y su código tendrá errores. O bien, si edges es de un tipo diferente, puede obtener un error o comportamiento extraño según su tipo de datos existente. Para estar seguro, debe hacer clear edges antes de inicializar usando la sintaxis de indexación. Por lo tanto, es mejor hacer una asignación completa con el formulario repmat.

PERO: Independientemente de cómo lo inicialice, una serie de estructuras como esta siempre será inherentemente lenta para conjuntos de datos más grandes. No se pueden realizar operaciones reales "vectorizadas" porque las matrices primitivas están divididas para separar las mxArrays dentro de cada elemento de estructura. Eso incluye la asignación de campo en su pregunta; no es posible vectorizar eso. En su lugar, debe cambiar una estructura de matrices como sugiere la respuesta de Brian L.

0

Se puede utilizar una estructura inversa y luego hacer todas las operaciones sin ningún error como este

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

y después de la operación como la siguiente

x.E 

ans = 

    3  8  5 

o como esto

x.E(1:2)=2 

x = 

    E: [2 2 5] 

o quizás esto

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

x = 

    E: [10 15 20] 

Es realmente más rápido que for_loop y no necesita otras funciones grandes para desacelerar su programa.