논리 연산자 정리

operator logical Boolean R rlang

R의 논리 연산자에 대해 설명합니다.

true
2017-07-31

드디어 블로그에서 Rmd를 사용할 수 있게 되어서 매우 기쁜 마음에 포스팅을 하나 기획하였습니다. 그것은 바로 논리 연산자! 기본적은 것은 다 쉬운데 데이터가 여러 개인 벡터(vector)일 때 동작에 대해서 많이 고민하지 않는 것 같아 이렇게 첫 번째 주제로 잡았습니다.

논리 연산자란

논리 연산자(Logical Operators)란 논리 자료형(logical로 표시하며 T/F를 의미)를 사칙 연산처럼 TRUE / FALSE 를 계산하여 하나의 결과를 만드는 것이라고 할 수 있습니다. 대표적으로 if 구문에 해당하는 조건문에서 사용하는데요, 코드를 짤 때 조건문을 피할 수 없기 때문에 동작에 대해 잘 이해하고 있는 것이 좋습니다.

R에서 사용하는 논리 연산은 아래와 같습니다.

논리 연산자

연산자 설명
a < b a가 b보다 작다
a <= b a가 b보다 작거나 같다
a > b a가 b보다 크다
a >= b a가 b보다 크거나 같다
a == b a와 b가 같다
a != a와 b가 같지 않다
!x x가 아니다
x | y x이거나 y이다
x & y x이고 y이다
isTRUE(x) x가 TRUE이다

더하기, 빼기 같은 산술 연산자도 있으니 R에서 사용하는 연산자 소개는 이곳을 참고하면 좋을 것 같습니다. 위에서 강조한 바와 같이 위에 논리 연산자는 모두 데이터가 1개 일때를 의미합니다. 정확하게는 연산자의 왼쪽인 a는 여러 데이터인 벡터(vector)여도 되는데, 연산자의 오른쪽인 b는 한 개 여야만 합니다. 아니, 여러 개일 때 어떻게 동작하는지 알고 있어야 합니다.

우선 a가 1개일 때와 여러 개일 때를 확인해 보겠습니다.

논리 연산자 왼쪽의 데이터가 1개 일때

데이터가 1개인 경우는 매우 간단하고, 모두 상상하는 처음 예시이기도 합니다.

a <- 10
b <- 30

a < b
[1] TRUE
a <= b
[1] TRUE
a > b
[1] FALSE
a >= b
[1] FALSE
a == b
[1] FALSE
a != b
[1] TRUE

위에는 모두 상상하시는 대로 결과가 나왔으리라 생각합니다. 맨 아래만 짚어보면 =<-와 같은 뜻이어서, 수학적인 등호의 역할을 하길 기대해야 하는 기호는 ==입니다. ==왼쪽과 오른쪽이 같다라는 뜻입니다. 그리고 맨 위에 !xx가 아니다라는 설명을 해두었습니다. x는 논리 데이터 1개라는 설명도 했죠. 그러다 보니 !=!(a == b)와 의미가 같습니다. 작성하기 편하게 하기위해서 약어처럼 정의를 해둔 것이지요. 보통은 논리 자료형 앞에 붙어서 원래 결과의 부정을 뜻합니다. |&는 잘 아시리라 생각하고, isTRUE()를 좀 확인해 보겠습니다.

isTRUE()

R에서 몇 가지 요령이 있는데, ?함수이름, help(함수이름)이라고 하면 설명이 나오고, ()를 붙이지 않고 함수이름만 콘솔에 입력하면 그 함수를 구성하는 코드가 출력됩니다.

isTRUE
function (x) 
is.logical(x) && length(x) == 1L && !is.na(x) && x
<bytecode: 0x7fdc2f2d0710>
<environment: namespace:base>

isTRUE()가 어떻게 동작하는 함수인지 궁금해서 코드를 확인해 봤습니다. 여러개가 있지만 눈에 들어오는 것은 identical(TRUE, x)네요. 입력인자인 xTRUE와 같은 것인지를 확인하는 것입니다. identical()정확하게 같은지를 결과로 줍니다. 예를 들면 같은 숫자인데 자료형이 다르던가, RNA를 계산할 수 없음으로 취급해서 ==으로 확인하면 NA로 결과를 줍니다. 그래서 rstudio를 사용하시면 == NA를 인식해서 is.ns()를 사용하라고 경고를 주기도 합니다. 아래 코드를 봐주세요.

2L == 2
[1] TRUE
identical(2L,2)
[1] FALSE
10 == NA
[1] NA
10 != NA
[1] NA
10 > NA
[1] NA
identical(10,NA)
[1] FALSE

그래서 isTRUE(x)는 x가 TRUE와 완전히 같은지를 확인해서 같으면 TRUE, 다르면 FALSE를 결과로 주는 함수입니다. 조건문 안에 identical()은 익숙해지면 자주 사용하는 함수이니 확인해주세요.

논리 연산자 왼쪽의 데이터가 여러개 일때

데이터가 여러개라는 것은 벡터(vector)로 구성한다는 뜻입니다. 우선 결과를 확인해 보겠습니다. 일반적으로 벡터를 만들 때는 c()를 사용합니다. ?c로 설명서를 확인해 보세요.

a <- c(10,20,30,40,50)
b <- 30

a <  b
[1]  TRUE  TRUE FALSE FALSE FALSE
a <= b
[1]  TRUE  TRUE  TRUE FALSE FALSE
a > b
[1] FALSE FALSE FALSE  TRUE  TRUE
a >= b
[1] FALSE FALSE  TRUE  TRUE  TRUE
a == b
[1] FALSE FALSE  TRUE FALSE FALSE
a != b
[1]  TRUE  TRUE FALSE  TRUE  TRUE

결과를 확인해 보면 모두 a 데이터의 개수인 5개 만큼 결과 또한 출력된 것을 확인할 수 있습니다. 이것은 산술 연산자에서도 확인할 수 있는 동작인데요.

a <- c(10,20,30,40,50)
b <- 30

a + b
[1] 40 50 60 70 80
a - b
[1] -20 -10   0  10  20
a * b
[1]  300  600  900 1200 1500
a / b
[1] 0.3333333 0.6666667 1.0000000 1.3333333 1.6666667
a ** b
[1] 1.000000e+30 1.073742e+39 2.058911e+44 1.152922e+48 9.313226e+50
a %% b
[1] 10 20  0 10 20
a %/% b
[1] 0 0 1 1 1

이렇게 연산자의 왼쪽이 여러개의 데이터인 벡터이고, 오른쪽이 데이터 1개 일때(데이터가 1개 일때도 벡터라고 합니다.) 연산자는 왼쪽 데이터를 기준으로 각각 연산을 수행해서 왼쪽 데이터의 개수만큼 결과를 보여줍니다. 그렇다면, 논리 연산자 오른쪽의 데이터가 여러개면 어떻게 될까요? 예상되실 겁니다.

a <- 30
b <- c(10,20,30,40,50)

a <  b
[1] FALSE FALSE FALSE  TRUE  TRUE
a <= b
[1] FALSE FALSE  TRUE  TRUE  TRUE
a > b
[1]  TRUE  TRUE FALSE FALSE FALSE
a >= b
[1]  TRUE  TRUE  TRUE FALSE FALSE
a == b
[1] FALSE FALSE  TRUE FALSE FALSE
a != b
[1]  TRUE  TRUE FALSE  TRUE  TRUE

논리 연산자 데이터가 모두 여러개 일때

그럼 이제 마지막 경우를 남겨두고 있습니다. 양쪽 다 여러개의 데이터인 경우인데요, 이 경우에서도 데이터의 개수가 같은 경우와 다른 경우로 달라집니다.

데이터의 개수가 같은 경우

먼저 결과부터 보시죠

a <- c(10,20,30,40,50)
b <- c(1,2,3,4,5)

a <  b
[1] FALSE FALSE FALSE FALSE FALSE
a <= b
[1] FALSE FALSE FALSE FALSE FALSE
a > b
[1] TRUE TRUE TRUE TRUE TRUE
a >= b
[1] TRUE TRUE TRUE TRUE TRUE
a == b
[1] FALSE FALSE FALSE FALSE FALSE
a != b
[1] TRUE TRUE TRUE TRUE TRUE

개수가 같으니 결과도 같은 개수만큼 나왔고, 그 의미는 각각의 위치에 것들이 각각 연산되었다는 뜻입니다. |&도 같이 동작하는지 보시죠

x <- c(T,T,T)
y <- c(T,F,F)

x & y
[1]  TRUE FALSE FALSE
x | y
[1] TRUE TRUE TRUE

|&도 똑같이 결과가 데이터의 갯수와 같이 3개로 나오는 것을 볼 수 있습니다. 그런데, 논리 자료형의 연산 결과가 여러 개인 경우는 사실 조건문을 사용하는데 좋은 결과가 아닙니다. 그래서 논리 자료형의 연산 결과를 1개로 정리하는 방법들이 있습니다.

x <- c(T,T,T)
y <- c(T,F,F)

any(y)
[1] TRUE
all(y)
[1] FALSE
x && y
[1] TRUE
x || y
[1] TRUE

첫번째 방법은 any(), all()함수를 사용하는 방법입니다. 두 함수 모두 인자로 논리 자료형의 벡터를 사용하는데요, any()는 뭐라도 하나 TRUE라면 TRUE를 결과로 줍니다. any라는 이름에 걸맞죠. all()은 전부 TRUE여야 TRUE를 줍니다. 역시 all이라는 이름에 걸맞습니다. 여러개의 논리 데이터를 하나의 결과로 정리한다는 점에서 조건문에 사용하기 좋습니다.

그 아래 ||&&도 결과를 한개만 준다는 점에서는 같습니다만 연산 방식이 다릅니다. 이 두 연산자는 양쪽에 데이터가 여러개가 온다고 하더라도 첫번째 데이터만 사용합니다. 조건문의 ()에 논리 데이터가 여러개이면 경고를 주고 첫번째 데이터만 사용한다고 합니다. ||&&은 의도적으로 모두 첫번째 데이터만 연산에 사용한다는 것을 명시적으로 표현하는 것입니다. 한번 확인해 보겠습니다.

x <- c(T,T,T)
y <- c(T,F,F)
x && y
[1] TRUE
x || y
[1] TRUE
x <- c(F,T,T)
y <- c(T,F,F)
x && y
[1] FALSE
x || y
[1] TRUE
x <- c(T,T,T)
y <- c(F,F,F)
x && y
[1] FALSE
x || y
[1] TRUE
x <- c(F,T,T)
y <- c(F,F,F)
x && y
[1] FALSE
x || y
[1] FALSE

지금은 앞에만 달리하며 출력했는데, 뒤에 데이터를 변경해서 결과가 달라지지 않는 것을 직접 확인해 보시면 좋을 것 같습니다. 더 자세한 정보는 ?"|"로 확인하실 수 있습니다. 한 문장만 가져와서 보여드리면

The longer form evaluates left to right examining only the first element of each vector.

여기서 longer form||&&를 뜻합니다.

데이터의 개수가 다른 경우

데이터의 개수가 다른 경우는 또 두 가지 경우로 나뉩니다. 두 데이터의 개수가 배수 관계에 있는 경우와 아닌 경우입니다. 배수인 경우 먼저 보시죠

데이터의 개수가 배수 관계인 경우

a <- c(10,20,30,40,50,60)
b <- c(10,20,30)

a <  b
[1] FALSE FALSE FALSE FALSE FALSE FALSE
a <= b
[1]  TRUE  TRUE  TRUE FALSE FALSE FALSE
a > b
[1] FALSE FALSE FALSE  TRUE  TRUE  TRUE
a >= b
[1] TRUE TRUE TRUE TRUE TRUE TRUE
a == b
[1]  TRUE  TRUE  TRUE FALSE FALSE FALSE
a != b
[1] FALSE FALSE FALSE  TRUE  TRUE  TRUE

우선 결과가 경고 없이 잘 나왔고, 총 6개입니다. 이때는 개수가 부족한 쪽이 개수가 많은 쪽 만큼 한번 더 돌아서(Recycle) 연산에 사용된 것을 알 수 있습니다. 이것을 Recycling Rule이라고 합니다. 배수관계에 있다보니 특별히 잘못했다고 경고를 주지도 않습니다. 그럼 배수 관계가 아닐 떈 어떻게 될까요?

데이터의 개수가 배수 관계가 아닌 경우

a <- c(10,20,30,40,50)
b <- c(10,20,30)

a <  b
[1] FALSE FALSE FALSE FALSE FALSE
a <= b
[1]  TRUE  TRUE  TRUE FALSE FALSE
a > b
[1] FALSE FALSE FALSE  TRUE  TRUE
a >= b
[1] TRUE TRUE TRUE TRUE TRUE
a == b
[1]  TRUE  TRUE  TRUE FALSE FALSE
a != b
[1] FALSE FALSE FALSE  TRUE  TRUE

똑같이 Recycling Rule이 적용되어 적은 쪽이 많은 쪽 개수 만큼 한번 더 돌아서 사용하여 연산하지만, 쓰다 말기 때문에 경고를 줍니다. 배수관계가 아닌데 너가 지금 잘 사용한 게 맞는거냐라고 물어보는 거죠. 맞다면 무시하시면 됩니다. 결과도 긴 데이터쪽 개수만큼인 5개로 나왔네요. 의도하신거라면 이대로 진행하시면 됩니다.

이렇게 논리 연산자도 데이터가 여러개일 때 산술 연산자와 같은 동작을 취합니다. 이 부분이 아마 기초를 배운 후에 조합해서 확인해 봐야 하는 부분인 것 같습니다. 그리고 많이 실수하시는 부분이 있습니다. 논리 자료형이라면 특별히 문제되지 않는데, ==의 동작을 오해하세요. 사실 이걸 이야기하고 싶어서 위에 저 많은 얘기를 했는데요. 이미 주제 하나 정도 되는 것 같아 포스트를 나누겠습니다.

다음 포스트는 %in% 연산자를 사용하는가? feat.== 입니다.

끝까지 읽어주셔서 감사합니다. 질문, 지적 대환영이며 댓글로 부탁드립니다.

Corrections

If you see mistakes or want to suggest changes, please create an issue on the source repository.

Reuse

Text and figures are licensed under Creative Commons Attribution CC BY-NC-ND 4.0. Source code is available at https://github.com/mrchypark/mrchypark.github.io, unless otherwise noted. The figures that have been reused from other sources don't fall under this license and can be recognized by a note in their caption: "Figure from ...".

Citation

For attribution, please cite this work as

Park (2017, July 31). mrchypark: 논리 연산자 정리. Retrieved from https://mrchypark.github.io/post/논리-연산자-정리/

BibTeX citation

@misc{park2017논리,
  author = {Park, Chanyub},
  title = {mrchypark: 논리 연산자 정리},
  url = {https://mrchypark.github.io/post/논리-연산자-정리/},
  year = {2017}
}