Le forum d'ADE4 s'anime autour de la question des « étiquettes » ; je m'en
réjouis, car c'est une difficulté qui m'irrite depuis le jour où j'ai
effectué mes premières analyses factorielles, en 1978 :-(( JP Benzécri
avait évacué la question en faisant réaliser les graphiques à la plume par
une « petite main ». A l'époque du FORTRAN (et de tout ce qui allait avec),
les listings comportaient, sous les graphiques, des listes (souvent longues)
de points superposés non imprimés, que l'on reportait donc au crayon, au
prix d'un effort très désagréable (variante de la méthode Benzécri ). Tout
le système graphique s'étant trouvé, depuis, bouleversé, on a pris
l'habitude, soit de tout afficher (d'où des gribouillis illisibles), soit de
n'afficher qu'une partie (une variante étant le principe des étiquettes
blanches d'ADE4). C'est, tout compte fait, presque pire que la méthode
Benzécri.
Alors, pourquoi si peu de réflexions et d'efforts pour mettre au point une
solution réaliste ? Depuis les travaux de Jacques Bertin (début dans les
années 40, fin vers 1975), je n'ai vu apparaître, du moins en français,
aucune réflexion vraiment méthodique sur la « statégie graphique » dans le
domaine statistique. C'est pourtant un champ vaste et très prometteur. Mais
qui suppose que l'on accepte l'idée qu'une exploration statistique n'est pas
à 100% une affaire de maths et de théorèmes... Ici, pour faire très bref, je
rappellerais seulement :
1. qu'il est aisé de produire une infinité de graphiques inutiles et/ou mal
conçus ;
2. que l'on doit toujours partir de l'idée, défendue régulièrement sur le
forum ADE4, mais nulle part dans les manuels, que, dans la grande majorité
des cas, il existe une « pluralité de solutions optimales », ce qui implique
qu'un grand nombre de discussions sont en réalité sans objet ;
3. que l'on peut et doit généraliser cette proposition, tout simplement
parce qu'un paquet de données plus ou moins structurées, même de petite
taille, nécessite presque toujours un nombre important de graphiques (souven
t plusieurs variantes du même, mais avec des paramètres différents).
D'où l'idée, qui paraît trop simple pour être intéressante, que toute
écriture de fonction graphique doit comporter par principe la possibilité de
modifier un grand nombre de paramètres, de manière à permettre facilement la
production très rapide d'une série de graphiques entre lesquels on
déterminera empiriquement ceux sur lesquels on voit quelque chose et ceux
sur lesquels on ne voit rien. Discuter à perte de vue sur « le bon paramètre
de bandwidth », quel gaspillage !
Les étiquettes. Il faut faire en sorte de fournir au chercheur devant son
écran toutes les informations qui peuvent lui être utiles, et dont il peut
avoir besoin. Et ce, sans qu'il lui soit nécessaire de manipuler des tas de
fichiers dont il ignore d'ailleurs les noms... Mais il faut aussi tenir
compte de deux contraintes, une matérielle et une théorique. 1. l'écran ou
la feuille de papier n'ont qu'une taille limitée, 2. un bon graphique,
lisible et interprétable, implique que le lecteur ne soit pas submergé. Je
me limite ici aux analyses factorielles classiques.
En simplifiant à outrance, il me semble que l'on se trouve devant un
éventail de difficultés, en fonction du nombre d'étiquettes affichables.
Grosso modo (à la louche) : jusqu'à 50, je veux voir toutes les étiquettes
instantanément, sinon je juge la fonction mal foutue ; entre 50 et 250, ça
se discute : on peut estimer qu'un affichage général de toutes les
étiquettes va embrouiller au lieu de clarifier, mais l'inverse aussi, tout
dépend de la signification desdites étiquettes ; il est plus que souhaitable
qu'une possibilité d'affichage complet existe ; au-delà de 250, un affichage
complet des étiquettes ne paraît plus guère se justifier, il faut faire
ressortir des nuages de points significatifs ; on doit toutefois préserver
la possibilité de faire apparaître ponctuellement l'étiquette de tel ou tel
point (cas en général trop développé des outliers, mais qui existent tout de
même).
Techniquement (je parle en toute incompétence) : le dernier cas est sans
doute le plus simple, sous R existe la fonction identify(), facile à mettre
en oeuvre. On peut imposer quelques règles simples : pas d'étiquette de plus
de 6 caractères, un affichage modeste (cex=.5, par exemple). Surtout, il est
excessivement gênant que R ne dispose pas d'une fonction de « zoom » facile
à mettre en oeuvre (si je me trompe et si elle existe, mille mercis de me le
signaler). Je comprends encore bien moins pourquoi le paramètre din
(par(din)) est en read.only, et pourquoi on ne peut pas définir sous R une
image de taille quelconque dans laquelle on pourrait naviguer avec les
barres de déroulement. (il existe apparemment une possibilité sous Linux,
que je n'ai pas testée). Ce serait sans doute la meilleure solution pour la
zone médiane (50-250). Il faudra bien un jour ou l'autre aller maniper dans
les fonctions graphiques de base pour régler ce problème. Reste, en tout
état de cause, la question des points strictement superposés, qui le
resteront quelle que soit la taille de l'écran ou de la feuille. Je ne vois
pas qu'on puisse faire l'économie d'un algorithme spécifiquement dédié à
cette affaire. JP Benzécri aurait pu se fatiguer un peu plus. D'autant que,
comme le montre l'expérience (eh oui, « le privilège de l'âge », comme je
l'ai déjà lu dans ce forum...), les analyses factorielles les plus
intéressantes sont le plus souvent celles qui font apparaître des « paquets
de points », ou des zones de forte densité (type effet Gutmann). Et donc des
étiquettes majoritairement superposées. Le gribouillis gâche tout le plaisir
et empêche de comprendre ce qui se passe.
C'est dans ces tristes conditions que j'ai imaginé la possibilité d'une
fonction bancale et rudimentaire qui ferait disparaître cet inconvénient.
L'idée est d'une navrante simplicité :
1. calculer la distance de tous les points par rapport au centre du
graphique.
2. trier.
3. en partant du centre, et en prenant les points dans l'ordre de cette
liste, rechercher, pour chaque point et par rapport à lui, les points situés
en deçà d'une distance x en largeur et y en hauteur.
4. repousser vers l'extérieur (en fonction du cadrant dans lequel on se
trouve), tous les points ainsi repérés, de telle sorte qu'ils se retrouvent
au pire à ces distances critiques x et y.
5. continuer jusqu'à extinction de la liste.
en pratique, des choix s'imposent ; en particulier, doit-on ou non
recalculer les distances au centre à chaque itération ? Sinon, repartir du
centre autant de fois que nécessaire ? (ou un compromis ??). La fonction
ci-dessous donne des résultats acceptables (c'est un avis) en dessous de 300
points. Au-delà, elle peut encore marcher, en théorie, mais les temps de
calcul s'allongent considérablement et finissent par tout bloquer. Surtout,
inconvénient majeur (que certains jugeront probablement dirimant), cet
algorithme déforme plus ou moins le nuage (dans des proportions très
variables, en fonction de la quantité de points et de leur répartition).
Pour mon usage personnel, je procède systématiquement en deux temps :
1.affichage complet brut (donne la forme exacte du nuage et divers
gribouillis), 2. affichage après passage de la fonction pousspouss(), nuage
plus ou moins déformé, mais toutes les étiquettes lisibles. Avec les deux
écrans ou les deux feuilles, je peux savoir immédiatement qui est qui, et
où. Avec moins de 50 points et des paramètres graphiques modestes, la
déformation est généralement à peine perceptible. Et quel confort !
Bien entendu, les remarques, critiques, et je l'espère les propositions de
modification / amélioration, me rendraient service. Cette fonction
(typiquement du « programme spaghetti ») est publique (type de toutes les
fonctions R). Il est à peu près certain qu'un algorithme meilleur doit
exister.
Alain Guerreau CNRS
*******************************************
"pousspouss" <-
function (x,y,lab="",cex=0.8, vert=1){
eps <- 0.00000009
tabdep <- as.data.frame(cbind(x,y,lab))
tabdep$x <- as.numeric(as.character(tabdep$x))
tabdep$y <- as.numeric(as.character(tabdep$y))
tabdep$lab <- as.character(tabdep$lab)
for (i in 1:length(tabdep$x)){
tabdep$ind[i] <- i
tabdep$disct2[i] <- (tabdep$x[i]^2)+(tabdep$y[i]^2)}
carlar <- max(nchar(tabdep$lab))
r <- max((abs(max(tabdep$x)-min(tabdep$x))),
((abs(max(tabdep$y)-min(tabdep$y)))*1.3))
recx <- r/(55/carlar/cex)
recy <- r/(40/cex)
ord <- order(tabdep$disct2)
tabdep <- tabdep[ord,]
tabpro <- tabdep
i <- 1
fin <- length(tabdep$x)
while (i <= fin){
px <- tabdep$x[i]
py <- tabdep$y[i]
ip <- NULL
for (j in 1:length(tabdep$x)){
if (i==j) next
dx <- px - tabdep$x[j]
dy <- py - tabdep$y[j]
if (abs(dx) < (recx-eps) && abs(dy) < (recy-eps)){
if ((abs(dy)*vert) <= abs(dx)){
tabdep$y[j] <- (py + (recy*sign(tabdep$y[j])))}
else {tabdep$x[j] <- (px + (recx*sign(tabdep$x[j])))}
ip <- c(ip , j)
}
}
i <- i+1
if (length(ip)> 0 && min(ip)< i) {
i <- min(ip)}
}
ord2 <- order(tabdep$ind)
tabdep <- tabdep[ord2,]
return(tabdep)
}
*******************************
This archive was generated by hypermail 2b30 : Mon Nov 22 2004 - 11:25:13 MET