I. Introduction▲
Il peut être utile de stocker des résultats de calculs MATLAB dans une feuille Excel. Ceci permet l'exploitation ultérieure des résultats sur des postes où MATLAB n'est pas installé, car la suite Microsoft Office est plus répandue. Ceci permet également l'exploitation des résultats par des utilisateurs ne maîtrisant pas la programmation sous MATLAB.
Avant de voir un exemple concret, il faut s'intéresser au fonctionnement du passage de valeurs entre Excel et MATLAB selon leur type et leurs dimensions.
Cet article se base sur les six articles précédents de la série. Leur lecture préalable est donc fortement conseillée.
II. Écriture de données▲
II-A. Type de données▲
Les valeurs sont écrites dans des cellules Excel en renseignant la propriété Value de l'objet Range qui contient l'adresse de ces cellules.
Depuis MATLAB, il est possible d'écrire des données numériques, des chaînes de caractères ou encore des valeurs booléennes.
Le code suivant :
ActiveSheet.Range('B2'
).Value = rand;
ActiveSheet.Range('C2'
).Value = 'MATLAB'
;
ActiveSheet.Range('D2'
).Value = true;
donne :
On remarque que, conformément au comportement d'Excel, les valeurs sont alignées horizontalement à l'intérieur des cellules selon leur type :
- valeur numérique : alignement à droite ;
- chaîne de caractères : alignement à gauche ;
- valeur booléenne : alignement centré.
II-B. Passage de valeurs numériques▲
Les données sous MATLAB sont souvent contenues dans des tableaux.
Il est tout à fait possible de parcourir le tableau élément par élément et d'écrire chaque valeur dans chaque cellule une à une.
Il est néanmoins plus simple d'écrire le tableau en une seule fois en faisant correspondre la plage désignée par l'objet Range aux dimensions du tableau.
Il faut alors être vigilant. Il faut impérativement que l'orientation de la plage de cellules corresponde à l'orientation du tableau.
Par exemple, pour écrire un vecteur ligne de dimension 1x5, il faut impérativement utiliser une plage de 5 cellules horizontales :
ActiveSheet.Range('B2:F2'
).Value = rand(1,5);
Ce qui donne bien :
Par contre, si l'on avait spécifié une plage verticale comme ceci :
ActiveSheet.Range('B2:B6'
).Value = rand(1,5);
le résultat n'aurait plus du tout été bon :
On remarque que c'est la première valeur du tableau qui est recopiée dans les cinq cellules.
Que se passe-t-il si la plage de cellules est plus petite que le nombre d'éléments contenus dans le tableau ?
Seules les données correspondant au nombre de cellules sont écrites.
Que se passe-t-il si la plage de cellules est plus grande que le nombre d'éléments contenus dans le tableau ?
Les cellules ne correspondant à aucun élément du tableau prennent la valeur d'erreur #N/A.
II-C. Passage de chaînes de caractères▲
Le passage de tableaux de chaînes de caractères repose sur le même principe que pour les valeurs numériques. Les chaînes de caractères doivent être contenues dans un tableau de cellules. Le nombre d'éléments du tableau ainsi que son orientation doivent correspondre au nombre de cellules de la plage et à son orientation.
Par exemple :
ActiveSheet.Range('B2:D2'
).Value = {'AAA'
'MATLAB'
'CCC'
};
donne bien :
II-D. Passage de données hétérogènes▲
Il est également possible de passer des valeurs de type différent en une seule fois en les stockant dans un tableau de cellules.
L'exemple du chapitre I-A :
ActiveSheet.Range('B2'
).Value = rand;
ActiveSheet.Range('C2'
).Value = 'MATLAB'
;
ActiveSheet.Range('D2'
).Value = true;
aurait pu s'écrire :
ActiveSheet.Range('B2:D2'
).Value = {rand 'MATLAB'
true};
ce qui donne aussi :
II-E. Passage de dates▲
Pour passer des dates de MATLAB vers Excel, il est préférable de les convertir sous forme sérielle plutôt que de les passer sous forme de chaînes de caractères.
Il faut dans ce cas tenir compte des différences d'interprétation entre Excel et MATLAB (voir l'article Limites).
Par exemple, pour passer la date courante :
ActiveSheet.Range('B2'
).Value = now-datenum('30-Dec-1899 00:00:00'
);
III. Exemple▲
III-A. Présentation▲
L'exemple utilisé dans cet article est basé sur des données publiques fournies par Météo France à cette adresse http://france.meteofrance.com/france/observations. Cliquez sur « Observations France au format CSV » pour télécharger une feuille de relevés de la semaine passée. L'explication sur le contenu de ce fichier est disponible à cette adresse : http://france.meteofrance.com/html/legendeObsCsv.html
Afin de ne pas alourdir cet article, j'ai préalablement transformé les données du fichier .csv vers un fichier .mat facilement lisible par MATLAB grâce à la fonction load.
Vous pouvez télécharger le fichier .mat ici.
Le contenu de ce fichier est obtenu grâce à la fonction whos :
>> whos -file obsFrance.mat
Name Size Bytes Class Attributes
echeance 1x24 192 double
indicatif 1x40 320 double
indicatif_OMM 960x1 3840 int32
nom 1x40 5446 cell
pression 40x24 7680 double
temperature 40x24 7680 double
vent 40x24 7680 double
La variable nom contient le nom des 40 villes où ont été effectués les relevés.
La variable temperature contient 960 valeurs avec une ligne par ville et 24 relevés de températures (quatre relevés journaliers sur six jours).
La variable echéance contient les 24 horaires de relevés (quatre horaires journaliers sur six jours).
Nous allons voir dans les prochains chapitres, comment écrire et mettre en forme ces relevés de températures sous Excel à partir de MATLAB.
Les codes présentés dans les deux chapitres suivants s'insèrent l'un après l'autre dans le squelette de code suivant :
load obsFrance.mat -mat
Excel = actxserver('Excel.Application'
);
Excel.Visible = false;
Workbook = Excel.Workbooks.Add;
% Écriture des données ici
% Mise en forme des données ici
xlspath = pwd ;
xlsfile = 'obsFrance.xlsx'
;
Workbook.SaveAs(fullfile(xlspath,xlsfile));
Workbook.Close;
Quit(Excel);
delete(Excel);
Le format xlsx n'est supporté que depuis Excel 2007. Si vous utilisez une version plus ancienne d'Excel, vous devez impérativement utiliser le format xls.
Vous trouverez le code MATLAB complet dans le fichier obsFrance2Excel.m disponible ici ainsi que le fichier Excel obsFrance.xlsx ici.
III-B. Écriture des données▲
Voici le code utilisé pour écrire les données dans la feuille de calcul Excel depuis MATLAB.
Les commentaires expliquent ce que font chaque bloc de code.
% Récupération de la première feuille de calcul dans la variable
% Activesheet pour simplifier le code par la suite
ActiveSheet = Excel.Worksheets.Item(1);
% Modification du nom de la première feuille de calcul.
% L'onglet de cette feuille prend le nom "Températures"
ActiveSheet.Name = 'Températures'
;
% Fusion des cellules B2 à Z3 pour écrire un titre avec une police de
% grande taille
range = get(ActiveSheet.Range('B2'
),'Resize'
,2,25);
range.Merge;
% Écriture du titre "Température du ??? au ???" dans les cellules
% fusionnées précédemment
titre = sprintf('Températures du %s au %s (en °C)'
, ...
datestr(echeance(1),'dd/mm/yyyy'
), ...
datestr(echeance(end),'dd/mm/yyyy'
));
range.Name = 'titre'
;
range.Value = titre;
% Fusion des cellules B4 et B5 pour améliorer le rendu visuel de la cellule
% contenant le titre "Villes"
range = ActiveSheet.Range('B4:B5'
);
range.Merge;
range.Value = 'Villes'
;
% Remplissage des noms des villes
range = ActiveSheet.Range('B6'
).get('Resize'
,numel(indicatif),1);
range.Value = nom;
% Attribution du nom "villes" à la plage contenant les noms des villes pour
% simplifier la mise en forme ultérieure
range.Name = 'villes'
;
% Modification des dates en fonction des réglages Excel
if
Workbook.Date1904
echeance = echeance-datenum('01-Jan-1904 00:00:00'
);
else
echeance = echeance-datenum('30-Dec-1899 00:00:00'
);
end
% Mise en place de la première ligne des dates correspondant aux jours des
% relevés de températures
for
n = 1:6
r(1) = ActiveSheet.Range('C4'
).get('Cells'
, 1, 4*(n-1)+1);
r(2) = ActiveSheet.Range('C4'
).get('Cells'
, 1, 4*n);
range = ActiveSheet.get('Range'
, r(1), r(2));
range.Merge;
range.Value = echeance(4*(n-1)+1);
end
clear r
% Mise en place de la première ligne des dates correspondant aux heures des
% relevés de températures
range = ActiveSheet.Range('C5'
).get('Resize'
, 1, 24);
range.value = echeance;
% Écriture des valeurs des relevés de températures
range = ActiveSheet.Range('C6'
).get('Resize'
,numel(indicatif),size(temperature,2));
range.Value = temperature;
Voici une vidéo montrant le résultat de l'exécution de ce code. De brèves pauses ont été volontairement ajoutées pour bien décomposer les différentes actions. Dans la réalité, le résultat est quasiment immédiat.
III-C. Mise en forme▲
Voici le code utilisé pour mettre en forme les données écrites précédemment depuis MATLAB.
Les commentaires expliquent ce que font chaque bloc de code.
% Définition des constantes VBA utiles
xlCenter = -4108;
xlMedium = -4138;
% Mise en place des bordures internes et externes du tableau
r = ActiveSheet.Range('B2'
).CurrentRegion;
r.Borders.Item('xlInsideHorizontal'
).LineStyle = 1;
r.Borders.Item('xlInsideHorizontal'
).Weight = 2;
r.Borders.Item('xlInsideVertical'
).LineStyle = 1;
r.Borders.Item('xlInsideVertical'
).Weight = 2;
r.BorderAround([],xlMedium);
% Mise en forme de la cellule (fusionnée) contenant le titre
range = ActiveSheet.Range('titre'
);
rgb = [0.7569 0.8667 0.7765];
range.Interior.Color = 256.^(0:2)*round(rgb(:)*255);
range.Font.Size = 28;
rgb = [0.2314 0.4431 0.3373];
range.Font.Color = 256.^(0:2)*round(rgb(:)*255);
range.Font.Bold = true;
range.HorizontalAlignment = xlCenter;
% Mise en forme de la cellule (fusionnée) contenant l'entête "Villes"
range = ActiveSheet.Range('B4'
);
range.HorizontalAlignment = xlCenter;
range.VerticalAlignment = xlCenter;
rgb = [0.9059 0.9059 0.9059];
range.Interior.Color = 256.^(0:2)*round(rgb(:)*255);
% Mise en forme des cellules contenant les entêtes des dates sous forme de
% jour
for
n = 1:6
range = ActiveSheet.Range('C4'
).get('Cells'
, 1, 4*(n-1)+1);
range.NumberFormatLocal = 'jj/mm'
;
range.HorizontalAlignment = xlCenter;
rgb = [0.9059 0.9059 0.9059];
range.Interior.Color = 256.^(0:2)*round(rgb(:)*255);
end
% Mise en forme des cellules contenant les entêtes des dates sous forme
% d'heures
r(1) = ActiveSheet.Range('C5'
);
r(2) = ActiveSheet.Range('C5'
).End('xlToRight'
);
range = ActiveSheet.get('Range'
, r(1), r(2));
range.NumberFormatLocal = 'hh:mm'
;
range.HorizontalAlignment = xlCenter;
rgb = [0.9059 0.9059 0.9059];
range.Interior.Color = 256.^(0:2)*round(rgb(:)*255);
clear r
range = ActiveSheet.Range('C4'
).get('Resize'
, 1, numel(echeance));
range = ActiveSheet.get('Range'
, r(1), r(2));
range.BorderAround([],xlMedium);
clear r
% Mise en forme des valeurs des températures
r(1) = ActiveSheet.Range('C6'
);
r(2) = ActiveSheet.Range('C6'
).End('xlToRight'
).End('xlDown'
);
range = ActiveSheet.get('Range'
, r(1), r(2));
range.HorizontalAlignment = xlCenter;
clear r
% Mise en forme d'une ligne sur deux pour les températures
for
n = 2:2:numel(indicatif)
r(1) = ActiveSheet.Range('C6'
).get('Cells'
,n,1);
r(2) = r(1).End('xlToRight'
);
range = ActiveSheet.get('Range'
, r(1), r(2));
rgb = [0.8941 0.9412 0.9020];
range.Interior.Color = 256.^(0:2)*round(rgb(:)*255);
end
clear r
ActiveSheet.Range('B4'
).BorderAround([],xlMedium);
% Mise en forme de la colonne contenant les noms des villes
range = ActiveSheet.Range('villes'
);
rgb = [0.8039 0.8784 0.9686];
range.Interior.Color = 256.^(0:2)*round(rgb(:)*255);
rgb = [0 0 1];
range.Font.Color = 256.^(0:2)*round(rgb(:)*255);
range.BorderAround([],xlMedium);
% Réduction esthétique de la largeur de la première colonne (A)
ActiveSheet.Range('A:A'
).ColumnWidth = 3;
% Ajustement automatique de la largeur des colonnes de l'ensemble du tableau
ActiveSheet.Range('B2'
).CurrentRegion.Columns.AutoFit;
clear r
Voici une vidéo montrant le résultat de l'exécution de ce code. De brèves pauses ont été volontairement ajoutées pour bien décomposer les différentes actions. Dans la réalité, le résultat est quasiment immédiat.
III-D. Mise en forme conditionnelle▲
La mise en forme conditionnelle est utilisée pour faire ressortir certaines valeurs d'un tableau selon un ou plusieurs critères.
On utilise la collection FormatConditions qui contient des objets FormatCondition. Cette collection est également une propriété de l'objet Range. Un objet Range ne peut contenir au maximum que trois objets FormatCondition.
Nous ne présenterons ici que la méthode Add de la collection FormatConditions appliquée aux valeurs d'une cellule ou d'une plage de cellules.
Complétons l'exemple précédent dans lequel on souhaite séparer visuellement les relevés de températures négatives en leur attribuant une police de couleur bleue et une casse grasse.
Voici un code permettant de faire ceci :
% Définition de constantes VBA utiles
xlCellValue = 1;
xlLess = 6;
% Mise en forme conditionnelle des valeurs des températures
r(1) = ActiveSheet.Range('C6'
);
r(2) = ActiveSheet.Range('C6'
).End('xlToRight'
).End('xlDown'
);
range = ActiveSheet.get('Range'
, r(1), r(2));
range.FormatConditions.Add(xlCellValue, xlLess, '0'
);
rgb = [0 0 1];
range.FormatConditions.Item(1).Font.Color = 256.^(0:2)*round(rgb(:)*255);
range.FormatConditions.Item(1).Font.Bold = true;
Voici un aperçu du résultat de ces commandes :
IV. Comparaison avec xlswrite▲
Voici le code équivalent à celui du chapitre III avec la fonction xlswrite :
function
obsFrance2Excel2
load(fullfile(pwd, 'obsFrance.mat'
), '-mat'
)
xlsxname = 'obsFrance2.xlsx'
;
xlswrite(xlsxname, {'Villes'
}, 'Température'
, 'B2'
);
xlswrite(xlsxname, nom, 'Température'
, 'B3:B42'
);
dat = datestr(echeance, 'HH:MM'
);
dat = cellstr(dat).';
xlswrite(xlsxname, dat, 'Température'
, 'C2:Z2'
);
xlswrite(xlsxname, temperature, 'Température'
, 'C3:Z42'
);
Ce code présente trois inconvénients que nous allons développer dans les chapitres suivants.
IV-A. Définition des plages de cellules▲
Le premier inconvénient est qu'il est nécessaire de passer par le style A1 pour spécifier les plages de cellules. Ce qui, comme on l'a vu dans l'article Les plages de cellules est assez peu pratique.
Alors qu'avec Automation, les propriétés Resize, Cells et End de l'objet Range nous permettent de les définir dynamiquement.
IV-B. Temps d'exécution▲
Il est judicieux ensuite de comparer les temps d'exécution des deux méthodes.
Afin d'être pertinent, nous avons retiré les blocs de lignes gérant la mise en forme des chapitres III-C et III-D de cet article.
Voici une la fonction MATLAB utilisée pour le test :
function
[t,m,s] = bench_obsFrance2Excel
for
n = 1:5
delete('obsFrance*.xlsx'
)
tic
obsFrance2Excel
t(n,1) = toc;
tic
obsFrance2Excel2
t(n,2) = toc;
end
m = mean(t);
s = m(2)/m(1);
Voici les résultats obtenus :
t =
0.8140 2.0347
0.8775 2.1046
0.8953 2.0502
0.9198 2.0926
0.8699 2.0751
m =
0.8753 2.0715
s =
2.3665
La méthode avec Automation est deux fois plus rapide que celle avec xlswrite.
Comme nous l'avons déjà vu, la fonction xlswrite utilise elle aussi la méthode à base d'Automation pour connecter Excel et MATLAB. La différence du temps d'exécution vient du fait qu'à chaque appel à xlswrite , la connexion est ouverte puis fermée. Et comme c'est une opération extrêmement coûteuse en temps, le temps global est donc plus long qu'avec notre code où une seule ouverture et fermeture sont effectuées.
IV-C. Mise en forme▲
Le dernier inconvénient est que la mise en forme des cellules (simple ou conditionnelle) n'est pas possible avec xlswrite. Il faut soit utiliser un modèle de classeur avec un contenu fixe, soit exécuter a posteriori une macro VBA pour gérer la mise en forme.
V. Conclusion▲
Vous savez maintenant comment écrire efficacement des données dans une feuille Excel à partir de MATLAB.
Vous savez également que la méthode à base d'Automation est extrêmement souple et rapide.
Vous pouvez maintenant poursuivre votre lecture par l'article intitulé Contrôle d'Excel par MATLAB via Automation - Lecture de données où vous allez voir comment mettre en pratique le contenu des six premiers articles pour la lecture de données.