Existiert der ternäre Operator in R?

175

Gibt es in R eine Kontrollsequenz, die dem ternären Operator von C ähnelt ? Wenn ja, wie verwenden Sie es? Vielen Dank!

Eykanal
quelle
1
Möchten Sie etwas Stärkeres als ifelseoder nur eine kompaktere Form?
Carl Witthoft
@CarlWitthoft Meist kompaktere Form; einfach ein Weg, um das Schreiben zu sparen if (x>1) y=2 else y=3. Das Schreiben hat y=einmal einen gewissen Reiz.
Eykanal

Antworten:

302

Wie ifist Funktion in Rund gibt die neueste Auswertung zurück, wenn-else gleichbedeutend ist mit ?:.

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

Die Potenz von R ist die Vektorisierung. Die Vektorisierung des ternären Operators lautet 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

Nur ein Scherz, Sie können C-Stil definieren ?::

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

Hier müssen Sie sich nicht um Klammern kümmern:

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

Für die Zuordnung benötigen Sie jedoch Klammern :(

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

Schließlich können Sie mit 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
  }
}       

Sie können Klammern loswerden:

> 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

Diese sind nicht für den täglichen Gebrauch gedacht, aber vielleicht gut, um einige Interna der R-Sprache zu lernen.

kohske
quelle
23

Wie alle anderen sagten, verwenden Sie ifelse, aber Sie können Operatoren so definieren, dass Sie fast die ternäre Operatorsyntax haben.

`%?%` <- 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

Es funktioniert tatsächlich, wenn Sie die Operatoren ohne die %Zeichen definieren, also könnten Sie haben

`?` <- 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"

(Dies funktioniert, weil die Priorität von :niedriger als ist ?.)

Leider bricht das dann die vorhandenen Hilfe- und Sequenzoperatoren.

Richie Cotton
quelle
5

So wie ein Streich, Sie können den neu definieren ?Operator (fast) Arbeit wie der ternäre Operator (Dies ist eine schlechte Idee):

`?` <- 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")

... Sie müssen die Ausdrücke jedoch in Klammern setzen, da die Standardpriorität nicht mit C übereinstimmt.

Denken Sie daran, die alte Hilfefunktion wiederherzustellen, wenn Sie mit dem Spielen fertig sind:

rm(`?`)
Tommy
quelle
5

Ich würde mir den ifelseBefehl ansehen . Ich würde es noch besser nennen, weil es auch vektorisiert ist. Ein Beispiel mit dem Autodatensatz:

> 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"
Paul Hiemstra
quelle
4
Hallo Paul - wolltest du ifelsemit deinem Beispiel etwas zeigen ? ;)
Josh O'Brien
4

Ihr Link verweist auf eine ifAussage.

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

Wenn Ihre Eingabevariable ein Vektor ist, ist dies ifelsemöglicherweise besser geeignet:

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

Um auf die Hilfeseite für zuzugreifen if, müssen Sie die folgenden ifBackticks einbetten :

?`if`

Die Hilfeseite für ifelseist unter:

`?ifelse`
Andrie
quelle
1
Wie @kohske sagte, wird dies auch funktionieren:print(if (x<2) "Less than" else "Greater than")
Ben Bolker
4

Es existiert nicht explizit, aber Sie können:

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

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

oder

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

Der Unterschied zwischen den beiden ist , daß condition1eine logische Vektor der Länge 1 sein muss, während condition2ein logischer Vektor die gleiche Länge wie sein muss x, yund z. Das erste gibt entweder yoder z(das gesamte Objekt) zurück, während das zweite das entsprechende Element von y( condition2==TRUE) oder z( condition2==FALSE) zurückgibt.

Beachten Sie auch, dass ifelsedies langsamer als if/ elseif condition,, yist und zalle Vektoren mit der Länge 1 sind.

Joshua Ulrich
quelle
danke Joshua, deine Antwort hat mir sehr geholfen. Ich habe die Antwort von dem Beitrag gefunden, den du erwähnt hast. stackoverflow.com/a/8792474/3019570
Mahdi Jadaliha
2

if funktioniert wie enthüllt, wenn es auf folgende Weise verwendet wird:

`if`(condition, doIfTrue, doIfFalse)

Der Vorteil der Verwendung gegenüber ifelse besteht darin, dass die Vektorisierung im Weg ist (dh ich habe skalare Boolesche Werte und Listen- / Vektorsachen als Ergebnis).

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