10 탐색적 데이터 분석(Exploratory data analysis)
10.1 소개
이 장에서는 시각화와 변형을 사용하여 체계적으로 데이터를 탐색하는 방법을 보여줄 것입니다. 이는 통계학자들이 탐색적 데이터 분석(exploratory data analysis), 줄여서 EDA라고 부르는 작업입니다. EDA는 반복적인 주기입니다. 여러분은:
데이터에 대한 질문을 생성합니다.
데이터를 시각화, 변형, 모델링하여 답을 찾습니다.
배운 내용을 사용하여 질문을 다듬거나 새로운 질문을 생성합니다.
EDA는 엄격한 규칙이 있는 공식적인 프로세스가 아닙니다. 무엇보다 EDA는 마음가짐입니다. EDA의 초기 단계에서는 떠오르는 모든 아이디어를 자유롭게 조사해야 합니다. 이러한 아이디어 중 일부는 성공할 것이고 일부는 막다른 골목일 것입니다. 탐색이 계속됨에 따라 결국 작성하여 다른 사람들에게 전달하게 될 몇 가지 특히 생산적인 통찰력에 집중하게 될 것입니다.
EDA는 주요 연구 질문이 쟁반에 담겨 제공되더라도 모든 데이터 분석의 중요한 부분입니다. 항상 데이터의 품질을 조사해야 하기 때문입니다. 데이터 정리는 EDA의 한 가지 적용일 뿐입니다. 데이터가 기대치를 충족하는지 여부에 대한 질문을 합니다. 데이터 정리를 하려면 EDA의 모든 도구인 시각화, 변형, 모델링을 배포해야 합니다.
10.1.1 선수 지식
이 장에서는 dplyr과 ggplot2에 대해 배운 내용을 결합하여 대화식으로 질문하고, 데이터로 답하고, 새로운 질문을 할 것입니다.
10.2 질문
“일상적인 통계 질문은 없고, 의심스러운 통계 일상만 있다.” — 데이비드 콕스(Sir David Cox)
“종종 모호한 올바른 질문에 대한 근사적인 답이 항상 정확하게 만들 수 있는 잘못된 질문에 대한 정확한 답보다 훨씬 낫다.” — 존 튜키(John Tukey)
EDA 동안의 목표는 데이터에 대한 이해를 발전시키는 것입니다. 이를 수행하는 가장 쉬운 방법은 질문을 도구로 사용하여 조사를 안내하는 것입니다. 질문을 하면 질문은 데이터셋의 특정 부분에 주의를 집중시키고 어떤 그래프, 모델 또는 변형을 만들지 결정하는 데 도움이 됩니다.
EDA는 근본적으로 창의적인 프로세스입니다. 그리고 대부분의 창의적인 프로세스와 마찬가지로 양질의 질문을 하는 열쇠는 다량의 질문을 생성하는 것입니다. 분석 시작 시점에 통찰력 있는 질문을 하는 것은 어렵습니다. 데이터셋에서 어떤 통찰력을 얻을 수 있는지 모르기 때문입니다. 반면에 새로운 질문을 할 때마다 데이터의 새로운 측면에 노출되고 발견을 할 가능성이 높아집니다. 각 질문에 대해 찾은 내용을 바탕으로 새로운 질문을 후속 조치하면 데이터의 가장 흥미로운 부분을 빠르게 파고들어 생각할 거리를 주는 일련의 질문을 개발할 수 있습니다.
연구를 안내하기 위해 어떤 질문을 해야 하는지에 대한 규칙은 없습니다. 그러나 데이터 내에서 발견을 하기 위해 항상 유용한 두 가지 유형의 질문이 있습니다. 이 질문들을 대략적으로 다음과 같이 표현할 수 있습니다:
변수 내에서 어떤 유형의 변동(variation)이 발생하는가?
변수 간에 어떤 유형의 공변동(covariation)이 발생하는가?
이 장의 나머지 부분에서는 이 두 가지 질문을 살펴볼 것입니다. 변동과 공변동이 무엇인지 설명하고 각 질문에 답하는 여러 가지 방법을 보여줄 것입니다.
10.3 변동(Variation)
변동은 측정할 때마다 변수의 값이 변하는 경향입니다. 실생활에서 변동을 쉽게 볼 수 있습니다. 연속형 변수를 두 번 측정하면 두 가지 다른 결과를 얻게 됩니다. 빛의 속도와 같이 일정한 양을 측정하더라도 마찬가지입니다. 각 측정에는 측정할 때마다 달라지는 소량의 오차가 포함됩니다. 다른 대상(예: 다른 사람들의 눈 색깔)이나 다른 시간(예: 다른 순간의 전자의 에너지 준위)에 걸쳐 측정하는 경우에도 변수가 달라질 수 있습니다. 모든 변수에는 고유한 변동 패턴이 있으며, 이는 동일한 관측값에 대한 측정 간에 그리고 관측값 전반에 걸쳐 어떻게 변하는지에 대한 흥미로운 정보를 드러낼 수 있습니다. 그 패턴을 이해하는 가장 좋은 방법은 변수 값의 분포를 시각화하는 것인데, 이는 Chapter 1 에서 배웠습니다.
diamonds 데이터셋에 있는 약 54,000개의 다이아몬드 무게(carat) 분포를 시각화하여 탐색을 시작하겠습니다. carat은 수치형 변수이므로 히스토그램을 사용할 수 있습니다:
ggplot(diamonds, aes(x = carat)) +
geom_histogram(binwidth = 0.5)이제 변동을 시각화할 수 있으므로 플롯에서 무엇을 찾아야 할까요? 그리고 어떤 유형의 후속 질문을 해야 할까요? 아래에 그래프에서 찾을 수 있는 가장 유용한 유형의 정보 목록과 각 유형의 정보에 대한 몇 가지 후속 질문을 정리했습니다. 좋은 후속 질문을 하는 열쇠는 호기심(무엇을 더 배우고 싶은가?)과 회의주의(이것이 어떻게 오해를 불러일으킬 수 있는가?)에 의존하는 것입니다.
10.3.1 전형적인 값
막대 차트와 히스토그램 모두에서 키가 큰 막대는 변수의 일반적인 값을 보여주고 짧은 막대는 덜 일반적인 값을 보여줍니다. 막대가 없는 곳은 데이터에서 보이지 않는 값을 나타냅니다. 이 정보를 유용한 질문으로 바꾸려면 예상치 못한 것을 찾으세요:
가장 흔한 값은 무엇입니까? 왜 그렇습니까?
드문 값은 무엇입니까? 왜 그렇습니까? 그것이 당신의 기대와 일치합니까?
특이한 패턴을 볼 수 있습니까? 무엇이 그것들을 설명할 수 있습니까?
더 작은 다이아몬드에 대한 carat 분포를 살펴보겠습니다.
smaller <- diamonds |>
filter(carat < 3)
ggplot(smaller, aes(x = carat)) +
geom_histogram(binwidth = 0.01)이 히스토그램은 몇 가지 흥미로운 질문을 제기합니다:
왜 정수 캐럿과 일반적인 캐럿 분수에 더 많은 다이아몬드가 있습니까?
왜 각 봉우리의 약간 오른쪽에는 다이아몬드가 더 많고 각 봉우리의 약간 왼쪽에는 더 적습니까?
시각화는 또한 클러스터를 드러낼 수 있으며, 이는 데이터에 하위 그룹이 존재함을 시사합니다. 하위 그룹을 이해하려면 다음을 질문하세요:
각 하위 그룹 내의 관측값은 서로 어떻게 유사합니까?
별도의 클러스터에 있는 관측값은 서로 어떻게 다릅니까?
클러스터를 어떻게 설명하거나 묘사할 수 있습니까?
클러스터의 모양이 오해를 불러일으킬 수 있는 이유는 무엇입니까?
이러한 질문 중 일부는 데이터로 답할 수 있는 반면 일부는 데이터에 대한 도메인 전문 지식이 필요합니다. 그 중 많은 질문은 변수 간의 관계를 탐색하도록 유도할 것입니다. 예를 들어 한 변수의 값이 다른 변수의 동작을 설명할 수 있는지 확인하는 것입니다. 곧 다루겠습니다.
10.3.2 특이한 값
이상치(Outliers)는 특이한 관측값입니다. 패턴에 맞지 않는 것처럼 보이는 데이터 포인트입니다. 때로는 이상치가 데이터 입력 오류이기도 하고, 때로는 단순히 이 데이터 수집에서 관찰된 극단적인 값이기도 하며, 다른 경우에는 중요한 새로운 발견을 암시하기도 합니다. 데이터가 많으면 히스토그램에서 이상치를 보기 어려울 때가 있습니다. 예를 들어 diamonds 데이터셋에서 y 변수의 분포를 가져와 보겠습니다. 이상치의 유일한 증거는 x축의 비정상적으로 넓은 한계입니다.
ggplot(diamonds, aes(x = y)) +
geom_histogram(binwidth = 0.5)일반적인 빈에는 너무 많은 관측값이 있어서 드문 빈은 매우 짧아 보기가 매우 어렵습니다(0을 뚫어지게 쳐다보면 뭔가 발견할 수도 있겠지만요). 특이한 값을 쉽게 볼 수 있도록 coord_cartesian()을 사용하여 y축의 작은 값으로 줌인해야 합니다:
ggplot(diamonds, aes(x = y)) +
geom_histogram(binwidth = 0.5) +
coord_cartesian(ylim = c(0, 50))coord_cartesian()에는 x축을 줌인해야 할 때를 위한 xlim() 인수도 있습니다. ggplot2에는 xlim() 및 ylim() 함수도 있는데, 이는 약간 다르게 작동합니다. 한계를 벗어나는 데이터를 버립니다.
이를 통해 0, ~30, ~60의 세 가지 특이한 값을 볼 수 있습니다. dplyr로 그것들을 뽑아냅니다:
unusual <- diamonds |>
filter(y < 3 | y > 20) |>
select(price, x, y, z) |>
arrange(y)
unusual
#> # A tibble: 9 × 4
#> price x y z
#> <int> <dbl> <dbl> <dbl>
#> 1 5139 0 0 0
#> 2 6381 0 0 0
#> 3 12800 0 0 0
#> 4 15686 0 0 0
#> 5 18034 0 0 0
#> 6 2130 0 0 0
#> 7 2130 0 0 0
#> 8 2075 5.15 31.8 5.12
#> 9 12210 8.09 58.9 8.06y 변수는 이 다이아몬드의 세 가지 치수 중 하나를 mm 단위로 측정합니다. 우리는 다이아몬드의 너비가 0mm일 수 없다는 것을 알고 있으므로 이 값들은 부정확해야 합니다. EDA를 수행함으로써 우리는 단순히 NA를 검색해서는 찾을 수 없었던 0으로 코딩된 결측 데이터를 발견했습니다. 앞으로는 오해의 소지가 있는 계산을 방지하기 위해 이 값들을 NA로 다시 코딩하는 것을 선택할 수 있습니다. 또한 32mm와 59mm의 측정값이 그럴듯하지 않다고 의심할 수도 있습니다. 그 다이아몬드들은 길이가 1인치가 넘지만 가격은 수십만 달러가 아니니까요!
이상치를 포함하고 제외하고 분석을 반복하는 것은 좋은 습관입니다. 결과에 미치는 영향이 미미하고 왜 거기에 있는지 파악할 수 없다면 생략하고 넘어가는 것이 합리적입니다. 그러나 결과에 상당한 영향을 미치는 경우 정당한 이유 없이 삭제해서는 안 됩니다. 원인(예: 데이터 입력 오류)을 파악하고 보고서에 제거했음을 공개해야 합니다.
10.3.3 연습문제
diamonds의x,y,z변수 각각의 분포를 탐색하세요. 무엇을 알게 되었습니까? 다이아몬드에 대해 생각하고 길이, 너비, 깊이인 치수를 어떻게 결정할 수 있을지 생각해 보세요.price의 분포를 탐색하세요. 특이하거나 놀라운 것을 발견했습니까? (힌트:binwidth에 대해 신중하게 생각하고 다양한 값을 시도해 보세요.)0.99 캐럿인 다이아몬드는 몇 개입니까? 1 캐럿인 것은 몇 개입니까? 차이의 원인은 무엇이라고 생각합니까?
히스토그램을 확대할 때
coord_cartesian()대xlim()또는ylim()을 비교하고 대조하세요.binwidth를 설정하지 않으면 어떻게 됩니까? 막대 절반만 보이도록 확대하려고 하면 어떻게 됩니까?
10.4 특이한 값
데이터셋에서 특이한 값을 발견했고 나머지 분석으로 넘어가고 싶다면 두 가지 옵션이 있습니다.
-
이상한 값이 있는 전체 행을 삭제합니다:
하나의 유효하지 않은 값이 해당 관측값의 다른 모든 값도 유효하지 않음을 의미하지는 않으므로 이 옵션은 권장하지 않습니다. 또한 품질이 낮은 데이터가 있는 경우 모든 변수에 이 접근 방식을 적용할 때쯤이면 데이터가 하나도 남지 않을 수 있습니다!
-
대신 특이한 값을 결측값으로 바꾸는 것을 권장합니다. 가장 쉬운 방법은
mutate()를 사용하여 변수를 수정된 사본으로 바꾸는 것입니다.if_else()함수를 사용하여 특이한 값을NA로 바꿀 수 있습니다:
결측값을 어디에 플롯해야 하는지 명확하지 않으므로 ggplot2는 플롯에 포함하지 않지만 제거되었다고 경고합니다:
ggplot(diamonds2, aes(x = x, y = y)) +
geom_point()
#> Warning: Removed 9 rows containing missing values or values outside the scale range
#> (`geom_point()`).경고를 억제하려면 na.rm = TRUE를 설정하세요:
ggplot(diamonds2, aes(x = x, y = y)) +
geom_point(na.rm = TRUE)어떤 때는 결측값이 있는 관측값이 기록된 값이 있는 관측값과 어떻게 다른지 이해하고 싶을 때가 있습니다. 예를 들어 nycflights13::flights1에서 dep_time 변수의 결측값은 항공편이 취소되었음을 나타냅니다. 따라서 취소된 항공편과 취소되지 않은 항공편의 예정된 출발 시간을 비교하고 싶을 수 있습니다. is.na()를 사용하여 dep_time이 누락되었는지 확인하여 새 변수를 만듦으로써 이를 수행할 수 있습니다.
그러나 취소된 항공편보다 취소되지 않은 항공편이 훨씬 많기 때문에 이 플롯은 훌륭하지 않습니다. 다음 섹션에서는 이 비교를 개선하기 위한 몇 가지 기술을 살펴볼 것입니다.
10.4.1 연습문제
10.5 공변동(Covariation)
변동이 변수 내의 동작을 설명한다면, 공변동은 변수 간의 동작을 설명합니다. 공변동은 두 개 이상의 변수 값이 관련된 방식으로 함께 변하는 경향입니다. 공변동을 발견하는 가장 좋은 방법은 두 개 이상의 변수 간의 관계를 시각화하는 것입니다.
10.5.1 범주형 변수와 수치형 변수
예를 들어 geom_freqpoly()를 사용하여 다이아몬드의 가격이 품질(cut으로 측정)에 따라 어떻게 변하는지 살펴보겠습니다:
ggplot(diamonds, aes(x = price)) +
geom_freqpoly(aes(color = cut), binwidth = 500, linewidth = 0.75)ggplot2는 cut이 데이터에서 순서형 팩터 변수로 정의되어 있기 때문에 cut에 대해 순서가 있는 색상 척도를 사용한다는 점에 유의하세요. 이것들에 대해서는 Section 16.6 에서 더 자세히 배울 것입니다.
geom_freqpoly()의 기본 모양은 여기서는 별로 유용하지 않습니다. 전체 개수에 의해 결정되는 높이가 cut마다 너무 다르기 때문에 분포 모양의 차이를 보기 어렵기 때문입니다.
비교를 더 쉽게 하려면 y축에 표시되는 것을 바꿔야 합니다. 개수를 표시하는 대신, 각 빈도 다각형 아래의 면적이 1이 되도록 개수를 표준화한 밀도(density) 를 표시할 것입니다.
ggplot(diamonds, aes(x = price, y = after_stat(density))) +
geom_freqpoly(aes(color = cut), binwidth = 500, linewidth = 0.75)밀도를 y에 매핑하고 있지만 density는 diamonds 데이터셋의 변수가 아니므로 먼저 계산해야 합니다. 그렇게 하기 위해 after_stat() 함수를 사용합니다.
이 플롯에는 다소 놀라운 점이 있습니다. (가장 낮은 품질인) Fair 다이아몬드의 평균 가격이 가장 높은 것 같습니다! 하지만 빈도 다각형은 해석하기가 조금 어려워서 그럴 수도 있습니다. 이 플롯에는 많은 내용이 들어 있습니다.
이 관계를 탐색하기 위해 시각적으로 더 단순한 플롯은 나란히 놓인 상자 그림을 사용하는 것입니다.
ggplot(diamonds, aes(x = cut, y = price)) +
geom_boxplot()분포에 대한 정보는 훨씬 적게 보이지만 상자 그림은 훨씬 더 간결하여 비교하기 쉽습니다(그리고 하나의 플롯에 더 많이 맞출 수 있습니다). 이는 더 좋은 품질의 다이아몬드가 일반적으로 더 저렴하다는 직관에 반하는 발견을 뒷받침합니다! 연습문제에서 그 이유를 알아내도록 도전하게 될 것입니다.
cut은 순서형 팩터입니다. Fair는 Good보다 나쁘고, Good은 Very Good보다 나쁘고 등등입니다. 많은 범주형 변수에는 이러한 내재적 순서가 없으므로 더 유익한 디스플레이를 만들기 위해 순서를 변경하고 싶을 수 있습니다. 그렇게 하는 한 가지 방법은 fct_reorder()를 사용하는 것입니다. Section 16.4 에서 그 함수에 대해 더 자세히 배우겠지만, 매우 유용하기 때문에 여기서는 간단히 미리보기를 제공하고자 합니다. 예를 들어 mpg 데이터셋의 class 변수를 가져와 보겠습니다. 고속도로 연비가 클래스에 따라 어떻게 달라지는지 알고 싶을 수 있습니다:
ggplot(mpg, aes(x = class, y = hwy)) +
geom_boxplot()추세를 더 쉽게 볼 수 있도록 hwy의 중앙값을 기준으로 class를 재정렬할 수 있습니다:
ggplot(mpg, aes(x = fct_reorder(class, hwy, median), y = hwy)) +
geom_boxplot()변수 이름이 긴 경우 geom_boxplot()을 90도 뒤집으면 더 잘 작동합니다. x와 y 심미적 매핑을 교환하여 그렇게 할 수 있습니다.
ggplot(mpg, aes(x = hwy, y = fct_reorder(class, hwy, median))) +
geom_boxplot()10.5.1.1 연습문제
취소된 항공편과 취소되지 않은 항공편의 출발 시간 시각화를 개선하기 위해 배운 내용을 사용하세요.
EDA를 바탕으로 할 때 다이아몬드 가격을 예측하는 데 가장 중요한 것으로 보이는 diamonds 데이터셋의 변수는 무엇입니까? 그 변수는 컷과 어떤 상관관계가 있습니까? 그 두 관계의 조합이 왜 품질이 낮은 다이아몬드가 더 비싼 결과로 이어집니까?
x와 y 변수를 교환하는 대신 수직 상자 그림에 새 레이어로
coord_flip()을 추가하여 수평 상자 그림을 만드세요. 변수를 교환하는 것과 비교하면 어떻습니까?상자 그림의 한 가지 문제점은 훨씬 더 작은 데이터셋 시대에 개발되었으며 엄청나게 많은 수의 “이상치 값”을 표시하는 경향이 있다는 것입니다. 이 문제를 해결하기 위한 한 가지 접근 방식은 letter value plot입니다. lvplot 패키지를 설치하고
geom_lv()를 사용하여 가격 대 컷의 분포를 표시해 보세요. 무엇을 알게 되었습니까? 플롯을 어떻게 해석합니까?geom_violin(), 패싯된geom_histogram(), 색상이 지정된geom_freqpoly(), 색상이 지정된geom_density()를 사용하여diamonds데이터셋의 다이아몬드 가격 대 범주형 변수의 시각화를 만드세요. 네 가지 플롯을 비교하고 대조하세요. 범주형 변수의 수준에 따라 수치형 변수의 분포를 시각화하는 각 방법의 장단점은 무엇입니까?작은 데이터셋이 있는 경우 때때로
geom_jitter()를 사용하여 오버플로팅을 방지하고 연속형 변수와 범주형 변수 간의 관계를 더 쉽게 확인하는 것이 유용합니다. ggbeeswarm 패키지는geom_jitter()와 유사한 여러 메서드를 제공합니다. 그것들을 나열하고 각각이 무엇을 하는지 간략하게 설명하세요.
10.5.2 두 개의 범주형 변수
범주형 변수 간의 공변동을 시각화하려면 이러한 범주형 변수 수준의 각 조합에 대한 관측값 수를 세어야 합니다. 그렇게 하는 한 가지 방법은 내장된 geom_count()에 의존하는 것입니다:
ggplot(diamonds, aes(x = cut, y = color)) +
geom_count()플롯에 있는 각 원의 크기는 각 값 조합에서 얼마나 많은 관측값이 발생했는지를 나타냅니다. 공변동은 특정 x 값과 특정 y 값 사이의 강한 상관관계로 나타날 것입니다.
이러한 변수 간의 관계를 탐색하기 위한 또 다른 접근 방식은 dplyr로 개수를 계산하는 것입니다:
diamonds |>
count(color, cut)
#> # A tibble: 35 × 3
#> color cut n
#> <ord> <ord> <int>
#> 1 D Fair 163
#> 2 D Good 662
#> 3 D Very Good 1513
#> 4 D Premium 1603
#> 5 D Ideal 2834
#> 6 E Fair 224
#> # ℹ 29 more rows그런 다음 geom_tile()과 채우기(fill) 심미성으로 시각화합니다:
범주형 변수에 순서가 없는 경우 흥미로운 패턴을 더 명확하게 드러내기 위해 seriation 패키지를 사용하여 행과 열을 동시에 재정렬할 수 있습니다. 더 큰 플롯의 경우 대화형 플롯을 생성하는 heatmaply 패키지를 사용해 볼 수 있습니다.
10.5.2.1 연습문제
색상 내의 컷 분포 또는 컷 내의 색상 분포를 더 명확하게 보여주기 위해 위의 카운트 데이터셋을 어떻게 재조정(rescale)할 수 있습니까?
색상이
x심미성에 매핑되고cut이fill심미성에 매핑된 경우 분할 막대 차트로 어떤 다른 데이터 통찰력을 얻을 수 있습니까? 각 세그먼트에 속하는 개수를 계산하세요.geom_tile()을 dplyr과 함께 사용하여 목적지 및 연중 월별로 평균 항공편 출발 지연이 어떻게 변하는지 탐색하세요. 플롯을 읽기 어렵게 만드는 것은 무엇입니까? 어떻게 개선할 수 있습니까?
10.5.3 두 개의 수치형 변수
두 수치형 변수 간의 공변동을 시각화하는 훌륭한 방법을 이미 보았습니다: geom_point()로 산점도를 그리는 것입니다. 점의 패턴으로 공변동을 볼 수 있습니다. 예를 들어 다이아몬드의 캐럿 크기와 가격 사이에 양의 관계를 볼 수 있습니다: 캐럿이 많은 다이아몬드일수록 가격이 높습니다. 관계는 지수적입니다.
ggplot(smaller, aes(x = carat, y = price)) +
geom_point()(이 섹션에서는 3캐럿보다 작은 다이아몬드 대부분에 집중하기 위해 smaller 데이터셋을 사용할 것입니다)
산점도는 데이터셋의 크기가 커질수록 덜 유용해집니다. 점들이 오버플로팅되기 시작하고 균일한 검은색 영역으로 쌓여 2차원 공간 전체의 데이터 밀도 차이를 판단하기 어렵게 만들고 추세를 파악하기 어렵게 만들기 때문입니다. 문제를 해결하는 한 가지 방법을 이미 보았습니다: alpha 심미성을 사용하여 투명도를 추가하는 것입니다.
ggplot(smaller, aes(x = carat, y = price)) +
geom_point(alpha = 1 / 100)하지만 투명도를 사용하는 것은 매우 큰 데이터셋의 경우 어려울 수 있습니다. 또 다른 해결책은 빈(bin)을 사용하는 것입니다. 이전에는 geom_histogram()과 geom_freqpoly()를 사용하여 1차원에서 비닝했습니다. 이제 geom_bin2d()와 geom_hex()를 사용하여 2차원에서 비닝하는 방법을 배울 것입니다.
geom_bin2d()와 geom_hex()는 좌표 평면을 2D 빈으로 나누고 채우기 색상을 사용하여 각 빈에 얼마나 많은 포인트가 속하는지 표시합니다. geom_bin2d()는 직사각형 빈을 만듭니다. geom_hex()는 육각형 빈을 만듭니다. geom_hex()를 사용하려면 hexbin 패키지를 설치해야 합니다.
또 다른 옵션은 하나의 연속형 변수를 범주형 변수처럼 작동하도록 비닝하는 것입니다. 그런 다음 배운 범주형 변수와 연속형 변수의 조합을 시각화하는 기술 중 하나를 사용할 수 있습니다. 예를 들어 carat을 비닝한 다음 각 그룹에 대해 상자 그림을 표시할 수 있습니다:
ggplot(smaller, aes(x = carat, y = price)) +
geom_boxplot(aes(group = cut_width(carat, 0.1)))
#> Warning: Orientation is not uniquely specified when both the x and y aesthetics are
#> continuous. Picking default orientation 'x'.위에서 사용된 cut_width(x, width)는 x를 너비가 width인 빈으로 나눕니다. 기본적으로 상자 그림은 관측값이 얼마나 많은지에 관계없이 대략 동일하게 보이므로(이상치 수 제외) 각 상자 그림이 다른 수의 포인트를 요약한다는 것을 알기 어렵습니다. 이를 보여주는 한 가지 방법은 varwidth = TRUE를 사용하여 상자 그림의 너비를 포인트 수에 비례하게 만드는 것입니다.
10.5.3.1 연습문제
조건부 분포를 상자 그림으로 요약하는 대신 빈도 다각형을 사용할 수 있습니다.
cut_width()대cut_number()를 사용할 때 고려해야 할 사항은 무엇입니까? 그것이carat과price의 2D 분포 시각화에 어떤 영향을 미칩니까?price로 파티션된carat의 분포를 시각화하세요.매우 큰 다이아몬드의 가격 분포는 작은 다이아몬드와 어떻게 비교됩니까? 예상대로입니까, 아니면 놀랍습니까?
배운 기술 중 두 가지를 결합하여 컷, 캐럿, 가격의 결합된 분포를 시각화하세요.
-
2차원 플롯은 1차원 플롯에서는 보이지 않는 이상치를 드러냅니다. 예를 들어 다음 플롯의 일부 점은 특이한
x와y값 조합을 가지고 있어x와y값을 개별적으로 조사했을 때는 정상으로 보일지라도 점들을 이상치로 만듭니다. 이 경우 비닝된 플롯보다 산점도가 더 나은 디스플레이인 이유는 무엇입니까?diamonds |> filter(x >= 4) |> ggplot(aes(x = x, y = y)) + geom_point() + coord_cartesian(xlim = c(4, 11), ylim = c(4, 11)) -
cut_width()로 동일한 너비의 상자를 만드는 대신cut_number()로 대략 동일한 수의 포인트를 포함하는 상자를 만들 수 있습니다. 이 접근 방식의 장단점은 무엇입니까?ggplot(smaller, aes(x = carat, y = price)) + geom_boxplot(aes(group = cut_number(carat, 20)))
10.6 패턴과 모델
두 변수 사이에 체계적인 관계가 존재하면 데이터에 패턴으로 나타날 것입니다. 패턴을 발견하면 자문해 보세요:
이 패턴이 우연(즉, 무작위 확률) 때문일 수 있습니까?
패턴이 암시하는 관계를 어떻게 설명할 수 있습니까?
패턴이 암시하는 관계는 얼마나 강력합니까?
어떤 다른 변수가 관계에 영향을 미칠 수 있습니까?
데이터의 개별 하위 그룹을 보면 관계가 변합니까?
데이터의 패턴은 관계에 대한 단서를 제공합니다. 즉, 공변동을 드러냅니다. 변동을 불확실성을 생성하는 현상으로 생각한다면, 공변동은 불확실성을 줄이는 현상입니다. 두 변수가 공변하면 한 변수의 값을 사용하여 두 번째 변수의 값을 더 잘 예측할 수 있습니다. 공변동이 인과 관계(특수한 경우) 때문이라면 한 변수의 값을 사용하여 두 번째 변수의 값을 제어할 수 있습니다.
모델은 데이터에서 패턴을 추출하기 위한 도구입니다. 예를 들어 diamonds 데이터를 고려해 보세요. 컷과 가격의 관계를 이해하기 어려운데, 그 이유는 컷과 캐럿, 그리고 캐럿과 가격이 밀접하게 관련되어 있기 때문입니다. 모델을 사용하여 가격과 캐럿 간의 매우 강한 관계를 제거하여 남은 미묘함을 탐색할 수 있습니다. 다음 코드는 carat에서 price를 예측하는 모델을 적합시킨 다음 잔차(예측값과 실제 값의 차이)를 계산합니다. 잔차는 캐럿의 효과가 제거된 후의 다이아몬드 가격에 대한 뷰를 제공합니다. price와 carat의 원시 값을 사용하는 대신 먼저 로그 변환하고 로그 변환된 값에 모델을 적합시킨다는 점에 유의하세요. 그런 다음 잔차를 지수화하여 원시 가격의 척도로 되돌립니다.
library(tidymodels)
#> Warning: package 'broom' was built under R version 4.5.2
#> Warning: package 'infer' was built under R version 4.5.2
#> Warning: package 'parsnip' was built under R version 4.5.2
diamonds <- diamonds |>
mutate(
log_price = log(price),
log_carat = log(carat)
)
diamonds_fit <- linear_reg() |>
fit(log_price ~ log_carat, data = diamonds)
diamonds_aug <- augment(diamonds_fit, new_data = diamonds) |>
mutate(.resid = exp(.resid))
ggplot(diamonds_aug, aes(x = carat, y = .resid)) +
geom_point()캐럿과 가격 사이의 강한 관계를 제거하고 나면 컷과 가격 사이의 관계에서 예상되는 것을 볼 수 있습니다: 크기에 비해 품질이 좋은 다이아몬드가 더 비쌉니다.
ggplot(diamonds_aug, aes(x = cut, y = .resid)) +
geom_boxplot()이 책에서는 모델링에 대해 논의하지 않는데, 모델이 무엇이고 어떻게 작동하는지 이해하는 것은 데이터 랭글링 및 프로그래밍 도구를 갖춘 후에 가장 쉽기 때문입니다.
10.7 요약
이 장에서는 데이터 내의 변동을 이해하는 데 도움이 되는 다양한 도구를 배웠습니다. 한 번에 단일 변수와 한 쌍의 변수와 함께 작동하는 기술을 보았습니다. 데이터에 수십 또는 수백 개의 변수가 있는 경우 이것이 고통스럽게 제한적으로 보일 수 있지만, 이것들은 다른 모든 기술이 구축되는 기초입니다.
다음 장에서는 결과를 소통하는 데 사용할 수 있는 도구에 초점을 맞출 것입니다.
함수(또는 데이터셋)가 어디에서 왔는지 명시해야 할 때는
package::function()또는package::dataset이라는 특별한 형식을 사용한다는 것을 기억하세요.↩︎