================================= Conception de graphiques avec |R| ================================= .. |R| image:: ./img/cran.jpg :scale: 50 .. _reStructuredText: ./index.txt .. _Docutils: http://docutils.sourceforge.net/ .. _christophe.lalanne@gmx.net: mailto:christophe.lalanne@gmx.net .. footer:: Cette page a a été générée à l'aide de Docutils_, à partir d'un source reStructuredText_. .. contents:: Sommaire :depth: 2 Nous explorons dans ce document les possibilités graphiques de **R**, à l'aide des fonctions graphiques standards et celles contenues dans le package `lattice`. Le lecteur intéressé trouvera d'autres illustrations, sans doute bien meilleures que celles présentées ici, sur le web, notamment : * `R Graphics (P. Murrell) `_ * `R Graph Gallery `_ * `Statistiques avec R (V. Zoonekynd) `_ * `Treillis displays `_ * `Categorical data analysis with graphics `_ ainsi qu'en consultant les documents disponibles sur le site `CRAN `_, notamment : * `R pour les débutants (E. Paradis) `_ * `S-PLUS (and R) Manual to Accompany Agresti's Categorical Data Analysis (L. Thompson) `_ * `Using R for Data Analysis and Graphics - Introduction, Examples and Commentary (J. Maindonald) `_ * `Statistical Computing and Graphics Course Notes (F. Harrell) `_ Les figures proposées sont au format `png` et ont été produites à l'aide de la commande :: png("file.png",width=500,height=500,pointsize=12,bg="white") La liste des fonctions graphiques utilisées est indiquée ci-dessous : :: plot hist lines density boxplot rug barplot sunflowerplot pie dotchart fourfoldplot rose.diag (circular) densityplot (lattice) -------------- Variables numériques ==================== Histogramme et estimateur de densité ------------------------------------ L'histogramme est un outil très utile pour visualiser la répartition des valeurs d'une variable numérique. Cependant, le choix de l'intervalle de découpage des valeurs n'est pas toujours aisé. En effet, avec un intervalle trop faible, on fait apparaître trop de variations souvent insignifiantes, tandis qu'avec un intervalle trop élevé les variations de la répartition s'effaçent au profit d'une distribution peu "discriminante" et ne laissent pas apparaître une éventuelle distribution bimodale. Le choix de la taille de l'intervalle s'effectue avec l'option `breaks` de la fonction `hist()`. Si cette option n'est pas renseignée, **R** calcule un intervalle par défaut (option `"Sturges"`) selon laquelle `h=range(x)/log2(n)+1`. Il existe également d'autres estimations de la taille optimale de l'intervalle de classes à partir d'algorithmes d'optimisation entre biais et variance pour des distributions de référence considérée comme normales [Venables2002]_ (p. 112). Comme indiqué dans l'aide en ligne (`?hist`), il faut spécifier la méthode `"Scott"` ou `"FD"` (pour "Friedman-Diaconis"). Pour ces méthodes, l'intervalle choisi est calculé comme h = 3.5*s*n^(-1/3) (Scott, 1979, voir [Venables2002]_) ou h = 2*R*n^(-1/3) (Freedman & Diaconis, 1981, *ibidem.*). On peut tester la première méthode à l'aide de données simulées : :: # on vérifie l'évolution de h(n) x <- rnorm(10000) n <- length(x) h <- vector("numeric",n-1) for (i in 2:n) { h[i-1] <- (max(x)-min(x))/(log(i,2)+1) } plot(h,type="l",xlab="n") # comparaison entre les choix pour `breaks` et l'option "Sturges" xx <- seq(-4,4,by=.01) idx <- c(50,20,8) par(mfrow=c(2,2)) hist(x,main="Méthode Sturges",ylab="Densité",ylim=c(0,0.4),proba=T) lines(xx,dnorm(xx),col="red",lwd=2) legend(1.1,0.4,legend="N(0;1)",lty=1,lwd=2,col="red") for (i in idx) { hist(x,breaks=seq(-4,4,len=i),main=paste("h=",8/i,"(n=50)"),ylim=c(0,0.4), ylab="Densité",proba=T) lines(xx,dnorm(xx),col="red",lwd=2) } .. figure:: img/histo.png :scale: 100 :align: center :alt: hist() Fig. 1 - `hist()` et l'option `breaks` Des estimateurs locaux de densité sont superposés sur chacun des graphiques précédents. Ces estimations de densité (non-paramétrique) sont accessibles à l'aide de la fonction `density()`, qui accepte pour principaux paramètres : * la taille de la fenêtre de lissage ; * le type de noyau à choisir parmi : "gaussian", "epanechnikov", "rectangular","triangular", "biweight", "cosine", et "optcosine". [A PRECISER!!!] On peut voir ce que cela donne en variant le paramètre `adj` : :: y <- rnorm(100,0,(1+2*rbinom(100,1,0.35))) # [Venables2002] par(mfrow=c(1,3)) for (i in seq(0.5,1.5,by=0.5)) { hist(y,main=paste("adj=",i),ylim=c(0,0.3),ylab="Densité",proba=T) lines(density(y,adj=i),col="blue",lwd=2) } .. figure:: img/density.png :scale: 100 :align: center :alt: density() Fig. 2 - `density()` et le paramètre `adj` Lorsque le paramètre de lissage est faible (`adj=0.5`), la courbe de densité souligne beaucoup de variations d'effectifs qui ne semblent pas particulièrement remarquables, tandis qu'à l'inverse, avec un fort lissage (`adj=1.5`), seule subsiste la tendance générale unimodale. On notera que l'asymétrie gauche reste visible, comme avec `adj=1` (option par défaut sous **R**). Voici d'autres exemples d'utilisation de l'histogramme sous **R** : :: library(MASS) data(survey) ng <- sum(survey$Sex=="Male",na.rm=T) nf <- sum(survey$Sex=="Female",na.rm=T) n <- ng+nf dst <- density(survey$Height,na.rm=T) dstg <- density(survey$Height[survey$Sex=="Male"],na.rm=T) dstf <- density(survey$Height[survey$Sex=="Female"],na.rm=T) hist(survey$Height,col="yellow",border="red",main=paste("Taille de", nrow(survey),"étudiants"),xlab="Taille [cm]",proba=T,ylim=c(0,max(dst$y))) lines(dstg$x,ng/n*dstg$y,lwd=3,col="darkblue") lines(dstf$x,nf/n*dstf$y,lwd=3,lty=3,col="darkred") lines(dst$x,dst$y) legend(185,0.04,legend=c("Filles","Garçons"),col=c("darkred","darkblue"), lty=c(3,1),lwd=2,pt.cex=2) .. figure:: img/hist_density.png :scale: 100 :align: center :alt: hist() + density() Fig. 2 - `hist() + density()` Graphiques conditionnels ------------------------ La librairie `lattice` offre des possibilités graphiques additionnelles puisqu'elle propose les mêmes classes d'outils graphique (histogramme, boîte à moustaches, diagramme, etc.) mais avec la possibilité d'effectuer des représentations graphiques conditionnellement aux valeurs ou niveaux d'une variable concomittante. :: library(ade4) library(lattice) data(deug) x <- deug$tab$Algebra y <- deug$result densityplot(~x|y,xlab="algèbre",ylab="densité",panel=function(x,...) { panel.mathdensity(dmath=dnorm,args=list(mean=mean(x),sd=sd(x)),col="red") panel.histogram(x,breaks=NULL,col="cyan")}) .. figure:: img/densityplot.png :scale: 100 :align: center :alt: densityplot() Fig. 2 - `densityplot()` :Note: On peut également exporter le graphique produit au format postscript, qui donne un meilleur rendu pour une insertion dans un document et qui offre la possibilité d'avoir un fond uniforme blanc, et non pas gris comme par défaut. On utilisera par exemple la commande :: postscript("densityplot.eps", width = 10.0, height = 10.0, horizontal = FALSE, onefile = FALSE, paper = "special", encoding = "ISOLatin1.enc") pour produire la figure `densityplot.eps <./img/densityplot.eps>`_. Diagrammes de dispersion ------------------------ :: plot(iris[,1:4],bg=c("red","green3","blue")[iris[,5]], pch=c(21,25,24)[iris[,5]],main="Les iris de Fisher", labels=c("Longueur\nsépale","Largeur\nsépale", "Longueur\npétale","Largeur\npétale")) .. figure:: img/plotiris.png :scale: 100 :align: center :alt: plotiris() Fig. x - `Les iris de Fisher` Boîtes à moustaches ------------------- :: ns <- c(15,28,10,20,35) n <- length(ns) group <- factor(rep(1:n,ns),labels=paste("g",1:n,sep="")) data <- rnorm(length(group),mean=100+(as.numeric(group)-2)^2) boxplot(data~group,border=1:n,xlab="Groupe",ylab="Réponse",varwidth=T) for (i in 1:n) { rug(data[as.numeric(group)==i],side=2,col=i) } .. figure:: img/boxplot.png :scale: 100 :align: center :alt: boxplot() Fig. x - `boxplot() + rug()` Variables qualitatives ====================== Distributions univariées ------------------------ Les graphiques en barres sont sans doute les plus utilisés dans le cadre de la représentation de la distribution des effectifs en fonction des modalités d'une variable qualitative. :: x1 <- c(23.2,34.5,76.3,65.8,12.6) x2 <- c(15.6,12.4,21.8,20,5.2) A <- gl(5,1,5,labels=c("a1","a2","a3","a4","a5")) data <- cbind(x1,x2) rownames(data) <- levels(A) barplot(x1,names.arg=levels(A)) barplot(t(data),beside=T,ylim=c(0,100),legend.text=colnames(data), col=c("grey50","grey80"),ylab="Fréquence") .. figure:: img/barplot.png :scale: 100 :align: center :alt: barplot() Fig. x - `barplot()` On peut également utiliser une boîte à camembert pour représenter les effectifs ventilés sur les modalités de la variable d'intérêt : :: names(x1) <- levels(A) pie(x1/100) .. figure:: img/pie.png :scale: 100 :align: center :alt: pie() Fig. x - `pie()` Remarquons cependant que ces représentations dans lesquelles les proportions relatives sont évaluées par des secteurs angulaires deviennent vite difficiles à analyser lorsqu'il y a beaucoup de modalités (cf. [#]_) ou lorsque l'on souhaite croiser différentes variables, et il est préférable d'utiliser des diagrammes en barres. Représentations conjointes -------------------------- Il existe plusieurs fonctions utiles dans le cadre de la représentation des effectifs ventilés sur les modalités de deux variables qualitatives. Ces méthodes de représentation permettent d'éviter l'éventuel superposition des points telle qu'on peut l'observer dans un simple diagramme de dipersion. Par exemple, la fonction `sunflowerplot()` représente les effectifs sous forme de diagramme étoilé pour chacun des croisements des modalités des variables : :: x <- rpois(500,lambda=2) y <- rpois(500,lambda=2) layout(t(matrix (1:2))) plot(x,y,pch=19) sunflowerplot(x,y,pch=19) .. figure:: img/sunflowerplot.png :scale: 100 :align: center :alt: sunflowerplot() Fig. x - `plot() et sunflowerplot()` Dans le cas de deux variables dichotomiques (e.g. en épidémiologie, exposition vs. maladie), on peut utiliser une représentation en quarts de cercle de l'association entre les deux variables : :: x <- c(24.3,75.7,16.8,83.2) a <- matrix(x,nr=2,byrow=T) var1 <- c("E-","E+") var2 <- c("M-","M+") dimnames(a) <- list(var2,var1) fourfoldplot(a) .. figure:: img/fourfoldplot.png :scale: 100 :align: center :alt: fourfoldplot() Fig. x - `fourfoldplot()` Représentations conditionnelles ------------------------------- Voici un exemple d'utilisation d'un diagramme de fréquence conditionnel (deux variables qualitatives), d'après l'exemple trouvé dans l'aide en ligne (`?dotchart`) : :: dotchart(VADeaths, main = "Death Rates in Virginia - 1940") .. figure:: img/dotchart.png :scale: 100 :align: center :alt: dotchart() Fig. x - `dotchart()` Données circulaires =================== Dans le cadre des données circulaires (ou directionnelles), des représentations appropriées peuvent être trouvées dans la librairie `circular`. Le problème des données circulaires est que, même si ce sont des données numériques, une représentation sous forme d'histogramme n'est pas toujours appropriée dans la mesure où les mesures classiques de position et de dispersion (moyenne et variance) ne s'appliquent pas à des données angulaires [#]_. [DEVELOPPER!!!] Avec les données `ex_circ.dat <./data/ex_circ.dat>`_, on peut regarder ce que cela donne : :: tmp <- read.table('ex_circ.dat',header=F) library(circular) data <- data.frame(angle=rad(tmp[,1]),cond=as.factor(tmp[,2]), suj=as.factor(tmp[,3])) levels(data$cond) <- c("a","b","c","d","e","f","g","h") data.circ <- circular(deg(data$angle[data$cond=="a"]),units="degrees") rose.diag(data.circ,bins=36,prop=1,shrink=1.2,tcl=0.05,col='blue') points(data.circ, stack=TRUE, bins=180,col='blue') tmp <- as.numeric(mean(data.circ)) x <- c(0,cos(rad(tmp))) y <- c(0,sin(rad(tmp))) arrows(x[1],y[1],x[2],y[2],lwd=3,col='blue',length=.2) x <- c(0.25,-0.25) y <- c(-0.75,-0.25) arrows(x[1],y[1],x[2],y[2],lwd=2,length=.2,code=3,col='blue') text(x[2]-0.15,y[2],"a",col='blue',cex=1.5) par(new=T) data.circ <- circular(deg(data$angle[data$cond=="c"]),units="degrees") rose.diag(data.circ, bins=36, prop=1, shrink=1.2,tcl=0.05,col='red') points(data.circ, stack=TRUE, bins=180,col='red') tmp <- as.numeric(mean(data.circ)) x <- c(0,cos(rad(tmp))) y <- c(0,sin(rad(tmp))) arrows(x[1],y[1],x[2],y[2],lwd=3,col='red',length=.2) x <- c(-0.25,0.25) y <- c(-0.75,-0.25) arrows(x[1],y[1],x[2],y[2],lwd=2,length=.2,code=3,col='red') text(x[2]+0.15,y[2],"c",col='red',cex=1.5) .. figure:: img/circular.png :scale: 100 :align: center :alt: rose.diag() Fig. x - `rose.diag()` (`circular`) Données de survie ================= Séries temporelles ================== Avec les données contenues dans l'image `ex_st.Rdata <./data/ex_st.Rdata>`_, on peut illustrer quelques-unes des facilités de **R** pour la personnalisation des graphiques. :: load("ex_st.Rdata") offset <- 100 xx <- 1:length(a.mean[,1])*0.01 # le temps en secondes (@100 Hz) plot(xx,a.mean[,1],type="n",xlab="Temps (s)",ylab="",ylim=c(-200,200), main="a",axes=F) xsda <- c(xx,rev(xx)) ysda <- c(a.mean[,1]+(a.sd[,1]/2)+offset,rev(a.mean[,1]-(a.sd[,1]/2)+offset)) polygon(xsda,ysda,col="grey",border=NA) xsdb <- c(xx,rev(xx)) ysdb <- c(b.mean[,1]+b.sd[,1]-offset,rev(b.mean[,1]-b.sd[,1]-offset)) polygon(xsdb,ysdb,col="grey",border=NA) lines(xx,a.mean[,1]+offset,type="l",lwd=2) lines(xx,b.mean[,1]-offset,type="l",lwd=2,lty=2) axis(1) xx <- 0 yy <- 0 lwb <- yy-15; upb <- yy+15; arrows(xx,lwb,xx,upb,length=0,angle=90,code=3) text(xx+.05,yy,labels="30 pix.",pos=4) text(0,180,"CL",cex=1.5,font=2,pos=4) .. figure:: img/st1.png :scale: 100 :align: center :alt: st1 Fig. x - time series #1 -------------- **Références** .. [Venables2002] Venables, W.N. & Ripley, B.D. (2002). Modern Applied Statistics with S. Springer-Verlag. .. [Everitt2005] Everitt, B. (2005). An R and S-PLUS Companion to Multivariate Analysis. Springer-Verlag. **Notes** .. [#] Cleveland (1985), page 264: "Data that can be shown by pie charts always can be shown by a dot chart. This means that judgements of position along a common scale can be made instead of the less accurate angle judgements." This statement is based on the empirical investigations of Cleveland and McGill as well as investigations by perceptual psychologists. [`?pie`] .. [#] Il suffit de songer à la moyenne de trois directions prises par des rats dans un labyrinthe : 1°, 359°, 3°. A priori, les rats vont globalement dans la même direction, alors que la moyenne arithmétique pour ces données vaut 121° !