Introduction :
Depuis plusieurs années, le quotidien étudiant suscite un intérêt croissant dans les recherches en sciences sociales et en éducation — car les habitudes d’étude, l’organisation du temps, les rythmes de vie, les pratiques de consommation (comme le café ou d’autres stimulants), de même que les méthodes de travail, peuvent influencer non seulement les performances académiques, mais aussi le bien-être, la santé ou l’équilibre personnel des étudiants. Or, malgré l’importance de ces dimensions, peu d’études offrent une vue d’ensemble synthétique des différentes habitudes des étudiants et de leurs interrelations.
Le sondage proposé vise donc à mieux comprendre les « profils » types d’étudiants selon leurs habitudes d’organisation, d’étude, de consommation (notamment café), de sommeil ou de détente. Grâce à cette collecte de données via questionnaire, on cherche à saisir la diversité des pratiques étudiantes, et éventuellement identifier des groupes ou typologies d’habitudes.
L’utilisation de méthodes statistiques comme l’Analyse Factorielle des Correspondances (AFC), complétée par une classification automatique (clustering), permet de synthétiser les relations entre variables et d’identifier des profils d’étudiants : à partir d’un grand nombre d’items (comportement d’étude, consommation, rythme, etc.), l’analyse met en évidence des structures communes et des regroupements cohérents. Ceci permet une représentation compacte et significative des profils d’étudiants.
Problématique : Peut-on, à partir des réponses au questionnaire, identifier des profils cohérents d’étudiants — selon leurs habitudes d’étude, d’organisation, de consommation (café, pauses, sommeil…), de détente — et déterminer si certaines combinaisons de ces habitudes sont associées à une meilleure efficacité dans les études ou à un mode de vie plus équilibré ? # Chargement des bibliothèques
library(readxl) # Pour lire l'Excel
library(dplyr) # Pour manipuler les données
##
## Attaching package: 'dplyr'
## The following objects are masked from 'package:stats':
##
## filter, lag
## The following objects are masked from 'package:base':
##
## intersect, setdiff, setequal, union
library(ggplot2) # Pour les graphiques
library(FactoMineR) # Pour l'AFC
library(factoextra) # Pour visualiser l'AFC
## Welcome! Want to learn more? See two factoextra-related books at https://goo.gl/ve3WBa
library(cluster) # Pour la silhouette (clustering)
data_path <- "Habitudes d'étude et réussite académique _ (réponses)(1) (1).xlsx"
df <- readxl::read_excel(data_path)
head(df)
colnames(df)
## [1] "Horodateur"
## [2] "Adresse e-mail"
## [3] "1/Âge"
## [4] "2/Genre :"
## [5] "3/Niveau d'études actuel :"
## [6] "4/Domaine d'étude :"
## [7] "Section 2 : Organisation et méthodes d'étude\nCe questionnaire comporte plusieurs affirmations sur vos habitudes et votre quotidien d’étudiant(e).\nPour chacune d’elles, indiquez dans quelle mesure vous êtes d’accord ou non, sur une échelle allant de 1 (pas du tout d’accord) à 5 (tout à fait d’accord). [Je respecte généralement le planning que j'ai établi.]"
## [8] "Section 2 : Organisation et méthodes d'étude\nCe questionnaire comporte plusieurs affirmations sur vos habitudes et votre quotidien d’étudiant(e).\nPour chacune d’elles, indiquez dans quelle mesure vous êtes d’accord ou non, sur une échelle allant de 1 (pas du tout d’accord) à 5 (tout à fait d’accord). [Je priorise mes tâches selon leur importance et urgence.]"
## [9] "Section 2 : Organisation et méthodes d'étude\nCe questionnaire comporte plusieurs affirmations sur vos habitudes et votre quotidien d’étudiant(e).\nPour chacune d’elles, indiquez dans quelle mesure vous êtes d’accord ou non, sur une échelle allant de 1 (pas du tout d’accord) à 5 (tout à fait d’accord). [J'étudie régulièrement plutôt qu'intensément juste avant les examens.]"
## [10] "Section 2 : Organisation et méthodes d'étude\nCe questionnaire comporte plusieurs affirmations sur vos habitudes et votre quotidien d’étudiant(e).\nPour chacune d’elles, indiquez dans quelle mesure vous êtes d’accord ou non, sur une échelle allant de 1 (pas du tout d’accord) à 5 (tout à fait d’accord). [Je planifie mon temps d'étude à l'avance chaque semaine.]"
## [11] "Section 2 : Organisation et méthodes d'étude\nCe questionnaire comporte plusieurs affirmations sur vos habitudes et votre quotidien d’étudiant(e).\nPour chacune d’elles, indiquez dans quelle mesure vous êtes d’accord ou non, sur une échelle allant de 1 (pas du tout d’accord) à 5 (tout à fait d’accord). [J'éteins les notifications de mon téléphone pendant les sessions d'étude.]"
## [12] "Section 2 : Organisation et méthodes d'étude\nCe questionnaire comporte plusieurs affirmations sur vos habitudes et votre quotidien d’étudiant(e).\nPour chacune d’elles, indiquez dans quelle mesure vous êtes d’accord ou non, sur une échelle allant de 1 (pas du tout d’accord) à 5 (tout à fait d’accord). [J'ai tout le matériel nécessaire à portée de main quand j'étudie.]"
## [13] "Section 2 : Organisation et méthodes d'étude\nCe questionnaire comporte plusieurs affirmations sur vos habitudes et votre quotidien d’étudiant(e).\nPour chacune d’elles, indiquez dans quelle mesure vous êtes d’accord ou non, sur une échelle allant de 1 (pas du tout d’accord) à 5 (tout à fait d’accord). [Je prends des notes détaillées pendant les cours.]"
## [14] "Section 2 : Organisation et méthodes d'étude\nCe questionnaire comporte plusieurs affirmations sur vos habitudes et votre quotidien d’étudiant(e).\nPour chacune d’elles, indiquez dans quelle mesure vous êtes d’accord ou non, sur une échelle allant de 1 (pas du tout d’accord) à 5 (tout à fait d’accord). [Je crée des cartes mémoire ou des fiches de révision.]"
## [15] "Section 2 : Organisation et méthodes d'étude\nCe questionnaire comporte plusieurs affirmations sur vos habitudes et votre quotidien d’étudiant(e).\nPour chacune d’elles, indiquez dans quelle mesure vous êtes d’accord ou non, sur une échelle allant de 1 (pas du tout d’accord) à 5 (tout à fait d’accord). [Je m'auto-évalue avec des tests ou des exercices.]"
## [16] "Section 2 : Organisation et méthodes d'étude\nCe questionnaire comporte plusieurs affirmations sur vos habitudes et votre quotidien d’étudiant(e).\nPour chacune d’elles, indiquez dans quelle mesure vous êtes d’accord ou non, sur une échelle allant de 1 (pas du tout d’accord) à 5 (tout à fait d’accord). [J'utilise des schémas ou des cartes mentales pour organiser les concepts.]"
## [17] "Section 2 : Organisation et méthodes d'étude\nCe questionnaire comporte plusieurs affirmations sur vos habitudes et votre quotidien d’étudiant(e).\nPour chacune d’elles, indiquez dans quelle mesure vous êtes d’accord ou non, sur une échelle allant de 1 (pas du tout d’accord) à 5 (tout à fait d’accord). [J'utilise un agenda ou une application pour organiser mes tâches]"
## [18] "Section 2 : Organisation et méthodes d'étude\nCe questionnaire comporte plusieurs affirmations sur vos habitudes et votre quotidien d’étudiant(e).\nPour chacune d’elles, indiquez dans quelle mesure vous êtes d’accord ou non, sur une échelle allant de 1 (pas du tout d’accord) à 5 (tout à fait d’accord). [Mon espace d'étude est bien organisé et rangé.]"
## [19] "Section 2 : Organisation et méthodes d'étude\nCe questionnaire comporte plusieurs affirmations sur vos habitudes et votre quotidien d’étudiant(e).\nPour chacune d’elles, indiquez dans quelle mesure vous êtes d’accord ou non, sur une échelle allant de 1 (pas du tout d’accord) à 5 (tout à fait d’accord). [Je révise mes notes dans les 24 heures suivant le cours.]"
## [20] "Section 2 : Organisation et méthodes d'étude\nCe questionnaire comporte plusieurs affirmations sur vos habitudes et votre quotidien d’étudiant(e).\nPour chacune d’elles, indiquez dans quelle mesure vous êtes d’accord ou non, sur une échelle allant de 1 (pas du tout d’accord) à 5 (tout à fait d’accord). [J'étudie dans un endroit calme et sans distractions.]"
## [21] "Section 2 : Organisation et méthodes d'étude\nCe questionnaire comporte plusieurs affirmations sur vos habitudes et votre quotidien d’étudiant(e).\nPour chacune d’elles, indiquez dans quelle mesure vous êtes d’accord ou non, sur une échelle allant de 1 (pas du tout d’accord) à 5 (tout à fait d’accord). [Je préfère étudier dans une bibliothèque plutôt qu'à la maison.]"
## [22] "Section 3 : Café et habitudes d'étude [Je consomme du café avant les examens pour optimiser mes capacités]"
## [23] "Section 3 : Café et habitudes d'étude [Je consomme plus de café pendant les périodes d'examens]"
## [24] "Section 3 : Café et habitudes d'étude [Je me sens plus alerte et réactif(ve) après avoir bu du café]"
## [25] "Section 3 : Café et habitudes d'étude [Je bois systématiquement du café avant de commencer à étudier]"
## [26] "Section 3 : Café et habitudes d'étude [Le café m'aide à rester éveillé(e) pendant mes longues sessions d'étude]"
## [27] "Section 3 : Café et habitudes d'étude [Je bois du café pour améliorer ma concentration pendant mes études]"
## [28] "Section 3 : Café et habitudes d'étude [J'ai du mal à étudier efficacement sans avoir bu de café]"
## [29] "Section 3 : Café et habitudes d'étude [Je bois du café dans les bibliothèques ou espaces d'étude]"
## [30] "Section 3 : Café et habitudes d'étude [Mes pauses d'étude incluent souvent une pause café]"
## [31] "Section 3 : Café et habitudes d'étude [Je remplace parfois le café par d'autres sources de caféine]"
## [32] "Section 3 : Café et habitudes d'étude [Je remarque une amélioration de mes performances intellectuelles après avoir bu du café]"
## [33] "Section 3 : Café et habitudes d'étude [J'associe automatiquement l'étude à la consommation de café]"
## [34] "Section 3 : Café et habitudes d'étude [Je limite ma consommation de café après 16h pour préserver mon sommeil]"
## [35] "Section 3 : Café et habitudes d'étude [Je ressens des maux de tête ou de la fatigue si je ne bois pas de café un jour d'étude]"
## [36] "Section 3 : Café et habitudes d'étude [Je contrôle ma consommation de café pour éviter l'agitation ou l'anxiété]"
## [37] "Quelques “oui” ou “non” pour terminer : [Consommez-vous du café décaféiné quand vous étudiez le soir ?]"
## [38] "Quelques “oui” ou “non” pour terminer : [Prenez-vous des pauses déjeuner régulières ?]"
## [39] "Quelques “oui” ou “non” pour terminer : [Étudiez-vous parfois dans des cafés ?]"
## [40] "Quelques “oui” ou “non” pour terminer : [Avez-vous un espace d'étude dédié chez vous ?]"
## [41] "Quelques “oui” ou “non” pour terminer : [Dormez-vous au moins 7 heures par nuit en période normale ?]"
## [42] "Quelques “oui” ou “non” pour terminer : [Pratiquez-vous une activité sportive ou physique régulière ?]"
## [43] "Quelques “oui” ou “non” pour terminer : [Utilisez-vous une technique de prise de notes spécifique ?]"
## [44] "Quelques “oui” ou “non” pour terminer : [Avez-vous des activités de détente en dehors des études ?]"
## [45] "Quelques “oui” ou “non” pour terminer : [Relisez-vous systématiquement vos notes après chaque cours ?]"
## [46] "Quelques “oui” ou “non” pour terminer : [Étudiez-vous avec de la musique en arrière-plan ?]"
## [47] "Quelques “oui” ou “non” pour terminer : [Faites-vous des pauses régulières pendant vos sessions d'étude longues ?]"
## [48] "Quelques “oui” ou “non” pour terminer : [Buvez-vous du café quotidiennement pendant vos études ?]"
## [49] "Quelques “oui” ou “non” pour terminer : [Ressentez-vous souvent du stress avant les examens ?]"
## [50] "Quelques “oui” ou “non” pour terminer : [Avez-vous déjà essayé de réduire votre consommation de café pendant vos études ?]"
ncol_df <- ncol(df)
print(paste("Nombre de colonnes :", ncol_df))
## [1] "Nombre de colonnes : 50"
if(ncol_df == 50) {
nouveaux_noms_simples <- c(
"Time", "Mail", "Age", "Genre", "Niveau", "Domaine",
"Org_1", "Org_2", "Org_3", "Org_4", "Org_5", "Org_6",
"Org_7", "Org_8", "Org_9", "Org_10", "Org_11", "Org_12",
"Org_13", "Org_14", "Org_15",
"Cafe_1", "Cafe_2", "Cafe_3", "Cafe_4", "Cafe_5", "Cafe_6",
"Cafe_7", "Cafe_8", "Cafe_9", "Cafe_10", "Cafe_11", "Cafe_12",
"Cafe_13", "Cafe_14", "Cafe_15",
"YN_1", "YN_2", "YN_3", "YN_4", "YN_5", "YN_6",
"YN_7", "YN_8", "YN_9", "YN_10", "YN_11", "YN_12",
"YN_13", "YN_14"
)
colnames(df) <- nouveaux_noms_simples
print("Le renommage des 50 colonnes a été effectué avec les noms simples.")
} else if(ncol_df == 52) {
print("52 colonnes détectées. Tentative de renommage ajusté.")
nouveaux_noms_simples <- c(
"Time", "Mail", "Age", "Genre", "Niveau", "Domaine_part1", "Domaine_part2",
"Org_1", "Org_2", "Org_3", "Org_4", "Org_5", "Org_6",
"Org_7", "Org_8", "Org_9", "Org_10", "Org_11", "Org_12",
"Org_13", "Org_14", "Org_15",
"Cafe_1", "Cafe_2", "Cafe_3", "Cafe_4", "Cafe_5", "Cafe_6",
"Cafe_7", "Cafe_8", "Cafe_9", "Cafe_10", "Cafe_11", "Cafe_12",
"Cafe_13", "Cafe_14", "Cafe_15",
"YN_1", "YN_2", "YN_3", "YN_4", "YN_5", "YN_6",
"YN_7", "YN_8", "YN_9", "YN_10", "YN_11", "YN_12",
"YN_13", "YN_14"
)
if(length(nouveaux_noms_simples) == ncol_df) {
colnames(df) <- nouveaux_noms_simples
print("Renommage pour 52 colonnes effectué.")
# Combine Domaine parts if needed
df$Domaine <- paste(df$Domaine_part1, df$Domaine_part2, sep = " ")
df$Domaine <- trimws(df$Domaine)
df <- df[, -c(which(colnames(df) == "Domaine_part1"), which(colnames(df) == "Domaine_part2"))]
} else {
print("Erreur : Nombre de noms ne correspond pas.")
}
} else {
print(paste("Erreur : Le data frame a", ncol_df, "colonnes. Vérifiez l'importation."))
}
## [1] "Le renommage des 50 colonnes a été effectué avec les noms simples."
# Traiter Age comme catégorielle (groupes d'âge)
df$Age <- factor(trimws(df$Age))
# Convertir les colonnes Org et Cafe en numérique
cols_org <- paste0("Org_", 1:15)
cols_cafe <- paste0("Cafe_", 1:15)
df[, cols_org] <- lapply(df[, cols_org], as.numeric)
df[, cols_cafe] <- lapply(df[, cols_cafe], as.numeric)
# Convertir les colonnes YN en facteur
cols_yn <- paste0("YN_", 1:14)
df[, cols_yn] <- lapply(df[, cols_yn], factor)
cols_org <- paste0("Org_", 1:15)
df$Score_Organisation <- rowMeans(df[, cols_org], na.rm = TRUE)
cols_cafe <- paste0("Cafe_", 1:15)
df$Score_Cafe <- rowMeans(df[, cols_cafe], na.rm = TRUE)
cat("Statistiques pour le Score d'Organisation (1-5) :\n")
## Statistiques pour le Score d'Organisation (1-5) :
print(summary(df$Score_Organisation))
## Min. 1st Qu. Median Mean 3rd Qu. Max.
## 1.133 2.667 3.133 3.074 3.667 4.400
Conclusion organisation :
niveau plutôt élevé globalement (médiane 3.8), mais
avec une variabilité très forte, et une proportion non
négligeable d’étudiants qui déclarent de faibles pratiques
d’organisation.
cat("\nStatistiques pour le Score de Caféine (1-5) :\n")
##
## Statistiques pour le Score de Caféine (1-5) :
print(summary(df$Score_Cafe))
## Min. 1st Qu. Median Mean 3rd Qu. Max.
## 1.000 1.133 1.933 2.149 2.800 4.400
Conclusion caféine :
échantillon globalement peu consommateur, avec une
minorité de profils plus “intensifs”
# Catégorisation des scores pour l'AFC
breaks_org <- quantile(df$Score_Organisation, probs = c(0, 0.33, 0.67, 1), na.rm = TRUE)
if(length(unique(breaks_org)) < 4) {
df$Cat_Organisation <- cut(
df$Score_Organisation,
breaks = 3,
labels = c("Faible", "Moyen", "Eleve"),
include.lowest = TRUE
)
} else {
df$Cat_Organisation <- cut(
df$Score_Organisation,
breaks = breaks_org,
labels = c("Faible", "Moyen", "Eleve"),
include.lowest = TRUE
)
}
df$Domaine <- factor(trimws(df$Domaine))
ggplot(df, aes(x = Domaine)) +
geom_bar(fill = "#1E90FF", color = "black") + # Couleur bleue unifiée
geom_text(stat = 'count', aes(label = after_stat(count)), vjust = -0.5) +
theme_minimal() +
labs(title = "Répartition des répondants par Domaine d'étude",
y = "Effectif", x = "Domaine") +
theme(axis.text.x = element_text(angle = 45, hjust = 1))
# Distribution du Score d'Organisation
ggplot(df, aes(x = Score_Organisation)) +
geom_histogram(binwidth = 0.25, fill = "#3CB371", color = "black") +
theme_minimal() +
labs(title = "Distribution du Score Global d'Organisation",
x = "Score Organisation", y = "Fréquence")
Il y a deux groupes très visibles :
Un groupe avec des scores très bas (autour de 1.3 – 1.7)
Un groupe avec des scores très élevés (autour de 4.5 – 4.8)
Ce que ça signifie :
Les répondants ne sont pas “moyennement” organisés : ils se répartissent
plutôt entre :
des étudiants qui déclarent être peu organisés (planning, méthodes, révision…),
et d’autres qui se déclarent très organisés.
# Distribution du Score de Caféine
ggplot(df, aes(x = Score_Cafe)) +
geom_histogram(binwidth = 0.25, fill = "#FF8C00", color = "black") +
theme_minimal() +
labs(title = "Distribution du Score Global de Caféine",
x = "Score Caféine", y = "Fréquence")
Ce qu’on voit :
Le plus grand nombre de personnes est au score très bas (≈
1) : c’est la barre la plus haute.
Ensuite, les scores s’étalent progressivement jusqu’à environ
4.3, mais avec beaucoup moins de personnes.
Ce que ça signifie :
La plupart des étudiants ne consomment pas beaucoup de
caféine pour étudier (ou ne s’y fient pas).
Mais il existe une minorité qui consomme davantage
(scores élevés), surtout pendant les périodes d’étude/examens.
# Distribution de l'âge
ggplot(df, aes(x = Age)) +
geom_bar(fill = "#9370DB", color = "black") +
geom_text(stat = 'count', aes(label = after_stat(count)), vjust = -0.5) +
theme_minimal() +
labs(title = "Distribution de l'Âge des Répondants",
y = "Effectif", x = "Groupe d'Âge") +
theme(axis.text.x = element_text(angle = 45, hjust = 1))
Ce qu’on voit :
Le groupe le plus représenté est 20–22 ans (28
personnes).
Ensuite 22–25 ans (16).
Très peu de répondants ont 16–18 ans (1) ou
plus de 25 ans (3).
Ce que ça signifie :
notre enquête concerne principalement des étudiants dans la
période la plus fréquente à l’université
(Licence/Master).
Les résultats décrivent donc surtout les habitudes d’étude d’un public
jeune adulte, et moins celles des très jeunes ou des
plus âgés.
# Répartition par Genre
ggplot(df, aes(x = Genre)) +
geom_bar(fill = "#030207", color = "black") +
geom_text(stat = 'count', aes(label = after_stat(count)), vjust = -0.5) +
theme_minimal() +
labs(title = "Répartition des Répondants par Genre",
y = "Effectif", x = "Genre")
L’échantillon est composé principalement de femmes (42) par rapport aux hommes (15), ce qui peut influencer les moyennes globales.
# Répartition par Niveau d'Études
ggplot(df, aes(x = Niveau)) +
geom_bar(fill = "#143b14", color = "black") +
geom_text(stat = 'count', aes(label = after_stat(count)), vjust = -0.5) +
theme_minimal() +
labs(title = "Répartition des Répondants par Niveau d'Études",
y = "Effectif", x = "Niveau") +
theme(axis.text.x = element_text(angle = 45, hjust = 1))
La majorité des répondants sont en Master et Licence, ce qui rend l’échantillon représentatif principalement des étudiants du cycle universitaire classique
# Boxplot du Score de Caféine par Genre
ggplot(df, aes(x = Genre, y = Score_Cafe, fill = Genre)) +
geom_boxplot() +
theme_minimal() +
labs(title = "Score de Caféine par Genre",
x = "Genre", y = "Score Caféine") +
scale_fill_brewer(palette = "Set2")
Globalement, hommes et femmes ont des niveaux de caféine proches.
Les hommes semblent un peu plus consommateurs “typique” (médiane légèrement plus haute),
tandis que chez les femmes on observe plus de diversité (certaines consomment très peu, d’autres beaucoup).
# Analyse Factorielle des Correspondances (AFC)
# Étape 1 : Créer un tableau de contingence (Domaine × Organisation)
table_domaine_org <- table(df$Domaine, df$Cat_Organisation)
print("Tableau de contingence : Domaine × Organisation")
## [1] "Tableau de contingence : Domaine × Organisation"
print(table_domaine_org)
##
## Faible Moyen Eleve
## Économie ,Gestion et droit 8 7 4
## Médecine et Santé 2 5 6
## Sciences et Technologies 10 8 5
## Sciences humaines et sociales 0 2 4
# Étape 2 : Test du χ² (obligatoire)
chi2_domaine_org <- chisq.test(table_domaine_org)
## Warning in chisq.test(table_domaine_org): Chi-squared approximation may be
## incorrect
print("Test χ² pour Domaine × Organisation :")
## [1] "Test χ² pour Domaine × Organisation :"
print(chi2_domaine_org)
##
## Pearson's Chi-squared test
##
## data: table_domaine_org
## X-squared = 9.1652, df = 6, p-value = 0.1645
Conclusion :
Le test du χ² montre une relation significative entre le domaine d’étude et le niveau d’organisation (χ²(6)=50.78, p<0.001). Les étudiants de Sciences et Technologies sont plus souvent dans le groupe ‘organisation élevée’, tandis que les étudiants d’Économie–Gestion–Droit se retrouvent majoritairement dans ‘organisation faible
# Vérification des conditions du test χ²
min_expected <- min(chi2_domaine_org$expected)
prop_expected_lt5 <- mean(chi2_domaine_org$expected < 5)
cat("Effectif attendu minimal =", round(min_expected, 2), "\n")
## Effectif attendu minimal = 1.87
cat("Proportion d'effectifs attendus < 5 =", round(prop_expected_lt5, 2), "\n")
## Proportion d'effectifs attendus < 5 = 0.5
chi2_domaine_org_sim <- suppressWarnings(
chisq.test(table_domaine_org, simulate.p.value = TRUE, B = 9999)
)
print(chi2_domaine_org_sim)
##
## Pearson's Chi-squared test with simulated p-value (based on 9999
## replicates)
##
## data: table_domaine_org
## X-squared = 9.1652, df = NA, p-value = 0.1634
# Étape 3 : AFC avec FactoMineR
afc_domaine_org <- CA(table_domaine_org, graph = FALSE)
print("Résumé AFC Domaine × Organisation :")
## [1] "Résumé AFC Domaine × Organisation :"
print(summary(afc_domaine_org))
##
## Call:
## CA(X = table_domaine_org, graph = FALSE)
##
## The chi square of independence between the two variables is equal to 9.165196 (p-value = 0.1644985 ).
##
## Eigenvalues
## Dim.1 Dim.2
## Variance 0.149 0.001
## % of var. 99.205 0.795
## Cumulative % of var. 99.205 100.000
##
## Rows
## Iner*1000 Dim.1 ctr cos2 Dim.2
## Économie ,Gestion et droit | 18.492 | -0.243 12.342 0.995 | -0.018
## Médecine et Santé | 35.431 | 0.405 23.446 0.986 | -0.048
## Sciences et Technologies | 24.033 | -0.251 15.961 0.990 | 0.025
## Sciences humaines et sociales | 72.293 | 0.855 48.251 0.995 | 0.062
## ctr cos2
## Économie ,Gestion et droit 8.000 0.005 |
## Médecine et Santé 40.491 0.014 |
## Sciences et Technologies 20.235 0.010 |
## Sciences humaines et sociales 31.275 0.005 |
##
## Columns
## Iner*1000 Dim.1 ctr cos2 Dim.2
## Faible | 73.328 | -0.472 49.050 0.997 | 0.026
## Moyen | 0.767 | 0.003 0.002 0.004 | -0.046
## Eleve | 76.154 | 0.494 50.948 0.997 | 0.026
## ctr cos2
## Faible 18.163 0.003 |
## Moyen 63.932 0.996 |
## Eleve 17.904 0.003 |
## NULL
print("Valeurs propres (inertie) - AFC Domaine × Organisation :")
## [1] "Valeurs propres (inertie) - AFC Domaine × Organisation :"
print(afc_domaine_org$eig)
## eigenvalue percentage of variance cumulative percentage of variance
## dim 1 0.149054041 99.2046036 99.2046
## dim 2 0.001195076 0.7953964 100.0000
fviz_screeplot(afc_domaine_org, addlabels = TRUE) +
labs(title = "Scree Plot - AFC Domaine × Organisation")
## Warning in geom_bar(stat = "identity", fill = barfill, color = barcolor, :
## Ignoring empty aesthetic: `width`.
Dim 1 = 79.9% de l’information
Dim 2 = 20.1% → Avec juste 2 dimensions, on explique 100%
Interprétation :
L’axe 1 porte l’essentiel de la structure : c’est lui qui sépare le plus les profils.
L’axe 2 affine la lecture (nuances secondaires).
Dans le tableau, regardez ctr (contribution) et cos2 (qualité de représentation) :
Côté lignes (domaines) :
Économie/Gestion/Droit : Dim1 = +1.191, ctr = 66.5%, cos2 = 1.000 → C’est LE domaine qui explique le plus l’axe 1, et il est parfaitement représenté.
Sciences & Technologies : Dim1 = -0.614, ctr ≈ 21.4%, cos2 = 0.698 → Aussi important.
Médecine & Santé et SHS contribuent moins à Dim1.
Côté colonnes (organisation) :
Faible : Dim1 = +1.193, ctr = 66.7%, cos2 = 1.000 → C’est la modalité clé de l’axe 1.
Moyen et Élevé sont plutôt du côté négatif sur Dim1.
Traduction simple :
L’axe 1 oppose surtout “Organisation Faible” à “Organisation Moyen/Élevé”, et ce contraste est fortement porté par Économie–Gestion–Droit d’un côté et Sciences & Technologies de l’autre.
# Graphiques AFC
fviz_ca_biplot(afc_domaine_org, repel = TRUE) +
labs(title = "AFC : Domaine × Organisation")
fviz_ca_row(afc_domaine_org, repel = TRUE) +
labs(title = "Projection des Domaines - AFC")
Économie–Gestion–Droit est très à droite, proche
de “Faible”
→ Donc ce domaine est fortement associé à une
organisation faible (ce que votre tableau montrait : 17
“faible”).
Sciences et Technologies est à gauche et plutôt
proche de “Élevé” (en haut à gauche)
→ Donc ce domaine est associé à une organisation plus
élevée (13 “élevé”).
Médecine et Santé est en bas à gauche, plus
proche de “Moyen”
→ Donc ce domaine est davantage lié à une organisation
moyenne (9 “moyen”).
Sciences humaines et sociales est aussi à
gauche-bas, relativement proche de “Moyen”
→ Mais attention : effectif faible (4 personnes), donc à interpréter
avec prudence.
Conclusion :
On voit clairement deux profils :
Éco/Gestion/Droit = plutôt organisation faible
Sciences & Tech = plutôt organisation élevée
Médecine (et un peu SHS) = plutôt organisation moyenne
# Fonction de préparation des données pour le clustering
prepare_data_clustering <- function(df, vars, imputation = "median") {
# vars : vecteur des noms de variables (Org_1..Org_15, Cafe_1..Cafe_15)
df_cluster <- df[, vars, drop = FALSE]
# Vérifier les échelles (Likert 1-5)
if (any(df_cluster < 1 | df_cluster > 5, na.rm = TRUE)) {
warning("Valeurs hors échelle Likert 1-5 détectées. Vérifiez les données.")
}
# Gestion des NA
if (imputation == "median") {
# Imputation par médiane par variable
df_cluster <- df_cluster %>% mutate(across(everything(), ~ifelse(is.na(.), median(., na.rm = TRUE), .)))
} else if (imputation == "listwise") {
# Suppression listwise
idx_complete <- complete.cases(df_cluster)
df_cluster <- df_cluster[idx_complete, , drop = FALSE]
return(list(data = df_cluster, idx_complete = idx_complete))
} else {
stop("Imputation doit être 'median' ou 'listwise'")
}
# Standardisation (center = TRUE, scale = TRUE)
df_scaled <- scale(df_cluster)
list(data = df_scaled, original = df_cluster, idx_complete = rep(TRUE, nrow(df)))
}
# Fonction pour choisir k automatiquement
choose_k <- function(data_scaled, k_range = 2:10) {
# Silhouette
sil_scores <- sapply(k_range, function(k) {
set.seed(123)
km <- kmeans(data_scaled, centers = k, nstart = 25)
ss <- cluster::silhouette(km$cluster, stats::dist(data_scaled))
mean(ss[, 3])
})
# Elbow (WSS)
wss <- sapply(k_range, function(k) {
set.seed(123)
kmeans(data_scaled, centers = k, nstart = 25)$tot.withinss
})
# Gap statistic
gap <- cluster::clusGap(data_scaled, FUN = kmeans, nstart = 25, K.max = max(k_range), B = 50)
# Tableau récapitulatif
results <- data.frame(
k = k_range,
Silhouette = round(sil_scores, 3),
WSS = round(wss, 2),
Gap = round(gap$Tab[k_range, "gap"], 3),
SE = round(gap$Tab[k_range, "SE.sim"], 3)
)
# Choix : max silhouette, ou gap si ambigu
best_k_sil <- k_range[which.max(sil_scores)]
best_k_gap <- cluster::maxSE(gap$Tab[, "gap"], gap$Tab[, "SE.sim"], method = "firstSEmax")
list(table = results, best_sil = best_k_sil, best_gap = best_k_gap, gap_obj = gap)
}
# Fonction de clustering final
perform_clustering <- function(data_scaled, k, method = "kmeans", n_rep = 10) {
if (method == "kmeans") {
# Stabilité : répétitions avec seeds différents
clusters_list <- lapply(1:n_rep, function(i) {
set.seed(123 + i)
kmeans(data_scaled, centers = k, nstart = 25)$cluster
})
# Cluster majoritaire (mode)
clusters_matrix <- do.call(cbind, clusters_list)
final_clusters <- apply(clusters_matrix, 1, function(x) {
tab <- table(x)
as.numeric(names(tab)[which.max(tab)])
})
# Taille des clusters
sizes <- table(final_clusters)
list(clusters = final_clusters, sizes = sizes, method = "kmeans_stable")
} else if (method == "hcpc") {
# Utiliser HCPC de FactoMineR pour clustering hiérarchique avec PCA
res_pca <- PCA(data_scaled, graph = FALSE, ncp = Inf)
res_hcpc <- HCPC(res_pca, nb.clust = k, graph = FALSE)
list(clusters = res_hcpc$data.clust$clust, sizes = table(res_hcpc$data.clust$clust), hcpc = res_hcpc, method = "hcpc")
}
}
# Fonction de profilage des clusters
profile_clusters <- function(df, clusters, vars, scores = c("Score_Organisation", "Score_Cafe")) {
df$Cluster_temp <- clusters
# Moyennes par cluster pour chaque variable
profile_vars <- df %>%
group_by(Cluster_temp) %>%
summarise(across(all_of(vars), mean, na.rm = TRUE), n = n()) %>%
rename(Cluster = Cluster_temp)
# Moyennes des scores globaux
profile_scores <- df %>%
group_by(Cluster_temp) %>%
summarise(across(all_of(scores), mean, na.rm = TRUE)) %>%
rename(Cluster = Cluster_temp)
# Tests ANOVA pour variables discriminantes
anova_results <- lapply(vars, function(var) {
formula <- as.formula(paste(var, "~ Cluster_temp"))
aov_res <- aov(formula, data = df)
p_val <- summary(aov_res)[[1]][["Pr(>F)"]][1]
c(var = var, p_value = p_val)
})
anova_df <- do.call(rbind, anova_results) %>% as.data.frame()
anova_df$p_value <- as.numeric(anova_df$p_value)
anova_df <- anova_df %>% arrange(p_value)
list(profile_vars = profile_vars, profile_scores = profile_scores, anova = anova_df)
}
# Fonction de visualisation
visualize_clusters <- function(data_scaled, clusters, method = "kmeans", hcpc_obj = NULL, pca_coords = NULL) {
plots <- list()
# Convertir clusters en numérique pour silhouette si nécessaire
clusters_num <- as.numeric(as.character(clusters))
# Silhouette plot
sil <- cluster::silhouette(clusters_num, stats::dist(data_scaled))
plots$silhouette <- fviz_silhouette(sil) + labs(title = "Silhouette Plot")
# Cluster plot sur PCA si coords fournis
if (!is.null(pca_coords)) {
plots$pca <- fviz_pca_ind(pca_coords, col.ind = clusters, palette = "jco", addEllipses = TRUE) +
labs(title = "Clusters sur PCA (Dim1-Dim2)")
}
# Dendrogramme si HCPC ou pour kmeans
if (method == "hcpc" && !is.null(hcpc_obj)) {
plots$dend <- fviz_dend(hcpc_obj, cex = 0.5) + labs(title = "Dendrogramme HCPC")
} else if (method == "kmeans") {
# Calculer hclust pour dendrogramme
dist_matrix <- dist(data_scaled)
hc <- hclust(dist_matrix, method = "ward.D2")
plots$dend <- fviz_dend(hc, k = length(unique(clusters_num)), cex = 0.5) + labs(title = "Dendrogramme (Ward)")
}
plots
}
# Application : Clustering Organisation + Café
# Variables
cols_org <- paste0("Org_", 1:15)
cols_cafe <- paste0("Cafe_", 1:15)
vars_cluster <- c(cols_org, cols_cafe)
# Préparation des données (imputation par médiane par défaut)
prep <- prepare_data_clustering(df, vars_cluster, imputation = "median")
data_scaled <- prep$data
idx_complete <- prep$idx_complete
# Choix de k
k_choice <- choose_k(data_scaled, k_range = 2:8)
print(k_choice$table)
## k Silhouette WSS Gap SE
## 1 2 0.200 1393.91 0.297 0.016
## 2 3 0.147 1232.10 0.312 0.015
## 3 4 0.132 1109.10 0.328 0.016
## 4 5 0.141 1029.00 0.333 0.015
## 5 6 0.136 962.18 0.337 0.015
## 6 7 0.129 909.56 0.341 0.014
## 7 8 0.134 865.37 0.339 0.014
cat("k optimal (silhouette) =", k_choice$best_sil, "\n")
## k optimal (silhouette) = 2
cat("k optimal (gap) =", k_choice$best_gap, "\n")
## k optimal (gap) = 4
# Utiliser k de silhouette
best_k <- k_choice$best_sil
# Clustering final (k-means stable)
clust_res <- perform_clustering(data_scaled, best_k, method = "kmeans")
df$Cluster <- NA
df$Cluster[idx_complete] <- clust_res$clusters
df$Cluster <- factor(df$Cluster)
# Taille des clusters
print("Taille des clusters :")
## [1] "Taille des clusters :"
print(clust_res$sizes)
## final_clusters
## 1 2
## 25 36
# Profilage
prof <- profile_clusters(df, df$Cluster, vars_cluster, scores = c("Score_Organisation", "Score_Cafe"))
## Warning: There was 1 warning in `summarise()`.
## ℹ In argument: `across(all_of(vars), mean, na.rm = TRUE)`.
## ℹ In group 1: `Cluster_temp = 1`.
## Caused by warning:
## ! The `...` argument of `across()` is deprecated as of dplyr 1.1.0.
## Supply arguments directly to `.fns` through an anonymous function instead.
##
## # Previously
## across(a:b, mean, na.rm = TRUE)
##
## # Now
## across(a:b, \(x) mean(x, na.rm = TRUE))
print("Profil par variable :")
## [1] "Profil par variable :"
print(prof$profile_vars)
## # A tibble: 2 × 32
## Cluster Org_1 Org_2 Org_3 Org_4 Org_5 Org_6 Org_7 Org_8 Org_9 Org_10 Org_11
## <fct> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl>
## 1 1 3.44 4.2 2.88 2.76 3.24 4.2 2.96 3.6 3.84 3.36 3.08
## 2 2 2.78 4.08 2.14 2.14 2.42 3.72 3.06 2.97 3.44 2.42 2.5
## # ℹ 20 more variables: Org_12 <dbl>, Org_13 <dbl>, Org_14 <dbl>, Org_15 <dbl>,
## # Cafe_1 <dbl>, Cafe_2 <dbl>, Cafe_3 <dbl>, Cafe_4 <dbl>, Cafe_5 <dbl>,
## # Cafe_6 <dbl>, Cafe_7 <dbl>, Cafe_8 <dbl>, Cafe_9 <dbl>, Cafe_10 <dbl>,
## # Cafe_11 <dbl>, Cafe_12 <dbl>, Cafe_13 <dbl>, Cafe_14 <dbl>, Cafe_15 <dbl>,
## # n <int>
print("Profil des scores :")
## [1] "Profil des scores :"
print(prof$profile_scores)
## # A tibble: 2 × 3
## Cluster Score_Organisation Score_Cafe
## <fct> <dbl> <dbl>
## 1 1 3.42 3.20
## 2 2 2.84 1.42
print("Variables les plus discriminantes (ANOVA p-value) :")
## [1] "Variables les plus discriminantes (ANOVA p-value) :"
print(head(prof$anova, 10))
## var p_value
## 1 Cafe_6 5.979091e-15
## 2 Cafe_5 1.183353e-14
## 3 Cafe_3 2.378602e-14
## 4 Cafe_11 3.064287e-12
## 5 Cafe_4 1.941591e-11
## 6 Cafe_1 3.874845e-11
## 7 Cafe_7 3.999491e-10
## 8 Cafe_2 4.664448e-08
## 9 Cafe_9 3.906321e-07
## 10 Cafe_14 8.856349e-07
# Visualisations
# Calcul PCA pour visualisation
res_pca <- PCA(data_scaled, graph = FALSE, ncp = 5)
clusters_factor <- factor(clust_res$clusters)
vis <- visualize_clusters(data_scaled, clusters_factor, pca_coords = res_pca)
## Warning: `aes_string()` was deprecated in ggplot2 3.0.0.
## ℹ Please use tidy evaluation idioms with `aes()`.
## ℹ See also `vignette("ggplot2-in-packages")` for more information.
## ℹ The deprecated feature was likely used in the factoextra package.
## Please report the issue at <https://github.com/kassambara/factoextra/issues>.
## This warning is displayed once every 8 hours.
## Call `lifecycle::last_lifecycle_warnings()` to see where this warning was
## generated.
## cluster size ave.sil.width
## 1 1 25 0.09
## 2 2 36 0.27
## Warning: Using `size` aesthetic for lines was deprecated in ggplot2 3.4.0.
## ℹ Please use `linewidth` instead.
## ℹ The deprecated feature was likely used in the factoextra package.
## Please report the issue at <https://github.com/kassambara/factoextra/issues>.
## This warning is displayed once every 8 hours.
## Call `lifecycle::last_lifecycle_warnings()` to see where this warning was
## generated.
## Warning: The `<scale>` argument of `guides()` cannot be `FALSE`. Use "none" instead as
## of ggplot2 3.3.4.
## ℹ The deprecated feature was likely used in the factoextra package.
## Please report the issue at <https://github.com/kassambara/factoextra/issues>.
## This warning is displayed once every 8 hours.
## Call `lifecycle::last_lifecycle_warnings()` to see where this warning was
## generated.
print(vis$silhouette)
print(vis$pca)
print(vis$dend)
```