MATLAB Language
Astuces utiles
Recherche…
Fonctions utiles sur les cellules et les matrices
Cet exemple simple fournit une explication sur certaines fonctions que j'ai trouvées extrêmement utiles depuis que j'ai commencé à utiliser MATLAB: cellfun
, arrayfun
. L'idée est de prendre un tableau ou une variable de classe de cellules, de parcourir tous ses éléments et d'appliquer une fonction dédiée sur chaque élément. Une fonction appliquée peut être anonyme, ce qui est généralement le cas, ou toute fonction régulière définie dans un fichier * .m.
Commençons par un problème simple et disons que nous devons trouver une liste de fichiers * .mat en fonction du dossier. Pour cet exemple, commençons par créer des fichiers * .mat dans un dossier en cours:
for n=1:10; save(sprintf('mymatfile%d.mat',n)); end
Après l'exécution du code, il devrait y avoir 10 nouveaux fichiers avec l'extension * .mat. Si nous exécutons une commande pour répertorier tous les fichiers * .mat, tels que:
mydir = dir('*.mat');
nous devrions avoir un tableau d'éléments d'une structure dir; MATLAB devrait donner un résultat similaire à celui-ci:
10x1 struct array with fields:
name
date
bytes
isdir
datenum
Comme vous pouvez le voir, chaque élément de ce tableau est une structure avec deux champs. Toutes les informations sont en effet importantes pour chaque fichier mais dans 99%, je suis plutôt intéressé par les noms de fichiers et rien d'autre. Pour extraire des informations d'un tableau de structure, j'ai créé une fonction locale qui impliquait la création de variables temporelles de taille correcte, de boucles, d'extraction d'un nom de chaque élément et de sauvegarde dans la variable créée. Un moyen beaucoup plus facile d'obtenir exactement le même résultat est d'utiliser l'une des fonctions susmentionnées:
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'
Comment fonctionne cette fonction? Il faut généralement deux paramètres: un handle de fonction comme premier paramètre et un tableau. Une fonction fonctionnera alors sur chaque élément d'un tableau donné. Les troisième et quatrième paramètres sont facultatifs mais importants. Si nous savons qu'une sortie ne sera pas régulière, elle doit être enregistrée dans la cellule. Ce doit être un paramètre de mise en évidence false
à UniformOutput
. Par défaut, cette fonction tente de renvoyer une sortie régulière telle qu'un vecteur de nombres. Par exemple, extrayons des informations sur la quantité d'espace disque prise par chaque fichier en octets:
mydirbytes = arrayfun(@(x) x.bytes, dir('*.mat'))
mydirbytes =
34560
34560
34560
34560
34560
34560
34560
34560
34560
34560
ou kilo-octets:
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
Cette fois, la sortie est un vecteur régulier de double. UniformOutput
été défini sur true
par défaut.
cellfun
est une fonction similaire. La différence entre cette fonction et arrayfun
est que cellfun
opère sur des variables de classe de cellules. Si nous souhaitons extraire uniquement les noms à partir d’une liste de noms de fichiers dans une cellule "mydirlist", il suffit de lancer cette fonction comme suit:
mydirnames = cellfun(@(x) x(1:end-4), mydirlist, 'UniformOutput', false)
mydirnames =
'mymatfile1'
'mymatfile10'
'mymatfile2'
'mymatfile3'
'mymatfile4'
'mymatfile5'
'mymatfile6'
'mymatfile7'
'mymatfile8'
'mymatfile9'
De même, comme une sortie n'est pas un vecteur de nombres régulier, une sortie doit être enregistrée dans une variable de cellule.
Dans l'exemple ci-dessous, je combine deux fonctions en une et renvoie uniquement une liste de noms de fichiers sans extension:
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'
C'est fou mais très possible parce que arrayfun
retourne une cellule à laquelle on attend l'entrée de cellfun
; Une autre remarque est que nous pouvons forcer l'une de ces fonctions à retourner des résultats dans une variable de cellule en définissant UniformOutput
sur false. Nous pouvons toujours obtenir des résultats dans une cellule. Nous ne pourrons peut-être pas obtenir de résultats dans un vecteur régulier.
Il y a une autre fonction similaire qui opère sur les champs une structure: structfun
. Je ne l'ai pas particulièrement trouvé aussi utile que les deux autres, mais cela brillerait dans certaines situations. Si par exemple on aimerait savoir quels champs sont numériques ou non numériques, le code suivant peut donner la réponse:
structfun(@(x) ischar(x), mydir(1))
Le premier et le second champ d'une structure de répertoire sont de type char. Par conséquent, la sortie est la suivante:
1
1
0
0
0
En outre, la sortie est un vecteur logique de true
/ false
. Par conséquent, il est régulier et peut être enregistré dans un vecteur; pas besoin d'utiliser une classe de cellules.
Préférences de pliage du code
Il est possible de modifier la préférence de pliage du code en fonction de vos besoins. Ainsi, le pliage de code peut être défini sur activer / impossible pour des constructions spécifiques (ex: if block
, for loop
, Sections
...).
Pour modifier les préférences de pliage, allez dans Préférences -> Pliage de code:
Ensuite, vous pouvez choisir quelle partie du code peut être pliée.
Des informations:
- Notez que vous pouvez également développer ou réduire tout le code d'un fichier en plaçant votre curseur n'importe où dans le fichier, en cliquant avec le bouton droit, puis en sélectionnant Pliage du code> Développer tout ou Plier le code> Plier tout dans le menu contextuel.
- Notez que le pliage est persistant, en ce sens qu'une partie du code qui a été développé / réduit conserve son statut après que Matlab ou que le fichier m ait été fermé et ré-ouvert.
Exemple: Pour activer le pliage des sections:
Une option intéressante est de permettre de plier des sections. Les sections sont délimitées par deux signes de pourcentage (
%%
).Exemple: Pour l'activer, cochez la case "Sections":
Ensuite, au lieu de voir un long code source similaire à:
Vous pourrez plier des sections pour avoir un aperçu général de votre code:
Extraire des données de figure
À quelques occasions, j'ai eu un personnage intéressant que j'ai enregistré mais j'ai perdu l'accès à ses données. Cet exemple montre comment obtenir des informations à partir d'une figure.
Les fonctions clés sont findobj et get . findobj renvoie un gestionnaire à un objet avec les attributs ou propriétés de l'objet, tels que Type
ou Color
, etc. Une fois qu'un objet ligne a été trouvé, get peut renvoyer toute valeur contenue dans les propriétés. Il s'avère que les objets Line
contiennent toutes les données dans les propriétés suivantes: XData
, YData
et ZData
; le dernier est généralement 0 sauf si un chiffre contient un tracé 3D.
Le code suivant crée un exemple qui montre deux lignes une fonction sin et un seuil et une légende
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'});
La première utilisation de findobj renvoie deux gestionnaires aux deux lignes:
findobj(gcf, 'Type', 'Line')
ans =
2x1 Line array:
Line (threshold)
Line (sin)
Pour limiter le résultat, findobj peut également utiliser une combinaison d’opérateurs logiques -and
, -or
ou de noms de propriétés. Par exemple, je peux trouver un objet ligne dont DiplayName
est sin
et lire ses XData
et YData
.
lineh = findobj(gcf, 'Type', 'Line', '-and', 'DisplayName', 'sin');
xdata = get(lineh, 'XData');
ydata = get(lineh, 'YData');
et vérifiez si les données sont égales.
isequal(t(:),xdata(:))
ans =
1
isequal(y(:),ydata(:))
ans =
1
De même, je peux affiner mes résultats en excluant la ligne noire (seuil):
lineh = findobj(gcf, 'Type', 'Line', '-not', 'Color', 'k');
xdata = get(lineh, 'XData');
ydata = get(lineh, 'YData');
et le dernier contrôle confirme que les données extraites de ce chiffre sont identiques:
isequal(t(:),xdata(:))
ans =
1
isequal(y(:),ydata(:))
ans =
1
Programmation fonctionnelle à l'aide de fonctions anonymes
Les fonctions anonymes peuvent être utilisées pour la programmation fonctionnelle. Le principal problème à résoudre est qu’il n’existe pas de méthode native pour ancrer une récursivité, mais cela peut toujours être implémenté sur une seule ligne:
if_ = @(bool, tf) tf{2-bool}();
Cette fonction accepte une valeur booléenne et un tableau de cellules de deux fonctions. La première de ces fonctions est évaluée si la valeur booléenne est vraie et la seconde si la valeur booléenne est fausse. Nous pouvons facilement écrire la fonction factorielle maintenant:
fac = @(n,f) if_(n>1, {@()n*f(n-1,f), @()1});
Le problème ici est que nous ne pouvons pas appeler directement un appel récursif, car la fonction n'est pas encore affectée à une variable lorsque le côté droit est évalué. Nous pouvons cependant compléter cette étape en écrivant
factorial_ = @(n)fac(n,fac);
Maintenant, @(n)fac(n,fac)
évalue récursivement la fonction factorielle. Une autre façon de le faire dans la programmation fonctionnelle en utilisant un combinateur y, qui peut également être facilement implémenté:
y_ = @(f)@(n)f(n,f);
Avec cet outil, la fonction factorielle est encore plus courte:
factorial_ = y_(fac);
Ou directement:
factorial_ = y_(@(n,f) if_(n>1, {@()n*f(n-1,f), @()1}));
Enregistrer plusieurs figures dans le même fichier .fig
En plaçant plusieurs descripteurs de figures dans un tableau graphique, plusieurs figures peuvent être enregistrées dans le même fichier .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);
Cela crée 3 diagrammes de dispersion de données aléatoires, chaque partie du tableau graphique h. Ensuite, le tableau graphique peut être enregistré en utilisant savefig comme avec un chiffre normal, mais avec le handle du tableau graphique comme argument supplémentaire.
Une remarque intéressante est que les chiffres ont tendance à rester disposés de la même manière qu’ils ont été sauvegardés lorsque vous les ouvrez.
Blocs de commentaires
Si vous souhaitez commenter une partie de votre code, des blocs de commentaires peuvent être utiles. Le bloc de commentaires commence par %{
dans une nouvelle ligne et se termine par %}
dans une autre nouvelle ligne:
a = 10;
b = 3;
%{
c = a*b;
d = a-b;
%}
Cela vous permet de plier les sections que vous avez commentées pour rendre le code plus propre et compact.
Ces blocs sont également utiles pour activer / désactiver des parties de votre code. Tout ce que vous avez à faire pour décommenter le bloc est d'ajouter un autre %
avant qu'il ne se manifeste:
a = 10;
b = 3;
%%{ <-- another % over here
c = a*b;
d = a-b;
%}
Parfois, vous souhaitez commenter une section du code, mais sans affecter son retrait:
for k = 1:a
b = b*k;
c = c-b;
d = d*c;
disp(b)
end
Habituellement, lorsque vous marquez un bloc de code et appuyez sur Ctrl + r pour le commenter (en ajoutant automatiquement le %
à toutes les lignes, lorsque vous appuyez plus tard sur Ctrl + i pour obtenir une indentation automatique, le bloc de code place, et déplacé trop à droite:
for k = 1:a
b = b*k;
% c = c-b;
% d = d*c;
disp(b)
end
Une façon de résoudre ce problème consiste à utiliser des blocs de commentaires, afin que la partie interne du bloc reste correctement en retrait:
for k = 1:a
b = b*k;
%{
c = c-b;
d = d*c;
%}
disp(b)
end