Buscar..


Funciones útiles que operan en celdas y matrices.

Este sencillo ejemplo proporciona una explicación de algunas funciones que me parecieron extremadamente útiles desde que comencé a usar MATLAB: cellfun , arrayfun . La idea es tomar una variable de clase de matriz o celda, recorrer todos sus elementos y aplicar una función dedicada a cada elemento. Una función aplicada puede ser anónima, que suele ser un caso, o cualquier función regular definida en un archivo * .m.

Comencemos con un problema simple y digamos que necesitamos encontrar una lista de archivos * .mat en la carpeta. Para este ejemplo, primero vamos a crear algunos archivos * .mat en una carpeta actual:

for n=1:10; save(sprintf('mymatfile%d.mat',n)); end

Después de ejecutar el código, debe haber 10 archivos nuevos con la extensión * .mat. Si ejecutamos un comando para listar todos los archivos * .mat, como:

mydir = dir('*.mat');

debemos obtener una serie de elementos de una estructura dir; MATLAB debería dar un resultado similar a este:

10x1 struct array with fields:
    name
    date
    bytes
    isdir
    datenum

Como puedes ver, cada elemento de esta matriz es una estructura con un par de campos. Toda la información es realmente importante con respecto a cada archivo, pero en el 99% estoy bastante interesado en los nombres de archivo y nada más. Para extraer información de una matriz de estructura, solía crear una función local que implicaría crear variables temporales de un tamaño correcto, para bucles, extraer un nombre de cada elemento y guardarla en la variable creada. Una forma mucho más fácil de lograr exactamente el mismo resultado es usar una de las funciones mencionadas anteriormente:

mydirlist = arrayfun(@(x) x.name, dir('*.mat'), 'UniformOutput', false)
mydirlist = 
    'mymatfile1.mat'
    'mymatfile10.mat'
    'mymatfile2.mat'
    'mymatfile3.mat'
    'mymatfile4.mat'
    'mymatfile5.mat'
    'mymatfile6.mat'
    'mymatfile7.mat'
    'mymatfile8.mat'
    'mymatfile9.mat'

¿Cómo funciona esta función? Por lo general, toma dos parámetros: un controlador de función como primer parámetro y una matriz. Entonces, una función operará en cada elemento de una matriz dada. Los parámetros tercero y cuarto son opcionales pero importantes. Si sabemos que una salida no será regular, debe guardarse en la celda. Esto debe ser señalado estableciendo false a UniformOutput . De forma predeterminada, esta función intenta devolver una salida regular, como un vector de números. Por ejemplo, extraigamos información sobre la cantidad de espacio en disco que toma cada archivo en bytes:

mydirbytes = arrayfun(@(x) x.bytes, dir('*.mat'))
mydirbytes =
       34560
       34560
       34560
       34560
       34560
       34560
       34560
       34560
       34560
       34560

o kilobytes:

mydirbytes = arrayfun(@(x) x.bytes/1024, dir('*.mat'))
mydirbytes =
   33.7500
   33.7500
   33.7500
   33.7500
   33.7500
   33.7500
   33.7500
   33.7500
   33.7500
   33.7500

Esta vez la salida es un vector regular de doble. UniformOutput se estableció en true de forma predeterminada.

cellfun es una función similar. La diferencia entre esta función y arrayfun es que cellfun opera en variables de clase de celda. Si deseamos extraer solo los nombres dados una lista de nombres de archivos en una celda 'mydirlist', solo tendríamos que ejecutar esta función de la siguiente manera:

mydirnames = cellfun(@(x) x(1:end-4), mydirlist, 'UniformOutput', false)
mydirnames = 
    'mymatfile1'
    'mymatfile10'
    'mymatfile2'
    'mymatfile3'
    'mymatfile4'
    'mymatfile5'
    'mymatfile6'
    'mymatfile7'
    'mymatfile8'
    'mymatfile9'

Nuevamente, como una salida no es un vector regular de números, una salida debe guardarse en una variable de celda.

En el siguiente ejemplo, combino dos funciones en una y devuelvo solo una lista de nombres de archivos sin una extensión:

cellfun(@(x) x(1:end-4), arrayfun(@(x) x.name, dir('*.mat'), 'UniformOutput', false), 'UniformOutput', false)
ans = 
    'mymatfile1'
    'mymatfile10'
    'mymatfile2'
    'mymatfile3'
    'mymatfile4'
    'mymatfile5'
    'mymatfile6'
    'mymatfile7'
    'mymatfile8'
    'mymatfile9'

Es una locura pero es muy posible porque arrayfun devuelve una celda que es la entrada esperada de cellfun ; una nota al margen de esto es que podemos forzar a cualquiera de esas funciones a devolver resultados en una variable de celda estableciendo UniformOutput en falso, explícitamente. Siempre podemos obtener resultados en una celda. Es posible que no podamos obtener resultados en un vector regular.

Hay una función más similar que opera en los campos de una estructura: structfun . No lo he encontrado particularmente tan útil como los otros dos, pero brillaría en algunas situaciones. Si, por ejemplo, alguien quisiera saber qué campos son numéricos o no numéricos, el siguiente código puede dar la respuesta:

structfun(@(x) ischar(x), mydir(1))

El primer y el segundo campo de una estructura dir son de tipo char. Por lo tanto, la salida es:

 1
 1
 0
 0
 0

Además, la salida es un vector lógico de true / false . En consecuencia, es regular y se puede guardar en un vector; No hay necesidad de usar una clase celular.

Preferencias de plegado de código

Es posible cambiar la preferencia de plegado de código para satisfacer sus necesidades. Por lo tanto, el plegado de código se puede configurar para habilitar / no poder para construcciones específicas (por ejemplo, if block , for loop , Sections ...).

Para cambiar las preferencias de plegado, vaya a Preferencias -> Plegado de código:

introduzca la descripción de la imagen aquí

A continuación, puede elegir qué parte del código se puede plegar.

Alguna información:

  • Tenga en cuenta que también puede expandir o contraer todo el código de un archivo colocando el cursor en cualquier lugar dentro del archivo, haga clic con el botón derecho y luego seleccione Plegado de código> Expandir todo o Plegado de código> Plegado todo en el menú contextual.
  • Tenga en cuenta que el plegado es persistente, en el sentido de que parte del código que se ha expandido / contraído mantendrá su estado después de que Matlab o el archivo-m se haya cerrado y se vuelva a abrir.

Ejemplo: Para habilitar el plegado de secciones:

Una opción interesante es permitir plegar secciones. Las secciones están delimitadas por dos signos de porcentaje ( %% ).

Ejemplo: Para habilitarlo marque la casilla "Secciones":

introduzca la descripción de la imagen aquí

Entonces, en lugar de ver un código fuente largo similar a:

introduzca la descripción de la imagen aquí

Podrá plegar secciones para tener una visión general de su código: introduzca la descripción de la imagen aquí

Extraer datos de figuras

En algunas ocasiones, tuve una figura interesante que guardé, pero perdí un acceso a sus datos. Este ejemplo muestra un truco de cómo extraer información de una figura.

Las funciones clave son findobj y get . findobj devuelve un controlador a un objeto dado los atributos o las propiedades del objeto, como Type o Color , etc. Una vez que se ha encontrado un objeto de línea, obtener puede devolver cualquier valor en poder de las propiedades. Resulta que los objetos de Line contienen todos los datos en las siguientes propiedades: XData , YData y ZData ; el último suele ser 0 a menos que una figura contenga un gráfico 3D.

El siguiente código crea una figura de ejemplo que muestra dos líneas, una función sin, un umbral y una leyenda

t = (0:1/10:1-1/10)';
y = sin(2*pi*t);
plot(t,y);
hold on;
plot([0 0.9],[0 0], 'k-');
hold off;
legend({'sin' 'threshold'});

El primer uso de findobj devuelve dos manejadores a ambas líneas:

findobj(gcf, 'Type', 'Line')
ans = 
  2x1 Line array:

  Line    (threshold)
  Line    (sin)

Para acotar el resultado, findobj también se puede utilizar combinación de operadores lógicos -and , -or y nombres de propiedades. Por ejemplo, puedo encontrar un objeto de línea cuyo DiplayName es sin y leer su XData y YData .

lineh = findobj(gcf, 'Type', 'Line', '-and', 'DisplayName', 'sin');
xdata = get(lineh, 'XData');
ydata = get(lineh, 'YData');

y compruebe si los datos son iguales.

isequal(t(:),xdata(:))
ans =
     1
isequal(y(:),ydata(:))
ans =
     1

Del mismo modo, puedo restringir mis resultados al excluir la línea negra (umbral):

lineh = findobj(gcf, 'Type', 'Line', '-not', 'Color', 'k');
xdata = get(lineh, 'XData');
ydata = get(lineh, 'YData');

y la última verificación confirma que los datos extraídos de esta figura son los mismos:

isequal(t(:),xdata(:))
ans =
     1
isequal(y(:),ydata(:))
ans =
     1

Programación funcional utilizando funciones anónimas

Se pueden utilizar funciones anónimas para la programación funcional. El principal problema a resolver es que no existe una forma nativa para anclar una recursión, pero esto todavía se puede implementar en una sola línea:

if_ = @(bool, tf) tf{2-bool}();

Esta función acepta un valor booleano y una matriz de celdas de dos funciones. La primera de esas funciones se evalúa si el valor booleano se evalúa como verdadero, y la segunda si el valor booleano se evalúa como falso. Podemos escribir fácilmente la función factorial ahora:

fac = @(n,f) if_(n>1, {@()n*f(n-1,f), @()1});

El problema aquí es que no podemos invocar directamente una llamada recursiva, ya que la función aún no está asignada a una variable cuando se evalúa el lado derecho. Sin embargo podemos completar este paso escribiendo

factorial_ = @(n)fac(n,fac);

Ahora @(n)fac(n,fac) evalúa recursivamente la función factorial. Otra forma de hacer esto en la programación funcional utilizando un combinador y, que también se puede implementar fácilmente:

y_ = @(f)@(n)f(n,f);

Con esta herramienta, la función factorial es aún más corta:

factorial_ = y_(fac);

O directamente:

factorial_ = y_(@(n,f) if_(n>1, {@()n*f(n-1,f), @()1}));

Guarda varias figuras en el mismo archivo .fig

Al colocar varios manejadores de figuras en una matriz de gráficos, se pueden guardar varias figuras en el mismo archivo .fig

h(1) = figure;
scatter(rand(1,100),rand(1,100));

h(2) = figure;
scatter(rand(1,100),rand(1,100));

h(3) = figure;
scatter(rand(1,100),rand(1,100));

savefig(h,'ThreeRandomScatterplots.fig');
close(h);

Esto crea 3 diagramas de dispersión de datos aleatorios, cada parte de la matriz gráfica h. Luego, la matriz de gráficos se puede guardar usando savefig como con una figura normal, pero con el controlador de la matriz de gráficos como un argumento adicional.

Una nota al margen interesante es que las figuras tienden a permanecer dispuestas de la misma manera que se guardaron al abrirlas.

Bloques de comentarios

Si desea comentar parte de su código, los bloques de comentarios pueden ser útiles. El bloque de comentarios comienza con %{ en una línea nueva y termina con %} en otra línea nueva:

a = 10;
b = 3;
%{
c = a*b;
d = a-b;
%}

Esto le permite plegar las secciones que comentó para que el código sea más limpio y compacto.

Estos bloques también son útiles para activar / desactivar partes de su código. Todo lo que tienes que hacer para descomentar el bloqueo es agregar otro % antes de que se establezca:

a = 10;
b = 3;
%%{ <-- another % over here
c = a*b;
d = a-b;
%}

A veces desea comentar una sección del código, pero sin afectar su sangría:

for k = 1:a
    b = b*k;
    c = c-b;
    d = d*c;
    disp(b)
end

Por lo general, cuando marca un bloque de código y presiona Ctrl + r para comentarlo (al agregar el % automáticamente a todas las líneas, luego cuando presiona Ctrl + i más tarde para la sangría automática, el bloque de código se mueve de su jerárquico correcto). lugar, y se movió demasiado a la derecha:

for k = 1:a
    b = b*k;
    %     c = c-b;
    %     d = d*c;
    disp(b)
end

Una forma de resolver esto es usar bloques de comentarios, para que la parte interna del bloque permanezca correctamente sangrada:

for k = 1:a
    b = b*k;
    %{
    c = c-b;
    d = d*c;
    %}
    disp(b)
end


Modified text is an extract of the original Stack Overflow Documentation
Licenciado bajo CC BY-SA 3.0
No afiliado a Stack Overflow