UP | HOME

Séance 1 : Algo & Prog avec R

Table des matières

1 Travail sous Windows (2ème étage) ou Linux (3ème étage) (CRIPS)

Vous allez travailler sur des ordinateurs sous le système d'exploitation Windows ou Linux. Peut-être utilisez-vous un ordinateur MacOS-X (basé sur UNIX comme Linux) ? Aucune importance puisque les logiciels qui nous serviront à programmer en R fonctionnent sur tous ces systèmes quasiment à l'identique. Nous supposons que vous savez utiliser votre ordinateur pour copier/déplacer des fichiers et lancer un logiciel ou un navigateur Web (Firefox, Chrome, etc).

Vous êtes donc devant votre machine. Vos notes de cours sont à portée de main et vos neurones commencent leurs échanges électro-chimiques, bravo !

Nous vous demandons d'acheter une "clé USB". Certaines clés USB font aussi office de lecteurs MP3 ou de téléphones portables ! En principe la machine est allumée, et en veille. En pressant une touche, elle devrait se réveiller et l'écran s'allumer. Appelez votre enseignant si ce n'est pas le cas, ou regardez comment font vos voisins … Vous arrivez sur une petite "fenêtre de dialogue" qui vous demande un nom d'utilisateur et un mot de passe (le vôtre). Après quelque temps, le "bureau" s'affiche, avec l'icône (petite image) du "poste de travail" en haut à gauche. Un double clic sur cette icône permet de visualiser le contenu des disques durs de l'ordinateur. Faites-le. Le disque système est administratif et protégé. Le bureau de Windows sert à abriter (le temps d'une séance de TP seulement) les fichiers des étudiants. Le bureau de Linux est sauvegardé d'une session à l'autre. Votre clef USB est un disque amovible.

Si vous avez une clé USB : travaillez directement sur votre clé USB qui sera votre disque dur et espace de travail durant le TP.

2 Getting Started

Suivez la première leçon du tutoriel interactif Try R (en anglais). Vous pouvez vous aider de google translate si vous avez des difficultés en anglais.

3 Top level

La fenêtre en bas à gauche de rstudio est la console. Elle contient un programme read-eval-print loop (REPL), aussi appelé interactive toplevel or language shell. C'est un environnement typique des langages interprétés (ne nécessitant pas de compilation) :

  1. takes single user inputs (i.e. single expressions),
  2. evaluates them,
  3. and returns the result to the user;

3.1 Expressions arithmétiques

Quelle est la valeur de chacune des expressions suivantes :

4 %/% 5 * 3 + 5 %/% 3 
4 %/% 5 * (3 + 5) %/% 3

N.B. Il est de bonne pratique de laisser un espace de part et d'autre d'un opérateur : il est plus facile de lire 2 – x // y que 2-x//y

  1. Si n est un entier écrit en décimal, que représentent au niveau des chiffres les opérations n %/% 10 et n %% 10 ?
  2. Quelle opération permet de savoir si un entier n est un multiple d'un entier d ?
  3. Quelle opération arithmétique permet de savoir si un entier n est pair ?
  4. Demandez en une ligne si 54 est plus grand que 45.
  5. Calculez la moyenne des entiers de 1 à 10. Le résultat est un nombre flottant (à virgule).
  6. Calculez la moyenne des entiers de 1 à 11. Ne retapez pas la formule complète, mais modifiez la précédente en tapant la flèche haut qui va chercher les lignes précédemment entrées (ce que les programmeurs nomment l'historique). On utilise la flèche bas en sens inverse.
  7. Vérifiez que les règles de calcul sur l'infini sont à peu près respectées.
n <- 1234
paste("n =",n)
paste(n %% 10, "represente le chiffre des unites de", n,"en base 10 (decimal)")
paste(n %% 2, "represente le chiffre des unites de", n,"en base 2 (binaire)")
paste(n %/% 10, "represente le nombre obtenu en gommant le chiffre des unites en base 10 (decalage a droite)")
paste(n %/% 2, "represente le nombre obtenu en gommant le chiffre des unites en base 2 (decalage a droite)")
paste(n,'est multiple de 3 :',n%%3==0)
paste(n,'est multiple de 2 :',n%%2==0)
paste('5 ** 4 > 4 ** 5 :',5 ** 4 > 4 ** 5)
paste('Moyenne des entiers de [1,10] :',(1 + 2 + 3 + 4 + 5 + 6 + 7 + 8 + 9 + 10) / 10)
[1] "n = 1234"
[1] "4 represente le chiffre des unites de 1234 en base 10 (decimal)"
[1] "0 represente le chiffre des unites de 1234 en base 2 (binaire)"
[1] "123 represente le nombre obtenu en gommant le chiffre des unites en base 10 (decalage a droite)"
[1] "617 represente le nombre obtenu en gommant le chiffre des unites en base 2 (decalage a droite)"
[1] "1234 est multiple de 3 : FALSE"
[1] "1234 est multiple de 2 : TRUE"
[1] "5 ** 4 > 4 ** 5 : FALSE"
[1] "Moyenne des entiers de [1,10] : 5.5"
paste('3 * Inf =',3 * Inf)
paste('-2 * Inf =',-2 * Inf)
paste('1 / 0 =',1/0)
## NaN signifie "Not A Number", forme indeterminee.
paste('Inf / Inf =',Inf/ Inf)      
paste('Inf / Inf =',Inf/ Inf)      
paste('0 / 0 =',0 / 0)

3.2 Obtenir de l'aide

Comme tout langage de programmation, R est constitué d'un noyau relativement réduit et d'un nombre important de modules. Un module fournit un ensemble de fonctions spécialisées dans une tâche donnée (maths, Internet, images, musique, etc).

Lisez attentivement http://www.statmethods.net/interface/help.html ou http://stackoverflow.com/questions/15289995/how-to-get-help-in-r. À quoi sert la fonction print ? cat ? paste ?

3.3 Affectations, affichage et échanges de valeurs

  1. Définissez deux variables: p ayant pour valeur 5 et q ayant pour valeur 3p.
  2. Avec une seule instruction paste(...), faites afficher la phrase suivante :
    p vaut 5 et q vaut 15 a , leur somme fait 20
    N.B. La solution suivante n'est PAS celle que l'on attend : paste('p vaut 5 et q vaut 15, leur somme fait 20').
  3. Demandez en R "q est-il un multiple de p?".
  4. Au toplevel, traduisez en R la phrase suivante : si q est un multiple de p, afficher OUI sinon afficher NON
  5. Sans consulter vos notes de cours, écrivez au toplevel les lignes de code permettant d'échanger les valeurs de p et q, en utilisant une variable temporaire tmp. Vérifiez ensuite que les valeurs de p et q ont bien été permutées.
  6. Echangez à nouveau les valeurs de p et q, mais SANS utiliser de variable temporaire! Soyez astucieux, jouez avec des opérations arithmétiques …
p <- 5
q <- 3*p
print(paste('p vaut',p,'et q vaut',q,', leur somme fait',p + q))

if (q %% p == 0) {
  paste(q,'est bien un multiple de',p)
}
## echange de deux variables p et q, avec une variable temporaire
tmp = p
p = q
q = tmp
paste('Apres echange, p =',p,'et q =',q)


## Si p et q sont deux nombres, on peut realiser astucieusement l'echange sans
## variable temporaire, mais en jouant sur les operations arithmetiques.
## Il s'agit d'un exo theorique, personne ne fait vraiment cela !
p <- p + q
q <- p - q
p <- p - q
paste('Apres echange, p =',p,'et q =',q)

3.4 Passons dans l'éditeur

Dans les exercices suivants, le travail se fait dans l'éditeur. Lorsque l'on vous demandera de sauver le contenu de votre éditeur, vous opterez pour un fichier de nom tp1.R sur votre clef USB, dans un dossier nommé R.

N.B. Vous prendrez l'habitude de n'utiliser des caractères accentués français qu'à l'intérieur des chaînes de caractères, et jamais dans les noms de fonctions ou de variables ou de fichiers. Vous risquez sinon, en changeant de machine, d'avoir de désagréables surprises …

4 Génération aléatoire d'un nombre   KEY

Nous allons utiliser une fonction sample tirant au hasard des éléments dans une collection, on dit aussi aléatoires. Pour afficher un entier aléatoire entre [1,100], tapez sample(1:100,size=1). Lisez la documentation help(sample) et exécutez les examples example(sample) avant d'essayer de répondre aux questions.

  1. Faites afficher trois entiers aléatoires de [100,200] avec un simple appel à sample.
  2. Définissez une fonction RandPair(n) prenant un entier n ≥ 0 et retournant un entier pair aléatoire de [0,n].
    • Est-ce que la définition RandPair <- function(n) 2*sample(n%/%2,1) respecte les spécifications ? Dans le cas contraire, corrigez la fonction.
    • Faites suivre la définition de votre fonction d'une instruction pour la tester.
    • Que se passe-t'il quand l'argument n est négatif ?
  3. Définissez une fonction MonteCarlo() sans argument retournant au hasard 2, 3 ou 5. Testez-la plusieurs fois.
  4. Définissez une fonction LasVegas() retournant 2, 3 ou 5 mais de manière truquée : 2 avec 1 chance sur 6, ou bien 3 avec 1 chance sur 3, ou bien 5 avec 1 chance sur 2.
    • Testez-la une dizaine de fois… les résultats sont-ils conformes à ce que l'on attend ?
    • Testez la commande replicate(10, LasVegas()) ? À quoi sert-elle ?
  5. Définissez une fonction RandChiffres(n) prenant un entier n ≥ 1 et retournant un entier aléatoire non nul contenant exactement n chiffres. Testez-la plusieurs fois …
sample(100:200, 3, replace=TRUE)

RandPair <- function(n) 2*sample(0:n%/%2,1)
paste('Comportement non spécifié pour n < 0 : ',RandPair(-2))
paste('Comportement défini pour n = 0 : ',RandPair(0))
paste('Voici un entier pair aleatoire de [0,2] :',RandPair(2))
paste('Voici un entier pair aleatoire de [0,20] :',RandPair(20))

MonteCarlo <- function() sample(c(2,3,5),1)
paste('monte_carlo() --> ',MonteCarlo())

LasVegas <- function() sample(c(2,3,3,5,5,5),1)
for(i in 1:5) print(paste('LasVegas() --> ',LasVegas()))
print('LasVegas() --> ')
print(replicate(10, LasVegas()))

## les entiers non nuls ayant n chiffres sont les elements de [10**(n-1),10**n-1]
RandChiffres <- function(n) sample(10**(n-1):10**n-1, 1)
print(replicate(5, paste('Voici un entier aleatoire de 4 chiffres :',RandChiffres(4))))
[1] 108 179 113
[1] 151 150 181
[1] "Comportement indéfini pour n < 0 :  0"
[1] "Comportement défini pour n = 0 :  0"
[1] "Voici un entier pair aleatoire de [0,2] : 2"
[1] "Voici un entier pair aleatoire de [0,20] : 8"
[1] "monte_carlo() -->  5"
[1] "LasVegas() -->  5"
[1] "LasVegas() -->  2"
[1] "LasVegas() -->  5"
[1] "LasVegas() -->  3"
[1] "LasVegas() -->  5"
[1] "LasVegas() --> "
 [1] 5 5 5 5 3 5 5 3 5 5
[1] "Voici un entier aleatoire de 8 chiffres : 66495533"
[2] "Voici un entier aleatoire de 8 chiffres : 79597943"
[3] "Voici un entier aleatoire de 8 chiffres : 74014571"
[4] "Voici un entier aleatoire de 8 chiffres : 79526092"
[5] "Voici un entier aleatoire de 8 chiffres : 75925821"

5 Maximum d'une somme

  1. Définissez la fonction Somme2Max(x,y,z) prenant trois entiers x, y, z et retournant la somme des deux plus grands. Deux solutions : ou bien vous emboîtez des if, ou bien vous utilisez max ou min.
  2. Définissez la fonction PrintSommeMax(x,y,z) faisant afficher la somme des deux plus grands des entiers x, y, z. Cette fonction n'aura aucun résultat (ne confondez pas l'effet et le résultat d'une fonction !).
Somme2Max <- function(x,y,z) {x+y+z-min(x,y,z)}

PrintSomme2Max <- function(x,y,z) {
  print(paste('Somme2Max(',x,',',y,',',z,') --> ',Somme2Max(x,y,z)))
}
PrintSomme2Max(3,5,7)

Somme2Max <- function(x,y,z) {max(x+y,y+z,x+z)}
PrintSomme2Max(3,5,7)

Somme2Max <- function(x,y,z) {
  if(x > y) {
    if(y > z) {return(x+y)}
    else {return(x+z)}
  } else {
    if(x > z) {return(y+x)}
    else {return(y+z)}
  }
}
PrintSomme2Max(3,5,7)

## à méditer ...
 PrintSomme2Max <- function(x,y,z) {
   sm <- quote(Somme2Max(x,y,z))
   print(sm)
   print(eval(sm))
 }
 PrintSomme2Max(3,5,7)
[1] "Somme2Max( 3 , 5 , 7 ) -->  12"
[1] "Somme2Max( 3 , 5 , 7 ) -->  12"
[1] "Somme2Max( 3 , 5 , 7 ) -->  12"
Somme2Max(x, y, z)
[1] 12

6 Circuit électrique   KEY

Dans le cours d'électricité du lycée, vous avez sans doute vu que :

  • la résistance équivalente de deux résistors R1 et R2 en série vaut R = R1 + R2,
  • tandis que si les résistors sont placés en parallèle, leur résistance globale vérifie 1/R=1/R1+1/R2.

Un électronicien travaille avec la portion de circuit suivante contenant trois résistors. Programmez la fonction Circuit1(r1,r2,r3) retournant la résistance équivalente de ce circuit.
A.N. Pour r1=5 Ω, r2=100 Ω et r3=25 Ω, le résultat est 25 Ω.

       +----------+               +----------+           
+------+    R1    +-------+-------+    R2    +------+---+
       +----------+       |       +----------+      |    
                          |                         |    
                          |       +----------+      |    
                          +-------+    R3    +------+    
                                  +----------+

Maintenant, programmez la fonction Circuit2(R1,R2,R3) retournant la résistance équivalente de ce circuit.

         +----------+                    +----------+           
-+-------+    R1    +------+---+-+-------+    R2    +------+---+
 |       +----------+      |     |       +----------+      |    
 |                         |     |                         |    
 |       +----------+      |     |       +----------+      |    
 +-------+    R2    +------+     +-------+    R3    +------+    
 |       +----------+      |             +----------+          
 |                         |  
 |       +----------+      |  
 +-------+    R3    +------+  
         +----------+

Indice : définir une fonction auxiliaire Serie(r1, r2) (respectivement Parallele(r1, r2)) qui calcule la résistance globale de deux résistors en série (respectivement en parallèle).

Serie <- function(r1,r2) {r1+r2}
Parallele <- function(r1,r2) {return(r1 * r2 / (r1 + r2))}
# Le circuit est vue comme une composition de sous-circuits
circuit1 <- function(r1,r2,r3) { 
  return(Serie(r1,Parallele(r2,r3)))
}
## \u03a9 : Unicode !
cat(paste('La resistance totale du circuit 1 est',circuit1(5,100,25),'\u03a9.\n'))

circuit2 <- function(r1,r2,r3) {
  return(Serie(Parallele(r1,Parallele(r2,r3)), Parallele(r2,r3)))
}
cat(paste('La resistance totale du circuit 2 est',circuit2(5,100,25),'\u03a9.\n'))
La resistance totale du circuit 1 est 25 Ω.
La resistance totale du circuit 2 est 24 Ω.

7 Conversion du temps

Programmez une fonction hconv(n) prenant un entier n > 0 représentant un nombre de secondes. L'effet de cette fonction est l'affichage d'une ligne exprimant la conversion de n secondes en heures-minutes-secondes.

hconv <- function(n) {
  s <- n %% 60;
  h <- n %/% 60;
  m <- h %% 60;
  h <- h %/% 60;
  print(sprintf("%d -> %02d:%02d:%02d",n,h,m,s))
}
hconv(4567)
hconv(3601)
hconv(123456789)
[1] "4567 -> 01:16:07"
[1] "3601 -> 01:00:01"
[1] "123456789 -> 34293:33:09"

8 Impôt sur le revenu   HARD

Supposons que l'impôt sur le revenu annuel soit calculé par tranches de la manière suivante.

  • Un salarié ne paye rien pour les 8000 premiers euros qu'il gagne.
  • Il paye 10% sur chaque euro gagné entre 8000 € et 25000 €,
  • et enfin 20% sur chaque euro gagné au-dessus de 25000 €.
  1. Définissez la fonction Tranche(s,b,h,p) retournant l'impôt dû pour un salaire annuel s dans la tranche [b,h] dont le pourcentage est p%.
  2. Définissez la fonction Impot(s) retournant l'impôt total dû pour un salaire annuel s.
Tranche <- function(s,b,h,p) {
  if(s < b) return(0)   # rien                    
  else if (s <= h) return( (s - b) * p / 100) # une portion de la tranche
  else return((h - b) * p / 100)     # toute la tranche
}
Impot <- function(s) Tranche(s,8000,25000,10)+Tranche(s,25000,s, 20)

Tranche(2500,2000,3000,10)    
Tranche(4000,2000,3000,10)                             
Impot(40000)
Tranche <- function(s,b,h,p) min(h-b, max(s-b, 0))*p/100
[1] 0
[1] 50
[1] 100
[1] 4700
Tranche(1500,2000,3000,10)    
Tranche(2500,2000,3000,10)    
Tranche(4000,2000,3000,10)                             
Impot(40000)
[1] 0
[1] 50
[1] 100
[1] 4700

9 Représentation des nombres   KEY

9.1 Représentation d'un entier

La fonction typeof renvoie le type d'un objet.

typeof(2105)
[1] "double"

la reponse du "top level" est interessante.

  1. Qu'est ce qu'un double en R ?

    double fait partie des 6 basic atomic vector types de R. donc 2015 est un vector (des cellules contigues) d'une seule cellule.

  2. Pourquoi ca rend double ?

    Voir la réponse ici.

  3. Comment travailler avec un entier ?
    typeof(2015L)
     v <- 2015
     typeof(as.integer(v))
    
    [1] "integer"
    [1] "integer"
    
  4. Comment sont représentés les entiers en machine ?
    intToBits(2015)
    
     [1] 01 01 01 01 01 00 01 01 01 01 01 00 00 00 00 00 00 00 00 00 00 00 00 00 00
    [26] 00 00 00 00 00 00 00
    

    Les entiers sont représentés dans un système binaire (base 2). Le système binaire le plus courant est l'équivalent en base deux de la numération de position que nous utilisons en base dix dans la vie courante.

  5. les objets de base de R sont les vecteurs.

    Même un entier "tout seul" est représenté par un vecteur … de une seule cellule. C'est comme ça : basic types ; expressions.

9.2 Conversion binaire-décimal   KEY

  1. Comment l'entier 2015 s'écrit-il en binaire (base 2) ?

    Il est obligatoire de comprendre ce tutoriel sur les systèmes binaire et l'hexadécimal. On procede par une suite de divisions par 2 jusqu'a tomber sur un quotient nul. On "remonte" alors tous les restes des divisions. Soit a convertir 123 en binaire.

    123 | 2
      1   61 | 2
           1   30 | 2
                0   15 | 2
                     1   7 | 2
                         1   3 | 2
                             1   1 | 2
                                 1   0=STOP
    

    Donc en binaire 123 s'ecrit '1111011'.

    rev(intToBits(2015))
    
     [1] 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 01 01 01 01
    [26] 01 00 01 01 01 01 01
    
  2. Quel est l'entier dont l'écriture binaire est 1101110 ? On verifie ?

    On n'utilise pas obligatoirement les puissances de 2, mais le "schema de Horner" qui consiste à eplucher l'ecriture binaire de gauche a droite en appliquant la question d. On part d'un accumulateur acc=0. Chaque fois que l'on rencontre un 0 on fait acc = 2*acc et si l'on rencontre un 1 on fait acc=2*acc+1. Exemple sur 1001101 : 0, 1, 2, 4, 9, 19, 38, 77. Facile ?

    bits <- strsplit('1001101','')[[1]]=="1"
    pows <- 2**((length(bits)-1):0)
    sum(pows*bits)
    
    [1] 77
    

10 Fraction irréductible   HARD

Comment feriez-vous pour savoir si la fraction 51/85 est irréductible ? En d'autres termes, peut-on la simplifier ? Par combien ?

Indice : calcul du pgcd par la méthode soustractive ou encore mieux l'algorithme d'euclide.

gcd <- function(a,b) ifelse (b==0, a, gcd(b, a %% b))
g = gcd(58,87)
if (g == 1) {
  print('La fraction 58/87 est irreductible !')
} else {
  paste("La fraction 58/87 n'est pas irreductible, on peut la simplifier par",g)
}

11 Évaluation des arguments d'une fonction   HARD

11.1 Il y a deux sortes de fonctions en R.

  1. Les fonctions prédéfinies
    abs
    typeof(abs)
    
    function (x)  .Primitive("abs")
    [1] "builtin"
    
  2. Les fonctions que vous programmez vous-même
    foo <- function(x) {x+1}
    typeof(foo)
    
    [1] "closure"
    

11.2 Les opérateurs sont des fonctions.

En R, même les opérateurs sont des fonction ! Par exemple, + est un operateur, mais c'est ausssi une fonction.

2 + 2
'+'(2,2)
2 == 3
'=='(2,3)
0 || 1 
'||'(0,1)

11.3 Évaluation paresseuse des arguments d'une fonction

Par exemple, si f est une fonction, au moment du calcul de f(a,b), l'évaluation des paramètre a et b de la fonction ne se fait pas avant que les résultats de cette évaluation ne soient réelle1asul"typeof(2105) atoi Cipe

  • a et nction
  • Qu'est ce qu'un double en28rammez 9, 19, 3>.
    gcd <- function(x) {x+1}
    typeof(foo)
    
    ,eee;xsrc-container"> <> uati rs édli> , v On oS utn1] "Voil du :xemp,or: #9s éitnction.Pourquoi ca rend double ?2crit-ilU"outlinv class="odia.