Comparez les instructions suivantes. La première instruction est tirée des notes du cours sur le prétraitement de données en R.
sc1 <- cut(cars$speed, breaks = c(-Inf, 10, 20, Inf), right = FALSE)
sc2 <- cut(
cars$speed,
breaks = c(-Inf, 10, 20, Inf),
right = FALSE,
labels = c("<10", "[10,20)", ">=20")
)
sc3 <- cut(
cars$speed,
breaks = c(-Inf, 10, 20, Inf),
right = FALSE,
labels = FALSE
)
sc4 <- findInterval(
x = cars$speed,
v = c(10, 20),
rightmost.closed = FALSE
)
D’abord, assurez-vous de comprendre ce que font ces instructions. Est-ce qu’elles retournent le même résultat ?
Quelle instruction est la plus rapide à évaluer ?
sc1 <- cut(cars$speed, breaks = c(-Inf, 10, 20, Inf), right = FALSE)
sc2 <- cut(
cars$speed,
breaks = c(-Inf, 10, 20, Inf),
right = FALSE,
labels = c("<10", "[10,20)", ">=20")
)
sc3 <- cut(
cars$speed,
breaks = c(-Inf, 10, 20, Inf),
right = FALSE,
labels = FALSE
)
sc4 <- findInterval(
x = cars$speed,
v = c(10, 20),
rightmost.closed = FALSE
)
# Comparaison des résultats
comparaison <- data.frame(sc1, sc2, sc3, sc4)
cbind(cars$speed, comparaison)
View(comparaison)
str(comparaison)
## 'data.frame': 50 obs. of 4 variables:
## $ sc1: Factor w/ 3 levels "[-Inf,10)","[10,20)",..: 1 1 1 1 1 1 2 2 2 2 ...
## $ sc2: Factor w/ 3 levels "<10","[10,20)",..: 1 1 1 1 1 1 2 2 2 2 ...
## $ sc3: int 1 1 1 1 1 1 2 2 2 2 ...
## $ sc4: int 0 0 0 0 0 0 1 1 1 1 ...
# Tâce réalisée : catégoriser une variable numérique
# Résultats équivalents (coupures aux mêmes endroits),
# mais résultats sous différents formats
# sc1 : facteur
# sc2 : facteur avec modalités (étiquettes) différentes de celles de sc1
# sc3 : vecteur d'entiers (1 à 3)
# sc4 : vecteur d'entiers (0 à 2)
# Comparaison des temps d'exécution
library(bench)
out_mark_q1 <- mark(
sc1_cut_auto_labels = cut(
cars$speed,
breaks = c(-Inf, 10, 20, Inf),
right = FALSE
),
sc2_cut_given_labels = cut(
cars$speed,
breaks = c(-Inf, 10, 20, Inf),
right = FALSE,
labels = c("<10", "[10,20)", ">=20")
),
sc3_cut_no_labels = cut(
cars$speed,
breaks = c(-Inf, 10, 20, Inf),
right = FALSE,
labels = FALSE
),
sc4_findInterval = findInterval(
x = cars$speed,
v = c(10, 20),
rightmost.closed = FALSE
),
check = FALSE
)
# Utilisons la fonction proposée dans les notes pour
# imprimer les résultats obtenus
print_bench_mark <- function(x){
df <- data.frame(
expression = as.character(x$expression),
n_itr = x$n_itr,
min = if (inherits(x$min, "bench_time")) as.character(x$min) else x$min,
median = if (inherits(x$min, "bench_time")) as.character(x$median) else x$median,
mem_alloc = as.character(x$mem_alloc),
n_gc = x$n_gc,
stringsAsFactors = FALSE
)
print(df)
invisible(df)
}
print_bench_mark(out_mark_q1)
# J'obtiens ceci :
## expression n_itr min median mem_alloc n_gc
## 1 sc1_cut_auto_labels 2548 134.8us 145.4us 1.84KB 6
## 2 sc2_cut_given_labels 6074 57.4us 62.9us 1.84KB 7
## 3 sc3_cut_no_labels 8205 39us 42.5us 248B 7
## 4 sc4_findInterval 9999 3.8us 7.9us 248B 1
# Réponse :
# instruction la plus rapide sur mon ordinateur = la dernière instruction
# Les temps vont en décroissant, entre sc1, sc2 et sc3, car d'une instruction à
# l'autre, le nombre d'évaluations à réaliser diminue.
# sc2 vs sc1 : pas besoin de déterminer des labels
# sc3 vs sc2 : pas besoin de créer un facteur
# De plus, findInterval semble être une fonction optimisée
# Une note dans la fiche d'aide de la fonction cut dit :
# « Instead of cut(*, labels = FALSE), findInterval() is more efficient. »Simulez 2 vecteurs numériques de longueur 10 000 (par exemple avec la
fonction rnorm).
Supposons que vous voulez additionner ces vecteurs, c’est-à-dire obtenir un vecteur de longueur 10 000 dont l’élément i est la somme de l’élément i du vecteur 1 et de l’élément i du vecteur 2.
Comparez les temps d’exécution des trois façons suivantes d’effectuer ce calcul.
mapply, qui réalise en
fait un calcul équivalent à celui effectué par la boucle
précédente.Quelle est la méthode la plus rapide?
n <- 10000
v1 <- rnorm(n)
v2 <- rnorm(n)
somme <- numeric(length = n)
out_mark_q2 <- mark(
vectoriel = {somme <- v1 + v2},
boucle = {for (i in seq_len(n)) somme[[i]] <- v1[[i]] + v2[[i]]},
mapply = {somme <- mapply("+", v1, v2)},
time_unit = "us",
check = FALSE
)
print_bench_mark(out_mark_q2)
# J'obtiens ceci :
## expression n_itr min median mem_alloc n_gc
## 1 vectoriel 9984 6.6 8.6 78.2KB 16
## 2 boucle 149 2630.7 2894.1 97KB 5
## 3 mapply 43 6670.9 7444.5 362.6KB 14
# Le calcul vectoriel est plus de 300 fois plus rapide que la boucle
# sur mon ordinateur. Le mapply est encore moins rapide que la boucle.
# Réponse : La méthode clairement la plus rapide est le calcul vectoriel.
# Remarque :
# En 2017, avec la version 3.3.2 de R et sur un ordinateur plus lent
# que mon ordinateur actuel, j'obtenais :
# (sortie obtenue avec le package microbenchmark)
## Unit: microseconds
## expr min lq mean median uq max neval cld
## vectoriel 9.452 11.5065 23.95748 26.711 28.765 62.05 100 a
## boucle 16195.522 17343.4230 18593.36604 17939.048 19476.294 28974.26 100 c
## mapply 9131.426 9710.6160 10529.64163 10211.320 11060.688 14914.28 100 b
# Avec cette ancienne version de R, le mapply était plus rapide que la boucle.
# À cause de la compilation en bytecode intégrée par défaut à R depuis la version
# 3.4.0, la boucle est devenue plus rapide que le mapply.Nous allons suivre la trace de l’emplacement en mémoire d’un objet R
dans trois blocs de code R. Pour ce faire, nous allons utiliser la
fonction tracemem.
Faites rouler les trois blocs de code R suivants.
out_matrice_croiss <- matrix(NA_real_, nrow = 0, ncol = 2)
tracemem(out_matrice_croiss)
for (i in 1:10) {
print(i)
out_matrice_croiss <- rbind(out_matrice_croiss, c(i, sqrt(i)))
print(tracemem(out_matrice_croiss))
}
out_matrice_croiss
untracemem(out_matrice_croiss)
out_matrice <- matrix(NA_real_, nrow = 10, ncol = 2)
tracemem(out_matrice)
for (i in 1:10) {
print(i)
out_matrice[i, ] <- c(i, sqrt(i))
print(tracemem(out_matrice))
}
out_matrice
untracemem(out_matrice)
out_df <- as.data.frame(matrix(NA_real_, nrow = 10, ncol = 2))
tracemem(out_df)
for (i in 1:10) {
print(i)
out_df[i, ] <- c(i, mean(1:i))
print(tracemem(out_df))
}
out_df
untracemem(out_df)
Pour chacun des blocs, combien de fois l’objet tracé est-il déplacé en mémoire?
# 1. Matrice de taille croissante :
out_matrice_croiss <- matrix(NA_real_, nrow = 0, ncol = 2)
tracemem(out_matrice_croiss)
for (i in 1:10) {
print(i)
out_matrice_croiss <- rbind(out_matrice_croiss, c(i, sqrt(i)))
print(tracemem(out_matrice_croiss))
}
out_matrice_croiss
untracemem(out_matrice_croiss)
# Réponse :
# L'objet change d'emplacement mémoire à chaque itération,
# donc 10 déplacement sont effectués.
# 2. matrice de taille fixe :
out_matrice <- matrix(NA_real_, nrow = 10, ncol = 2)
tracemem(out_matrice)
for (i in 1:10) {
print(i)
out_matrice[i, ] <- c(i, sqrt(i))
print(tracemem(out_matrice))
}
out_matrice
untracemem(out_matrice)
# Réponse :
# - si toutes les lignes sont soumises d'un coup : aucun déplacement
# - si les lignes sont soumises une après l'autre :
# un seul déplacement (au début de la boucle)
# 3. data frame de taille fixe :
out_df <- as.data.frame(matrix(NA_real_, nrow = 10, ncol = 2))
tracemem(out_df)
for (i in 1:10) {
print(i)
out_df[i, ] <- c(i, mean(1:i))
print(tracemem(out_df))
}
out_df
untracemem(out_df)
# Réponse :
# L'objet change d'emplacement mémoire à chaque itération.
# De plus, si les lignes sont soumises une après l'autre :
# il est également déplacé au début de la boucle.
# Le nombre de déplacements effectués est donc 10 ou 11.Chargez la package parallel.
Vérifiez combien de coeurs logiques possède l’ordinateur sur lequel vous travaillez.
Montez une grappe de calcul locale utilisant tous les coeurs de
l’ordinateur sur lequel vous travaillez sauf 1 avec la fonction
makeCluster (nommez-la grappe).
Créez une grande matrice avec l’instruction suivante :
mat <- matrix(rnorm(1000*1000), nrow = 1000, ncol = 1000)
Supposons que vous voulez calculer la moyenne des valeurs sur chacune des lignes de cette matrice. Comparez les temps d’exécution des cinq instructions suivantes pour effectuer le calcul. Les deux dernières instructions lancent un calcul en parallèle.
rowMeans(mat).rowMeans(mat, m = 1000, n = 1000)apply(mat, 1, mean)parApply(grappe, mat, 1, mean)parRapply(grappe, mat, mean)Quelle est l’instruction la plus rapide?
library(parallel)
coeurs <- detectCores()
coeurs
## [1] 4
grappe <- makeCluster(coeurs - 1)
grappe
## socket cluster with 3 nodes on host ‘localhost’
mat <- matrix(rnorm(1000*1000), nrow = 1000, ncol = 1000)
# Je vais d'abord vérifier que toutes les instructions retournent
# bien le même résultat.
moy1 <- rowMeans(mat)
moy2 <- .rowMeans(mat, m = 1000, n = 1000)
moy3 <- apply(mat, 1, mean)
moy4 <- parApply(grappe, mat, 1, mean) # calcul en parallèle
moy5 <- parRapply(grappe, mat, mean) # calcul en parallèle
all.equal(moy1, moy2)
all.equal(moy1, moy3)
all.equal(moy1, moy4)
all.equal(moy1, moy5)
# Oui, elles retournent toutes la même chose.
# Je compare maintenant les temps d'exécution
out_mark_q4 <- mark(
rowMeans = rowMeans(mat),
.rowMeans = .rowMeans(mat, m = 1000, n = 1000),
apply = apply(mat, 1, mean),
parApply = parApply(grappe, mat, 1, mean),
parRapply = parRapply(grappe, mat, mean)
)
# Sur mon ordinateur, le calcul en parallèle n'apporte pas de gain en temps d'exécution.
# J'obtiens ceci :
print_bench_mark(out_mark_q4)
## expression n_itr min median mem_alloc n_gc
## 1 rowMeans 230 2.07ms 2.12ms 7.86KB 0
## 2 .rowMeans 235 2.06ms 2.1ms 7.86KB 0
## 3 apply 7 30.48ms 31.32ms 19.19MB 8
## 4 parApply 2 67.16ms 68.02ms 19.34MB 6
## 5 parRapply 9 29.5ms 32.5ms 7.82MB 6
# Le calcul en parallèle, aussi bien avec parApply qu'avec parRapply, est
# plus lent que le apply dans cet exemple sur mon ordinateur. Si votre
# ordinateur possède plus de coeurs de calcul que le mien, vous obtenez
# peut-être des gains en temps (comparativement à apply) avec le calcul
# en parallèle.
# Réponse : Les deux instructions les plus rapides, selon la médiane des
# temps d'exécution, sont les appels aux fonctions rowMeans et .rowMeans.Nous allons effectuer une simulation Monte Carlo pour estimer la valeur du nombre \(\pi\) (exercice inspiré de la page Wikipédia sur les méthodes de Monte-Carlo).
Nous avons déjà appris que l’aire d’un cercle vaut \(\pi r^2\) où \(r\) est le rayon du cercle. Ainsi, si nous considérons un cercle de rayon 1, son aire vaut \(\pi\). Maintenant, si nous traçons ce cercle dans un plan cartésien et considérons seulement le premier quadrant du plan ( \(x \ge 0\) et \(y \ge 0\)), l’aire du quart-de-cercle se trouvant dans ce cadran vaut \(\pi/4\).
Il est possible d’estimer la valeur \(\pi/4\) en plaçant de façon aléatoire des points dans le carré unitaire (\(x \in [0,1], y \in [0,1]\)), puis en calculant la proportion de points ayant tombé à l’intérieur du quart-de-cercle. Plus grand est le nombre de points placés aléatoirement, plus cette proportion devrait se rapprocher de \(\pi/4\).
Pour tester si un point est tombé à l’intérieur du quart-de-cercle, rappelons-nous que l’équation d’un cercle centré à l’origine d’un plan cartésien est \(x^2 + y^2 = r^2\). Ici \(r = 1\), donc un point à l’intérieur du quart-de-cercle est un point pour lequel \(x^2 + y^2 \le 1\).
Pour simuler cette approximation en R, utilisons la fonction suivante :
proportion_dans_cercle <- function(n_ech = 100){
x <- runif(n_ech) # par défaut entre 0 et 1
y <- runif(n_ech)
sum(x^2 + y^2 <= 1)/n_ech
}
L’instruction suivante suivante lance la simulation avec génération
pseudo-aléatoire de 100 points (à cause de la valeur par défaut de
n_ech) :
prop1 <- proportion_dans_cercle()
Regardez le résultat que vous obtenez. Obtenez-vous une valeur près de \(\pi/4\) ?
Maintenant, pour obtenir une estimation encore plus précise, mais sans grossir la longueur des vecteurs de valeurs générées aléatoirement (utile par exemple si la mémoire de notre ordinateur ne nous permettait pas de créer des vecteurs plus gros), nous allons :
n_ech = 100),Le code suivant réalise ces étapes.
B <- 500
props <- numeric(length = B)
for (b in seq_len(B)) {
props[b] <- proportion_dans_cercle()
}
prop2 <- mean(props)
Est-ce que cette estimation se rapproche plus de \(\pi/4\) que la première ?
Les trois blocs de code R suivants effectuent la simulation Monte-Carlo que nous venons de décrire de différentes façons :
props1 <- numeric(length = B)
for (b in seq_len(B)) {
props1[b] <- proportion_dans_cercle()
}
replicateprops2 <- replicate(n = B, expr = proportion_dans_cercle())
replicateclusterExport(grappe, varlist = c("B", "n_coeurs", "proportion_dans_cercle"))
props3 <- unlist(
clusterEvalQ(
grappe,
replicate(n = B/n_coeurs, expr = proportion_dans_cercle())
)
)
Quel bloc de code est le plus rapide sur votre ordinateur
(B <- 500, n_coeurs = le nombre de coeurs
sur votre ordinateur - 1) ?
proportion_dans_cercle <- function(n_ech = 100){
x <- runif(n_ech) # par défaut entre 0 et 1
y <- runif(n_ech)
sum(x^2 + y^2 <= 1)/n_ech
}
# Étape 1 : Réalisation de la simulation en R
prop1 <- proportion_dans_cercle()
# Est-ce que cette proportion vaut approximativement pi/4 ?
pi/4
prop1
all.equal(pi/4, prop1)
# Oui, les valeurs sont assez rapprochées.
# Étape 2 : Réalisation d'une simulation Monte-Carlo en R
B <- 500
props <- numeric(length = B)
for (b in seq_len(B)) {
props[b] <- proportion_dans_cercle()
}
prop2 <- mean(props)
# Est-ce que cette estimation se rapproche plus de pi/4 que la première ?
pi/4
prop2
all.equal(pi/4, prop2)
# Oui!
# Étape 3 : Comparaison de temps d'exécution de 3 approches
B <- 500
n_coeurs <- coeurs - 1
# Méthode 1 : boucle for
props1 <- numeric(length = B)
for (b in seq_len(B)) {
props1[b] <- proportion_dans_cercle()
}
# Méthode 2 : fonction replicate
props2 <- replicate(n = B, expr = proportion_dans_cercle())
# Méthode 3 : fonction replicate avec calculs en parallèle
clusterExport(grappe, varlist = c("B", "n_coeurs", "proportion_dans_cercle"))
props3 <- unlist(
clusterEvalQ(
grappe,
replicate(n = B/n_coeurs, expr = proportion_dans_cercle())
)
)
# Vérification des résultats
all.equal(pi/4, mean(props1))
all.equal(pi/4, mean(props2))
all.equal(pi/4, mean(props3))
# Les trois approches arrivent approximativement à la valeur pi/4.
# Comparaison des temps d'exécution
out_mark_q5 <- mark(
boucle = for (b in seq_len(B)) {
props1[b] <- proportion_dans_cercle()
},
replicate = replicate(B, proportion_dans_cercle()),
parallel_replicate = unlist(
clusterEvalQ(
grappe,
replicate(n = B/n_coeurs, expr = proportion_dans_cercle())
)
),
check = FALSE
)
# J'obtiens :
print_bench_mark(out_mark_q5)
## expression n_itr min median mem_alloc n_gc
## 1 boucle 55 6.79ms 7.43ms 4.28MB 5
## 2 replicate 70 5.43ms 5.66ms 4.28MB 6
## 3 parallel_replicate 109 3.46ms 3.88ms 62.91KB 0
# Résultat : Ici, le calcul en parallèle est un peu plus rapide que les
# autres solutions sur mon ordinateur (utilisation de 3 coeurs logiques
# de calcul). Aussi, replicate est un peu plus rapide que la boucle.Soit la liste suivante :
liste <- list(e1 = 5, e2 = -4:4, e3 = "Bonjour")
Écrivez une boucle R qui permet de créer des copies des éléments de
liste, soit les sous-objets e1,
e2 et e3, directement dans l’environnement de
travail. Ces objets doivent conserver les mêmes noms que dans la
liste.
En quoi cette boucle diffère-t-elle de l’instruction suivante ?
attach(liste)
Quel problème pourrait survenir avec l’utilisation de cette boucle si nous ne vérifions pas avant de la soumettre quels sont les noms des éléments de la liste?
liste <- list(e1 = 5, e2 = -4:4, e3 = "Bonjour")
# Réponse :
for (i in seq_along(liste)) {
assign(names(liste)[i], liste[[i]])
}
# En quoi cette boucle diffère-t-elle de l'instruction suivante ?
attach(liste)
# La boucle ajoute des objets dans l'environnement de travail
# alors que le attach ajoute un environnement dans le chemin
# de recherche de R.
detach(liste)
# Danger : écraser sans s'en rendre compte des objets déjà présents
# dans l'environnement de travail et portant les mêmes noms.
# Exemple :
e1 <- "objet très important"
for (i in seq_along(liste)) {
assign(names(liste)[i], liste[[i]])
}
e1
## [1] 5Créez une boucle R permettant d’effectuer toutes les régressions
simples entre la variable réponse mpg du jeu de données
mtcars (du package datasets) et les autres
variables du jeu de données, prises une à la fois.
Je vous demande en fait d’écrire plusieurs versions de cette boucle, en respectant les contraintes suivantes :
as.formula,parse et eval,as.formula,
parse ou eval.str(mtcars)
## 'data.frame': 32 obs. of 11 variables:
## $ mpg : num 21 21 22.8 21.4 18.7 18.1 14.3 24.4 22.8 19.2 ...
## $ cyl : num 6 6 4 6 8 6 8 4 4 6 ...
## $ disp: num 160 160 108 258 360 ...
## $ hp : num 110 110 93 110 175 105 245 62 95 123 ...
## $ drat: num 3.9 3.9 3.85 3.08 3.15 2.76 3.21 3.69 3.92 3.92 ...
## $ wt : num 2.62 2.88 2.32 3.21 3.44 ...
## $ qsec: num 16.5 17 18.6 19.4 17 ...
## $ vs : num 0 0 1 1 0 1 0 1 1 1 ...
## $ am : num 1 1 1 0 0 0 0 0 0 0 ...
## $ gear: num 4 4 4 3 3 3 3 4 4 4 ...
## $ carb: num 4 4 1 1 2 1 4 2 2 4 ...
yname <- colnames(mtcars)[1]
xnames <- colnames(mtcars)[-1]
# a) utiliser la fonction `as.formula`,
for (i in seq_along(xnames)) {
assign(
paste0("reg_a", i),
lm(as.formula(paste(yname, "~", xnames[i])), data = mtcars)
)
}
# Exemple d'objet obtenu
reg_a1
## Call:
## lm(formula = as.formula(paste(yname, "~", xnames[i])), data = mtcars)
##
## Coefficients:
## (Intercept) cyl
## 37.885 -2.876
# b) utiliser les fonctions `parse` et `eval`,
for (i in seq_along(xnames)) {
instruction <- paste0("reg_b", i, " <- lm(", yname, " ~ ", xnames[i], ", data = mtcars)")
print(instruction) # seulement pour mieux comprendre
eval(parse(text = instruction))
}
# Exemple d'objet obtenu
reg_b1
## Call:
## lm(formula = mpg ~ cyl, data = mtcars)
##
## Coefficients:
## (Intercept) cyl
## 37.885 -2.876
# c) ne pas utiliser les fonctions `as.formula`, `parse` ou `eval`.
for (i in 2:ncol(mtcars)) {
assign(paste0("reg_c", i - 1), lm(mtcars[, 1] ~ mtcars[, i]))
}
# Exemple d'objet obtenu
reg_c1
## Call:
## lm(formula = mtcars[, 1] ~ mtcars[, i])
##
## Coefficients:
## (Intercept) mtcars[, i]
## 37.885 -2.876
# Les trois solutions ajustent les mêmes modèles,
# mais certains éléments dans les sorties produites
# peuvent différer, comme les noms des variables et
# l'allure des appels (Call) à la fonction lm.Supposons que vous possédez un objet contenant le nom d’une fonction,
par exemple
transfo <- "log"
ou
transfo <- "exp"
Vous voulez appeler la fonction portant ce nom, en lui donnant en
entrée l’argument x = iris$Sepal.Length. Comment
pouvez-vous procéder? Vous devez obligatoirement utiliser l’objet
transfo, vous ne pouvez donc pas écrire directement
l’instruction comme suit
log(x = iris$Sepal.Length)
ou
exp(x = iris$Sepal.Length)
Programmez différentes stratégies :
get,do.call,call et eval.parse et eval.transfo <- "log"
# ou
transfo <- "exp"
# (a) get
fct <- get(transfo)
sol_a <- fct(x = iris$Sepal.Length)
# ou directement
sol_a <- get(transfo)(x = iris$Sepal.Length)
# (b) do.call
sol_b <- do.call(transfo, args = list(x = iris$Sepal.Length))
# (c) call et eval
sol_c <- eval(call(transfo, x = iris$Sepal.Length))
# (d) parse et eval
sol_d <- eval(parse(text = paste0(transfo, "(x = iris$Sepal.Length)")))
# Comparaison des vecteurs obtenus
identical(sol_a, sol_b)
## [1] TRUE
identical(sol_a, sol_c)
## [1] TRUE
identical(sol_a, sol_d)
## [1] TRUE
# tous identiquesCréez une fonction R qui calcule les sommes et les différences élément par élément entre deux vecteurs. Cette fonction doit retourner une liste de deux éléments, le premier contenant les sommes, le second les différences. Ces éléments doivent cependant porter des noms créés en fonction de l’appel de la fonction, comme dans les exemples suivants.
addition_soustraction(v1 = 1:5, v2 = 6:10)
## $`1:5 + 6:10`
## [1] 7 9 11 13 15
##
## $`1:5 - 6:10`
## [1] -5 -5 -5 -5 -5
x <- 1:5
y <- 6:10
addition_soustraction(v1 = x, v2 = y)
## $`x + y`
## [1] 7 9 11 13 15
##
## $`x - y`
## [1] -5 -5 -5 -5 -5
addition_soustraction <- function(v1, v2){
v1name <- deparse(substitute(v1))
v2name <- deparse(substitute(v2))
out <- list(v1 + v2, v1 - v2)
names(out) <- paste(v1name, c("+", "-"), v2name)
return(out)
}
# ou
addition_soustraction <- function(v1, v2){
call <- match.call()
v1name <- deparse(call$v1)
v2name <- deparse(call$v2)
out <- list(v1 + v2, v1 - v2)
names(out) <- paste(v1name, c("+", "-"), v2name)
return(out)
}
addition_soustraction(v1 = 1:5, v2 = 6:10)
## $`1:5 + 6:10`
## [1] 7 9 11 13 15
##
## $`1:5 - 6:10`
## [1] -5 -5 -5 -5 -5
x <- 1:5
y <- 6:10
addition_soustraction(v1 = x, v2 = y)
## $`x + y`
## [1] 7 9 11 13 15
##
## $`x - y`
## [1] -5 -5 -5 -5 -5