Méthodes non paramétriques par permutations Denis Puthier 11 juin 2008 Laboratoire INSERM TAGC/ERM206, Parc Scientifique de Luminy case 928,13288 MARSEILLE cedex 09, FRANCE. http://biologie.univ-mrs.fr/view-data.php?id=245 Table des matières 1 Introduction 1 2 Présentation du jeu de données. 2 3 Chargement des données. 2 4 Quel(s) critère(s) pour la comparaison? 3 5 Calcul des distributions réelles et empiriques des SNR. 4 5.1 Implantation d une fonction pour le calcul du SNR............... 4 5.2 Calcul des SNR observés pour chacun des gène................. 6 5.3 Calcul d une distribution empirique....................... 6 6 Clustering hiérarchique à partir des données retenues. 9 7 SAM. 12 1 Introduction Ces travaux dirigées proposent une introduction à la recherche de gènes discriminant avec le logiciel R (nous nous limiterons essentiellement au cas où deux classes d échantillons sont rencontrées). Après une introduction à la problématique nous présenterons une solution statistique permettant d y faire face. La solution proposée, basée sur des permutations est proche de celle proposée par Golub et al (1) et s apparente à la solution SAM (Significance Analysis of Microarrays) implémentée dans la librairie samr ou siggenes. 1
2 Présentation du jeu de données. Nous utilisons ici un jeu de données obtenues à partir de microarrays nylon hybridés avec des échantillons de tissus sains prélevés chez la souris. Bien que ce jeu de données comporte 14 tissus différents, nous nous concentrerons sur deux d entre eux (la rate et le rein) et tenterons de mettre à jour les gènes spécifiques de chacun de ces organes. Notez que les données ont été préalablement normalisées et transformées en logarithme base 2. 3 Chargement des données.. Les commandes suivantes permettent d obtenir un vecteur contenant le nom des échantillons et une matrice dont les lignes correspondent aux gènes et les colonnes aux échantillons. > mt.matrix <- read.table("http://tagc.univ-mrs.fr/staff/puthier/courses/mt.matrix.txt", + sep = "\t", head = T, row.names = 1) > dim(mt.matrix) [1] 8527 40 > is(mt.matrix) [1] "data.frame" "oldclass" > mt.matrix <- as.matrix(mt.matrix) > mt.cl <- read.table("http://tagc.univ-mrs.fr/staff/puthier/courses/mt.cl.txt", + sep = "\t", head = T, row.names = 1) > mt.cl <- mt.cl[, 1] > mt.cl <- as.character(mt.cl). Les instructions suivantes permettent de sélectionner les échantillons d intérêt. Le vecteur ind est un vecteur logique. L indexation de mt.cl par ind permet de renvoyer les valeurs de mt.cl pour lesquelles les valeurs des indices correspondants de ind sont vraies. > ind <- mt.cl == "spleen" mt.cl == "kydney" > ind [1] FALSE TRUE FALSE FALSE FALSE FALSE FALSE TRUE FALSE FALSE FALSE FALSE [13] FALSE FALSE TRUE FALSE FALSE FALSE TRUE TRUE FALSE FALSE FALSE FALSE [25] FALSE FALSE TRUE FALSE FALSE FALSE FALSE TRUE FALSE FALSE TRUE FALSE [37] FALSE FALSE FALSE FALSE 2
> rr.cl <- mt.cl[ind] > rr.cl [1] "spleen" "kydney" "spleen" "spleen" "spleen" "kydney" "kydney" "kydney". Question : Comment aurions nous selectionné tous les échantillons sauf la Rate?. Dans le cas de la matrice qui contient deux dimensions, seules les colonnes sont indexées. L absence d indexation sur les lignes sous-entend que toutes les lignes devront être renvoyées. > rr.mat <- mt.matrix[, ind] > colnames(rr.mat) [1] "spleen" "kydney" "spleen.1" "spleen.2" "spleen.3" "kydney.1" "kydney.2" [8] "kydney.3" > length(row.names(rr.mat)) [1] 8527. Voilà nos données prêtes à être analysées. 4 Quel(s) critère(s) pour la comparaison?. Etant donné que l on souhaite rechercher des gènes dont l expression est particulièrement différente ( différentielle ) entre les échantillons de la classe 1 (disons la rate) et les échantillons de la classe 2 (disons le rein) une solution naturelle qui s offre à nous est de comparer les moyennes des niveaux d expression. Exemple, pour le premier gène de la matrice : > rr.cl[rr.cl == "spleen"] = 1 > rr.cl[rr.cl == "kydney"] = 2 > is(rr.cl) [1] "character" "vector" > rr.cl <- as.numeric(rr.cl) > rr.cl [1] 1 2 1 1 1 2 2 2 > m1 <- mean(rr.mat[1, rr.cl == 1]) > m2 <- mean(rr.mat[1, rr.cl == 2]) > m1 - m2 3
[1] 0.2299961. Le problème est que nos mesures sont plus ou moins fiables (dispersées) et que la moyenne est particulièrement sensible aux valeurs extrèmes. Pour un gène, une seule valeur très forte dans l une ou l autre des classes, pourrait nous laisser penser, à tort, que celui-ci est différentiel. Une solution assez séduisante serait que le score que nous calculons intègre cette notion de varabilité dans la mesure (ie ; que les gènes dont la mesure est très fiable soient favorisés). Une des solutions que l on peut proposer est le calcul d un critère de type SNR (Signal to Noise Ratio). Dans ce score, utilisé notamment par Golub et al, la différence des moyennes est divisée par la somme des écart-types. Notez que ce score est extrêment proche du critère mesuré dans le cadre du test t. Pour le premier gène cela donne : > sd1 <- sd(rr.mat[1, rr.cl == 1]) > sd2 <- sd(rr.mat[1, rr.cl == 2]) > snr1 <- (m1 - m2)/(sd1 + sd2) > snr1 [1] 0.3041731. C est très intéressant, mais que dire de la valeur snr1? Peut-on considérer que c est une valeur forte, que c est une valeur faible? Puisque nous parlions du t pourquoi ne pas plutôt utiliser le test t et calculer une p.value? Problème, nos données ne sont pas distribuées selon un loi gaussienne... Pour le vérifier on peut représenter les quantiles (les valeurs) d une série gaussienne et les comparer au valeurs dont nous disposons. Cela peut être réalisé à l aide de la fonction qqnorm (figure 1). > qqnorm(mt.matrix[,1],pch="."). Si nos données ne sont sont pas gaussiennes, on ne connait pas la distribution du t et on ne connait donc pas les probabilités d occurrence (la p.value) des valeurs de t observées. Cependant, si la distribution théorique de t dans nos conditions (non gaussiennes) n est pas connue, on pourrait par contre calculer très facilement une distribution empirique. Pour simplifier un peu le calcul nous n utiliserons pas le t mais le critère SNR qui est très similaire. 5 Calcul des distributions réelles et empiriques des SNR. 5.1 Implantation d une fonction pour le calcul du SNR.. Implantons une fonction qui permet de calculer les SNR pour l ensemble des gènes d une matrice. 4
Normal Q Q Plot Sample Quantiles 2 0 2 4 6 4 2 0 2 4 Theoretical Quantiles Fig. 1 Un graphique quantiles-quantiles utilisable pour vérifier le caractère gaussien d une distribution. Dans le cas d une série gaussienne, une droite est observée (vous pouvez le tester). 5
> snr4mat <- function(x, y) { + m1 <- rowmeans(x[, y == 1]) + m2 <- rowmeans(x[, y == 2], 1) + sd1 <- apply(x[, y == 1], 1, sd) + sd2 <- apply(x[, y == 2], 1, sd) + return((m1 - m2)/(sd1 + sd2)) + } 5.2 Calcul des SNR observés pour chacun des gène.. Question : Calculez les SNR pour l ensemble des gènes et stockez les dans une variable nommée snr.real. Triez les à l aide de la fonction sort et analysez les noms des gènes les plus différentiels. Tracez l histogramme de fréquences de snr.real et la densité de probabilité calculée à l aide de la fonction density. Vous deviez obtenir les resultats des figures 2 et 3. 5.3 Calcul d une distribution empirique.. Le principe est simple. Je souhaite générer des valeurs de SNR aléatoires en utilisant la structure intrinsèque de mes données. Pour ce faire, nous allons calculer le score SNR sur des classes d échantillons fictives. L algorithme sera le suivant : permutation des échantillons calcul des snr sur l ensemble des gènes stockage dans une matrice. Nous effectuerons 50 permutations (ce qui est relativement faible mais sera suffisant pour comprendre le principe et pas trop couteux en terme de temps car notre implémentation est lente...). On peut créer une matrice (snr.alea) qui permettra le stockage des valeurs aléatoires avec l instruction suivante : > nr <- nrow(rr.mat) > nperm <- 50 > NA4snr.alea <- rep(na, nr * nperm) > snr.alea <- matrix(na4snr.alea, ncol = nperm) > dim(snr.alea) [1] 8527 50. Pour permuter les échantillons on peut utiliser la fonction sample. > sample(rr.cl) [1] 2 2 2 1 1 1 2 1 6
histogramme de fréquences obtenu à partir de snr.real Frequency 0 500 1000 1500 15 10 5 0 5 10 15 snr.real Fig. 2 Histogramme de fréquence de snr.real. Correspond à la distribution des scores SNR obtenus en comparant les échantillons de rate et de rein. 7
densité de probabilité obtenue à partir de snr.real Density 0.0 0.1 0.2 0.3 0.4 15 10 5 0 5 10 15 N = 8527 Bandwidth = 0.1602 Fig. 3 Densité de probabilité obtenus pour les valeurs réelles de SNR (comparaison rate versus rein). 8
. Question : A l aide d une boucle for générez 50 classes fictives et stockez les resultats de SNR correspondants dans la matrice (attention, c est un peu long).. Nous allons maintenant definir les valeurs de SNR observées au hasard dans 1 cas pour 5000. On peut les visualiser sur un graphiques et extraire les valeurs d intérêt à l aide de la fonction quantile. > (1/5000)/2 > seuil.bas <- quantile(snr.alea,0.0001) > seuil.haut <- quantile(snr.alea,1-0.0001) > plot(density(snr.alea)) > points(density(snr.real),pch=".") > abline(v=seuil.bas,col="red") > abline(v=seuil.haut,col="red") 6 Clustering hiérarchique à partir des données retenues.. Question : Stockez dans une matrice nommée rr.conserve les valeurs d expressions correspondant aux gènes dont la valeur de SNR est inférieure à seuil.bas ou supérieure à seuil.haut.. A ce stade les gènes retenus sont hautement différentiels et doivent permettre de classer sans soucis nos échantillons. On peut le vérifier à l aide de la fonction heatplot que nous utilisons pour effectuer une classification hiérarchique sur les gènes et sur les échantillons (figure 5). > heatplot(rr.conserve). Pour isoler le nom des gènes on peut utiliser, par exemple, les instructions suivantes. > genes.down <- names(snr.real[snr.real < seuil.bas]) > genes.down <- strsplit(genes.down, "_") > genes.down <- unlist(genes.down) > out <- grep("mdf", genes.down) > genes.down <- genes.down[-out] > write.table(genes.down, file = "genes.down.txt", quote = F, row.names = F) > unique(sort(genes.down))[1:10] [1] "9230106L14Rik" "Aldh7a1" "Aldrl6" "Ass1" [5] "B3gat1" "Calb3" "Egf" "Fxyd2" [9] "Gsta2" "Hsd3b4" 9
density.default(x = snr.alea) Density 0.0 0.2 0.4 0.6 0.8 1.0 15 10 5 0 5 10 15 N = 426350 Bandwidth = 0.02989 Fig. 4 Densité de probabilité obtenus pour les valeurs réelles et aleatoire de SNR. 10
Color Key 1 0 1 Row Z Score MDF024.012_Cd19 MDF008.288_Snrpe MDF024.075_Ccr7 MDF016.164_Lyst MDF024.014_Cd19 MDF013.124_Abhd1 MDF012.272_Cd83 MDF012.241_Cd79a MDF012.182_Ii MDF015.353_Fcna MDF011.025_Tmsb4x MDF013.166_Kcnn4 MDF012.236_A230020G22Rik MDF007.242_ MDF020.039_Mrrf MDF019.233_ MDF024.061_Cd72 MDF013.038_Plek MDF013.363_Ifi1 MDF018.048_B3gat1 MDF012.370_Egf MDF018.304_Wbscr14 MDF001.263_Aldh7a1 MDF016.246_Pcbd MDF013.149_Selenbp1 MDF008.041_9230106L14Rik MDF018.303_S100a1 MDF008.344_Hsd3b4 MDF011.275_Tpi MDF003.156_Pgam1 MDF016.055_Fxyd2 MDF012.279_Wasl MDF010.195_Ndufa8 MDF010.008_Ldh2 MDF021.270_Uqcr MDF007.255_Calb3 MDF012.167_Ptplb MDF001.112_Neu1 MDF018.326_Pthr1 MDF017.240_Temt MDF016.274_Gsta2 MDF005.088_Aldrl6 MDF013.236_Ass1 kydney.1 kydney.3 kydney kydney.2 spleen spleen.3 spleen.1 spleen.2 Fig. 5 Clustering hierarchique obtenu à partir des gènes retenus. SNR. 11
> genes.up <- names(snr.real[snr.real > seuil.haut]) > genes.up <- strsplit(genes.up, "_") > genes.up <- unlist(genes.up) > out <- grep("mdf", genes.up) > genes.up <- genes.up[-out] > unique(sort(genes.up))[1:10] [1] "A230020G22Rik" "Abhd1" "Ccr7" "Cd19" [5] "Cd72" "Cd79a" "Cd83" "Fcna" [9] "Ifi1" "Ii" > write.table(genes.up, file = "genes.up.txt", quote = F, row.names = F) 7 SAM. La méthode SAM est implémentée dans la librairie siggenes. Utilisez la pour trouver les gènes qui discriminent les deux conditions. Références [1] Golub TR, Slonim DK, Tamayo P, Huard C, Gaasenbeek M, Mesirov JP, Coller H, Loh ML, Downing JR, Caligiuri MA, Bloomfield CD, Lander ES. Molecular classification of cancer : class discovery and class prediction by gene expression monitoring. Science. 1999 Oct 15;286(5439) :531-7 12