Introduction à la programmation R (exercices)
Séance 4 : Boucles et itérations
gilles.hunault "at" univ-angers.fr
Table des matières cliquable
1. Equivalent TANT QUE d'une boucle POUR
2. Nombre d'occurrences du maximum avec puis sans boucle
Il est possible d'afficher toutes les solutions via ?solutions=1 et de toutes les masquer avec via ?solutions=0.
1. Equivalent TANT QUE d'une boucle POUR
Ecrire en R une boucle TANT QUE équivalente à la boucle POUR suivante
# une boucle POUR simple nbEtap <- 10 # nombre d'étapes en tout for (indEtap in (1:nbEtap)) { cat("étape ",sprintf("%2d",indEtap),"sur",nbEtap,"\n") # [...] suite des calculs de l'étape } # fin pour indEtap cat("après la boucle, l'indice indEtap vaut ",indEtap,"\n")Solution : masquer la solution
Voici une solution possible :
# une boucle TANT QUE pour remplacer la boucle POUR simple nbEtap <- 10 # nombre d'étapes en tout indEtap <- 1 # initialisation while (indEtap <= nbEtap) { cat("étape ",sprintf("%2d",indEtap),"sur",nbEtap,"\n") # [...] suite des calculs de l'étape indEtap <- indEtap + 1 # incrémentation } # fin pour indEtap indEtap <- indEtap - 1 # décrémentation pour correspondre à la boucle POUR cat("après la boucle, l'indice indEtap vaut ",indEtap,"\n")Ce n'est en général pas une bonne idée que d'utiliser la valeur d'un indice de boucle en dehors de la boucle. Un indice de boucle doit être une variable locale qui ne doit pas exister en dehors de la boucle.
2. Nombre d'occurrences du maximum avec puis sans boucle
On veut trouver le maximum du vecteur V et son nombre d'occurrences. Ecrire une première boucle POUR qui détermine le maximum et une seconde boucle POUR qui calcule le nombre d'occurrences.
Réécrire ensuite tout cela avec une seule boucle POUR puis enfin donner la "bonne" solution R sans boucle explicite.
Serait-ce beaucoup plus compliqué de renvoyer la ou les positions du maximum dans le vecteur ?
Solution : masquer la solution
Voici la première solution demandée :
# nombre de valeurs nbVal <- length(V) # boucle 1 : calcul du maximum maxV <- V[ nbVal ] for (indv in (1:(nbVal-1))) { valC <- V[ indv ] if (valC>maxV) { # nouveau maximum maxV <- valC } # fin si } # fin pour indv # boucle 2 : nombre d'occurrences nbOcc <- 0 for (indv in (1:nbVal)) { if (V[ indv] == maxV) { nbOcc <- nbOcc + 1 } # fin si } # fin pour indv # affichage cat("le maximum est",maxV,"vu",nbOcc,"fois\n")Puis la seconde :
# nombre de valeurs nbVal <- length(V) # calcul du maximum et du nombre d'occurrences # en une seule boucle maxV <- V[ nbVal ] nbOcc <- 1 for (indv in (1:(nbVal-1))) { valC <- V[ indv ] if (valC>maxV) { # nouveau maximum maxV <- valC nbOcc <- 1 # on recommence à compter } else { if (valC==maxV) { # nouvelle occurrence nbOcc <- nbOcc + 1 } # fin si valC==maxV } # fin si valC>maxV } # fin pour indv # affichage cat("le maximum est",maxV,"vu",nbOcc,"fois\n")Enfin, voici la "vraie" solution qui utilise la fonction max() et le filtrage vectoriel :
# calcul du maximum maxV <- max(V) # nombre d'occurrences nbOcc <- sum( V==maxV ) # affichage cat("le maximum est",maxV,"vu",nbOcc,"fois\n")Vouloir déterminer la ou les positions du maximum est à peine plus compliqué. C'est un peu délicat parce qu'on ne connait pas à l'avance la taille du vecteur qui contiendra les positions. Voir l'exercice 3 de notre cours numéro 2 de programmation R avancée et sa solution.
La solution R vectorielle tient en une seule instruction :
posmax <- which( V==maxV ) cat("les positions du maximum sont :") cat(" ",paste(posmax,collapse=" "),"\n")
3. Sommes par colonne
On dispose d'une matrice de valeurs numériques, comme par exemple celle définie par
m <- matrix(1:12,nrow=2,byrow=TRUE)Calculer la somme de chaque colonne avec une boucle POUR puis sans boucle POUR à l'aide de la fonction apply(). Quelle est enfin la "bonne" solution R ?
Solution : masquer la solution
Voici la solution avec une boucle POUR explicite
# calculs des sommes par colonne avec une boucle POUR explicite nbCol <- ncol(m) somcols <- rep(NA,nbCol) for (indc in (1:nbCol)) { somcols[indc] <- sum( m[,indc] ) } # fin pour indcLa solution avec apply() utilise une boucle POUR implicite
## calculs des sommes par colonne avec la fonction apply # pas besoin de nommer les paramètres, il s'agit de l'ordre # utilisé dans la définition de la fonction apply : apply(m,2,sum) # mieux, utilisation du nom des paramètres : apply(X=m,MARGIN=2,FUN=sum) # acceptable et reconnu par R : apply(X=m,M=2,F=sum) # sans doute la meilleure solution car elle suit # l'ordre somme colonnes matrice : apply(FUN=sum,MARGIN=2,X=m)La "vraie" solution fait appel à la fonction colSums() :
# calculs des sommes par colonne colSums(m)
4. Des boucles TANT QUE surprenantes
Que font les boucles TANT QUE suivantes ?
# Des boucles TANT QUE surprenantes # #################################### # une boucle TANT que non infinie # ------------------------------- n1 <- 1 n2 <- n1/10 while (abs(n1-n2)>0) { n1 <- n2 n2 <- n2/10 cat(n1,n2,"\n") } # # fin de tant que # une boucle TANT qui va à l'infini # ------------------------------- n <- 1 while (!is.infinite(n)) { n <- 10*n cat(n,"\n") } ; # fin tant queSolution : masquer la solution
La première boucle permet de voir à quel moment R confond un très petit nombre avec 0 (puisque 0=0/10). La seconde montre à quel moment R confond un très grand nombre avec l'infini. Voici un extrait de l'exécution de ces boucles qui, heureusement, ne durent pas longtemps :
# boucle TANT QUE numéro 1 > n1 = 1 > n2 = n1/10 > while (abs(n1-n2)>0) { + n1 = n2 + n2 = n2/10 + cat(n1,n2,"\n") + } # # fin de tant que 0.1 0.01 0.01 0.001 0.001 1e-04 1e-04 1e-05 1e-05 1e-06 1e-06 1e-07 [...] 1e-315 1e-316 1e-316 9.999997e-318 9.999997e-318 9.999987e-319 9.999987e-319 9.999889e-320 9.999889e-320 9.999889e-321 9.999889e-321 9.980126e-322 9.980126e-322 9.881313e-323 9.881313e-323 9.881313e-324 9.881313e-324 0 0 0 > # boucle TANT QUE numéro 2 > n <- 1 > while (!is.infinite(n)) { + n <- 10*n + cat(n,"\n") + } ; # fin tant que 10 100 1000 10000 1e+05 1e+06 1e+07 [...] 1e+307 1e+308 Inf >On pourra consulter les variables et utiliser les fonctions suivantes pour avoir plus de détails sur les limitations machines et la version de R utilisée :
# variables (noter le point an début) .Machine .Platform version # fonctions Sys.info() sessionInfo()
5. Calculs complexes sur séries de fichiers
On veut traiter n fichiers, disons ficSerie01.txt, ficSerie02.txt, ficSerie03.txt... mais ce pourrait être bien sûr des fichiers Excel. Une fois le traitement de chaque fichier effectué, on veut obtenir un tableau et un fichier .CSV résumé des traitements. Le traitement consiste à renvoyer le nombre de lignes et le plus petit age et le plus grand age. Peut-on aussi calculer la moyenne des ages et la moyenne générale des ages ainsi ?
Solution : masquer la solution
Par rapport à l'exemple de la séance 4, il suffit de modifier ce que renvoie la fonction qui traite les fichiers et d'augmenter le data frame résultat, soit le code
# traitement d'un fichier : # on détermine son nombre de lignes # on trouve le min et le max de l'age # on renvoie des NA si le fichier n'est pas vu traiteFichier <- function( nomFic ) { cats(paste("Traitement du fichier",nomFic)) if (!file.exists(nomFic)) { cat("fichier",nomFic,"non vu.\n") dataRet <- list(nbl=NA,minage=NA,maxage=NA) } else { dataFic <- read.table(nomFic,head=TRUE) nblc <- nrow(dataFic) mina <- min(dataFic$AGE) maxa <- max(dataFic$AGE) cat("il y a",nblc,"lignes de données dans",nomFic,"\n") cat("age min =",mina,"; age max =",maxa,"\n") dataRet <- list(nbl=nblc,minage=mina,maxage=maxa) } # fin si return(dataRet) } # fin de fonction traiteFichier # traitement d'une série de fichiers # à l'aide de la fonction traiteFichier() # on sauvegarde dans un tableau les informations # renvoyées, on les affiche # et on les exporte dans un fichier .CSV # désignation des fichiers nbfic <- 3 nomsFichiers <- paste("ficSerie",sprintf("%02d",1:nbfic),".txt",sep="") # structure d'accueil des résultats (data fram) tabRes <- data.frame(matrix(NA,nrow=nbfic+1,ncol=4)) names(tabRes) <- c("fichier","nombre de lignes","age min","age max") # boucle de traitement et remplissage de la structure d'accueil idf <- 0 # numéro courant de fichier for (nomfic in nomsFichiers) { idf <- idf + 1 lRet <- traiteFichier( nomfic ) tabRes[ idf, 1 ] <- nomfic tabRes[ idf, 2 ] <- lRet$nbl tabRes[ idf, 3 ] <- lRet$minage tabRes[ idf, 4 ] <- lRet$maxage } # fin pour nomfic cats("Analyse globale des fichiers") # ajout de la moyenne sur l'ensemble des fichiers tabRes[ (nbfic+1), 1 ] <- "global" tabRes[ (nbfic+1), 2 ] <- mean(as.numeric(tabRes[(1:nbfic),2])) tabRes[ (nbfic+1), 3 ] <- min(as.numeric(tabRes[(1:nbfic),3])) tabRes[ (nbfic+1), 4 ] <- max(as.numeric(tabRes[(1:nbfic),4])) # affichage et export cat("Voici le tableau résumé\n") print(tabRes) nomCsv <- "tabRes.csv" write.csv(x=tabRes,file=nomCsv) cat("\nrésultats écrits dans",nomCsv,"\n")Exemple d'exécution avec les fichiers cités :
Traitement du fichier ficSerie01.txt ==================================== il y a 100 lignes de données dans ficSerie01.txt age min = 11 ; age max = 78 Traitement du fichier ficSerie02.txt ==================================== il y a 30 lignes de données dans ficSerie02.txt age min = 11 ; age max = 78 Traitement du fichier ficSerie03.txt ==================================== il y a 40 lignes de données dans ficSerie03.txt age min = 12 ; age max = 93 Analyse globale des fichiers ============================ Voici le tableau résumé fichier nombre de lignes age min age max 1 ficSerie01.txt 100.00000 11 78 2 ficSerie02.txt 30.00000 11 78 3 ficSerie03.txt 40.00000 12 93 4 global 56.66667 11 93 résultats écrits dans tabRes.csvFichier CSV produit :
"","fichier","nombre de lignes","age min","age max" "1","ficSerie01.txt",100,11,78 "2","ficSerie02.txt",30,11,78 "3","ficSerie03.txt",40,12,93 "4","global",56.6666666666667,11,93Pour la moyenne de l'age, on ne peut pas procéder de la même manière parce que la moyenne des moyennes n'est pas la moyenne générale. Il faudrait calculer une moyenne pondérée des moyennes par fichier... Si le calcul n'est pas associatif, il n'est pas possible de traiter séparément les fichiers, c'est bien le problème des fameux map/reduce lié au Big Data : la somme et le max sont associatifs, pas la moyenne...
Code-source php de cette page. Retour à la page principale du cours.
Retour à la page principale de (gH)