web-dev-qa-db-fra.com

L'opérateur ternaire existe-t-il dans R?

Comme le demande la question, existe-t-il une séquence de contrôle dans R similaire à C opérateur ternaire ? Si oui, comment l'utilisez-vous? Merci!

159
eykanal

Comme if est fonctionnel dans R et renvoie la dernière évaluation, if-else est équivalent à ?:.

> a <- 1
> x <- if(a==1) 1 else 2
> x
[1] 1
> x <- if(a==2) 1 else 2
> x
[1] 2

La puissance de R est la vectorisation. La vectorisation de l'opérateur ternaire est ifelse:

> a <- c(1, 2, 1)
> x <- ifelse(a==1, 1, 2)
> x
[1] 1 2 1
> x <- ifelse(a==2, 1, 2)
> x
[1] 2 1 2

Je plaisante, vous pouvez définir c-style ?::

`?` <- function(x, y)
    eval(
      sapply(
        strsplit(
          deparse(substitute(y)), 
          ":"
      ), 
      function(e) parse(text = e)
    )[[2 - as.logical(x)]])

ici, vous n'avez pas besoin de vous soucier des supports:

> 1 ? 2*3 : 4
[1] 6
> 0 ? 2*3 : 4
[1] 4
> TRUE ? x*2 : 0
[1] 2
> FALSE ? x*2 : 0
[1] 0

mais vous avez besoin de crochets pour l'affectation :(

> y <- 1 ? 2*3 : 4
[1] 6
> y
[1] 1
> y <- (1 ? 2*3 : 4)
> y
[1] 6

Enfin, vous pouvez faire de manière très similaire avec c:

`?` <- function(x, y) {
  xs <- as.list(substitute(x))
  if (xs[[1]] == as.name("<-")) x <- eval(xs[[3]])
  r <- eval(sapply(strsplit(deparse(substitute(y)), ":"), function(e) parse(text = e))[[2 - as.logical(x)]])
  if (xs[[1]] == as.name("<-")) {
    xs[[3]] <- r
        eval.parent(as.call(xs))
  } else {
    r
  }
}       

Vous pouvez vous débarrasser des crochets:

> y <- 1 ? 2*3 : 4
> y
[1] 6
> y <- 0 ? 2*3 : 4
> y
[1] 4
> 1 ? 2*3 : 4
[1] 6
> 0 ? 2*3 : 4
[1] 4

Ce ne sont pas pour une utilisation quotidienne, mais peut-être bon pour apprendre certains internes du langage R.

274
kohske

Comme tout le monde le dit, utilisez ifelse, mais vous pouvez définir des opérateurs de sorte que vous ayez presque la syntaxe d'opérateur ternaire.

`%?%` <- function(x, y) list(x = x, y = y)
`%:%` <- function(xy, z) if(xy$x) xy$y else z

TRUE %?% rnorm(5) %:% month.abb
## [1]  0.05363141 -0.42434567 -0.20000319  1.31049766 -0.31761248
FALSE %?% rnorm(5) %:% month.abb
## [1] "Jan" "Feb" "Mar" "Apr" "May" "Jun" "Jul" "Aug" "Sep" "Oct" "Nov" "Dec"
# or, more generally
condition %?% value1 %:% value2

Cela fonctionne réellement si vous définissez les opérateurs sans le % signes, vous pourriez donc avoir

`?` <- function(x, y) if(x) y[[1]] else y[[2]]
`:` <- function(y, z) list(y, z)

TRUE ? rnorm(5) : month.abb
## [1]  1.4584104143  0.0007500051 -0.7629123322  0.2433415442  0.0052823403
FALSE ? rnorm(5) : month.abb
## [1] "Jan" "Feb" "Mar" "Apr" "May" "Jun" "Jul" "Aug" "Sep" "Oct" "Nov" "Dec"

(Cela fonctionne car la précédence de : est inférieur à ?.)

Malheureusement, cela rompt ensuite les opérateurs d’aide et de séquence existants.

20
Richie Cotton

Juste comme une blague, vous pouvez redéfinissez le ? opérateur pour (presque) fonctionner comme l’opérateur ternaire (THIS IS Une mauvaise idée):

`?` <- function(x, y) { y <-substitute(y); if(x) eval(y[[2]], parent.frame()) else eval(y[[3]], parent.frame()) }

x <- 1:3
length(x) ? (x*2) : 0
x <- numeric(0)
length(x) ? (x*2) : 0

for(i in 1:5) cat(i, (i %% 2) ? "Odd\n" : "Even\n")

... Mais vous devez mettre les expressions entre parenthèses, car la priorité par défaut ne ressemble pas à C.

N'oubliez pas de restaurer l'ancienne fonction d'aide lorsque vous avez fini de jouer:

rm(`?`)
5
Tommy

Je voudrais jeter un oeil à la commande ifelse. Je l'appellerais encore mieux car il est également vectorisé. Un exemple utilisant le jeu de données cars:

> cars$speed > 20
 [1] FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE
[13] FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE
[25] FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE
[37] FALSE FALSE FALSE FALSE FALSE FALSE FALSE  TRUE  TRUE  TRUE  TRUE  TRUE
[49]  TRUE  TRUE

> ifelse(cars$speed > 20, 'fast', 'slow')
 [1] "slow" "slow" "slow" "slow" "slow" "slow" "slow" "slow" "slow" "slow"
[11] "slow" "slow" "slow" "slow" "slow" "slow" "slow" "slow" "slow" "slow"
[21] "slow" "slow" "slow" "slow" "slow" "slow" "slow" "slow" "slow" "slow"
[31] "slow" "slow" "slow" "slow" "slow" "slow" "slow" "slow" "slow" "slow"
[41] "slow" "slow" "slow" "fast" "fast" "fast" "fast" "fast" "fast" "fast"
4
Paul Hiemstra

Votre lien pointe vers une instruction if.

> x <- 1
> if(x < 2) print("Less than") else print("Greater than")
[1] "Less than"

Si votre variable d'entrée est un vecteur, alors ifelse pourrait être plus approprié:

> x <- 1:3
> ifelse(x<=2, "Less than or equal", "Greater than")
[1] "Less than or equal" "Less than or equal" "Greater than"   

Pour accéder à la page d'aide pour if, vous devez incorporer les if dans des backticks:

?`if`

La page d'aide pour ifelse est à:

`?ifelse`
4
Andrie

Cela n'existe pas explicitement, mais vous pouvez faire:

set.seed(21)
y <- 1:10
z <- rnorm(10)

condition1 <- TRUE
x1 <- if(condition1) y else z

ou

condition2 <- sample(c(TRUE,FALSE),10,TRUE)
x2 <- ifelse(condition2, y, z)

La différence entre les deux est que condition1 doit être un vecteur logique de longueur 1, tandis que condition2 doit être un vecteur logique de même longueur que x, y et z. Le premier retournera soit y ou z (l'objet entier), tandis que le second renverra l'élément correspondant de y (condition2==TRUE) ou z (condition2==FALSE).

Notez également que ifelse sera plus lent que if/else si condition, y et z sont tous vecteurs de longueur 1.

4
Joshua Ulrich

if fonctionne comme si ce n'était pas vectorisé si elle était utilisée de la manière suivante:

`if`(condition, doIfTrue, doIfFalse)

L’avantage d’utiliser ceci par rapport à ifelse, c’est lorsque la vectorisation est gênante (c’est-à-dire que j’ai des objets scalaires booléens et des listes/vecteurs).

ifelse(TRUE, c(1,2), c(3,4))
[1] 1
`if`(TRUE, c(1,2), c(3,4))
[1] 1 2
2
UpsideDownRide