3 Visualisation de séries de données et flots de données
📊 Projet Java : Analyse de Notes d’Étudiants avec Streams et XChart
深度求索 (Shēndù Qiúsuǒ) vous propose un projet d’analyse de notes d’étudiants qui utilise les Streams Java pour le traitement de données et XChart pour la visualisation graphique.
3.1 🎯 Objectifs du Projet
- Manipuler les Streams Java (filter, map, reduce, collect)
- Générer des visualisations avec XChart
- Répondre à des questions analytiques sur un jeu de données
3.2 📦 Structure du Projet
ProjetNotesEtudiants/
├── src/
│ ├── Etudiant.java
│ ├── Matiere.java
│ ├── AnalyseurNotes.java
│ └── Main.java
├── data/
│ └── notes.csv
└── charts/
└── (dossiers pour les graphiques générés)
3.3 📝 Code Source
3.3.1 1. Classe Etudiant
public class Etudiant {
private String nom;
private String prenom;
private Map<Matiere, Double> notes;
public Etudiant(String nom, String prenom, Map<Matiere, Double> notes) {
this.nom = nom;
this.prenom = prenom;
this.notes = notes;
}
public double getMoyenne() {
return notes.values().stream()
.mapToDouble(Double::doubleValue)
.average()
.orElse(0.0);
}
// Getters et autres méthodes...
}
3.3.2 2. Classe Matiere (énumération)
public enum Matiere {
, PHYSIQUE, INFORMATIQUE, FRANCAIS, HISTOIRE;
MATHEMATIQUES}
3.3.3 3. Classe principale avec Streams et XChart
import org.knowm.xchart.*;
import java.util.*;
import java.util.stream.Collectors;
public class AnalyseurNotes {
private List<Etudiant> etudiants;
public AnalyseurNotes(List<Etudiant> etudiants) {
this.etudiants = etudiants;
}
// Question 1: Moyenne générale par étudiant (diagramme en barres)
public void afficherMoyennesParEtudiant() {
Map<String, Double> moyennes = etudiants.stream()
.collect(Collectors.toMap(
-> e.getPrenom() + " " + e.getNom(),
e ::getMoyenne
Etudiant));
= new CategoryChartBuilder()
CategoryChart chart .width(800).height(600)
.title("Moyennes par étudiant")
.xAxisTitle("Étudiants").yAxisTitle("Moyenne")
.build();
.addSeries("Moyennes",
chartnew ArrayList<>(moyennes.keySet()),
new ArrayList<>(moyennes.values()));
new SwingWrapper<>(chart).displayChart();
}
// Question 2: Moyenne par matière (diagramme en barres)
public void afficherMoyennesParMatiere() {
Map<Matiere, Double> moyennes = Arrays.stream(Matiere.values())
.collect(Collectors.toMap(
-> matiere,
matiere -> etudiants.stream()
matiere .mapToDouble(e -> e.getNote(matiere))
.average()
.orElse(0.0)
));
= new CategoryChartBuilder()
CategoryChart chart .width(800).height(600)
.title("Moyennes par matière")
.xAxisTitle("Matières").yAxisTitle("Moyenne")
.build();
.addSeries("Moyennes",
chartArrays.stream(Matiere.values()).map(Enum::name).collect(Collectors.toList()),
new ArrayList<>(moyennes.values()));
new SwingWrapper<>(chart).displayChart();
}
// Question 3: Répartition des mentions (diagramme circulaire)
public void afficherRepartitionMentions() {
Map<String, Long> mentions = etudiants.stream()
.collect(Collectors.groupingBy(
-> {
e double moyenne = e.getMoyenne();
if (moyenne >= 16) return "Très bien";
if (moyenne >= 14) return "Bien";
if (moyenne >= 12) return "Assez bien";
return "Passable";
},
.counting()
Collectors));
= new PieChartBuilder()
PieChart chart .width(800).height(600)
.title("Répartition des mentions")
.build();
.forEach((mention, count) ->
mentions.addSeries(mention + " (" + count + ")", count));
chart
new SwingWrapper<>(chart).displayChart();
}
// Question 4: Corrélation entre deux matières (nuage de points)
public void afficherCorrelation(Matiere m1, Matiere m2) {
List<Double> notesM1 = etudiants.stream()
.map(e -> e.getNote(m1))
.collect(Collectors.toList());
List<Double> notesM2 = etudiants.stream()
.map(e -> e.getNote(m2))
.collect(Collectors.toList());
= new XYChartBuilder()
XYChart chart .width(800).height(600)
.title("Correlation entre " + m1 + " et " + m2)
.xAxisTitle(m1.name()).yAxisTitle(m2.name())
.build();
= chart.addSeries("Notes", notesM1, notesM2);
XYSeries series .setMarker(SeriesMarkers.CIRCLE);
series.setLineStyle(SeriesLines.NONE);
series
.getStyler().setDefaultSeriesRenderStyle(XYSeriesRenderStyle.Scatter);
chart.getStyler().setMarkerSize(10);
chart
new SwingWrapper<>(chart).displayChart();
}
}
3.3.4 4. Classe Main pour l’exécution
import java.util.Arrays;
import java.util.List;
public class Main {
public static void main(String[] args) {
// Création des données d'exemple
List<Etudiant> etudiants = Arrays.asList(
creerEtudiant("Dupont", "Jean", 15.0, 12.0, 18.0, 14.0, 13.0),
creerEtudiant("Martin", "Marie", 18.0, 16.0, 17.0, 15.0, 14.0),
creerEtudiant("Bernard", "Pierre", 10.0, 11.0, 9.0, 12.0, 8.0),
creerEtudiant("Dubois", "Sophie", 14.0, 13.0, 15.0, 16.0, 12.0),
creerEtudiant("Moreau", "Luc", 12.0, 14.0, 11.0, 13.0, 15.0)
);
= new AnalyseurNotes(etudiants);
AnalyseurNotes analyseur
// Réponses aux questions avec visualisation
.afficherMoyennesParEtudiant();
analyseur.afficherMoyennesParMatiere();
analyseur.afficherRepartitionMentions();
analyseur.afficherCorrelation(Matiere.MATHEMATIQUES, Matiere.PHYSIQUE);
analyseur}
private static Etudiant creerEtudiant(String nom, String prenom,
double maths, double physique,
double info, double francais,
double histoire) {
Map<Matiere, Double> notes = new HashMap<>();
//...
= new Etudiant(nom, prenom, notes);
Etudiant etudiant
return etudiant;
}
}
3.4 🔍 Questions et indications pour l’implémentation
- Quelle est la moyenne de chaque étudiant ?
- Utilisation de
Collectors.toMap
pour créer une association étudiant→moyenne - Visualisation avec un diagramme en barres
- Utilisation de
- Quelle est la moyenne par matière ?
- Utilisation de
Arrays.stream()
sur l’énumération des matières - Combinaison avec
Collectors.toMap
etmapToDouble
- Visualisation avec un diagramme en barres
- Utilisation de
- Comment se répartissent les mentions ?
- Utilisation de
Collectors.groupingBy
avec une fonction de classification - Comptage avec
Collectors.counting()
- Visualisation avec un diagramme circulaire
- Utilisation de
- Existe-t-il une corrélation entre deux matières ?
- Extraction des notes avec
map
etcollect
- Visualisation avec un nuage de points
- Extraction des notes avec
3.5 🛠️ Compétences Développées
- Streams API :
filter, map, reduce, collect, groupingBy, toMap
- XChart : Création de différents types de graphiques
- Programmation fonctionnelle avec lambda expressions
- Manipulation de collections et traitement de données
3.6 📊 Extensions Possibles
- Ajouter la lecture depuis un fichier CSV
- Implémenter des calculs statistiques plus avancés
- Ajouter des fonctionnalités d’export des graphiques
- Créer une interface utilisateur interactive
Ce projet permet de manipuler les concepts clés des Streams Java tout en produisant des visualisations concrètes avec XChart, idéal pour comprendre l’utilité du traitement de données en programmation.
3.7 Annexe : les flots (streams)
3.7.1 Les collecteurs
🧠 Comprendre les Collectors dans les Streams Java
3.7.1.1 🎯 C’est quoi un Collector ?
Imaginez que vous avez un flux d’eau (un Stream) avec des objets qui flottent. Un Collector, c’est comme un récipient qui va permettre de récupérer ces objets de la manière souhaitée :
- Dans une liste (comme un panier),
- Dans un ensemble (comme un tamis qui enlève les doublons),
- Dans un dictionnaire (comme des étagères triées),
- Ou même les compter ou les additionner,
3.7.1.2 🧰 Les Collectors de base
3.7.1.2.1 1. Collecter dans une Liste
List<String> noms = Arrays.asList("Alice", "Bob", "Alice", "Charlie");
List<String> listeNoms = noms.stream()
.collect(Collectors.toList());
// Résultat: ["Alice", "Bob", "Alice", "Charlie"]
3.7.1.2.2 2. Collecter dans un Set (enlève les doublons)
Set<String> nomsUniques = noms.stream()
.collect(Collectors.toSet());
// Résultat: ["Alice", "Bob", "Charlie"]
3.7.1.2.3 3. Collecter dans une Map
Map<String, Integer> mapLongueurs = noms.stream()
.collect(Collectors.toMap(
-> nom, // Clé
nom -> nom.length() // Valeur
nom ));
// Résultat: {"Alice"=5, "Bob"=3, "Charlie"=7}
3.7.1.3 🎪 Les Collectors avancés
3.7.1.3.1 4. Grouper des éléments
Map<Integer, List<String>> parLongueur = noms.stream()
.collect(Collectors.groupingBy(
-> nom.length()
nom ));
// Résultat: {3=["Bob"], 5=["Alice", "Alice"], 7=["Charlie"]}
3.7.1.3.2 5. Compter les éléments
Map<String, Long> comptage = noms.stream()
.collect(Collectors.groupingBy(
-> nom,
nom .counting()
Collectors));
// Résultat: {"Alice"=2, "Bob"=1, "Charlie"=1}
3.7.1.3.3 6. Joindre des chaînes
String tousLesNoms = noms.stream()
.collect(Collectors.joining(", "));
// Résultat: "Alice, Bob, Alice, Charlie"
3.7.1.5 🎓 Analogie avec la vie réelle
Collector | Analogie |
---|---|
toList() |
Mettre des courses dans un caddie |
toSet() |
Ranger des livres uniques dans une bibliothèque |
groupingBy() |
Classer des vêtements par couleur |
joining() |
Enfiler des perles sur un fil |
3.7.1.6 💡 Exemple complet
import java.util.*;
import java.util.stream.*;
public class ExempleCollector {
public static void main(String[] args) {
List<String> fruits = Arrays.asList("pomme", "banane", "orange", "pomme", "kiwi");
// 1. Liste sans modification
List<String> liste = fruits.stream()
.collect(Collectors.toList());
// 2. Ensemble sans doublons
Set<String> ensemble = fruits.stream()
.collect(Collectors.toSet());
// 3. Grouper par longueur
Map<Integer, List<String>> parTaille = fruits.stream()
.collect(Collectors.groupingBy(
-> fruit.length()
fruit ));
// 4. Compter les occurrences
Map<String, Long> compteur = fruits.stream()
.collect(Collectors.groupingBy(
-> fruit,
fruit .counting()
Collectors));
System.out.println("Liste: " + liste);
System.out.println("Ensemble: " + ensemble);
System.out.println("Par taille: " + parTaille);
System.out.println("Compteur: " + compteur);
}
}
Résultat :
Liste: [pomme, banane, orange, pomme, kiwi]
Ensemble: [banane, orange, pomme, kiwi]
Par taille: {4=[kiwi], 5=[pomme, pomme], 6=[banane, orange]}
Compteur: {banane=1, orange=1, pomme=2, kiwi=1}
3.7.1.7 🚀 Pourquoi c’est utile ?
- Plus concis : Moins de code que les boucles traditionnelles
- Plus lisible : Le code exprime l’intention clairement
- Plus efficace : Optimisé pour les traitements parallèles
- Plus flexible : Combinaison possible de multiples opérations
Les Collectors transforment les Streams d’éléments éphémères en structures de données permanentes et organisées, comme une usine qui transformerait des matières premières en produits finis ! 🏭➡️📦
3.7.1.4 🔧 Comment ça marche ?
Un Collector a 3 composants principaux :
ArrayList::new
)List::add
)List::addAll
)