Apakah operator ternary ada di R?

175

Seperti yang ditanyakan, apakah ada urutan kontrol dalam R yang mirip dengan operator ternary C ? Jika demikian, bagaimana Anda menggunakannya? Terima kasih!

eykanal
sumber
1
Apakah Anda menginginkan sesuatu yang lebih kuat daripada ifelse, atau hanya bentuk yang lebih kompak?
Carl Witthoft
@CarlWitthoft Bentuk yang lebih ringkas; hanya cara menghemat tulisan if (x>1) y=2 else y=3. Menulis y=sekali memiliki daya tarik tertentu untuk itu.
eykanal

Jawaban:

302

Seperti ifberfungsi Rdan mengembalikan evaluasi terbaru, jika-lain setara dengan ?:.

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

Kekuatan R adalah vektorisasi. Vektorisasi operator ternary adalah 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

Hanya bercanda, Anda dapat mendefinisikan c-style ?::

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

di sini, Anda tidak perlu peduli tentang tanda kurung:

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

tetapi Anda membutuhkan tanda kurung untuk penugasan :(

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

Akhirnya, Anda dapat melakukan cara yang sangat mirip dengan 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
  }
}       

Anda dapat menyingkirkan tanda kurung:

> 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

Ini bukan untuk penggunaan sehari-hari, tetapi mungkin baik untuk mempelajari beberapa bahasa R internal.

kohske
sumber
23

Seperti kata orang lain, gunakan ifelse, tetapi Anda dapat mendefinisikan operator sehingga Anda hampir memiliki sintaksis operator ternary.

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

Ini benar-benar berfungsi jika Anda mendefinisikan operator tanpa %tanda, sehingga Anda dapat melakukannya

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

(Ini berfungsi karena diutamakan :lebih rendah daripada ?.)

Sayangnya, itu kemudian merusak bantuan dan urutan operator yang ada.

Richie Cotton
sumber
5

Sama seperti sebuah lelucon, Anda dapat mendefinisikan kembali ?operator untuk (hampir) bekerja seperti operator ternary (INI ADALAH BAD IDEA):

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

... Tapi Anda harus meletakkan ekspresi di dalam tanda kurung karena presedensi default tidak seperti dalam C.

Ingatlah untuk mengembalikan fungsi bantuan lama saat Anda selesai bermain:

rm(`?`)
Tommy
sumber
5

Saya akan melihat ifelseperintah. Saya akan menyebutnya lebih baik karena itu juga vektor. Contoh menggunakan dataset mobil:

> 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
sumber
4
Hai Paul - apakah Anda bermaksud menunjukkan sesuatu tentang ifelsecontoh Anda? ;)
Josh O'Brien
4

Tautan Anda menunjuk ke sebuah ifpernyataan.

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

Jika variabel input Anda adalah vektor, maka ifelsemungkin lebih cocok:

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

Untuk mengakses halaman bantuan if, Anda harus menyematkan ifbacktick di:

?`if`

Halaman bantuan untuk ifelseberada di:

`?ifelse`
Andrie
sumber
1
Seperti yang dikatakan @kohske, ini juga akan berhasil:print(if (x<2) "Less than" else "Greater than")
Ben Bolker
4

Tidak ada secara eksplisit, tetapi Anda dapat melakukannya:

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

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

atau

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

Perbedaan antara keduanya adalah bahwa condition1harus menjadi vektor logis dari panjang 1, sementara condition2harus menjadi vektor logis panjang yang sama dengan x, y, dan z. Yang pertama akan mengembalikan salah satu yatau z(seluruh objek), sedangkan yang kedua akan mengembalikan elemen yang sesuai dari y( condition2==TRUE) atau z( condition2==FALSE).

Juga catatan itu ifelseakan lebih lambat dari if/ elsejika condition, ydan zsemua vektor dengan panjang 1.

Joshua Ulrich
sumber
terima kasih Joshua, jawaban Anda banyak membantu, saya menemukan jawaban dari posting yang Anda sebutkan stackoverflow.com/a/8792474/3019570
Mahdi Jadaliha
2

if berfungsi seperti ifelse yang tidak di-revisi jika digunakan dengan cara berikut:

`if`(condition, doIfTrue, doIfFalse)

Keuntungan menggunakan ini lebih dari ifelse adalah ketika vektorisasi di jalan (yaitu saya punya skalar boolean dan daftar / hal vektor sebagai hasilnya)

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