Contrôle d'Excel par MATLAB via Automation - Écriture de données et mise en forme

Apprendre à interfacer MATLAB et Excel par la programmation

Cet article présente l'écriture de données et la mise en forme de cellules dans le cadre du contrôle d'Excel par MATLAB via Automation. Il fait partie d'une série d'articles dont vous trouverez la liste complète sur cette page.

Après une brève introduction, le deuxième chapitre s'intéressera à l'écriture des données à proprement parler. Le troisième chapitre présentera un exemple et les codes d'écriture, de mise en forme et de mise en forme conditionnelle associés. Le quatrième chapitre portera sur une comparaison des techniques présentées dans cet article avec la fonction xlswrite de MATLAB.

Votre avis et vos suggestions sur cet article m'intéressent !
Alors après votre lecture, n'hésitez pas : 17 commentaires Donner une note à l'article (5)

Article lu   fois.

L'auteur

Profil ProSite personnelPage personelle de Jérôme Briot (Dut)

Liens sociaux

Viadeo Twitter Facebook Share on Google+   

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 :

 
Sélectionnez
ActiveSheet.Range('B2').Value = rand;
ActiveSheet.Range('C2').Value = 'MATLAB';
ActiveSheet.Range('D2').Value = true;

donne :

Image non disponible

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 :

 
Sélectionnez
ActiveSheet.Range('B2:F2').Value = rand(1,5);

Ce qui donne bien :

Image non disponible

Par contre, si l'on avait spécifié une plage verticale comme ceci :

 
Sélectionnez
ActiveSheet.Range('B2:B6').Value = rand(1,5);

le résultat n'aurait plus du tout été bon :

Image non disponible

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 :

 
Sélectionnez
ActiveSheet.Range('B2:D2').Value = {'AAA' 'MATLAB' 'CCC'};

donne bien :

Image non disponible

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 :

 
Sélectionnez
ActiveSheet.Range('B2').Value = rand;
ActiveSheet.Range('C2').Value = 'MATLAB';
ActiveSheet.Range('D2').Value = true;

aurait pu s'écrire :

 
Sélectionnez
ActiveSheet.Range('B2:D2').Value = {rand 'MATLAB' true};

ce qui donne aussi :

Image non disponible

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 :

 
Sélectionnez
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 :

 
Sélectionnez
>> 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 :

 
Sélectionnez
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.

 
Sélectionnez
% 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.


Cliquez pour visualiser la vidéo



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.

 
Sélectionnez
% 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.


Cliquez pour visualiser la vidéo



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 :

 
Sélectionnez
% 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 :

Image non disponible

IV. Comparaison avec xlswrite

Voici le code équivalent à celui du chapitre III avec la fonction xlswrite :

 
Sélectionnez
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 :

 
Sélectionnez
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 :

 
Sélectionnez
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.

Vous avez aimé ce tutoriel ? Alors partagez-le en cliquant sur les boutons suivants : Viadeo Twitter Facebook Share on Google+   

  

Copyright © 2013-2014 Jerome Briot. Aucune reproduction, même partielle, ne peut être faite de ce site et de l'ensemble de son contenu : textes, documents, images, etc. sans l'autorisation expresse de l'auteur. Sinon vous encourez selon la loi jusqu'à trois ans de prison et jusqu'à 300 000 € de dommages et intérêts.