Valid XHTML     Valid CSS2    

Décomposition, Conception et Réalisation d'Applications

T.D. 3 : Comptages, moyennes, tracés et statistiques

                    gilles.hunault "at" univ-angers.fr

Table des matières cliquable

  1. Comptages élémentaires pour une variable qualitative binaire

  2. Comptages avancés

  3. Comptages élémentaires pour une variable qualitative éventuellement ordinale

  4. Moyennes d'une variable quantitative

  5. Tracés adaptés aux comptages et moyennes

  6. Statistiques descriptives, statistiques inférentielles et programmation

  7. Affichages «intelligents» de résultats

  8. Search and display en Python, R et PHP

Il est possible d'afficher toutes les solutions via ?solutions=1 et de les masquer via ?solutions=0.

 

1. Comptages élémentaires pour une variable qualitative binaire

On s'intéresse ici au comptage d'une variable qualitative binaire, disons le code-sexe d'un individu dans un échantillon donné. comme pour le fichier cs.txt ci-dessous, où le code-sexe est défini par 1 pour Homme et 2 pour Femme. Quels calculs peut-on faire ? Quels calculs doit-on faire ? Comment présenter les résultats ? Quelles sont les formules ? Peut-on se contenter de juste calculer et afficher les pourcentages ?

Fichier cs.txt :


     IDEN AGE PROF ETUD SEXE REGI USAG
     M001  62    1    2    2    2    3
     M002  60    9    3    1    4    1
     M003  31    9    4    2    4    1
     M004  27    8    4    2    1    1
     M005  22    8    4    1    1    2
     M006  70    4    1    2    1    1
     M007  19    8    4    2    4    2
     M008  53    6    2    2    2    3
     M009  62   16    4    1    2    2
     M010  63   16    0    2    1    0
     M011  65   16    0    1    1    0
     M012  11   12    2    2    4    2
     M013  78   16    3    1    4    1
     M014  20    8    4    1    4    2
     M015  48    1    2    2    3    1
     M016  50    1    1    2    4    2
     M017  49    7    4    1    4    1
     M018  44    1    1    2    2    1
     M019  21    8    4    2    2    2
     M020  23   13    3    2    1    2
     M021  28    6    4    1    3    1
     M022  47    6    2    1    1    1
     M023  64    2    2    1    1    2
     M024  26    8    4    1    4    1
     M025  14   12    2    2    1    2
     M026  43    1    1    1    1    2
     M027  61   16    1    2    1    0
     M028  15   12    3    2    1    2
     M029  29    0    2    2    1    1
     M030  24    1    2    2    1    2
     M031  41    9    4    2    1    1
     M032  37    9    4    2    2    1
     M033  73   16    1    2    1    1
     M034  12   12    2    2    4    1
     M035  40   13    2    2    4    0
     M036  42    7    3    1    3    0
     

Ecrire un programme AWK pour fournir le nombre et le pourcentage d'hommes et de femmes (numéro de la colonne SEXE à déterminer par programme) pour le fichier cs.csv -- le séparateur est le point-virgule -- dont voici un extrait :

Fichier cs.csv (extrait) :


     IDEN;AGE;PROF;ETUD;SEXE;REGI;USAG
     M001;62;1;2;2;2;3
     M002;60;9;3;1;4;1
     M003;31;9;4;2;4;1
     M004;27;8;4;2;1;1
     

Modèle d'affichage :


     Résultats pour la colonne SEXE, colonne numéro 5 (36 valeurs)
      moda 1 efectif 13 pourcentage  36.11
      moda 2 efectif 23 pourcentage  63.89
     

Donner enfin le code R et Python qui effectue les mêmes calculs à l'aide de fonctions déjà existantes.

En admettant que les valeurs du code sexe correspondent au champ SEXE d'une table CS dans une base de données RECENSE, quel serait le code MySQL équivalent, sachant qu'on désirerait un affichage (sans doute incomplet) comme ci-dessous :


     +------+----------+-------+---+
     | sexe | Effectif | pct   | % |
     +------+----------+-------+---+
     |    2 |       23 | 63.89 | % |
     |    1 |       13 | 36.11 | % |
     +------+----------+-------+---+
     

On pourra suivre la méthode ci-dessous pour réaliser le tri à plat :


     ## méthode pour calculer le tri à plat d'une variable qualitative
     
     # 1. lire      les données
     # 2. calculer  les effectifs
     # 3. calculer  les pourcentages
     # 4. créer     une structure d'accueil des modalités, des effectifs et des pourcentages
     # 5. trier     éventuellement cette structure par pourcentage décroissant
     
     # 6. afficher  la structure
     

Solution :  

Pour une variable qualitative, binaire, on doit dénombrer les effectifs absolus $e_i$ (notés aussi $n_i$), les effectifs relatifs $f_i$ et les pourcentages $p_i$ associés, relativement aux modalités $m_i$, c'est-à-dire ici compter le nombre d'hommes, de femmes avec les fréquences et pourcentages associés. Il est d'usage de présenter les résultats par pourcentages décroissants et d'afficher à la fois les comptages et les pourcentages, sans oublier l'effectif total, comme ci-dessous (qui ne correspond pas aux données proposées) :


     TRI A PLAT DE :  code SEXE (ordre des modalités)  pour 416 valeurs
     
                          Homme         Femme  Total
      Effectif              187           229    416
      Cumul Effectif        187           416    416
      Frequence (en %)       45            55    100
      Cumul fréquences       45           100    100
     
     VARIABLE :  code SEXE (par fréquence décroissante sur 416 codes SEXE)
     
                          Femme    Homme   Total
       Effectif             229      187    416
       Cumul Effectif       187      416    416
       Frequence (en %)      55       45    100
       Cumul fréquences      55      100    100
     

Formules :

Pour $n$ valeurs et $p$ modalités $m_i$ avec $i$ de 1 à $p$, étant donnés les comptages $n_i$ de chaque modalité, on doit vérifier que la somme des $n_i$ vaut $n$ avant de calculer $f_i=n_i/n$ et $p_i=100\times f_i$.

Les cumuls $c_i$ avec $i$ de 1 à $p$ sont définis par $c_i=\displaystyle \displaystyle\sum_{j=1}^i   n_j$

On ne peut pas se contenter des pourcentages $p_i$ si on ne dispose pas du nombre total $n$ de valeurs parce que les effectifs $e_i$ se calculent par $e_i=p_i\times n$.

Voici le code AWK demandé :


     ### (gH)   -_-  cs.awk
     
     ## comptages pour la variable qualitative binaire SEXE dans le fichier cs.cv
     ## s'utilise par :  gawk --file=cs.awk --field-separator=";" cs.csv
     
     BEGIN { colSexe = -1 ; tot = 0 }  # inutile si on est pressé(e)
     
     # détermination de la colonne pour SEXE
     
     (FNR==1) {
       for (idc=1;idc<=NF;idc++) {
         if ($idc=="SEXE") { colSexe = idc }
       } # fin pour idc
     } # fin traitement ligne 1
     
     # effectifs (comptages) à la volée
     
     (FNR>1) {
       ntot++
       eff[ $colSexe ]++
     } # fin traitement autres lignes
     
     END {
       print "Résultats pour la colonne SEXE, colonne numéro " colSexe " (" ntot " valeurs)"
       for (moda in eff) {
           pct[moda] = 100*eff[moda]/ntot
           print " moda " moda " effectif " eff[moda]  " pourcentage " sprintf("%6.2f",pct[moda])
       } # fin pour moda
     } # fin de partie END
     

Voici le code R demandé, on notera qu'il est vectoriel (on ne voit aucune boucle dans le code) :


     ### (gH)   -_-  cs.r
     
     ## comptages pour la variable qualitative binaire SEXE dans le fichier cs.cv
     ## s'utilise par : R --vanilla --quiet --slave --encoding=latin1 --file=cs.r
     ## ou par : Rscript cs.r
     
     allData    <- read.csv2("cs.csv",header=TRUE)                            # lecture des données
     tap        <- table(allData[,"SEXE"])                                    # calcul des effectifs
     pct        <- 100*prop.table(tap)                                        # calcul des pourcentages
     mdr        <- data.frame(cbind(names(tap),tap,sprintf("%6.2f",pct)))     # structure d'accueil
     names(mdr) <- c("Moda","Effectif","Pct")
     
     print(mdr,quote=FALSE,row.names=FALSE)                                   # affichage
     

Et son affichage :


     $gh> R --vanilla --quiet --slave --encoding=latin1 --file=cs.r
     
      Moda Effectif    Pct
         1       13  36.11
         2       23  63.89
     

Voici le code Python correspondant, on notera qu'il est vectoriel aussi :


     # -*- coding:latin1 -*-
     
     ### (gH)   -_-  cs.py
     
     ## comptages pour la variable qualitative binaire SEXE dans le fichier cs.cv
     ## s'utilise par :  python cs.py
     
     import pandas as pd
     
     allData            = pd.read_table("cs.csv",sep=";")                 # lecture des données
     tap                = allData["SEXE"].value_counts()                  # calcul des effectifs
     pct                = 100*tap/allData["SEXE"].size                    # calcul des pourcentages
     mdr                = pd.concat([tap,pct],axis=1,ignore_index=True)   # structure d'accueil
     mdr.columns        = ["Effectif","Pourcentage"]                      # noms des colonnes
     mdr["Pourcentage"] = mdr["Pourcentage"].map('{:,.2f}'.format)        # formatage des données
     
     print(mdr)                                                           # affichage
     

Son affichage est légèrement différent de celui de R :


        Effectif Pourcentage
     2        23       63.89
     1        13       36.11
     

Pour MySQL, il est facile de calculer les effectifs, les pourcentages et de les afficher en une seule requête grâce au mot clé GROUP BY :


      SET @ntot = (SELECT COUNT(sexe) FROM recense) ;
     
      SELECT sexe, COUNT(sexe) AS Effectif, ROUND(COUNT(sexe)*100/@ntot,2) AS pct, "%"
             FROM     recense
             GROUP BY sexe
             ORDER BY pct DESC
      ; # fin de select
     

A cause des numéros de modalités pour le code-sexe, un affichage plus correct serait :


      SET @ntot = (SELECT COUNT(sexe) FROM recense) ;
     
      SELECT IF(sexe=1,"hommes","femmes") AS genre ,
             COUNT(sexe) AS Effectif, ROUND(COUNT(sexe)*100/@ntot,2) AS pct, "%"
             FROM     recense
             GROUP BY sexe
             ORDER BY pct DESC
      ; # fin de select
     

Comme détaillé en cours, chaque action élémentaire classique a sa solution classique sous forme d'une fonction en R ou en Python. Il ne faut donc pas réécrire du code pour ces actions.

De l'intérêt de bien relire son code et les solutions :

  • L'affichage des résultats du script cs.awk ne correspond pas rigoureusement au code fourni car il manque un f dans le mot efectif.

    Il ne faut pas laisser de telles fautes d'orthographe simples à corriger car un utilisateur, une utilisatrice verra toujours ce genre de fautes et pensera, sans doute à juste titre, que vous ne faites pas assez attention aux résultats. S'il y a une faute d'orthographe, il y a peut-être aussi une faute de calcul...

  • Dans ce même code AWK, il y a une variable nommée tot qui est initialisée dans la partie BEGIN qui n'est jamais utilisée ailleurs (c'est la variable ntot qui calcule l'effectif total). Les langages interprétés n'obligent pas à déclarer les variables, certains langages comme AWK n'obligent pas à les initialiser et donc cela peut mener à des erreurs. Avec le mode strict le langage Perl signalerait que la variables tot n'est utilisée qu'une seule fois...

  • Le tri décroissant sur les pourcentages arrondis n'est rigoureusement égal dans tous les cas au tri décroissant sur les effectifs par ce que justement les pourcentages sont arrondis. Il serait prudent dans la requête MySQL de trier sur Effectif plutôt que sur pct.

  • Relire, c'est aussi l'occasion de peaufiner les commentaires, d'ajuster le formatage et la lisibilité du code, de penser aux tests à effectuer, selon les trois règles d'or :

    1. d'abord on écrit les commentaires ;

    2. ensuite on écrit le code ;

    3. enfin on teste tout ce qu'il est raisonnable de tester...

 

2. Comptages avancés

On admet maintenant qu'il s'agit d'informations qualitatives (sexe : homme/femme ; polio : malade/sain ; état civil : célibataire, marié, veuf-veuve...) issues de grands fichiers de données, disons, plusieurs milliers ou millions de lignes, comme pour le fichier polio.dar qui contient presque 402 000 lignes. On suppose également que chaque fichier est utilisé une seule fois par an, en fin d'année. Peut-on définir, pour une année donnée, un fichier de taille minimale correspondant aux mêmes informations résumées ? Que doit-on alors stocker comme informations si on veut pouvoir comparer les comptages par année, si on veut les cumuler ?

Si on nomme $m_{i}$ les modalités de la variable, $n_{i,a}$ les effectifs associés pour l'année $a$ et $f_{i,a}$ les effectifs relatifs pour l'année $a$, donner la formule qui permet d'obtenir les effectifs cumulés $C_{i,a+1}$ et celle des effectifs relatifs cumulés $F_{i,a+1}$ à partir des valeurs de $n_{i,a}$, de $c_{i,a}$ et de $n_{i,a+1}$ si on suppose qu'on est rendu à l'année $a+1$ et qu'on vient de calculer les $n_{i,a+1}$.

Au passage, comment produit-on des formules de mathématiques dans une page Web ?

Solution :  

Comme on s'intéresse ici au cumul d'informations résumées et puisqu'une variable qualitative est définie par ses modalités, on doit pouvoir se contenter en fin d'année, de ne conserver que les effectifs absolus, c'est-à-dire les comptages $n_{i,a}$ où $i$ correspond à la modalité et $a$ à l'année.

Comme ni l'énoncé ni ces explications ne sont sans doute pas très claires, voici un exemple :


      Année   Hommes  Femmes  cumulHommes  cumulFemmes
      [...]
      2017       50     30        250        300
      2018       80     90        330        390
      2019      100    350        ???        ???
     

$n_{1,2019}$ représente le nombre d'hommes recensés en 2019. Le cumul $C_{1,2019}$ correspond au nombre d'hommes en tout pour 2019. On l'obtient en ajoutant l'effectif de 2019, soit 100, au cumul $C_{1,2018}$ des années précédentes qui est 330. On doit donc trouver ici 430.

Comme il est d'usage de calculer les effectifs relatifs (ou les pourcentages), il faut, en plus des $C_{i,a+1}$, calculer les effectifs relatifs $f_{i,a+1}$ à partir des effectifs relatifs $n_{i,a+1}$, ce qui impose de calculer auparavant la somme $n_{a+1}$ des effectifs absolus pour l'année $a+1$. Il faut enfin calculer la somme $N_{a+1}$ des effectifs cumulés $C_{i,a+1}$ et finir par le calcul des effectifs cumulés relatifs $F_{i,a+1}$ à partir des effectifs cumulés $C_{i,a+1}$ et de la somme $N_{a+1}$.

Voici les formules :

Nom Notation Formule
Effectif total pour l'année $a+1$ $n_{a+1}$ $\displaystyle \displaystyle\sum_{i=1}^n   n_{i,a+1}$
Effectifs relatifs pour l'année $a+1$ $f_{i,a+1}$ $100 \times n_{i,a+1} / n_{a+1}$
Effectifs cumulés pour l'année $a+1$ $C_{i,a+1}$ $C_{i,a} + n_{i,a+1}$
Effectif total cumulé pour l'année $a+1$ $N_{a+1}$ $\displaystyle \displaystyle\sum_{i=1}^n   C_{i,a+1}$
Effectifs cumulés relatifs pour l'année $a+1$ $F_{i,a+1}$ $100 \times C_{i,a+1} / N_{a+1}$

Voici à quoi pourrait ressembler le tableau final demandé (nom complété) :


      Année   Hommes  Femmes  cumulHommes  cumulFemmes  totalAn   totalCumul  pctHommes pctFemmes pctCumulHommes pctCumulFemmes
      [...]
      2017       50     30        250        300
      2018       80     90        330        390
      2019      100    350        ???        ???
     

Si on admet qu'on dispose de la toute première année de comptages, alors on peut réduire le tableau d'informations initiales aux seuls effectifs par modalité, et n'avoir que ces effectifs à rajouter chaque année, soit par exemple ici


      Année   Hommes  Femmes
      2016      200    270
      2017       50     30
      2018       80     90
      2019      100    350
     

A titre d'exemple, nous vous laissons écrire, disons en AWK (ou en Python, ou en R...) le calcul des cumuls, des pourcentages d'effectifs et de cumuls tels que demandés. La seule question ici est sans doute : faut-il écrire une ou plusieurs fonctions (et lesquelles ?) pour simplifier le code ?

Pour écrire des formules mathématiques dans une page Web, le plus simple est sans doute de passer par MathJax avec une syntaxe $\LaTeX$. Un test en ligne est possible ici.

 

3. Comptages élémentaires pour une variable qualitative éventuellement ordinale

On travaille maintenant avec une variable ordinale, par exemple le degré de sévérité d'une maladie, comme dans le fichier malades.txt où le code 0 signifie non malade, le code 1 signifie un peu malade, le code 2 signifie malade, le code 3 signifie très malade et le code 4 signifie très gravement malade. Un exemple de code réel est par exemple le score METAVIR F.

Fichier malades.txt :


     iden Score
     I0001 1
     I0002 4
     I0003 4
     I0004 0
     I0005 1
     [...]
     I9805 2
     I9806 3
     

Reprendre la discussion sur les calculs et le stockage.

Solution :  

Pour une variable qualitative autre que binaire, les mêmes calculs s'appliquent : on doit aussi calculer les pourcentages. Si la variable est nominale, comme par exemple l'état-civil simplifié (marié ou en couple, célibataire, veuf), il est d'usage d'afficher par pourcentage décroissant.


     ETAT CIVIL (2067 valeurs)
     
     Modalité  Effectif Pourcentage  cumulEffectif  cumulPourcentage
     Marié        1141       55 %         1141           55 %
     Célibataire   527       25 %         1668           80 %
     Veuve-Veuf    285       14 %         1953           94 %
     Autre         114        6 %         2067          100 %
     
     

Pour une variable qualitative ordinale, il faut impérativement garder l'ordre des modalités dans les tableaux de résultats et afficher le cumul des pourcentages, comme ci-dessous :


     Distribution des 5 modalités F0 F1 F2 F3 F4 (1012 valeurs vues)
     ---------------------------------------------------------------
     
     Num Modalité  Effectifs  Eff. cumulés   Pct    Cumul_Pct
     1         F0        44           44     4.35      4.35
     2         F1       439          483    43.38     47.73
     3         F2       273          756    26.98     74.71
     4         F3       141          897    13.93     88.64
     5         F4       115         1012    11.36    100.00
     

Au niveau des calculs annuels, les mêmes formules s'appliquent aussi. On aura juste plus de colonnes à calculer.

 

4. Moyennes d'une variable quantitative

On s'intéresse ici au résumé nommé moyenne arithmétique d'une variable quantitative, comme par exemple l'âge d'une personne exprimé en années. Si on dispose d'un fichier comme ages.txt où sur chaque ligne on a un identifiant et l'âge, comment calculer rapidement cette moyenne ?

Fichier ages.txt :


     Patient  Age
     I001     35
     I012     18
     I103     12
     I017     20
     I088     14
     

On vient de calculer la moyenne arithmétique $m$ de $n$ valeurs. Si on rajoute une $n+1$-ème valeur notée $x$, quelle est la nouvelle valeur de la moyenne ?

On vient de calculer la moyenne arithmétique $m_1$ de $n_1$ valeurs et la moyenne arithmétique $m_2$ de $n_2$ valeurs. Quelle est la valeur de la moyenne des $n_1+n_2$ valeurs ?

On admet maintenant qu'il s'agit d'informations quantitatives (ages, salaires...) issues de grands fichiers de données, disons, plusieurs milliers ou millions de lignes, comme les fichiers de recensement nationaux. On suppose également que chaque fichier est utilisé une seule fois par an, en fin d'année pour des informations résumées. Peut-on définir, pour une année donnée, un fichier de taille minimale correspondant aux mêmes informations résumées si on s'intéresse juste à la moyenne ? Que doit-on alors stocker comme informations si on veut pouvoir comparer les moyennes par année, si on veut les cumuler ?

Si,pour la variable $i$, on note $n_{i,a}$ le nombre de valeurs pour l'an $a$ et $s_{i,a}$ la somme de ces valeurs pour l'an $a$, donner la formule qui permet d'obtenir pour l'année suivante la moyenne $M_{i,a+1}$ globale cumulée à partir des valeurs de $n_{j,a}$, de $s_{j,a}$ et $m_{j,a}$ pour $i\leqslant j$.

Est-ce qu'il existe d'autre types de moyennes ? Ou d'autres résumés des données ?

Que signifie l'expression « la moyenne est invariante par permutation » ? A quoi cela peut-il servir de le savoir si on programme un calcul de moyenne ?

Solution :  

La moyenne arithmétique est définie comme le quotient de la somme des valeurs divisée par le nombre de valeurs. Pour la calculer ici, un programme AWK en mode one-liner comme ci-dessous serait suffisant :


     awk -e ' (FNR>1) { sumAges += $2 } ; END { print " moyenne " sumAges/(FNR-1) } ' ages.txt
     

Il c'est clair que c'est du code rapide et jetable car non robuste. La valeur $2 n'est valable que pour ce fichier. Et il a fallu diviser par FNR-1 à cause de l'entête. Utiliser R ou Excel aurait été possible ici aussi, mais au prix d'un temps d'écriture ou de réalisation des calculs plus longs.

Puis que la moyenne arithmétique est la somme des valeurs divisée par le nombre de valeurs, la formule est $m=s/n$. Si on ajoute la valeur $x$ alors la somme devient $s+x$ et la nouvelle moyenne est donc $(s+x)/(n+1)$. On peut se passer de la variable intermédiaire $s$ puisque $s=n\times m$. La formule de calcul de la nouvelle moyenne est donc $m =(n\times m+x)/(n+1)$.

Si on calculé la moyenne arithmétique $m_1$ de $n_1$ valeurs et la moyenne arithmétique $m_2$ de $n_2$ valeurs, alors la somme $s_1$ des $n_1$ valeurs est $s_1=n_1\times m_1$. De même, la somme $s_2$ des $n_2$ valeurs est $s_2=n_2\times m_2$. La somme globale des $n_1+n_2$ valeurs est donc $s_1+s_2$ et leur moyenne est alors $(s_1+s_2)/(n_1+n_2)$. Si on se passe des variables intermédiaires $s_i$, la formule de calcul de la nouvelle moyenne est donc celle d'une moyenne pondérée : $(n_1\times m_1 + n_2\times m_2)/(n_1+n_2)$.

Au niveau des formules, si on prend en compte les années, cela donne $m_a=s_a/n_a$. Si maintenant il s'agit de la variable numéro $i$, alors la formule est $m_{i,a}=s_{i,a}/n_{i,a}$. Vouloir stocker en fin d'année juste la moyenne de l'année et la moyenne cumulée jusque là n'est pas correct par ce qu'il n'est pas possible de calculer la moyenne générale cumulée $M_{i,a+1}$ à partir seulement des valeurs de $M_{i,a}$ et de $m_{i,a+1}$. Il faut au minimum conserver le nombre de valeurs par année et leur cumul pour pouvoir calculer la moyenne générale cumulée $M_{i,a+1}$.

Le plus astucieux est certainement de garder, année par année, le cumul $N_{i,a}$ du nombre de valeurs et de leur somme $S_{i,a}$. La moyenne de l'année se calcule directement par $m_{i,a+1}=s_{i,a+1}/n_{i,a+1}$. Puisqu'on met à jour le cumul du nombre de valeurs par la formule $N_{i,a+1}=N_{i,a} + n_{i,a+1}$ et leur somme cumulée par $S_{i,a+1}=S_{i,a} + s_{i,a+1}$, la nouvelle moyenne générale s'obtient directement par $M_{i,a+1}=S_{i,a+1}/N_{i,a+1}$.

Il existe plusieurs autres types de moyennes que la moyenne arithmétique. Il y a la moyenne géométrique, la moyenne harmonique, la moyenne quadratique... Voir l'article Wikipedia correspondant pour leur définition et le lien hubaut pour des exemples de calcul.

L'expression « la moyenne est invariante par permutation » signifie que l'ordre des valeurs n'a pas d'importance pour le calcul de la moyenne. Ainsi les notes trimestrielles aux examens 18, 5, 12 fournissent la même moyenne que les notes 18, 12, 5 et que les notes 5, 12, 18. Comme il s'agit de notes trimestrielles, l'ordre des notes apporte une information sur la progression ou la diminution des notes que le résumé moyenne ne traduit pas.

Savoir qu'il y a invariance par permutation n'apporte rien au programmeur quant au code informatique. Par contre savoir qu'une moyenne est un résumé non robuste peut amener à d'autres calculs comme le celui de la médiane, celui de la moyenne tronquée... et surtout à d'autres interprétations.

 

5. Tracés adaptés aux comptages et moyennes

Y a-t-il des représentations graphiques classiques pour les comptages, pour les valeurs, pour les moyennes ? Comment les nomme-t-on ? Est-ce que les logiciels Microsoft Excel et Libre Office Calc les implémentent ? Y a-t-il des précautions à prendre au niveau des graphiques ?

Solution :  

Solution volontairement non détaillée ici. Venez en TD !

 

6. Statistiques descriptives, statistiques inférentielles et programmation

Comment décrit-on en statistiques une variable qualitative ? Et une variable quantitative ? Et les deux en même temps ?

Comment fait-on en statistiques pour comparer des comptages, des moyennes, et plus généralement des séries de valeurs ?

Que faut-il en déduire si on doit programmer des calculs statistiques ?

Solution :  

Ceci n'est pas de l'informatique. Consulter par exemple l'introduction non élémentaire au logiciel R ou formules pour répondre en partie à ces questions.

Lorsqu'on programme ce genre de calculs, ce n'est pas tant la difficulté des calculs qui est en jeu -- car ils sont souvent déjà programmés -- que de savoir lesquels appliquer. Ainsi, pour une variable qualitative dont on connait et maitrise l'unité, disons dont les valeurs sont stockées dans le vecteur V, le calcul de la moyenne de V s'obtient sans doute par mean(V) ou V.mean() alors que le calcul de la médiane de V s'obtient sans doute par median(V) ou V.median(). Mais quel descripteur faut-il utiliser ? La moyenne ou la médiane ? Et ce n'est pas une question simple, à cause des distributions multimodales.

            non su

De même, comparer des effectifs ou des moyennes, des distributions ne s'effectue pas avec un simple calcul de différence. Pour que la différence soit considérée comme significative, il faut s'appuyer sur un test statistique qui ne s'applique parfois que lorsque certaines conditions (normalité, homoscédasticité...) sont vérifiées. Savoir quel test statistique s'applique n'est pas un problème simple. Voir par exemple la page whatstat.

 

7. Affichages «intelligents» de résultats

7.1 Affichages en général

Si on devait présenter les résultats de plusieurs variables qualitatives non nécessairement binaires, quel serait l'affichage le plus «intelligent» statistiquement ?

Et pour plusieurs variables quantitatives non nécessairement de même unité ?

Là encore, que faut-il en déduire si on doit programmer des affichages issus de calculs statistiques ?

7.2 Affichage des résultats de variables qualitatives

Reprendre les solutions AWK et R de la question 1 afin d'avoir un affichage avec des labels de modalité, des effectifs décroissants, des pourcentages avec un nombre choisi de décimales.

En supposant qu'on dispose en PHP du tableau suivant (obtenu comment ?), donner ensuite une solution PHP et Javascript qui permet de choisir la colonne de tri.


           array (size=2)
             'Homme' => int 13
             'Femme' => int 23
     
     
     
           array (
             'Homme' => 13,
             'Femme' => 23,
           )
     

Trouver un exemple numérique qui montre que le tri par pourcentage arrondi décroissant n'est pas équivalent au tri par effectif décroissant.

Solution :  

7.1 Affichages en général

A nouveau, ceci n'est pas de l'informatique. On peut raisonnablement penser qu'afficher les variables statistiques qui varient le plus en premier est un bon choix, mais comment apprécier cette variation ? On pourra consulter rappels statistiques pour trouver quelques pistes de réflexion.

Si on doit programmer de tels affichages, il faut certainement coopérer avec des statisticiennes et des statisticiens pour savoir dans quel ordre afficher les résultats. Ensuite, quelques tris et indexations sur les vecteurs et tableaux de résultats doivent suffire...

7.2 Affichage des résultats de variables qualitatives

Voici le code AWK demandé, sans variable tot inutilisée.


     ### (gH)   -_-  cs-tri.awk
     
     ## comptages pour la variable qualitative binaire SEXE dans le fichier cs.cv
     ## s'utilise par :  gawk --file=cs-tri.awk --field-separator=";" --assign=decim=1 --assign=labels="Homme Femme" cs.csv
     
     BEGIN {
       colSexe = -1
       ntot    =  0
     } # fin de partie BEGIN
     
     # détermination de la colonne pour SEXE
     
     (FNR==1) {
       for (idc=1;idc<=NF;idc++) {
         if ($idc=="SEXE") { colSexe = idc }
       } # fin pour idc
     } # fin traitement ligne 1
     
     
     (FNR>1) {
       ntot++
       eff[ $colSexe ]++
     } # fin traitement autres lignes
     
     END {
     
       print "Résultats pour la colonne SEXE, colonne numéro " colSexe " (" ntot " valeurs)"
     
       for (moda in eff) {
           pct[moda] = 100*eff[moda]/ntot
           print " moda " moda " effectif " eff[moda]  " pourcentage " sprintf("%6.2f",pct[moda])
       } # fin pour moda
     
       # ajouts :
     
       print "Résultats triés pour la colonne SEXE, colonne numéro " colSexe " (" ntot " valeurs) avec " decim " décimale(s) "
     
       split(labels,noms," ")
       fmt = sprintf("%s%s%s","%6.",decim,"f")
       n   = asort(eff)
       for (idm=1;idm<=n;idm++) {
           moda = n + 1 - idm # car le tri est toujours croissant
           pct[moda] = 100*eff[moda]/ntot
           print " modalité " noms[moda] " effectif " eff[moda]  " pourcentage " sprintf(fmt,pct[moda]) " %"
       } # fin pour moda
     
     } # fin de partie END
     

Utilisation et affichage :


     $gh> gawk --file=cs-tri.awk --field-separator=";" --assign=decim=1 --assign=labels="Homme Femme" cs.csv
     
     Résultats pour la colonne SEXE, colonne numéro 5 (36 valeurs)
      moda 1 effectif 13 pourcentage  36.11
      moda 2 effectif 23 pourcentage  63.89
     
     Résultats triés pour la colonne SEXE, colonne numéro 5 (36 valeurs) avec 1 décimale(s)
      modalité Femme effectif 23 pourcentage   63.9 %
      modalité Homme effectif 13 pourcentage   36.1 %
     

Maintenant, si on est pressé(e), il aurait été possible, juste pour le tri, d'utiliser un pipe avec un sort décroissant sur la clé 4 :


     $gh> gawk --file=cs.awk --field-separator=";" cs.csv
     
     Résultats pour la colonne SEXE, colonne numéro 5 (36 valeurs)
      moda 1 effectif 13 pourcentage  36.11
      moda 2 effectif 23 pourcentage  63.89
     
     $gh> gawk --file=cs.awk --field-separator=";" cs.csv | awk -e ' { print $4 } '
     
     colonne
     13
     23
     
     $gh> gawk --file=cs.awk --field-separator=";" cs.csv | sort -r -k 4,4
     
     Résultats pour la colonne SEXE, colonne numéro 5 (36 valeurs)
      moda 2 effectif 23 pourcentage  63.89
      moda 1 effectif 13 pourcentage  36.11
     

Il n'y a pas grand chose à changer dans le code R précédent pour qu'il corresponde à ce qui est demandé :


     ### (gH)   -_-  cs-tri.r
     
     ## comptages pour la variable qualitative binaire SEXE dans le fichier cs.cv
     ## s'utilise par : Rscript  --vanilla -cs-tri.r NBDEC
     
     args <- commandArgs(TRUE) ;
     deci <- 1 ; if (length(args)>0) { deci <- as.numeric(args[1]) }                    # nouveauté
     
     allData          <- read.csv2("cs.csv",header=TRUE)
     allData[,"SEXE"] <- factor(allData[,"SEXE"],levels=1:2,labels=c("Homme","Femme"))  # nouveauté
     tap              <- table(allData[,"SEXE"])
     pct              <- 100*prop.table(tap)
     fmt              <- paste("%6.",deci,"f %%",sep="")                                # nouveauté
     mdr              <- data.frame(cbind(names(tap),tap,sprintf(fmt,pct)))
     names(mdr)       <- c("Moda","Effectif","Pct")
     oda              <- order(mdr[,2],decreasing=TRUE)                                 # nouveauté
     
     print(mdr[oda,],quote=FALSE,row.names=FALSE)
     

Utilisation et affichage :


     $gh> Rscript --vanilla cs-tri.r 3
     
      Moda  Effectif      Pct
      Femme       23 63.889 %
      Homme       13 36.111 %
     
     $gh> Rscript --vanilla cs-tri.r  # même sans paramètres !
     
      Moda  Effectif      Pct
      Femme       23   63.9 %
      Homme       13   36.1 %
     

PHP dispose de nombreuses fonctions avec des tableaux en paramètre, dont print_r, array_sum et arsort.

Les affichages fournis sont obtenus via var_export et var_dump.

La solution que nous utilisons pour trier le tableau en Javascript se nomme sortable, très légèrement aménagée. Elle fait partie des 33 solutions proposées en 2009 sur le site tympanu : javascript-solutions-for-sorting-tables.

On lira donc avec attention le code source PHP et Javascript de la page cs-tri, codes sources disponibles via des liens en bas de page et dont le tableau interactif est reproduit ci-dessous.

Modalité Effectif Pourcentage
Femme 23 63.89 %
Homme 13 36.11 %

Au passage, est-ce risqué de permettre de visualiser du code PHP sur un site ?

On trouvera ci-dessous un exemple en R qui montre que des pourcentages arrondis ne permettent pas de trier comme le feraient des effectifs :


     eff <- c(5129,351,352)  ; cbind(eff,printf("%6.1f",eff*100/sum(eff)))
     
     
     [1,] 5129   87.9
     [2,]  351    6.0
     [3,]  352    6.0
     

Les pourcentages arrondis ont aussi un autre défaut : leur somme ne fait pas toujours 100 %, comme dans l'exemple précédent.

 

8. Search and display en Python, R et PHP

A partir d'un fichier Excel nommé elf.xls supposé exister, avec une colonne AGE de nombres entiers on veut afficher les nbVal premières lignes triées par age décroissant. Donner une solution en interactif puis faire une fonction avec le nom du fichier et le nom de la colonne passés en paramètres obligatoires et nbVal passé en paramètre optionnel, avec la valeur 5 par défaut. On fournira une solution R puis une solution Python.

Donner les grandes lignes d'un code PHP qui réalise la même chose et qui en plus produit un fichier PDF des données sélectionnées.

Solution :  

Les actions à exécuter sont simples : ouvrir le fichier elf.xls, trier selon la colonne AGE, puis afficher les nbVal premières lignes. On peut donc écrire la méthode de résolution en trois étapes :


     ## searchdisp : recherche et affichage
     
     # 1. lecture du fichier Excel
     
     # 2. définition de l'ordre d'affichage ou tri
     
     # 3. affichage des nbVal premières lignes
     

Voici le code R interactif écrit à la suite du commentaire pour chaque étape :


     ## searchdisp1.r  : recherche et affichage
     
     # 1. lecture du fichier Excel
     
     library("gdata")
     fichier <- "elf.xls"
     elf     <- read.xls(fichier)
     
     # 2. définition de l'ordre d'affichage
     
     idx     <- order(elf$AGE,decreasing=TRUE) # ou order(elf[,"AGE"],decreasing=TRUE)
     
     # 3. affichage des nbVal premières lignes
     
     nbVal   <- 5
     print(head(elf[idx,],n=nbVal))
     

Il est très facile d'en faire une fonction :


     ## searchdisp2.r  : recherche et affichage
     
     # définition de la fonction
     
     afficheExcel <- function(nomFichier,colonne,nbVal=5) {
     
           suppressMessages( library("gdata") )
           data    <- read.xls(nomFichier)                     # 1. lecture du fichier Excel
           idx     <- order(data[,colonne],decreasing=TRUE)    # 2. définition de l'ordre d'affichage
           print(head(data[idx,],n=nbVal))                     # 3. affichage des nbVal premières ligne
     
     } # fin de fonction  afficheExcel
     
     # exemples d'utilisation
     
     afficheExcel("elf.xls","AGE")
     afficheExcel("iris.xls","largsep",3)
     

Voici le résultat de son exécution :


         Num SEXE AGE PROF ETUD REGI USAGE
     13 M013    0  78   16    3    4     1
     48 M048    1  76   16    3    1     0
     33 M033    1  73   16    1    1     1
     43 M043    1  73   16    4    4     0
     85 M085    1  73   16    3    1     0
        iden espece longsep largsep longpet largpet
     16 I016      1     5.7     4.4     1.5     0.4
     34 I034      1     5.5     4.2     1.4     0.2
     33 I033      1     5.2     4.1     1.5     0.1
     

mais c'est encore mieux de faire une fonction robuste. Il y a trois paramètres, donc trois risques d'erreurs possibles : fichier non vu, colonne non présente, valeur non numérique. Trois vérifications suffisent pour garantir le bon fonctionnement de la fonction.


     ## searchdisp3.r
     
     # définition de la fonction
     
     afficheExcel <- function(nomFichier,colonne,nbVal=5) {
     
       if (!file.exists(nomFichier)) { stop(paste("fichier",nomFichier,"non vu.\n")) }       # nouveau
       if (!is.numeric(nbVal))       { stop(paste("valeur",nbVal,"non numérique.\n"))}       # nouveau
     
           suppressMessages( library("gdata") )
           data    <- read.xls(nomFichier)                                                               # 1. lecture du fichier Excel
       if (!colonne %in% names(data)) { stop(paste("colonne",colonnef,"non présente.\n")) }  # nouveau
           idx     <- order(data[,colonne],decreasing=TRUE)                                              # 2. définition de l'ordre d'affichage
           print(head(data[idx,],n=nbVal))                                                               # 3. affichage des nbVal premières ligne
     
     } # fin de fonction  afficheExcel
     
     

Voici le fichier des tests et le résultat de l'exécution des tests :


     $gh> cat searchdisp3.sh
     
     Rscript -e ' source("searchdisp3.r",encoding="latin1") ; afficheExcel("NON.xls","AGE") '
     Rscript -e ' source("searchdisp3.r",encoding="latin1") ; afficheExcel("elf.xls","Age") '
     Rscript -e ' source("searchdisp3.r",encoding="latin1") ; afficheExcel("iris.xls","largsep","oui") '
     
     $gh> sh searchdisp3.sh
     
     Error in afficheExcel("NON.xls", "AGE") : fichier NON.xls non vu.
     Exécution arrêtée
     
     Error in afficheExcel("elf.xls", "Age") : colonne Age non présente.
     Exécution arrêtée
     
     Error in afficheExcel("iris.xls", "largsep", "oui") : valeur oui non numérique.
     Exécution arrêtée
     

Il resterait, pour généraliser un peu cette fonction, à fournir un paramètre facultatif pour indiquer si le tri est croissant ou décroissant (valeur par défaut). Nous vous laissons écrire le code correspondant...

La solution Python est similaire, puisque pour chaque action on dispose aussi d'une fonction ou d'une méthode :


     # -*- coding: iso-8859-15 -*-
     
     ## searchdisp1.py : recherche et affichage
     
     import pandas as pd
     
     # 1. lecture du fichier Excel
     
     elf = pd.read_excel("elf.xls")
     
     # 2. tri par age décroissant
     
     elfTri = elf.sort_values(["AGE"],ascending=False)
     
     # 3. affichage des nbVal premières lignes
     
     nbVal = 5 # attention : on commence à 0
     print(elfTri.head(n=nbVal))
     

En faire une fonction est là aussi très simple :


     # -*- coding: iso-8859-15 -*-
     
     ## searchdisp2.py : recherche et affichage
     
     # définition de la fonction
     
     def afficheExcel(fichierExcel, colonne, nbVal=5) :
     
             import pandas as pd
     
             # 1. lecture du fichier Excel
     
             data = pd.read_excel(fichierExcel)
     
             # 2. tri par age décroissant
     
             dataTri = data.sort_values([colonne],ascending=False)
     
             # 3. affichage des nbVal premières lignes
     
             print(dataTri.head(n=nbVal))
     
     # fin de fonction  afficheExcel
     
     # exemples d'utilisation
     
     afficheExcel("elf.xls","AGE")
     afficheExcel("iris.xls","largsep",3)
     

Voici le résultat de son exécution :


          Num  SEXE  AGE  PROF  ETUD  REGI  USAGE
     12  M013     0   78    16     3     4      1
     47  M048     1   76    16     3     1      0
     84  M085     1   73    16     3     1      0
     42  M043     1   73    16     4     4      0
     32  M033     1   73    16     1     1      1
         iden  espece  longsep  largsep  longpet  largpet
     15  I016       1      5.7      4.4      1.5      0.4
     33  I034       1      5.5      4.2      1.4      0.2
     32  I033       1      5.2      4.1      1.5      0.1
     

Passons là encore à une fonction robuste :


     # -*- coding: iso-8859-15 -*-
     
     ## searchdisp3.py  : recherche et affichage
     
     # définition de la fonction
     
     def afficheExcel(fichierExcel, colonne, nbVal=5) :
     
             import sys
             import os
     
             if (not os.path.isfile(fichierExcel)) :
               print("le fichier "+fichierExcel+" n'existe pas. Stop.\n")
               sys.exit()
             # fin de si
     
             import re
     
             if (not re.search("^[0-9]+$",str(nbVal))) :
               print("la valeur "+str(nbVal)+" n'est pas un nombre entier. Stop.\n")
               sys.exit()
             # fin de si
     
             import pandas as pd
     
             # 1. lecture du fichier Excel
     
             data = pd.read_excel(fichierExcel)
     
             if (not colonne in data.columns) :
               print("colonne "+colonne+" non vue. Stop.\n")
               sys.exit()
             # fin de si
     
             # 2. tri par age décroissant
     
             dataTri = data.sort_values([colonne],ascending=False)
     
             # 3. affichage des nbVal premières lignes
     
             print(dataTri.head(n=nbVal))
     
     # fin de fonction  afficheExcel
     
     # exemples d'utilisation (auto test)
     
     if __name__ == "__main__" :
     
        afficheExcel("elf.xls","AGE")
        afficheExcel("iris.xls","largsep",3)
     
     # fin de l'auto-test
     
     

Voici le fichier des tests et le résultat de l'exécution des tests :


     $gh> cat  searchdisp3_py.sh
     
     python3 -c 'from searchdisp3 import * ;afficheExcel("NON.xls", "AGE") '
     python3 -c 'from searchdisp3 import * ;afficheExcel("elf.xls", "Age") '
     python3 -c 'from searchdisp3 import * ;afficheExcel("elf.xls", "AGE","oui") '
     
     $gh> sh  searchdisp3_py.sh
     
     le fichier NON.xls n'existe pas. Stop.
     colonne Age non vue. Stop.
     la valeur oui n'est pas un nombre entier. Stop.
     

Il faut noter ici la très grande similarité des affichages entre R et Python, la seule différence notable étant le numéro des lignes de données.

On appréciera aussi que le corollaire de l'adage classique « à problème classique, solution classique » se double de la remarque pratique « si c'est simple et classique, il y a bien une fonction R ou Python qui résoud le problème».

On lira avec attention la page comparison qui fournit un affichage synoptique des commandes Python/pandas et R pour les mêmes actions.

De même, les pages R_vs_Py et python-vs-r fournissent les mêmes calculs statistiques en R et en Python.

Si en plus de trier les données pour une certaine colonne, on devait filtrer les lignes (par exemple en n'affichant que les femmes pour notre exemple), il faudrait utiliser un filtre vectoriel en R, comme elf[ elf$SEXE==2, ] ou une méthode de filtrage en Python, comme elf[ "SEXE==2"], donc sans virgule cette fois-ci.

Pour réaliser la même chose en PHP, il y a deux difficultés : la lecture de fichiers Excel n'est pas native et la structure de données data.frame n'existe pas. Il est toutefois possible de contourner le problème via des librairies externes comme PhpSpreadsheet et la fonction multisort.

Voici le fichier principal :


     <?php
     
     ### affichage trié des données d'un fichier Excel
     
     require 'searchdisp-inc.php' ;
     require 'vendor/autoload.php';
     use PhpOffice\PhpSpreadsheet\Spreadsheet;
     
     ## exemples d'utilisation :
     
     # afficheExcel("elf.xls","AGE")               ;
     # afficheExcel("iris.xls","largsep",3)        ;
     
     ?>
     

et son fichier inclus :


     <?php
     
     require 'vendor/autoload.php';
     use PhpOffice\PhpSpreadsheet\Spreadsheet ;
     
     ### affichage trié des données d'un fichier Excel
     
     ##########################################################################
     
     function printLigne($tab,$gauche=1) {
     
     ##########################################################################
     
       $icol = 0 ;
       foreach ($tab as $element) {
         $icol++ ;
         if ($icol==$gauche) {
           echo sprintf("%-6s", $element) ;
         } else {
           echo sprintf("%6s", $element) ;
         } # finsi
       } # fin pour
       echo "\n" ;
     
     } # fin fonction printLigne
     
     ##########################################################################
     
     function afficheExcel($nomXls,$nomCol,$nbVal=5) {
     
     ##########################################################################
     
     
     if (!preg_match("/^[1-9][0-9]*$/",$nbVal)) {
       echo " La valeur $nbVal n'est pas un  entier strictement positif." ;
       exit() ;
     } # fin si
     
     # 1. lecture des données
     
     if (!file_exists($nomXls)) {
       echo" Fichier $nomXls non vu.\n" ;
       exit() ;
     } # fin si
     
     $reader      = new \PhpOffice\PhpSpreadsheet\Reader\Xls();
     $spreadsheet = $reader->load($nomXls);
     
     $dfObjet = array($spreadsheet->getActiveSheet() ->toArray(null,true,true,true));
     $df      = $dfObjet[0] ;
     # pour debug : print_r($elf) ;
     
     # recherche de la colonne désirée
     
     $headers =  $df[1] ;
     unset($df[1]) ;
     # pour debug : print_r($headers) ;
     
     $numCol = -1 ;
     
     foreach ($headers as $indice=>$colonne) {
       # echo " indice : $indice, colonne $colonne \n" ;
       if ($colonne==$nomCol) { $numCol = $indice ; } ;
     } # fin pour chaque
     
     if ($numCol==-1) {
       echo" Colonne $nomCol non vue. Voici la liste des colonnes présentes :\n" ;
       print_r($headers) ;
       exit() ;
     } # fin si
     
     # extraction des valeurs de la colonne sélectionnée
     
     $valeurs = [] ;
     foreach ($df as $ligne=>$donnees) {
       $valeurs[$ligne] = $donnees[$numCol] ;
     } # fin pour chaque
     # pour debug : print_r($valeurs) ;
     
     # 2. tri des deux structures selon le même ordre
     
     array_multisort($valeurs,SORT_DESC,$df) ;
     
     # 3. affichage trié des nbVal lignes
     
     printLigne($headers) ;
     $ilig = 0 ;
     foreach ($df as $ligne=>$donnees) {
       $ilig++ ;
       if ($ilig<=$nbVal) {
         printLigne($donnees) ;
       } # fin si
     } # finpour chaque
     
     } # fin de fonction afficheExcel
     
     ?>
     

Le fichier des tests est là encore simple à écrire :


     ## tests php ligne de commande pour searchdisp
     
     # 1. tout va bien (1) nbVal par défaut
     
     echo "<?php include('searchdisp-inc.php') ; afficheExcel('elf.xls','AGE') ?>" | php
     
     # 2. tout va bien (2) nbVal=3
     
     echo "<?php include('searchdisp-inc.php') ; afficheExcel('iris.xls','largsep',3) ?>" | php
     
     # 3. rien ne va plus : (1) fichier non vu
     
     echo "<?php include('searchdisp-inc.php') ; afficheExcel('NON.xls','largsep',3) ?>" | php
     
     # 4. rien ne va plus : (2) colonne non vue
     
     echo "<?php include('searchdisp-inc.php') ; afficheExcel('iris.xls','LARGE',3) ?>" | php
     
     # 5. rien ne va plus : (3) nbVal non numérique
     
     echo "<?php include('searchdisp-inc.php') ; afficheExcel('iris.xls','largsep','oui') ?>" | php
     

Voici ce qu'il produit :


     Num     SEXE   AGE  PROF  ETUD  REGI USAGE
     M013       0    78    16     3     4     1
     M048       1    76    16     3     1     0
     M033       1    73    16     1     1     1
     M043       1    73    16     4     4     0
     M085       1    73    16     3     1     0
     iden  especelongseplargseplongpetlargpet
     I016       1   5.7   4.4   1.5   0.4
     I034       1   5.5   4.2   1.4   0.2
     I033       1   5.2   4.1   1.5   0.1
      Fichier NON.xls non vu.
      Colonne LARGE non vue. Voici la liste des colonnes présentes :
     Array
     (
         [A] => iden
         [B] => espece
         [C] => longsep
         [D] => largsep
         [E] => longpet
         [F] => largpet
     )
      La valeur oui n'est pas un  entier strictement positif.

En ce qui concerne la production d'un PDF pour les données sélectionnées, on pourra lire 5 Best opensource PHP pdf generation libraries.

 

       retour au plan de cours  

 

Code-source PHP de cette page ; code Javascript associé.

 

retour gH    Retour à la page principale de   (gH)