1. Application : moyenne et ecart-type, fichier CSV
1.1. Problématique
1.1.1. Description du problème
On dispose d'un fichier de notes obtenues par des étudiants pour les mathématiques et l'informatique. On désire
- calculer la moyenne et l'écart-type pour les mathématiques et l'informatique
- exporter les notes ainsi que moyenne et écart-type en HTML et LaTeX
Si le fichier contient une centaine de lignes cela peut prendre du temps d'exporter le fichier en faisant celà à la main,
s'il s'agit de milliers de lignes, c'est inenvisageable.
Les données initiales sont les suivantes (fichier notes.csv) :
Etudiant","Math","Info"
"Jean Bon", 12, 17
"Marc Decafer" , 7, 10
"Henri Quatre", 12, 11
"Marie Ney" , 16, 18
"Santa Claus", 19, 20
"Pascal Line", 13, 13
"Noël Blanc", 14, 13
"Charlotte Ofrèze", 11, 15
"Sarah Longe", 14, 19
"Charles Magne", 13, 8
"Anne Atoll", 7, 15
Il s'agit donc d'un fichier .csv doté d'un header (première ligne) qui contient la description des colonnes.
1.1.2. Analyse du problème
- il faut pouvoir charger le fichier .csv, réaliser les calculs et les ajouter à la fin du fichier
- il faut pouvoir traduire les données sous forme de table en HTML et en LaTeX
1.2. Comment faire en ligne de commande ?
1.2.1. Première étape
Supprimer le header et les lignes vides et créer un nouveau fichier notes_sans_header.csv :
richer@solaris\$sed '1d' notes.csv | grep -v "^$" > notes_sans_header.csv
La commande sed est utilisée pour supprimer la première ligne '1d' (1 delete), puis on utilise grep
pour supprimer les lignes vides. On pourrait égalemet supprimer les lignes qui ne contiendraient que des espaces.
1.2.2. Deuxième étape
Écrire le fichier transforme.awk, il s'agit d'un fichier awk qui est un langage
de traitement par lignes de fichiers plats proche du C.
Afficher le code ens/l1/python1/exemple_1/transforme.awk
BEGIN {
# set field separator to commad ','
FS=","
}
/.*/ {
cols=NF;
for(i=2; i<=NF; i++) {
sum[i] += $i;
sumsq[i] += $i*$i;
}
count++;
print($0);
}
END {
printf("Moyenne");
average = 0
for(i=2; i<=cols; i++) {
average = sum[i]/count;
printf(", %.2f", average);
}
printf("\n");
# commentaire
printf("Ecart-type");
for(i=2; i<=cols; i++) {
average = sum[i]/count;
variance = (sumsq[i] / count) - (average * average);
printf(", %.2f", sqrt(variance) );
}
printf("\n");
}
1.2.3. Troisième étape
Exécuter le script awk sur le fichier .csv sans header :
richer@solaris\$awk -f transforme.awk notes_sans_header.csv > donnees.csv
"Jean Bon", 12, 17
"Marc Decafer" , 7, 10
"Henri Quatre", 12, 11
"Marie Ney" , 16, 18
"Santa Claus", 19, 20
"Pascal Line", 13, 13
"Noël Blanc", 14, 13
"Charlotte Ofrèze", 11, 15
"Sarah Longe", 14, 19
"Charles Magne", 13, 8
"Anne Atoll", 7, 15
Moyenne, 12.55, 14.45
Ecart-type, 3.34, 3.68
1.2.4. Quatrième étape
Créer d'autres scripts awk pour transformer un fichier .csv en tableau HTML ou en LaTeX
1.3. Comment faire avec C++ ?
Voici le code C++ qui permet de faire ce qui est demandé, il s'agit d'un programme de 140 lignes donc assez court
mais qui demande une bonne maîtrise de la STL.
Afficher le code ens/l1/python1/exemple_1/traite_donnees.cpp
#include <iostream>
#include <fstream>
#include <vector>
#include <string>
#include <sstream>
#include <cmath>
#include <iomanip>
#include <numeric>
using namespace std;
// Structure pour stocker les données du tableau
struct Table {
vector<string> headers;
vector<vector<string>> rows;
};
// Fonction pour découper une ligne CSV en colonnes
vector<string> split(const string& s, char delimiter) {
vector<string> tokens;
string token;
istringstream tokenStream(s);
while (getline(tokenStream, token, delimiter)) {
tokens.push_back(token);
}
return tokens;
}
// Fonction pour lire le CSV
Table readCSV(const string& filename) {
Table table;
ifstream file(filename);
string line;
if (getline(file, line)) {
table.headers = split(line, ',');
}
while (getline(file, line)) {
if (line.length() == 0) continue;
table.rows.push_back(split(line, ','));
}
return table;
}
// Fonction pour calculer les stats et ajouter les lignes
void addStatistics(Table& table) {
int numCols = table.headers.size();
int numRows = table.rows.size();
vector<string> meanRow(numCols, "-");
vector<string> stdDevRow(numCols, "-");
// On commence à i=1 si la colonne 0 contient des noms/textes
// Ici on tente le calcul sur toutes les colonnes
for (int j = 0; j < numCols; ++j) {
vector<double> values;
for (int i = 0; i < numRows; ++i) {
try {
values.push_back(stod(table.rows[i][j]));
} catch (...) {
// Ignore les valeurs non numériques
}
}
if (!values.empty()) {
double sum = accumulate(values.begin(), values.end(), 0.0);
double mean = sum / values.size();
double sq_sum = 0;
for (double v : values) sq_sum += (v - mean) * (v - mean);
double stdDev = sqrt(sq_sum / values.size());
stringstream ssMean, ssStd;
ssMean << fixed << setprecision(2) << mean;
ssStd << fixed << setprecision(2) << stdDev;
meanRow[j] = ssMean.str();
stdDevRow[j] = ssStd.str();
}
}
meanRow[0] = "MOYENNE";
stdDevRow[0] = "ECART-TYPE";
table.rows.push_back(meanRow);
table.rows.push_back(stdDevRow);
}
// Export HTML
void saveToHTML(const Table& table, const string& filename) {
ofstream file(filename);
file << "<html><body><table border='1'><thead><tr>";
for (const auto& h : table.headers) file << "<th>" << h << "</th>";
file << "</tr></thead><tbody>";
for (const auto& row : table.rows) {
file << "<tr>";
for (const auto& cell : row) file << "<td>" << cell << "</td>";
file << "</tr>";
}
file << "</tbody></table></body></html>";
}
// Export LaTeX
void saveToLaTeX(const Table& table, const string& filename) {
ofstream file(filename);
file << "\\begin{tabular}{" << string(table.headers.size(), 'l') << "}\n\\hline\n";
for (size_t i = 0; i < table.headers.size(); ++i) {
file << table.headers[i] << (i == table.headers.size() - 1 ? "" : " & ");
}
file << " \\\\\n\\hline\n";
for (const auto& row : table.rows) {
for (size_t i = 0; i < row.size(); ++i) {
file << row[i] << (i == row.size() - 1 ? "" : " & ");
}
file << " \\\\\n";
}
file << "\\hline\n\\end{tabular}";
}
int main(int argc, char *argv[]) {
string inputFile = "donnees.csv";
if (argc > 1) {
inputFile = argv[1];
}
Table table = readCSV(inputFile);
if (table.headers.empty()) {
cout << "Erreur de lecture ou fichier vide." << endl;
return 1;
}
addStatistics(table);
saveToHTML(table, "resultat.html");
saveToLaTeX(table, "resultat.tex");
cout << "Traitement terminé. Fichiers resultat.html et resultat.tex générés." << endl;
return 0;
}
1.4. Comment faire avec Python ?
Le programme python qui permet de répondre au problème posé est composé d'une trentaine de lignes et fait appel à des
fonctionnalités déjà existantes notamment celles du package pandas.
1.4.1. Chargement des données
On utilise le package pandas qui permet de gérer les fichiers .csv.
import pandas as pd
# lecture du fichier de données sous forme d'un data frame
df = pd.read_csv('notes.csv')
# affiche le data frame
print(df)
Le package pandas lit le fichier .csv et le stocke en mémoire sous forme d'un data frame qui est une matrice de données.
Le résultat de l'affichage est :
Etudiant Math Info
0 Jean Bon 12 17
1 Marc Decafer 7 10
2 Henri Quatre 12 11
3 Marie Ney 16 18
4 Santa Claus 19 20
5 Pascal Line 13 13
6 Noël Blanc 14 13
7 Charlotte Ofrèze 11 15
8 Sarah Longe 14 19
9 Charles Magne 13 8
10 Anne Atoll 7 15
On voit que les lignes commencent à 0 et que la première ligne est considérée comme un header qui contient le nom des colonnes.
1.4.2. Calcul et ajout de la moyenne et de l'écart-type
Grâces aux instructions suivantes on calcule la moyenne (mean) et on l'ajoute à la fin du data frame, puis on fait de même
pour l'écart-type (std = standard deviation) :
# calculer la moyenne de chaque colonne et ajouter à la fin du data frame
# attention il faut donner un nom à la ligne à ajouter ici 'moyenne'
df.loc['moyenne'] = df.mean(numeric_only=True)
df.iloc[-1,0] = "Moyenne"
# calculer l'écart-type et ajouter à la fin du data frame
# attention il faut donner un nom à la ligne à ajouter ici 'ecart-type'
df.loc['ecart-type'] = df.std(numeric_only=True)
df.iloc[-1,0] = "Ecart-type"
# affichage du nouveau data frame
print(df)
La méthode loc associée au data frame permet de sélectionner des lignes et des colonnes, son format est :
df.loc[sélection_des_lignes, sélection_des_colonnes]
Le data frame modifié est alors :
Etudiant Math Info
0 Jean Bon 12.000000 17.000000
1 Marc Decafer 7.000000 10.000000
2 Henri Quatre 12.000000 11.000000
3 Marie Ney 16.000000 18.000000
4 Santa Claus 19.000000 20.000000
5 Pascal Line 13.000000 13.000000
6 Noël Blanc 14.000000 13.000000
7 Charlotte Ofrèze 11.000000 15.000000
8 Sarah Longe 14.000000 19.000000
9 Charles Magne 13.000000 8.000000
10 Anne Atoll 7.000000 15.000000
moyenne Moyenne 12.545455 14.454545
ecart-type Ecart-type 3.340213 3.677045
1.4.3. Export vers HTML
On utilise la méthode to_html associée au data frame :
# export vers HTML
html_table = df.to_html(index = False, header = True, float_format='{:.2f}'.format)
with open("tableau.html", "w") as f:
f.write(html_table)
- Le paramètre index permet d'indiquer si on désire inclure les numéros de lignes
- Le paramètre header indique d'utiliser les noms de colonnes (header)
- Le paramètre float_format indique d'afficher les nombres flottants avec deux chiffres après la virgule
Le résultat obtenu ressemble (application du style CSS de ce site) à ce qui suit :
| Etudiant |
Math |
Info |
| Jean Bon |
12.00 |
17.00 |
| Marc Decafer |
7.00 |
10.00 |
| Henri Quatre |
12.00 |
11.00 |
| Marie Ney |
16.00 |
18.00 |
| Santa Claus |
19.00 |
20.00 |
| Pascal Line |
13.00 |
13.00 |
| Noël Blanc |
14.00 |
13.00 |
| Charlotte Ofrèze |
11.00 |
15.00 |
| Sarah Longe |
14.00 |
19.00 |
| Charles Magne |
13.00 |
8.00 |
| Anne Atoll |
7.00 |
15.00 |
| Moyenne |
12.55 |
14.45 |
| Ecart-type |
3.34 |
3.68 |
1.4.4. Export vers LaTeX
On utilise la méthode to_latex associée au data frame :
# export vers LaTeX
latex_code = df.to_latex(index = False, caption="Comparaison des modèles", label="tab:modeles")
with open("tableau.tex", "w") as f:
f.write(latex_code)
- Le paramètre index permet d'indiquer si on désire inclure les numéros de lignes
- Le paramètre caption représente la légende du tableau
- Le paramètre label définit un identifiant qui peut être réutilisé pour faire référence au tableau
Le résultat obtenu est :
\begin{table}
\caption{Comparaison des modèles}
\label{tab:modeles}
\begin{tabular}{lrr}
\toprule
Etudiant & Math & Info \\\\
\midrule
Jean Bon & 12.000000 & 17.000000 \\\\
Marc Decafer & 7.000000 & 10.000000 \\\\
Henri Quatre & 12.000000 & 11.000000 \\\\
Marie Ney & 16.000000 & 18.000000 \\\\
Santa Claus & 19.000000 & 20.000000 \\\\
Pascal Line & 13.000000 & 13.000000 \\\\
Noël Blanc & 14.000000 & 13.000000 \\\\
Charlotte Ofrèze & 11.000000 & 15.000000 \\\\
Sarah Longe & 14.000000 & 19.000000 \\\\
Charles Magne & 13.000000 & 8.000000 \\\\
Anne Atoll & 7.000000 & 15.000000 \\\\
Moyenne & 12.545455 & 14.454545 \\\\
Ecart-type & 3.340213 & 3.677045 \\\\
\bottomrule
\end{tabular}
\end{table}
Le fichier résultat après compilation et ajout de packages LaTeX est tableau.pdf.
1.5. En résumé
Avec Python on dispose de packages opérationnels qui permettent de faire rapidement ce que l'on veut, il suffit de leur
procurer les paramètres adéquats.
Avec le Shell ou les scripts awk, ou encore le C++, on a plus de lignes de code à écrire.