library(tidyverse)
#> Warning: package 'ggplot2' was built under R version 4.5.2
#> Warning: package 'readr' was built under R version 4.5.2
library(nycflights13)12 논리형 벡터
12.1 소개
이 장에서는 논리형 벡터(logical vectors)로 작업하는 도구를 배울 것입니다. 논리형 벡터는 각 요소가 TRUE, FALSE, NA의 세 가지 가능한 값 중 하나만 될 수 있기 때문에 가장 단순한 유형의 벡터입니다. 원시 데이터에서 논리형 벡터를 찾는 것은 상대적으로 드물지만, 거의 모든 분석 과정에서 논리형 벡터를 생성하고 조작하게 될 것입니다.
숫자 비교를 통해 논리형 벡터를 만드는 가장 일반적인 방법에 대해 논의하는 것으로 시작하겠습니다. 그런 다음 부울 대수(Boolean algebra)를 사용하여 서로 다른 논리형 벡터를 결합하는 방법과 유용한 요약에 대해 배울 것입니다. 논리형 벡터로 구동되는 조건부 변경을 수행하기 위한 두 가지 유용한 함수인 if_else()와 case_when()으로 마무리할 것입니다.
12.1.1 선수 지식
이 장에서 배울 대부분의 함수는 기본(base) R에서 제공하므로 tidyverse가 필요하지 않지만, 데이터 프레임으로 작업하기 위해 mutate(), filter() 및 친구들을 사용할 수 있도록 tidyverse를 로드할 것입니다. 또한 nycflights13::flights 데이터셋에서 예제를 계속 가져올 것입니다.
그러나 더 많은 도구를 다루기 시작하면서 완벽한 실제 예제가 항상 있는 것은 아닙니다. 그래서 c()로 더미 데이터를 만들기 시작할 것입니다:
x <- c(1, 2, 3, 5, 7, 11, 13)
x * 2
#> [1] 2 4 6 10 14 22 26이렇게 하면 데이터 문제에 어떻게 적용될 수 있는지 보기 어렵게 만드는 대신 개별 함수를 설명하기가 더 쉬워집니다. 자유 부동(free-floating) 벡터에 수행하는 모든 조작은 mutate() 및 친구들을 사용하여 데이터 프레임 내부의 변수에 수행할 수 있다는 점을 기억하세요.
12.2 비교
논리형 벡터를 만드는 매우 일반적인 방법은 <, <=, >, >=, !=, ==를 사용한 숫자 비교를 통하는 것입니다. 지금까지 우리는 주로 filter() 내에서 논리형 변수를 일시적으로 생성했습니다. 계산되고, 사용된 다음, 버려졌습니다. 예를 들어 다음 필터는 대략 제시간에 도착하는 모든 주간 출발 항공편을 찾습니다:
flights |>
filter(dep_time > 600 & dep_time < 2000 & abs(arr_delay) < 20)
#> # A tibble: 172,286 × 19
#> year month day dep_time sched_dep_time dep_delay arr_time sched_arr_time
#> <int> <int> <int> <int> <int> <dbl> <int> <int>
#> 1 2013 1 1 601 600 1 844 850
#> 2 2013 1 1 602 610 -8 812 820
#> 3 2013 1 1 602 605 -3 821 805
#> 4 2013 1 1 606 610 -4 858 910
#> 5 2013 1 1 606 610 -4 837 845
#> 6 2013 1 1 607 607 0 858 915
#> # ℹ 172,280 more rows
#> # ℹ 11 more variables: arr_delay <dbl>, carrier <chr>, flight <int>, …이것이 단축키이며 mutate()로 기본 논리형 변수를 명시적으로 생성할 수 있다는 것을 아는 것이 유용합니다:
flights |>
mutate(
daytime = dep_time > 600 & dep_time < 2000,
approx_ontime = abs(arr_delay) < 20,
.keep = "used"
)
#> # A tibble: 336,776 × 4
#> dep_time arr_delay daytime approx_ontime
#> <int> <dbl> <lgl> <lgl>
#> 1 517 11 FALSE TRUE
#> 2 533 20 FALSE FALSE
#> 3 542 33 FALSE FALSE
#> 4 544 -18 FALSE TRUE
#> 5 554 -25 FALSE FALSE
#> 6 554 12 FALSE TRUE
#> # ℹ 336,770 more rows중간 단계의 이름을 지정하면 코드를 읽고 각 단계가 올바르게 계산되었는지 확인하기가 더 쉽기 때문에 더 복잡한 로직에 특히 유용합니다.
종합하면 초기 필터는 다음과 동일합니다:
12.2.1 부동 소수점 비교
숫자와 함께 ==를 사용할 때는 주의하세요. 예를 들어 이 벡터에는 숫자 1과 2가 포함된 것처럼 보입니다:
하지만 동일성을 테스트하면 FALSE를 얻습니다:
x == c(1, 2)
#> [1] FALSE FALSE무슨 일일까요? 컴퓨터는 고정된 소수점 자릿수로 숫자를 저장하므로 1/49 또는 sqrt(2)를 정확하게 표현할 방법이 없으며 후속 계산은 아주 약간 벗어나게 됩니다. digits1 인수로 print()를 호출하여 정확한 값을 볼 수 있습니다:
print(x, digits = 16)
#> [1] 0.9999999999999999 2.0000000000000004R이 왜 이 숫자를 반올림하는지 알 수 있습니다. 실제로는 예상한 것과 매우 가깝습니다.
이제 ==가 실패하는 이유를 알았으니 어떻게 해야 할까요? 한 가지 옵션은 작은 차이를 무시하는 dplyr::near()를 사용하는 것입니다:
12.2.2 결측값
결측값은 알 수 없음을 나타내므로 “전염성”이 있습니다. 알 수 없는 값과 관련된 거의 모든 연산도 알 수 없게 됩니다:
NA > 5
#> [1] NA
10 == NA
#> [1] NA가장 혼란스러운 결과는 이것입니다:
NA == NA
#> [1] NA맥락을 조금 더 인위적으로 제공하면 이것이 왜 사실인지 이해하기 가장 쉽습니다:
# 메리의 나이를 모릅니다
age_mary <- NA
# 존의 나이를 모릅니다
age_john <- NA
# 메리와 존은 동갑입니까?
age_mary == age_john
#> [1] NA
# 우리는 모릅니다!따라서 dep_time이 누락된 모든 항공편을 찾으려면 다음 코드는 작동하지 않습니다. dep_time == NA는 모든 단일 행에 대해 NA를 생성하고 filter()는 결측값을 자동으로 삭제하기 때문입니다:
flights |>
filter(dep_time == NA)
#> # A tibble: 0 × 19
#> # ℹ 19 variables: year <int>, month <int>, day <int>, dep_time <int>,
#> # sched_dep_time <int>, dep_delay <dbl>, arr_time <int>, …대신 새로운 도구인 is.na()가 필요합니다.
12.2.3 is.na()
is.na(x)는 모든 유형의 벡터와 작동하며 결측값에 대해서는 TRUE를 반환하고 그 외의 모든 것에 대해서는 FALSE를 반환합니다:
is.na()를 사용하여 dep_time이 누락된 모든 행을 찾을 수 있습니다:
flights |>
filter(is.na(dep_time))
#> # A tibble: 8,255 × 19
#> year month day dep_time sched_dep_time dep_delay arr_time sched_arr_time
#> <int> <int> <int> <int> <int> <dbl> <int> <int>
#> 1 2013 1 1 NA 1630 NA NA 1815
#> 2 2013 1 1 NA 1935 NA NA 2240
#> 3 2013 1 1 NA 1500 NA NA 1825
#> 4 2013 1 1 NA 600 NA NA 901
#> 5 2013 1 2 NA 1540 NA NA 1747
#> 6 2013 1 2 NA 1620 NA NA 1746
#> # ℹ 8,249 more rows
#> # ℹ 11 more variables: arr_delay <dbl>, carrier <chr>, flight <int>, …is.na()는 arrange()에서도 유용할 수 있습니다. arrange()는 일반적으로 모든 결측값을 끝에 배치하지만 먼저 is.na()로 정렬하여 이 기본값을 재정의할 수 있습니다:
flights |>
filter(month == 1, day == 1) |>
arrange(dep_time)
#> # A tibble: 842 × 19
#> year month day dep_time sched_dep_time dep_delay arr_time sched_arr_time
#> <int> <int> <int> <int> <int> <dbl> <int> <int>
#> 1 2013 1 1 517 515 2 830 819
#> 2 2013 1 1 533 529 4 850 830
#> 3 2013 1 1 542 540 2 923 850
#> 4 2013 1 1 544 545 -1 1004 1022
#> 5 2013 1 1 554 600 -6 812 837
#> 6 2013 1 1 554 558 -4 740 728
#> # ℹ 836 more rows
#> # ℹ 11 more variables: arr_delay <dbl>, carrier <chr>, flight <int>, …
flights |>
filter(month == 1, day == 1) |>
arrange(desc(is.na(dep_time)), dep_time)
#> # A tibble: 842 × 19
#> year month day dep_time sched_dep_time dep_delay arr_time sched_arr_time
#> <int> <int> <int> <int> <int> <dbl> <int> <int>
#> 1 2013 1 1 NA 1630 NA NA 1815
#> 2 2013 1 1 NA 1935 NA NA 2240
#> 3 2013 1 1 NA 1500 NA NA 1825
#> 4 2013 1 1 NA 600 NA NA 901
#> 5 2013 1 1 517 515 2 830 819
#> 6 2013 1 1 533 529 4 850 830
#> # ℹ 836 more rows
#> # ℹ 11 more variables: arr_delay <dbl>, carrier <chr>, flight <int>, …Chapter 18 에서 결측값을 더 깊이 다루기 위해 다시 돌아올 것입니다.
12.2.4 연습문제
-
dplyr::near()는 어떻게 작동합니까? 소스 코드를 보려면near를 입력하세요.sqrt(2)^2는 2에 가깝습니까? -
mutate(),is.na(),count()를 함께 사용하여dep_time,sched_dep_time,dep_delay의 결측값이 어떻게 연결되어 있는지 설명하세요.
12.3 부울 대수(Boolean algebra)
여러 논리형 벡터가 있으면 부울 대수를 사용하여 결합할 수 있습니다. R에서 &는 “그리고(and)”, |는 “또는(or)”, !는 “아님(not)”, xor()는 배타적 또는(exclusive or)2입니다. 예를 들어 df |> filter(!is.na(x))는 x가 누락되지 않은 모든 행을 찾고 df |> filter(x < -10 | x > 0)은 x가 -10보다 작거나 0보다 큰 모든 행을 찾습니다. Figure 12.1 는 일반적으로 사용되는 부울 연산의 예와 작동 방식을 보여줍니다.
x는 왼쪽 원, y는 오른쪽 원이며, 음영 처리된 영역은 각 연산자가 선택하는 부분을 보여줍니다.
& 및 | 외에도 R에는 && 및 ||도 있습니다. dplyr 함수에서 사용하지 마세요! 이를 단락(short-circuiting) 연산자라고 하며 단일 TRUE 또는 FALSE만 반환합니다. 이것들은 데이터 과학이 아니라 프로그래밍에 중요합니다.
12.3.1 결측값
부울 대수의 결측값 규칙은 언뜻 보기에 일관성이 없어 보이기 때문에 설명하기가 조금 까다롭습니다:
무슨 일이 일어나고 있는지 이해하려면 NA | TRUE(NA 또는 TRUE)에 대해 생각해 보세요. 논리형 벡터의 결측값은 값이 TRUE 또는 FALSE일 수 있음을 의미합니다. TRUE | TRUE와 FALSE | TRUE는 둘 중 하나가 적어도 TRUE이므로 둘 다 TRUE입니다. NA | TRUE 또한 NA가 TRUE 또는 FALSE일 수 있으므로 TRUE여야 합니다. 그러나 NA | FALSE는 NA가 TRUE인지 FALSE인지 모르기 때문에 NA입니다. 두 조건이 모두 충족되어야 한다는 점을 고려하면 &에도 유사한 추론이 적용됩니다. 따라서 NA & TRUE는 NA가 TRUE 또는 FALSE일 수 있으므로 NA이고, NA & FALSE는 조건 중 하나가 적어도 FALSE이므로 FALSE입니다.
12.3.2 연산 순서
연산 순서가 영어처럼 작동하지 않는다는 점에 유의하세요. 11월 또는 12월에 출발한 모든 항공편을 찾는 다음 코드를 살펴보세요:
flights |>
filter(month == 11 | month == 12)영어에서 말하는 것처럼 쓰고 싶을 수도 있습니다: “11월 또는 12월에 출발한 모든 항공편 찾기.”
flights |>
filter(month == 11 | 12)
#> # A tibble: 336,776 × 19
#> year month day dep_time sched_dep_time dep_delay arr_time sched_arr_time
#> <int> <int> <int> <int> <int> <dbl> <int> <int>
#> 1 2013 1 1 517 515 2 830 819
#> 2 2013 1 1 533 529 4 850 830
#> 3 2013 1 1 542 540 2 923 850
#> 4 2013 1 1 544 545 -1 1004 1022
#> 5 2013 1 1 554 600 -6 812 837
#> 6 2013 1 1 554 558 -4 740 728
#> # ℹ 336,770 more rows
#> # ℹ 11 more variables: arr_delay <dbl>, carrier <chr>, flight <int>, …이 코드는 오류가 발생하지 않지만 작동하지도 않는 것 같습니다. 무슨 일일까요? 여기서 R은 먼저 month == 11을 평가하여 nov라고 하는 논리형 벡터를 생성합니다. nov | 12를 계산합니다. 논리 연산자와 함께 숫자를 사용하면 0을 제외한 모든 것을 TRUE로 변환하므로 이는 nov | TRUE와 동일하며, 이는 항상 TRUE이므로 모든 행이 선택됩니다:
flights |>
mutate(
nov = month == 11,
final = nov | 12,
.keep = "used"
)
#> # A tibble: 336,776 × 3
#> month nov final
#> <int> <lgl> <lgl>
#> 1 1 FALSE TRUE
#> 2 1 FALSE TRUE
#> 3 1 FALSE TRUE
#> 4 1 FALSE TRUE
#> 5 1 FALSE TRUE
#> 6 1 FALSE TRUE
#> # ℹ 336,770 more rows
12.3.3 %in%
==와 |를 올바른 순서로 가져오는 문제를 피하는 쉬운 방법은 %in%를 사용하는 것입니다. x %in% y는 x와 길이가 같은 논리형 벡터를 반환하며 x의 값이 y의 어디에든 있을 때마다 TRUE입니다.
따라서 11월과 12월의 모든 항공편을 찾으려면 다음과 같이 쓸 수 있습니다:
%in%은 NA에 대해 ==와 다른 규칙을 따릅니다. NA %in% NA는 TRUE이기 때문입니다.
이것은 유용한 단축키가 될 수 있습니다:
flights |>
filter(dep_time %in% c(NA, 0800))
#> # A tibble: 8,803 × 19
#> year month day dep_time sched_dep_time dep_delay arr_time sched_arr_time
#> <int> <int> <int> <int> <int> <dbl> <int> <int>
#> 1 2013 1 1 800 800 0 1022 1014
#> 2 2013 1 1 800 810 -10 949 955
#> 3 2013 1 1 NA 1630 NA NA 1815
#> 4 2013 1 1 NA 1935 NA NA 2240
#> 5 2013 1 1 NA 1500 NA NA 1825
#> 6 2013 1 1 NA 600 NA NA 901
#> # ℹ 8,797 more rows
#> # ℹ 11 more variables: arr_delay <dbl>, carrier <chr>, flight <int>, …12.3.4 연습문제
-
arr_delay는 누락되었지만dep_delay는 누락되지 않은 모든 항공편을 찾으세요.arr_time이나sched_arr_time은 누락되지 않았지만arr_delay는 누락된 모든 항공편을 찾으세요. -
dep_time이 누락된 항공편은 몇 개입니까? 이 행들에서 다른 어떤 변수들이 누락되었습니까? 이 행들은 무엇을 나타낼 수 있습니까? -
dep_time누락이 항공편 취소를 의미한다고 가정하고 일별 취소된 항공편 수를 살펴보세요. 패턴이 있습니까? 취소된 항공편 비율과 취소되지 않은 항공편의 평균 지연 사이에 연관성이 있습니까?
12.4 요약(Summaries)
다음 섹션에서는 논리형 벡터를 요약하는 데 유용한 몇 가지 기술을 설명합니다. 논리형 벡터와 구체적으로 작동하는 함수뿐만 아니라 숫자 벡터와 작동하는 함수도 사용할 수 있습니다.
12.4.1 논리형 요약
두 가지 주요 논리형 요약이 있습니다: any()와 all(). any(x)는 |와 동일합니다. x에 TRUE가 하나라도 있으면 TRUE를 반환합니다. all(x)는 &와 동일합니다. x의 모든 값이 TRUE인 경우에만 TRUE를 반환합니다. 대부분의 요약 함수와 마찬가지로 na.rm = TRUE로 결측값을 없앨 수 있습니다.
예를 들어 all()과 any()를 사용하여 모든 항공편이 출발 시 최대 1시간 지연되었는지 또는 도착 시 5시간 이상 지연된 항공편이 있는지 알아볼 수 있습니다. 그리고 group_by()를 사용하면 일별로 수행할 수 있습니다:
flights |>
group_by(year, month, day) |>
summarize(
all_delayed = all(dep_delay <= 60, na.rm = TRUE),
any_long_delay = any(arr_delay >= 300, na.rm = TRUE),
.groups = "drop"
)
#> # A tibble: 365 × 5
#> year month day all_delayed any_long_delay
#> <int> <int> <int> <lgl> <lgl>
#> 1 2013 1 1 FALSE TRUE
#> 2 2013 1 2 FALSE TRUE
#> 3 2013 1 3 FALSE FALSE
#> 4 2013 1 4 FALSE FALSE
#> 5 2013 1 5 FALSE TRUE
#> 6 2013 1 6 FALSE FALSE
#> # ℹ 359 more rows그러나 대부분의 경우 any()와 all()은 다소 조잡하며, 얼마나 많은 값이 TRUE 또는 FALSE인지에 대한 세부 정보를 얻을 수 있다면 좋을 것입니다. 그것은 우리를 숫자 요약으로 이끕니다.
12.4.2 논리형 벡터의 숫자 요약
숫자 맥락에서 논리형 벡터를 사용할 때 TRUE는 1이 되고 FALSE는 0이 됩니다. 이것은 sum()과 mean()을 논리형 벡터와 함께 사용할 때 매우 유용하게 만듭니다. sum(x)는 TRUE의 개수를 제공하고 mean(x)는 TRUE의 비율을 제공합니다(mean()은 단지 sum()을 length()로 나눈 것이기 때문입니다).
예를 들어 이를 통해 출발 시 최대 1시간 지연된 항공편의 비율과 도착 시 5시간 이상 지연된 항공편의 수를 볼 수 있습니다:
flights |>
group_by(year, month, day) |>
summarize(
proportion_delayed = mean(dep_delay <= 60, na.rm = TRUE),
count_long_delay = sum(arr_delay >= 300, na.rm = TRUE),
.groups = "drop"
)
#> # A tibble: 365 × 5
#> year month day proportion_delayed count_long_delay
#> <int> <int> <int> <dbl> <int>
#> 1 2013 1 1 0.939 3
#> 2 2013 1 2 0.914 3
#> 3 2013 1 3 0.941 0
#> 4 2013 1 4 0.953 0
#> 5 2013 1 5 0.964 1
#> 6 2013 1 6 0.959 0
#> # ℹ 359 more rows12.4.3 논리형 부분집합
요약에서 논리형 벡터를 사용하는 마지막 용도가 하나 있습니다. 논리형 벡터를 사용하여 단일 변수를 관심 있는 하위 집합으로 필터링할 수 있습니다. 이것은 기본(base) [ (부분집합이라고 발음) 연산자를 사용하며, 이에 대해서는 Section 27.2 에서 더 자세히 배울 것입니다.
실제로 지연된 항공편에 대한 평균 지연만 보고 싶다고 상상해 보세요. 그렇게 하는 한 가지 방법은 먼저 항공편을 필터링한 다음 평균 지연을 계산하는 것입니다:
flights |>
filter(arr_delay > 0) |>
group_by(year, month, day) |>
summarize(
behind = mean(arr_delay),
n = n(),
.groups = "drop"
)
#> # A tibble: 365 × 5
#> year month day behind n
#> <int> <int> <int> <dbl> <int>
#> 1 2013 1 1 32.5 461
#> 2 2013 1 2 32.0 535
#> 3 2013 1 3 27.7 460
#> 4 2013 1 4 28.3 297
#> 5 2013 1 5 22.6 238
#> 6 2013 1 6 24.4 381
#> # ℹ 359 more rows이것은 작동하지만 일찍 도착한 항공편에 대한 평균 지연도 계산하고 싶다면 어떻게 해야 할까요? 별도의 필터 단계를 수행한 다음 두 데이터 프레임을 결합하는 방법을 파악해야 합니다3. 대신 [를 사용하여 인라인 필터링을 수행할 수 있습니다: arr_delay[arr_delay > 0]은 양의 도착 지연만 산출합니다.
결과는 다음과 같습니다:
flights |>
group_by(year, month, day) |>
summarize(
behind = mean(arr_delay[arr_delay > 0], na.rm = TRUE),
ahead = mean(arr_delay[arr_delay < 0], na.rm = TRUE),
n = n(),
.groups = "drop"
)
#> # A tibble: 365 × 6
#> year month day behind ahead n
#> <int> <int> <int> <dbl> <dbl> <int>
#> 1 2013 1 1 32.5 -12.5 842
#> 2 2013 1 2 32.0 -14.3 943
#> 3 2013 1 3 27.7 -18.2 914
#> 4 2013 1 4 28.3 -17.0 915
#> 5 2013 1 5 22.6 -14.0 720
#> 6 2013 1 6 24.4 -13.6 832
#> # ℹ 359 more rows또한 그룹 크기의 차이에 유의하세요. 첫 번째 청크에서 n()은 일별 지연된 항공편 수를 제공합니다. 두 번째에서는 n()이 전체 항공편 수를 제공합니다.
12.4.4 연습문제
12.5 조건부 변환
논리형 벡터의 가장 강력한 기능 중 하나는 조건부 변환, 즉 조건 x에 대해 한 가지 작업을 수행하고 조건 y에 대해 다른 작업을 수행하는 데 사용하는 것입니다. 이를 위한 두 가지 중요한 도구는 if_else()와 case_when()입니다.
12.5.1 if_else()
조건이 TRUE일 때 한 값을 사용하고 FALSE일 때 다른 값을 사용하려면 dplyr::if_else()4를 사용할 수 있습니다. 항상 if_else()의 처음 세 인수를 사용합니다. 첫 번째 인수 condition은 논리형 벡터이고, 두 번째 true는 조건이 참일 때 출력을 제공하고, 세 번째 false는 조건이 거짓일 때 출력을 제공합니다.
숫자 벡터에 “+ve”(양수) 또는 “-ve”(음수)로 레이블을 지정하는 간단한 예제로 시작하겠습니다:
입력이 NA인 경우 사용될 선택적 네 번째 인수 missing이 있습니다:
if_else(x > 0, "+ve", "-ve", "???")
#> [1] "-ve" "-ve" "-ve" "-ve" "+ve" "+ve" "+ve" "???"true 및 false 인수에 벡터를 사용할 수도 있습니다. 예를 들어 이를 통해 abs()의 최소 구현을 만들 수 있습니다:
if_else(x < 0, -x, x)
#> [1] 3 2 1 0 1 2 3 NA지금까지 모든 인수는 동일한 벡터를 사용했지만 물론 섞어서 사용할 수 있습니다. 예를 들어 다음과 같이 coalesce()의 간단한 버전을 구현할 수 있습니다:
위의 레이블링 예제에서 작은 부적절함을 눈치챘을 수 있습니다. 0은 양수도 음수도 아닙니다. if_else()를 추가하여 이를 해결할 수 있습니다:
이것은 이미 읽기가 조금 어렵고 조건이 더 많으면 더 어려워질 것이라고 상상할 수 있습니다. 대신 dplyr::case_when()으로 전환할 수 있습니다.
12.5.2 case_when()
dplyr의 case_when()은 SQL의 CASE 문에서 영감을 받았으며 다양한 조건에 대해 다른 계산을 수행하는 유연한 방법을 제공합니다. 불행히도 tidyverse에서 사용할 다른 어떤 것과도 비슷해 보이지 않는 특별한 구문을 가지고 있습니다. condition ~ output과 같은 쌍을 취합니다. condition은 논리형 벡터여야 합니다. TRUE이면 output이 사용됩니다.
즉, 이전의 중첩된 if_else()를 다음과 같이 다시 만들 수 있습니다:
이것은 더 많은 코드이지만 더 명시적입니다.
case_when()이 어떻게 작동하는지 설명하기 위해 몇 가지 더 간단한 경우를 살펴보겠습니다. 일치하는 사례가 없으면 출력은 NA를 얻습니다:
case_when(
x < 0 ~ "-ve",
x > 0 ~ "+ve"
)
#> [1] "-ve" "-ve" "-ve" NA "+ve" "+ve" "+ve" NA“기본값”/포괄적(catch all) 값을 만들려면 .default를 사용하세요:
case_when(
x < 0 ~ "-ve",
x > 0 ~ "+ve",
.default = "???"
)
#> [1] "-ve" "-ve" "-ve" "???" "+ve" "+ve" "+ve" "???"그리고 여러 조건이 일치하면 첫 번째 조건만 사용됩니다:
case_when(
x > 0 ~ "+ve",
x > 2 ~ "big"
)
#> [1] NA NA NA NA "+ve" "+ve" "+ve" NAif_else()와 마찬가지로 ~의 양쪽에서 변수를 사용할 수 있으며 문제에 필요한 대로 변수를 섞어서 사용할 수 있습니다. 예를 들어 case_when()을 사용하여 도착 지연에 대해 사람이 읽을 수 있는 레이블을 제공할 수 있습니다:
flights |>
mutate(
status = case_when(
is.na(arr_delay) ~ "cancelled",
arr_delay < -30 ~ "very early",
arr_delay < -15 ~ "early",
abs(arr_delay) <= 15 ~ "on time",
arr_delay < 60 ~ "late",
arr_delay < Inf ~ "very late",
),
.keep = "used"
)
#> # A tibble: 336,776 × 2
#> arr_delay status
#> <dbl> <chr>
#> 1 11 on time
#> 2 20 late
#> 3 33 late
#> 4 -18 early
#> 5 -25 early
#> 6 12 on time
#> # ℹ 336,770 more rows이런 종류의 복잡한 case_when() 문을 작성할 때는 주의하세요. 저의 처음 두 번의 시도는 <와 >를 섞어 사용했고 계속해서 겹치는 조건을 실수로 만들었습니다.
12.5.3 호환 가능한 유형
if_else()와 case_when() 모두 출력에 호환 가능한(compatible) 유형이 필요합니다. 호환되지 않으면 다음과 같은 오류가 표시됩니다:
전반적으로 한 유형의 벡터를 다른 유형으로 자동 변환하는 것은 오류의 일반적인 원인이기 때문에 호환되는 유형은 상대적으로 적습니다. 다음은 호환되는 가장 중요한 사례입니다:
- Section 12.4.2 에서 논의했듯이 숫자형 벡터와 논리형 벡터는 호환됩니다.
- 문자열과 팩터(Chapter 16)는 호환됩니다. 팩터를 제한된 값 집합을 가진 문자열로 생각할 수 있기 때문입니다.
- Chapter 17 에서 논의할 날짜와 날짜-시간은 호환됩니다. 날짜를 날짜-시간의 특수한 경우로 생각할 수 있기 때문입니다.
- 기술적으로 논리형 벡터인
NA는 모든 것과 호환됩니다. 모든 벡터에는 결측값을 나타내는 방법이 있기 때문입니다.
이러한 규칙을 암기할 필요는 없지만 tidyverse 전체에 일관되게 적용되므로 시간이 지남에 따라 제2의 천성이 될 것입니다.
12.5.4 연습문제
숫자는 2로 나누어 떨어지면 짝수이며, R에서는
x %% 2 == 0으로 알 수 있습니다. 이 사실과if_else()를 사용하여 0에서 20 사이의 각 숫자가 짝수인지 홀수인지 확인하세요.x <- c("Monday", "Saturday", "Wednesday")와 같은 요일 벡터가 주어지면if_else()문을 사용하여 주말이나 평일로 레이블을 지정하세요.if_else()를 사용하여x라는 숫자 벡터의 절대값을 계산하세요.flights의month및day열을 사용하여 중요한 미국 공휴일(예: 새해 첫날, 7월 4일, 추수감사절, 크리스마스)에 레이블을 지정하는case_when()문을 작성하세요. 먼저TRUE또는FALSE인 논리형 열을 만든 다음 공휴일 이름을 제공하거나NA인 문자 열을 만드세요.
12.6 요약
논리형 벡터의 정의는 각 값이 TRUE, FALSE 또는 NA여야 하므로 간단합니다. 그러나 논리형 벡터는 엄청난 힘을 제공합니다. 이 장에서는 >, <, <=, >=, ==, !=, is.na()로 논리형 벡터를 만드는 방법, !, &, |로 결합하는 방법, any(), all(), sum(), mean()으로 요약하는 방법을 배웠습니다. 또한 논리형 벡터의 값에 따라 값을 반환할 수 있는 강력한 if_else() 및 case_when() 함수를 배웠습니다.
다음 장들에서 논리형 벡터를 계속해서 보게 될 것입니다. 예를 들어 Chapter 14 에서는 pattern과 일치하는 x 요소에 대해 TRUE인 논리형 벡터를 반환하는 str_detect(x, pattern)에 대해 배우고, Chapter 17 에서는 날짜와 시간의 비교에서 논리형 벡터를 생성할 것입니다. 하지만 지금은 다음으로 가장 중요한 유형의 벡터인 수치형 벡터로 넘어가겠습니다.
R은 일반적으로 사용자를 위해 print를 호출하지만(
x는print(x)의 단축키임), 다른 인수를 제공하려는 경우 명시적으로 호출하는 것이 유용합니다.↩︎즉,
xor(x, y)는 x가 참이거나 y가 참이지만 둘 다 참은 아닌 경우 참입니다. 이것이 우리가 일반적으로 영어에서 “or”를 사용하는 방식입니다. “아이스크림이나 케이크 드실래요?”라는 질문에 “둘 다”는 일반적으로 허용되는 대답이 아닙니다.↩︎Chapter 19 에서 다룰 것입니다.↩︎
dplyr의
if_else()는 기본 R의ifelse()와 매우 유사합니다.ifelse()보다if_else()의 두 가지 주요 장점이 있습니다: 결측값에 대해 어떤 일이 발생해야 하는지 선택할 수 있고, 변수 유형이 호환되지 않는 경우if_else()가 의미 있는 오류를 제공할 가능성이 훨씬 더 높습니다.↩︎