R est un langage structuré en bloc comme C, C ++, Python, Perl, etc. Comme vous l'avez fait déjà vu, les blocs sont délimités par des accolades, bien que les accolades sont facultatives si le bloc se compose d'une simple déclaration. Les déclarations sont séparées par des caractères de nouvelle ligne ou, éventuellement, par des points-virgules.
Dans cette partie, nous allons couvrir les structures de base de R en tant que langage de programmation. Nous allons passer en revue quelques détails supplémentaires sur les boucles, puis continuer tout droit dans le sujet des fonctions, qui occuperont la majeure partie de cette partie.
Comme pour beaucoup langages de script, vous ne "déclarez" pas les variables dans R.
Les fonctions R sont des objets de première classe (de la classe "fonction", bien sûr), ce qui signifie qu'ils peuvent être utilisés pour la plupart comme d'autres objets. Cela se voit dans la syntaxe de la création de fonctions:
Ici, function () est une fonction R intégrée dont le travail est de créer des fonctions! Sur le côté droit, il y a vraiment deux arguments pour function (): La première est la liste des arguments formels pour la fonction que nous créons - ici, seulement x - et la seconde est le corps de cette fonction - ici, juste le retour d'état unique (x + 1). Ce second argument doit être de classe "expression". Donc, le fait est que le membre de droite crée un objet de fonction, qui est ensuite assigné à g.
Par ailleurs, même le "{" est une fonction, comme vous pouvez le vérifier en tapant ceci:
Son travail est de faire une seule unité de ce qui pourrait être plusieurs déclarations.
Ces deux arguments pour function () peuvent ensuite être accédés via les fonctions R formals () et body (), comme suit:
Rappelez-vous que lorsque vous utilisez R en mode interactif, il suffit de taper le nom d'un objet pour imprimer cet objet à l'écran. Les fonctions ne font pas exception, puisqu'elles sont des objets comme n'importe quoi d'autre.
C'est utile si vous utilisez une fonction que vous avez écrite mais dont vous avez oublié les détails. L'impression d'une fonction est également utile si vous n'êtes pas sûr de ce que fait une fonction de bibliothèque R. En regardant le code, vous pouvez le comprendre mieux. Par exemple, si vous n'êtes pas sûr du comportement exact de la fonction graphique abline (), vous pouvez parcourir son code pour mieux comprendre comment l'utiliser.
Si vous souhaitez afficher une longue fonction de cette manière, lancez-la à travers la page ():
Une alternative est de le modifier en utilisant la fonction edit (), dont nous parlerons dans la section 7.11.2.
Notez cependant que certaines des fonctions intégrées les plus fondamentales de R sont écrites directement dans C et ne sont donc pas visibles de cette manière. Voici un exemple:
Les fonctions étant des objets, vous pouvez également les assigner, les utiliser comme arguments pour d'autres fonctions, etc.
Et comme les fonctions sont des objets, vous pouvez parcourir une liste composée de plusieurs fonctions. Cela serait utile, par exemple, si vous souhaitiez écrire une boucle pour tracer un certain nombre de fonctions sur le même graphe, comme suit:
Les fonctions formals () et body () peuvent même être utilisées comme fonctions de remplacement. Nous discuterons des fonctions de remplacement dans la Section 7.10, mais pour l'instant, réfléchissez à la façon dont vous pourriez modifier le corps d'une fonction par affectation:
La raison pour laquelle quote () était nécessaire est que techniquement, le corps d'une fonction a la classe "call", qui est la classe produite par quote (). Sans l'appel à citer (), R essaierait d'évaluer la quantité 2 * x + 3. Donc, si x avait été défini et égal à 3, par exemple, nous assignerions 9 au corps de g (), certainement pas ce que nous voulons. D'ailleurs, puisque * et + sont des fonctions (comme décrit dans la section 2.4.1), en tant qu'objet de langage, 2 * x + 3 est en effet un appel entrant, c'est un appel de fonction imbriqué dans un autre.
Dans la section 5.1.2, nous lisons dans un ensemble de données à partir d'un fichier appelé examens:
L'argument header = TRUE indique à R que nous avons une ligne d'en-tête, donc R ne devrait pas compter cette première ligne dans le fichier en tant que donnée.
Ceci est un exemple d'utilisation d'arguments nommés. Voici les premières lignes de la fonction:
Le deuxième argument formel est nommé en-tête. Le champ = FALSE signifie que cet argument est optionnel, et si nous ne le spécifions pas, la valeur par défaut sera FALSE. Si nous ne voulons pas la valeur par défaut, nous devons nommer l'argument dans notre appel:
D'où la terminologie nommée argument.
Notez cependant que parce que R utilise l'évaluation paresseuse, il n'évalue pas une expression tant que l'argument nommé ne peut pas être utilisé.
Le tableau 7-1 répertorie les opérateurs de base.
Tableau 7-Ì: Opérateurs R de base
Description d'opération
x + y Addition
x - y Soustraction
x * y Multiplication
Division x / y
x n y Exponentiation
x %% y Arithmétique modulaire
x% /% y Division entière
x == y Test d'égalité
x <= y Test inférieur ou égal à
x> = y Test supérieur ou égal à
x y Booléen ET pour les scalaires
x Il y Booléen OU pour les scalaires
x S y Boolean ET pour les vecteurs (vecteur x, y, résultat)
x I y Booléen OU pour les vecteurs (vecteur x, y, résultat)
! x négation booléenne
Bien que R ostensiblement n'a pas de types scalaires, les scalaires étant traités comme des vecteurs à un élément, nous voyons l'exception dans le Tableau 7-1: Il existe différents opérateurs booléens pour les cas scalaires et vectoriels. Cela peut sembler étrange, mais un simple exemple démontrera la nécessité d'une telle distinction.
Le point central est qu'en évaluant un if, nous avons besoin d'un seul booléen, pas d'un vecteur de booléens, d'où l'avertissement vu dans l'exemple précédent, ainsi que la nécessité d'avoir à la fois les opérateurs & et &&.
Les valeurs booléennes VRAI et FAUX peuvent être abrégées en tant que T et F (les deux doivent être en majuscules). Ces valeurs changent à 1 et 0 dans les expressions arithmétiques:
Dans le deuxième calcul, par exemple, la comparaison 1 <2 renvoie TRUE, et 3 <4 donne VRAI aussi. Les deux valeurs sont traitées comme 1 valeurs, donc le produit est 1.
En surface, les fonctions R ressemblent à celles de C, Java, etc. Cependant, ils ont beaucoup plus d'une saveur de programmation fonctionnelle, ce qui a des implications directes pour le programmeur R.
Les instructions de contrôle dans R ressemblent beaucoup à celles des langages de la famille descendante ALGOL mentionnés ci-dessus. Ici, nous examinerons les boucles et les instructions if-else.
1 Boucles
Dans la section 1.3, nous avons défini la fonction oddcount (). Dans cette fonction, la ligne suivante aurait dû être immédiatement reconnue par les programmeurs Python:
Cela signifie qu'il y aura une itération de la boucle pour chaque composante du vecteur x, avec n prenant les valeurs de ces composantes dans la première itération, n = x [1]; dans la deuxième itération, n = x [2]; etc. Par exemple, le code suivant utilise cette structure pour sortir le carré de chaque élément d'un vecteur:
La boucle de style C avec while et repeat est également disponible, avec break, une instruction qui fait que le contrôle quitte la boucle. Voici un exemple qui utilise tous les trois:
Dans le premier fragment de code, la variable i a pris les valeurs 1, 5, 9 et 13 comme la boucle a traversé ses itérations. Dans ce dernier cas, la condition i <= 10 a échoué, alors la pause s'est installée et nous avons quitté la boucle.
Ce code montre trois façons différentes d'accomplir la même chose, la pause jouant un rôle clé dans les deuxième et troisième façons.
Notez que repeat n'a pas de condition de sortie booléenne. Vous devez utiliser break (ou quelque chose comme return ()). Bien sûr, break peut aussi être utilisé avec les boucles for.
Une autre instruction utile est la suivante, qui demande à l'interpréteur de passer le reste de l'itération en cours de la boucle et de passer directement au suivant. Cela permet d'éviter d'utiliser des constructions if-then-else complexes, ce qui peut rendre le code déroutant. Jetons un oeil à un exemple qui utilise ensuite. Le code suivant provient d'un examen étendu du chapitre 8:
Il y a les prochaines déclarations aux lignes 8 et 10. Voyons comment ils fonctionnent et comment ils améliorent les alternatives. Les deux instructions suivantes se trouvent dans la boucle qui commence à la ligne 4. Ainsi, lorsque la condition if se trouve à la ligne 8, les lignes 9 à 11 seront ignorées et le contrôle sera transféré à la ligne 4. La situation de la ligne 10 est similaire.
Sans utiliser ensuite, nous aurions besoin de recourir à des instructions imbriquées if, quelque chose comme ceci:
Parce que cet exemple simple n'a que deux niveaux, ce n'est pas trop mal. Cependant, les instructions imbriquées peuvent devenir déroutantes lorsque vous avez plus de niveaux.
La construction for fonctionne sur n'importe quel vecteur, quel que soit le mode. Vous pouvez faire une boucle sur un vecteur de noms de fichiers, par exemple. Disons que nous avons un fichier nommé file 1 avec le contenu suivant:
1
2
3
4
5
6
Nous avons aussi un fichier nommé file 2 avec ces contenus:
5
12
13
La boucle suivante lit et imprime chacun de ces fichiers. Nous utilisons ici la fonction scan () pour lire dans un fichier de nombres et stocker ces valeurs dans un vecteur. Nous parlerons plus en détail de scan () au chapitre 10.
Donc, fn est d'abord réglé sur filel, et le fichier de ce nom est lu et imprimé. Ensuite, la même chose se produit pour file2.
2 Bouclage sur des ensembles non-vecteurs
R ne supporte pas directement l'itération sur les ensembles non-vecteurs, mais il existe quelques moyens indirects mais faciles à réaliser:
• Utilisez lapply (), en supposant que les itérations de la boucle sont indépendantes les unes des autres, ce qui permet de les exécuter dans n'importe quel ordre.
• Utilisez get (). Comme son nom l'indique, cette fonction prend comme argument une chaîne de caractères représentant le nom d'un objet et renvoie l'objet de ce nom. Cela semble simple, mais get () est une fonction très puissante.
Regardons un exemple d'utilisation de get (). Supposons que nous ayons deux matrices, u et v, contenant des données statistiques, et nous souhaitons appliquer à chacun d'eux la fonction de régression linéaire lm ().
Ici, m a été mis pour la première fois. Alors ces lignes assignent la matrice u à z, ce qui permet l'appel à lm () sur u:
La même chose se produit alors avec v.
3 if-else
La syntaxe de if-else ressemble à ceci:
Cela semble simple, mais il y a ici une subtilité importante. La section if se compose d'une seule déclaration:
Donc, vous pourriez deviner que les accolades autour de cette déclaration ne sont pas nécessaires. Cependant, ils sont en effet nécessaires.
L'accolade droite avant le else est utilisée par l'analyseur R pour déduire qu'il s'agit d'un if-else plutôt que d'un if. En mode interactif, sans accolades, l'analyseur pense par erreur à ce dernier et agit en conséquence, ce qui n'est pas ce que nous voulons.
Une instruction if-else fonctionne comme un appel de fonction et, à ce titre, renvoie la dernière valeur attribuée.
Cela mettra v au résultat de l'expression! ou expression2, selon que la condition est vraie. Vous pouvez utiliser ce fait pour compacter votre code. Voici un exemple simple:
Sans prendre ce point, le code
consisterait plutôt en un peu plus encombré
Dans des exemples plus complexes, expression! et / ou expression2 pourraient être des appels de fonction. D'un autre côté, vous ne devriez probablement pas laisser la compacité prendre le pas sur la clarté.
Lorsque vous travaillez avec des vecteurs, utilisez la fonction ifelse (), comme indiqué au chapitre 2, car elle produira probablement un code plus rapide.