ChallengeR #5

Votre dernière mission consistait à trouver la fonction locf (qui remplace les NA par la dernière valeur connue) la plus compacte possible (étant bien entendu, comme d'habitude, que les espaces, les indentations et les retours à la ligne ne sont pas considérés comme des caractères). L’exemple que je vous ai donné était le suivant :

> x <- 10:20
> x[5:6] <- NA
> locf(x)
 [1] 10 11 12 13 13 13 16 17 18 19 20
>

Ce faisant, j’ai commis une petite erreur : j’aurais dû préciser que locf devrait aussi faire ça :

> x <- 10:20
> x[c(1:2, 5:6)] <- NA
> locf(x)
 [1] NA NA 12 13 13 13 16 17 18 19 20
>

En tenant compte de cet oubli de ma part, vous êtes deux à remporter ce 4ème ChallengeR avec un corps de fonction d’à peine 34 caractères.

La première solution est de @dickoah :

locf = function(x) {
 x[cummax((!is.na(x)) * 1:length(x))]
}

La seconde, qui présente l'avantage de traiter les NA en début de vecteur, nous est proposée par @navarre_julien :

locf = function(x) {
 i = !is.na(x)
 c(NA, x[i])[cumsum(i)+1]
}

Notez que, pour répondre strictement à mon énoncé (et donc sans supporter les NA en début de vecteur), on pouvait raccourcir la version de Julien comme suit :

locf = function(x) {
 i = !is.na(x)
 x[i][cumsum(i)]
}

La fonction locf idéale est donc quelque chose du genre :

locf = function(x) {
 if(is.matrix(x)) {
  res <- apply(x, 2, locf)
 } else {
  i <- !is.na(x)
  res <- c(NA, x[i])[cumsum(i)+1]
 }
 res
}

Bravo à tous les deux ! Voici donc ChallengeR, 5ème du nom.

Votre mission, si vous l’acceptez, consiste à coder le tricheur parfait dans mon petit tournois d’algos pour un dilemme du prisonnier répété. Vous devez concevoir une fonction de la forme :

msp = function(p, o, n = 2000) {
 # faire quelque chose...
 return(res)
}

De telle sorte qu’elle pulvérise tous les records en trichant vilement. Edit : Tout le code est accessible sur Github.

ChallengeR #4

Bon, vous êtes non seulement forts mais vous êtes en plus de grands malades. Votre dernière mission consistait à trouver une fonction R qui renvoie la suite de Syracuse de n'importe quel entier x jusqu'à ce qu'elle atteigne 1. Il y a deux façons de faire ça :

En passant par une fonction récursive :

syracuse = function(x) {
 a <- tail(x, 1)
 if(a == 1) {
  return(x)
 } else {
  syracuse(c(x, ifelse(a%%2, a*3+1, a/2)))
 }
}

Ou en passant par une fonction récursive anonyme :

syracuse = function(x) {
 a <- tail(x, 1)
 if(a == 1) {
  return(x)
 } else {
  Recall(c(x, ifelse(a%%2, a*3+1, a/2)))
 }
}

Je dis que vous êtes des grands malades parce que, sur Twitter, vous avez assez rapidement décidé de trouver la fonction la plus condensée possible (sans parler de celui qui voulait faire ça sans if ni else). Du coup, je vous donne la version la plus courte, trouvée par @AlekVladNevski :

syracuse = function(x) {
 c(x, if(x>1) Recall(if(x%%2) x*3+1 else x/2 ))
}

Bravo à @_antoineb, @_pvictorr, @After_Eight, @AlekVladNevski, @bZ1O91, @ClementinC, @dickoah, @francois_ls, @mac_picsou, @NicolasBenezet, @PierreRinder, @privefl, @StephaneOrlins et @thedudeparis.

Nous allons donc pouvoir passer à l'étape 4.

Considérez le vecteur x suivant :

x <- 10:20
x[5:6] <- NA

Votre mission, si vous l’acceptez, consiste à coder trouver la fonction locf (pour Last Observation Copied Forward) qui, comme son nom le suggère, remplace les NA par la dernière observation connue :

> locf(x)
 [1] 10 11 12 13 13 13 16 17 18 19 20
>

Le gagnant sera celui qui proposera la fonction qui utilise le moins de caractères possibles (étant entendu que les espaces, les indentations et autres retours à la ligne ne son *pas* considérés comme des caractères).

Iterated prisoner’s dilemma

This is an iterated prisoner’s dilemma between yourself and 9 unknown opponents. Each match will last for 10 rounds and you’ll only know who was your opponent at the end of the match. For each round, you must choose between Cooperate (C) or Defect (D).

The payment matrix is :

C D
C [3,3] [0,5]
D [5,0] [1,1]

In words:

  • If you both cooperate ([C,C]), you'll get 3 points each;
  • If you cooperate while your opponent defects ([C, D]), you'll get 0 points but he'll get 5 points;
  • If you defect while your opponent cooperates ([D, C]), you'll get 5 points and he'll get nothing;
  • If you both defect ([D,D]), you'll get 1 point each.

The number of points earned by each of you during one specific match are calculated at the bottom of the tables and you'll get you total score at the end of the post.

Ready? Let's start!

Match 1

This is round 1. What do you do?



You Opponent
- -
- -
- -
- -
- -
- -
- -
- -
- -
- -
0 0
Match 2

This is round 1. What do you do?



You Opponent
- -
- -
- -
- -
- -
- -
- -
- -
- -
- -
0 0
Match 3

This is round 1. What do you do?



You Opponent
- -
- -
- -
- -
- -
- -
- -
- -
- -
- -
0 0
Match 4

This is round 1. What do you do?



You Opponent
- -
- -
- -
- -
- -
- -
- -
- -
- -
- -
0 0
Match 5

This is round 1. What do you do?



You Opponent
- -
- -
- -
- -
- -
- -
- -
- -
- -
- -
0 0
Match 6

This is round 1. What do you do?



You Opponent
- -
- -
- -
- -
- -
- -
- -
- -
- -
- -
0 0
Match 7

This is round 1. What do you do?



You Opponent
- -
- -
- -
- -
- -
- -
- -
- -
- -
- -
0 0
Match 8

This is round 1. What do you do?



You Opponent
- -
- -
- -
- -
- -
- -
- -
- -
- -
- -
0 0
Match 9

This is round 1. What do you do?



You Opponent
- -
- -
- -
- -
- -
- -
- -
- -
- -
- -
0 0
Conclusion

Still there? Great!

  • Always Defects always defects regardless of what you do;
  • Tit-for-Tat cooperates first and then reproduce your last move;
  • Grudger cooperates until you defect; then defects all the time;
  • Always Cooperates always cooperates regardless of what you do;
  • Detective cooperates, defects, cooperates then checks what you did on the 3rd round: if you haven't retaliated, it defects all the time; otherwise it plays Tit-for-Tat;
  • Random just cooperates or defect randomly (with a probability of 1/2);
  • Win-Stay-Lose-Shift cooperates first then, if you've cooperated on the last round, repeat its last move; otherwise switches;
  • Tit-For-Two-Tats same as Tit-for-Tat but only retaliates after two defections in a row;
  • Alternates defects first and then just alternates regardless of what you do.

Your total score is 0 points. Please, tell me how much you've made (either in the comments below or on Twitter) and explain how you did it.

ChallengeR #3

Votre dernière mission consistait à compter le nombre de chiffres pairs du vecteur x avec le moins de code possible (étant bien entendu que les espaces, les indentations et les retours à la ligne ne sont pas comptés comme des caractères).

x <- sample(0:9, 100, TRUE)

La meilleure réponse possible était :

sum(! x %% 2)

... et vous êtes 14 à l'avoir trouvée. Dans l'ordre d'arrivée : @thedudeparis, @_antoineb, @_pvictorr, @davidgohel, @loicmolinari, @AlekVladNevski @privefl, @basiliximAb @dickoah, @ClementinC @StephaneOrlins, @claramorganexxx (ahem..), @francois_ls et @After_Eight. Bravo à toutes et à tous !

Comme vous êtes bons, on va augmenter la difficulté. Votre mission, si vous l'acceptez, consiste à coder une fonction syracuse qui renvoie la suite de Syracuse de n'importe quel entier x jusqu'à ce qu'elle atteigne 1. En d'autres termes, nous cherchons une fonction de type :

syracuse = function(x) {
 # faire quelque chose...
}

De telle sorte que :

> syracuse(42)
[1] 42 21 64 32 16  8  4  2  1

Mais comme ça, c'est trop facile, vous allez faire ça sans boucle (ni for ni while ni repeat !).

ChallengeR #2

Je vais poster régulièrement des petits challenges de code sous R. Les résultats du premier sont ci-dessous, la question du second est en fin d’article.

Résultats du premier challenge

Votre mission consistait donc à reproduire la matrice suivante sous R avec le moins de code possible (étant précisé que les espaces, les indentations et les retours à la ligne ne sont pas considérés comme des caractères) :

> a
     [,1] [,2] [,3] [,4] [,5]
[1,]    1    2    3    4    5
[2,]    2    4    6    8   10
[3,]    3    6    9   12   15
[4,]    4    8   12   16   20
[5,]    5   10   15   20   25

Avant de vous donner les meilleures réponses, voici quelques unes des nombreuses façons de faire ça ; à commencer par la bonne vieille double boucle :

a <- matrix(NA, 5, 5)
for(i in 1:5) {
 for(j in 1:5) {
  a[i, j] <- i*j
 }
}

On pouvait aussi faire ça avec une seule boucle :

a <- matrix(1:5, 5, 5)
for(i in 1:5) a[, i] <- a[, i]*i

Ou :

a <- matrix(NA, 5, 5)
for(i in 1:5) a[, i] <- 1:5*i

Ou encore :

a <- matrix(1:5, 1, 5)
for(i in 2:5) a <- rbind(a, a[1, ]*i)

Mais sous R, dans la plupart des cas, on n'a pas besoin de boucle. Une alternative classique consiste à utiliser une des fonctions de la famille apply (apply, sapply, lapply, tapply ou rapply) :

sapply(1:5, function(i) i*1:5)

Ou, encore mieux et trouvée par @thedudeparis :

sapply(1:5, "*", 1:5)

On pouvait y arriver aussi avec une simple multiplication. Un certain @realDonaldTrump a proposé :

t(matrix(rep(1:5,5),5))*1:5

Ou, en utilisant l'argument byrow=TRUE dans matrix :

matrix(1:5, 5, 5, T) * 1:5

Ou encore, en transposant (avec t) :

a <- matrix(1:5, 5, 5)
a * t(a)

De là, certains d'entre vous ont pensé à un produit matriciel (avec l'opérateur %*%) ; c'est notamment le cas, encore, de @thedudeparis avec  :

1:5 %*% t(1:5)

Mais la meilleure solution consistait à utiliser la fonction outer ; @ClementinC à été le premier à chercher dans cette voie :

outer(1:5, 1:5, "*")

Ce qui, sachant que "*" est la valeur par défaut de l'argument FUN de outer, pouvait aussi s'écrire :

outer(1:5, 1:5)

Mais c'est @_AntoineB qui a pensé à utiliser l'opérateur de outer (%o%) et a donc remporté ce quiz avec à peine 9 caractères (bravo à lui !) :

1:5 %o% 1:5
Nouveau challenge

Soit le vecteur suivant :

x <- sample(0:9, 100, TRUE)

Comment compter le nombre de chiffres pairs avec le moins de code possible (les espaces, les indentations et les retours à la ligne ne sont pas comptés comme des caractères) ?

La meilleure réponse sera publiée ici même. Vous avez jusqu'au vendredi 19 janvier à 12h00 (heure de Paris).

Quick R

Je croise souvent des gens qui veulent se mettre à programmer sous R mais qui, par manque de temps ou parce que les tutos qu’on trouve sur internet font parfois un peu peur, ne s’y mettent pas. Je vous propose donc une très rapide introduction pour aider celles et ceux d’entre vous à sauter le pas. Elle est volontairement très simple et incomplète : l’idée, c’est de permettre à quelqu’un qui n’a jamais programmé de sa vie de s’y mettre sans trop de difficultés, rien de plus.

Premiers pas

Pour installer R sur votre machine, rendez vous ici, cliquez sur CRAN (pour Comprehensive R Archive Network), choisissez le serveur le plus proche de chez vous et suivez la procédure. Si vous êtes sous Windows, téléchargez l'exécutable et choisissez toutes les options par défaut. Si vous travaillez sous Mac ou Linux, il n'est pas inutile d'installer RStudio après R lui-même (c'est une interface graphique qui va considérablement simplifier votre vie).

Quand c'est fait, lancez une cession. Les utilisateurs de Windows devraient voir ça :

Ce que vous voyez, c’est le GUI (pour Graphical User Interface), un logiciel très basique qui vous permet d’interagir avec R lui-même (si vous êtes sous Mac ou Linux, c’est le rôle de RStudio). La fenêtre sur fond blanc, c’est la console. C’est là-dedans que nous allons envoyer des instructions pour exécution et voir ce que nous répond R.

Sous Windows, attention : si vous cliquez que l’une ou l’autre des petites croix blanches sur fonds rouge, vous quittez R. Essayez et vous devriez voir apparaître ça :

À chaque fois que vous voyez ça et jusqu’à ce que vous sachiez exactement de quoi il est question : cliquez « Non » (dans l’immédiat, cliquez plutôt sur « Annuler ».)

Nous allons très rapidement passer sur les manipulations dans la console — principalement parce que n’est pas là-dedans que nous allons travailler. Commençons par nous débarrasser de ce message d’accueil : cliquez dans la console (n’importe où) et pressez Ctrl+L.

Toujours dans la console, devant le signe >tapez 1+1 puis Entrée. Vous devriez avoir ça :

En rouge, votre instruction (1+1) et en bleue, la réponse de R (2) — le [1] n’est qu’un indicateur de position, vous allez très vite comprendre à quoi il sert.

Si vous essayez de modifier votre première instruction (1+1), vous devriez assez rapidement constater que c’est impossible. Pour R, c’est du passé. Ce que vous pouvez faire, en revanche, c’est cliquer que la petite flèche qui pointe vers le haut sur votre clavier pour rappeler la dernière instruction envoyée à la console et là, avant de la ré-exécuter, vous pouvez la modifier. Essayez de faire ça en remplaçant le 2ème 1 par un 2.

Comme vous pouvez le constater vous-même, ça n’est pas pratique du tout (mais alors pas du tout). La bonne nouvelle c’est que nous n’allons pas travailler comme ça.

Script

Dans Fichier, sélectionnez Nouveau script (ou pressez Ctrl+N). Vous devriez avoir quelque chose comme ça :

Sans titre est un script. C’est un fichier texte dans lequel nous allons écrire des instructions avant de les envoyer à la console pour exécution. Là-dedans, à part, de la mise en forme, vous pouvez faire ce que vous voulez : copier, couper, coller et modifier à loisir. Vous pouvez aussi (et vous devriez) le sauvegarder pour ne pas perdre votre travail (sous Windows, ce sera un fichier .R que vous pouvez ouvrir avec n’importe quel éditeur de texte).

Dans votre script, saisissez 6*7 puis, en maintenant le curseur sur cette ligne, pressez Ctrl+R (sous Rstudio, c'est Cmd+Entrée). En principe, vous devriez observer que votre instruction a été envoyée à la console pour exécution :

C’est comme ça que nous allons travailler : écrire des instructions dans un script et les envoyer à la console pour exécution : plusieurs lignes de code sauvegardées dans un script, ça fait un programme.

Bases

La règle suprême de la programmation sous R s’énonce très simplement : pour reprendre les termes de John Chambers, un de ses concepteurs, « tout ce qui existe est un objet et tout ce qui arrive est un appel à une fonction. » Nous allons surtout nous concentrer sur la première partie : « tout ce qui existe est un objet. »

Avant ça, nous allons nous mettre d’accord sur une petite convention entre nous. Quand j’écris :

6*7

C’est une instruction dans un script. Mais si j’écris :

> 6*7
[1] 42
>

C’est le résultat de l’exécution de cette instruction dans la console (i.e. n’allez pas me coller des > ou des [1] au début des lignes de vos scripts !)

Dans votre script, tapez machin et exécutez (Ctrl+R sous Windows, Cmd+Entrée si vous utilisez RStudio). En principe, vous obtenez un message d'erreur :

> machin
Erreur : objet 'machin' introuvable
>

R n’ayant jamais entendu parler de machin, il vous informe qu’il ne sait pas de quoi vous parlez. Nous allons donc créer un objet machin qui sera, par exemple, la suite des entiers de 1 à 5. Dans votre script, écrivez et exécutez :

machin <- 1:5

Le signe <- permet d'assigner une valeur à un objet. Vous pouvez aussi l'utiliser dans l'autre sens (1:5 -> machin) et vous pouvez aussi faire ça avec le signe = (machin = 1:5) mais je vous le déconseille pour le moment.

Dans la console, vous observez un accusé de réception :

> machin <- 1:5
>

Si vous voulez voir vous ce que contient machin, vous pouvez écrire machin dans la console ou sélectionner juste le mot machin dans votre script (le mettre en surbrillance) et exécuter. En principe :

> machin 
[1] 1 2 3 4 5
>

Dans la console, si vous tapez ls(), R vous confirme qu'il connait bien un objet nommé machin (la fonction ls liste le nom des objets existants dans l'environnement de travail actuel) :

> ls()
[1] "machin"
>

Et la fonction class, vous dira de quelle sorte d'objet il s'agit (ici integer, pour entiers) :

> class(machin)
[1] "integer"
>

Et puisque machin existe, nous pouvons jouer avec (tapez ça dans votre script, exécutez d'un coup et essayez de deviner ce que fait chaque fonction) :

length(machin)
sum(machin)
mean(machin)
max(machin)
rev(machin)
diff(machin)
rep(machin, 3)
machin+1
machin^2

Notez, pour les deux dernières, une petite particularité magique de R : il « recycle » l’élément le plus court de votre calcul. Quand vous lui demandez de calculer machin+1, il rajoute 1 à chaque élément de machin (idem pour machin au carré).

Maintenant, essayez ça :

machin <- c(2, 1, 3, 10, 1000)
bidule <- 2
machin*bidule

Delà, vous observez deux choses : (i) vous pouvez créer plusieurs objets et jouer avec et (ii) vous venez d'écraser l'ancien machin (la séquence des entiers de 1 à 5) pour le remplacer par une nouvelle définition de machin (avec la fonction c qui concatène à peu près tout ce que vous voulez).

Fonctions

Il existe une quantité invraisemblable de fonctions dans R auxquelles se rajoutent des libraires entières de fonctions spécialisées (les packages du CRAN) et celles que vous pouvez créer pour vos propres besoins. D’une façon générale, on utilise une fonction de la façon suivante :

nom_de_la_fonction(argument1, argument2, ...)

Sauf que, dans bien des cas, R nous simplifie la vie. Un bon exemple, c'est la fonction seq qui créé des séquences (l'opérateur :, que nous avons vu plus haut, est une sorte de raccourci pour seq). Pour savoir comment l'utiliser, vous pouvez exécuter :

help()

Ou :

?seq

La page d'aide qui s'ouvre vous informe, en autres choses, que seq admet 5 arguments : from (valeur par défaut : 1), to (valeur par défaut : 1), by (valeur par défaut : ((to - from)/(length.out - 1))), length.out (valeur par défaut : NULL) et along.with (valeur par défaut : NULL).

Par exemple, pour créer la suite des entiers de 1 à 10 avec un incrément de 2, vous pouvez écrire :

seq(from = 1, to = 10, by = 2)

Mais, puisque vous donnez les arguments dans l'ordre normal, vous n'êtes pas obligé de les nommer :

seq(1, 10, 2)

Du coup, vous pouvez les donner dans le désordre en les nommant :

seq(by = 2, from = 1, to = 10)

Et puisque la valeur par défaut de from c'est déjà 1, vous pouvez carrément omettre de le préciser :

seq(to = 10, by = 2)

Ou :

seq(, 10, 2)

Et si vous voulez une séquence qui commence à 10, progresse par 10 et a une longueur de 200 (notez que je ne suis pas obligé d'écrire length.out : je peux juste écrire len ou même l) :

seq(10, by = 10, len = 200)

Si vous cherchez une fonction sans savoir comment elle s’appelle (par exemple, pour trier), utilisez :

help.search("sort")

Ou :

??sort

Enfin, vous pouvez créer vos propres fonctions. Supposez, par exemple, que vous voulez une fonction qui calcule l'aire d'un disque en fonction de son rayon (pi est déjà en mémoire, vous pouvez l'utiliser directement) :

aire_disque = function(rayon) {
 res <- rayon*pi^2
 return(res)
}

Utilisation :

> aire_disque(2)
[1] 19.73921
>
Quelques classes utiles

Voici quelques-uns des objets les plus utiles :

Vecteurs

Avec runif (qui renvoie des nombres aléatoires qui suivent une distribution uniforme) vous pouvez créer un vecteur de classe numeric :

x <- runif(10)

En effet :

> class(x)
[1] "numeric"
>

Comme tous les vecteurs, il a une longueur mais pas de dimensions (je sais, c'est bizarre mais c'est comme ça) :

> length(x)
[1] 10
> dim(x)
NULL
>

Si vous souhaitez récupérer un élément de x (mettons le 5ème), faites x[5]. Pour les 3 premiers éléments, x[1:3]. Pour tous les éléments sauf le premier, le plus simple c'est x[-1].

Vous pouvez aussi assigner une valeur à un élément de votre vecteur :

x[5] <- 1000

Les vecteurs de classe integer (nombres entiers, nous en avons vu un plus haut) fonctionnent exactement de la même façon.

Dans la grande famille des vecteurs, il a aussi les vecteurs de classe character (c'est du texte). Par exemple :

x <- c("papa", "maman")

Ces choses-là sont notamment très utiles pour donner des noms aux autres objets :

# Ceci est un commentaire
# (ce qui est écrit après un dièse n'est pas exécuté).
# Soit un vecteur 'integer'
x <- 1:26
# L'objet 'letters' existe déjà en mémoire :
names(x) <- letters

Du coup :

> x[c("a", "m", "z")]
 a  m  z 
 1 13 26 
>

Il y a aussi des vecteurs de classe Date (notez le format des dates dans R) :

as.Date("2018-01-15")-10:1

Et, mes préférés, les vecteurs de classe logical (les booléens c'est-à-dire des vecteurs de TRUE et de FALSE) :

1:10 > 5

Ils permettent, entre autres, de faire des choses comme ça :

> x <- 1:26
> x[x > 10]
 [1] 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26
>

Matrices

Une matrice, très simplement, c'est un tableau de nombres (un array à deux dimensions dans lequel, en principe, vous ne devriez avoir que des nombres).

x <- matrix(1:15, nrow = 5, ncol = 3)

Ce qui nous donne :

> x
     [,1] [,2] [,3]
[1,]    1    6   11
[2,]    2    7   12
[3,]    3    8   13
[4,]    4    9   14
[5,]    5   10   15
>

Une matrice a deux dimensions — un nombre de ligne et un nombre de colonnes (par convention, toujours dans cet ordre) :

> dim(x)
[1] 5 3
>

Pour extraire la première ligne faites x[1, ]. Pour la première colonne faites x[, 1] (notez que dans les deux cas précédents, R fait « sauter » les dimensions : vous récupérez des vecteurs). Vous pouvez aussi, par exemple, récupérer les 2 premières lignes des colonnes 2 et 3 avec x[1:2, c(2, 3)].

Pour remplacer tous les éléments des x qui sont supérieurs à 10 par 10, faites simplement :

x[x > 10] <- 10

Supposez, par exemple, que vous souhaitez récupérer les lignes de x pour lesquelles les nombre de la seconde colonne sont pairs. Avec %%, l’opérateur modulo, notre condition s’écrit :

x[, 2] %% 2 == 0

Nous n'avons donc qu'à faire :

x[x[, 2] %% 2 == 0, ]

Et puisque 1 == TRUE et 0 == FALSE, on peut faire encore plus court en utilisant l'opérateur ! de la négation (!TRUE == FALSE) :

x[!x[, 2]%%2, ]

Listes

Les listes permettent de stocker à peu près n'importe quoi. Par exemple :

x <- list(1:10, letters, matrix(1, 3, 3))

Si vous voulez récupérer le 3ème élément (les lettres de l'alphabet) utilisez x[[3]]. Pour les deux 1ers, faites x[1:2] ou x[-3].

Vous pouvez aussi utiliser les noms de la liste comme ceci :

x <- list(
 nombres = 1:10,
 lettres = letters,
 matrice = matrix(1, 3, 3))

x$lettres
x[c("nombres", "matrice")]

Si vous préférez finalement des lettres majuscules :

x$lettres <- LETTERS
# Ou :
x$lettres <- toupper(x$lettres)

Data.frames

Un data.frame, c'est un peu comme une table de base de donnée : un tableau dans lequel chaque 'colonne' peut être d'une classe différente :

x <- data.frame(
 numbr = 1:26,
 alpha = LETTERS,
 dates = Sys.Date()-26:1)

Techniquement, ces choses-là sont des listes avec une structure. Vous pouvez donc les manipuler comme des matrices ou comme des listes :

x[1:2, ]
x$dates

Notez que pour la colonne alpha, vous obtenez un objet de classe factor :

> al <- x$alpha
> class(al)
[1] "factor"
> 

Pour éviter le problème, vous pouvez simplement faire ça :

> al <- as.character(x$alpha)
> class(al)
[1] "character"
> 
Boucles

Celles et ceux d’entre vous qui programment dans d’autres langages se demandent sans doute comment on écrit des boucles sous R. En général, on ne le fait pas : dans la plupart des cas, il y a des méthodes plus rapides et moins verbeuses. Mais puisque vous insistez, voici deux petits exemples qui devraient répondre à votre question.

Nous voulons les 20 premiers éléments de la suite de Fibonnacci :

n <- 20

# Vecteur vide (NA) de longueur n:
res <- rep(NA, n)

# On colle des 1 dans les 2 premiers items :
res[1:2] <- 1

# Boucle *for* -- Pour chaque valeur de i, de 3 à n
for(i in 3:n) {
 # res, position i est égal à :
 # res, position i-2 plus
 # res, position i-1
 res[i] <- res[i-2]+res[i-1]
}

Notez qu'en utilisant la formule de Binet :

phi <- (1+sqrt(5))/2
n <- 1:20
res <- (phi^n-(1-phi)^n)/sqrt(5)

Avec le vecteur res trouvé précédemment, on cherche n tel que la somme des éléments de res de 1 à n soit la plus grande possible mais inférieure à 1000 :

i <- 1
somme <- res[i]
# Boucle *while* -- Tant que somme < 1000
while(somme < 1000) {
 # On incrémente i de 1 :
 i <- i+1
 # On calcule la somme de res de la
 # position 1 à la position i :
 somme <- sum(res[1:i])
}
n <- i-1

Mais sous R, on préférera :

max(which(cumsum(res) < 1000))

Je m’arrête ici. Je posterai de temps en temps des petits challenges pour celles et ceux qui veulent s’exercer.

Axelrod, le tournois

Au début des années 1980, Robert Axelrod avait organisé un concours d’algorithmes pour un dilemme du prisonnier répété. Je vous propose de remettre ça tous les ans, le premier vendredi de mars, en mettant à profit les progrès réalisés par l’informatique depuis (notamment en termes d’accessibilité au commun des mortels) et les possibilités que nous offre l’existence d’internet.

Le Principe

Le but du jeu est de créer l’algorithme qui, lorsqu’il sera confronté à tous les autres lors d’un tournoi, gagnera le plus de points.

En notant $n$ le nombre d’algorithmes en lice, un tournois est donc composé de $\binom{n}{2}$ matches :

$$\binom{n}{2} = \frac{n!}{(n-2)!2!}$$

Chaque match est composé de 2000 dilemmes du prisonnier successifs (2000 rounds). Cette année vous le savez mais, dès 2019, vous ne pourrez plus exploiter cette information (le nombre de rounds sera inconnu).

La matrice des paiements retenue est celle de Robert Axelrod dans les années 1980 :

CD
C[3,3][0,5]
D[5,0][1,1]

Pour la première année, le tournois aura lieu le 2 mars 2018. Les résultats seront publiés ici même (et sur Twitter).

Programmation

Vos algorithmes doivent être des fonctions codées sous R (téléchargeable gratuitement ici) qui respectent la forme suivante :

foo = function(p, o, n = 2000) {
 # faire quelque chose...
 return(res)
}

Où :

  • foo est le nom de la fonction (c'est-à-dire de votre algorithme),
  • L'argument p est la liste des coups joués par foo depuis le début de la partie (c'est une vecteur de logical ou booléens — c'est-à-dire une suite de TRUE pour coopération et de FALSE pour défaut),
  • L'argument o est l'historique des coups joués par l'adversaire de foo durant cette partie (même format),
  • L'argument n est de nombre de rounds dans une partie (par défaut, n = 2000),
  • res est le coup joué (TRUE s'il coopère ou FALSE s'il fait défaut).

Vous pouvez donc très facilement savoir où vous en êtes dans la partie : si la longueur de p est égale à 0 (length(p) == 0) c’est que vous jouez le premier round, si elle est égale à 5, c’est que vous jouez le 6ème round et si elle est égale à n-1 c’est que allez jouer le dernier coup de ce match.

Si vous ne savez pas coder sous R (ou pas coder du tout), vous pouvez quand même participer en décrivant précisément ce que fait votre algo. J’essaierais de le programmer pour vous.

Si vous cherchez de l’inspiration, vous pouvez consulter la liste des algos déjà en lice et tester vos œuvres avec les fonctions prévues à cet effet (je fournis aussi quelques exemples d’utilisations).

Lisez le README sur Github pour plus de détails (oui, c'est en anglais).

Participer

Pour soumettre vos algos, utilisez ce formulaire (avant le 1er mars 2018 à minuit, heure de Paris, au plus tard) :

Si vous avez des questions, posez-les dans les commentaires ci-dessous.

ChallengeR #5

Votre dernière mission consistait à trouver la fonction locf (qui remplace les NA par la dernière valeur connue) la plus compacte possibl...