library(tidyverse)
#> Warning: package 'ggplot2' was built under R version 4.5.2
#> Warning: package 'readr' was built under R version 4.5.2
#> ── Attaching core tidyverse packages ───────────────────── tidyverse 2.0.0 ──
#> ✔ dplyr 1.1.4 ✔ readr 2.1.6
#> ✔ forcats 1.0.1 ✔ stringr 1.6.0
#> ✔ ggplot2 4.0.1 ✔ tibble 3.3.0
#> ✔ lubridate 1.9.4 ✔ tidyr 1.3.1
#> ✔ purrr 1.2.0
#> ── Conflicts ─────────────────────────────────────── tidyverse_conflicts() ──
#> ✖ dplyr::filter() masks stats::filter()
#> ✖ dplyr::lag() masks stats::lag()
#> ℹ Use the conflicted package (<http://conflicted.r-lib.org/>) to force all conflicts to become errors1 데이터 시각화
1.1 소개
“단순한 그래프는 다른 어떤 장치보다 더 많은 정보를 데이터 분석가의 마음에 전달해 왔다.” — John Tukey
R에는 그래프를 만들기 위한 여러 시스템이 있지만, ggplot2는 가장 우아하고 다재다능한 시스템 중 하나입니다. ggplot2는 그래프를 설명하고 구축하기 위한 일관된 시스템인 그래픽 문법(grammar of graphics) 을 구현합니다. ggplot2를 사용하면 하나의 시스템을 배워서 여러 곳에 적용함으로써 더 많은 일을 더 빠르게 할 수 있습니다.
이 장에서는 ggplot2를 사용하여 데이터를 시각화하는 방법을 알려줍니다. 간단한 산점도를 만드는 것으로 시작하여 ggplot2의 기본 구성 요소인 심미적 매핑(aesthetic mappings)과 기하학적 객체(geometric objects)를 소개하는 데 사용합니다. 그런 다음 단일 변수의 분포를 시각화하는 방법과 두 개 이상의 변수 간의 관계를 시각화하는 방법을 안내합니다. 마지막으로 플롯을 저장하는 방법과 문제 해결 팁으로 마무리합니다.
1.1.1 선수 지식
이 장은 tidyverse의 핵심 패키지 중 하나인 ggplot2에 중점을 둡니다. 이 장에서 사용되는 데이터셋, 도움말 페이지, 함수에 액세스하려면 다음을 실행하여 tidyverse를 로드하세요:
이 한 줄의 코드는 핵심 tidyverse를 로드합니다. 이는 거의 모든 데이터 분석에서 사용하게 될 패키지들입니다. 또한 tidyverse의 어떤 함수가 기본(base) R(또는 로드했을 수 있는 다른 패키지)의 함수와 충돌하는지 알려줍니다1.
이 코드를 실행했는데 there is no package called 'tidyverse'라는 오류 메시지가 나타나면 먼저 설치한 다음 library()를 다시 실행해야 합니다.
install.packages("tidyverse")
library(tidyverse)패키지는 한 번만 설치하면 되지만, 새로운 세션을 시작할 때마다 로드해야 합니다.
tidyverse 외에도 팔머 군도(Palmer Archipelago)의 세 섬에 있는 펭귄의 신체 치수 데이터가 포함된 penguins 데이터셋이 있는 palmerpenguins 패키지와 색맹에 안전한 색상 팔레트를 제공하는 ggthemes 패키지도 사용할 것입니다.
library(palmerpenguins)
#>
#> Attaching package: 'palmerpenguins'
#> The following objects are masked from 'package:datasets':
#>
#> penguins, penguins_raw
library(ggthemes)
#> Warning: package 'ggthemes' was built under R version 4.5.21.2 첫 걸음
지느러미(flipper)가 더 긴 펭귄이 지느러미가 짧은 펭귄보다 무게가 더 많이 나갈까요? 아니면 적게 나갈까요? 이미 답을 알고 계실지 모르지만, 답을 정확하게 만들어 보세요. 지느러미 길이와 체중(body mass) 사이의 관계는 어떤 모습일까요? 양의 관계일까요? 음의 관계일까요? 선형일까요? 비선형일까요? 관계가 펭귄의 종(species)에 따라 달라질까요? 펭귄이 사는 섬에 따라서는 어떨까요? 이러한 질문에 답하는 데 사용할 수 있는 시각화를 만들어 봅시다.
1.2.1 penguins 데이터 프레임
palmerpenguins에 있는 penguins 데이터 프레임(일명 palmerpenguins::penguins)을 사용하여 해당 질문에 대한 답을 테스트할 수 있습니다. 데이터 프레임은 변수(열)와 관측값(행)의 직사각형 모음입니다. penguins에는 Kristen Gorman 박사와 Palmer Station, Antarctica LTER2가 수집하고 제공한 344개의 관측값이 포함되어 있습니다.
논의를 더 쉽게 하기 위해 몇 가지 용어를 정의해 봅시다:
변수(variable) 는 측정할 수 있는 수량, 품질 또는 속성입니다.
값(value) 은 변수를 측정할 때의 상태입니다. 변수의 값은 측정할 때마다 달라질 수 있습니다.
관측값(observation) 은 유사한 조건에서 수행된 측정값의 집합입니다(보통 하나의 관측값에 있는 모든 측정은 동시에 동일한 대상에 대해 수행됩니다). 관측값은 여러 값을 포함하며, 각 값은 서로 다른 변수와 연관됩니다. 때때로 관측값을 데이터 포인트라고 부르기도 합니다.
표 형식 데이터(Tabular data) 는 각 값이 변수 및 관측값과 연관된 값들의 집합입니다. 각 값이 고유한 “셀”에 배치되고, 각 변수가 고유한 열에, 각 관측값이 고유한 행에 배치되면 표 형식 데이터는 깔끔(tidy) 합니다.
이 맥락에서 변수는 모든 펭귄의 속성을 나타내며, 관측값은 단일 펭귄의 모든 속성을 나타냅니다.
콘솔에 데이터 프레임의 이름을 입력하면 R이 그 내용의 미리보기를 출력합니다. 미리보기 상단에 tibble이라고 표시된 것을 주목하세요. tidyverse에서는 곧 더 자세히 배우게 될 티블(tibble) 이라는 특별한 데이터 프레임을 사용합니다.
penguins
#> # A tibble: 344 × 8
#> species island bill_length_mm bill_depth_mm flipper_length_mm
#> <fct> <fct> <dbl> <dbl> <int>
#> 1 Adelie Torgersen 39.1 18.7 181
#> 2 Adelie Torgersen 39.5 17.4 186
#> 3 Adelie Torgersen 40.3 18 195
#> 4 Adelie Torgersen NA NA NA
#> 5 Adelie Torgersen 36.7 19.3 193
#> 6 Adelie Torgersen 39.3 20.6 190
#> # ℹ 338 more rows
#> # ℹ 3 more variables: body_mass_g <int>, sex <fct>, year <int>이 데이터 프레임에는 8개의 열이 포함되어 있습니다. 모든 변수와 각 변수의 처음 몇 개의 관측값을 볼 수 있는 다른 뷰를 보려면 glimpse()를 사용하세요. 또는 RStudio를 사용 중이라면 View(penguins)를 실행하여 대화형 데이터 뷰어를 여세요.
glimpse(penguins)
#> Rows: 344
#> Columns: 8
#> $ species <fct> Adelie, Adelie, Adelie, Adelie, Adelie, Adelie, A…
#> $ island <fct> Torgersen, Torgersen, Torgersen, Torgersen, Torge…
#> $ bill_length_mm <dbl> 39.1, 39.5, 40.3, NA, 36.7, 39.3, 38.9, 39.2, 34.…
#> $ bill_depth_mm <dbl> 18.7, 17.4, 18.0, NA, 19.3, 20.6, 17.8, 19.6, 18.…
#> $ flipper_length_mm <int> 181, 186, 195, NA, 193, 190, 181, 195, 193, 190, …
#> $ body_mass_g <int> 3750, 3800, 3250, NA, 3450, 3650, 3625, 4675, 347…
#> $ sex <fct> male, female, female, NA, female, male, female, m…
#> $ year <int> 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2…penguins의 변수 중 일부는 다음과 같습니다:
species: 펭귄의 종 (Adelie, Chinstrap, 또는 Gentoo).flipper_length_mm: 펭귄의 지느러미 길이 (밀리미터 단위).body_mass_g: 펭귄의 체중 (그램 단위).
penguins에 대해 더 자세히 알아보려면 ?penguins를 실행하여 도움말 페이지를 여세요.
1.2.2 최종 목표
이 장의 최종 목표는 펭귄의 종을 고려하여 지느러미 길이와 체중 사이의 관계를 보여주는 다음 시각화를 재현하는 것입니다.
1.2.3 ggplot 만들기
단계별로 이 플롯을 재현해 봅시다.
ggplot2에서는 ggplot() 함수로 플롯을 시작하여 플롯 객체를 정의한 다음 여기에 레이어(layer) 를 추가합니다. ggplot()의 첫 번째 인수는 그래프에 사용할 데이터셋이므로 ggplot(data = penguins)는 penguins 데이터를 표시할 준비가 된 빈 그래프를 생성하지만, 아직 시각화 방법을 알려주지 않았으므로 지금은 비어 있습니다. 이것은 별로 흥미롭지 않은 플롯이지만, 플롯의 나머지 레이어를 그릴 빈 캔버스와 같다고 생각할 수 있습니다.
ggplot(data = penguins)다음으로, 데이터의 정보가 시각적으로 어떻게 표현될지 ggplot()에 알려줘야 합니다. ggplot() 함수의 mapping 인수는 데이터셋의 변수가 플롯의 시각적 속성(심미성, aesthetics)에 매핑되는 방식을 정의합니다. mapping 인수는 항상 aes() 함수 내에 정의되며, aes()의 x와 y 인수는 x축과 y축에 매핑할 변수를 지정합니다. 지금은 지느러미 길이를 x 심미성에, 체중을 y 심미성에만 매핑하겠습니다. ggplot2는 data 인수, 이 경우 penguins에서 매핑된 변수를 찾습니다.
다음 플롯은 이러한 매핑을 추가한 결과를 보여줍니다.
이제 빈 캔버스에 더 많은 구조가 생겼습니다. 지느러미 길이가 표시될 위치(x축)와 체중이 표시될 위치(y축)가 명확해졌습니다. 하지만 펭귄 자체는 아직 플롯에 없습니다. 이는 데이터 프레임의 관측값을 플롯에 어떻게 표현할지 코드에서 아직 명시하지 않았기 때문입니다.
그렇게 하려면 지옴(geom) 을 정의해야 합니다. 지옴은 플롯이 데이터를 나타내는 데 사용하는 기하학적 객체입니다. 이러한 기하학적 객체는 ggplot2에서 geom_으로 시작하는 함수로 사용할 수 있습니다. 사람들은 종종 플롯이 사용하는 지옴의 유형으로 플롯을 설명합니다. 예를 들어, 막대 차트는 막대 지옴(geom_bar())을 사용하고, 선 차트는 선 지옴(geom_line())을 사용하며, 상자 그림은 상자 그림 지옴(geom_boxplot())을 사용하고, 산점도는 점 지옴(geom_point())을 사용하는 식입니다.
geom_point() 함수는 플롯에 점 레이어를 추가하여 산점도를 만듭니다. ggplot2에는 각각 플롯에 다른 유형의 레이어를 추가하는 많은 지옴 함수가 함께 제공됩니다. 책 전체, 특히 Chapter 9 에서 다양한 지옴을 배우게 될 것입니다.
ggplot(
data = penguins,
mapping = aes(x = flipper_length_mm, y = body_mass_g)
) +
geom_point()
#> Warning: Removed 2 rows containing missing values or values outside the scale range
#> (`geom_point()`).이제 우리가 “산점도”라고 생각하는 것과 비슷한 것이 생겼습니다. 아직 “최종 목표” 플롯과는 일치하지 않지만, 이 플롯을 사용하여 우리의 탐색 동기가 되었던 질문에 답하기 시작할 수 있습니다. “지느러미 길이와 체중 사이의 관계는 어떤 모습인가?” 관계는 양의 관계(지느러미 길이가 증가함에 따라 체중도 증가함)로 보이고, 꽤 선형적이며(점들이 곡선보다는 선 주위에 모여 있음), 중간 정도로 강합니다(선 주위에 산포가 너무 많지 않음). 지느러미가 더 긴 펭귄은 일반적으로 체중 면에서 더 큽니다.
이 플롯에 레이어를 더 추가하기 전에 잠시 멈추고 우리가 받은 경고 메시지를 검토해 봅시다:
Removed 2 rows containing missing values (
geom_point()). (결측값이 포함된 2개의 행이 제거되었습니다 (geom_point()).)
이 메시지가 표시되는 이유는 데이터셋에 체중 및/또는 지느러미 길이 값이 누락된 펭귄이 두 마리 있는데, ggplot2는 이 두 값 없이 플롯에 이들을 표현할 방법이 없기 때문입니다. R과 마찬가지로 ggplot2는 결측값이 조용히 사라져서는 안 된다는 철학을 따릅니다. 이 유형의 경고는 실제 데이터로 작업할 때 가장 흔하게 볼 수 있는 경고 중 하나일 것입니다. 결측값은 매우 흔한 문제이며 책 전체, 특히 Chapter 18 에서 더 자세히 배우게 될 것입니다. 이 장의 나머지 플롯에서는 우리가 만드는 모든 플롯 옆에 이 경고가 출력되지 않도록 숨길 것입니다.
1.2.4 심미성과 레이어 추가
산점도는 두 수치형 변수 간의 관계를 표시하는 데 유용하지만, 두 변수 사이의 명백한 관계에 대해 항상 회의적인 태도를 취하고 이 명백한 관계를 설명하거나 성격을 바꾸는 다른 변수가 있을 수 있는지 묻는 것이 좋습니다. 예를 들어, 지느러미 길이와 체중 사이의 관계가 종에 따라 다를까요? 종을 플롯에 통합하여 이것이 변수들 간의 명백한 관계에 대한 추가적인 통찰력을 드러내는지 확인해 봅시다. 종을 다른 색상의 점으로 표현하여 이를 수행할 것입니다.
이를 달성하기 위해 심미성을 수정해야 할까요, 아니면 지옴을 수정해야 할까요? “심미적 매핑, aes() 내부”라고 추측했다면 이미 ggplot2로 데이터 시각화를 만드는 요령을 터득한 것입니다! 그렇지 않더라도 걱정하지 마세요. 책 전체에 걸쳐 더 많은 ggplot을 만들고 만들면서 직관을 확인할 기회가 훨씬 더 많을 것입니다.
ggplot(
data = penguins,
mapping = aes(x = flipper_length_mm, y = body_mass_g, color = species)
) +
geom_point()범주형 변수가 심미성에 매핑되면 ggplot2는 자동으로 변수의 각 고유 수준(세 종 각각)에 심미성의 고유 값(여기서는 고유 색상)을 할당하는데, 이 과정을 스케일링(scaling) 이라고 합니다. 또한 ggplot2는 어떤 값이 어떤 수준에 해당하는지 설명하는 범례를 추가합니다.
이제 레이어를 하나 더 추가해 봅시다: 체중과 지느러미 길이 사이의 관계를 보여주는 매끄러운 곡선입니다. 진행하기 전에 위의 코드를 다시 참조하고 기존 플롯에 이것을 어떻게 추가할 수 있을지 생각해 보세요.
이것은 데이터를 나타내는 새로운 기하학적 객체이므로 점 지옴 위에 레이어로 새로운 지옴인 geom_smooth()를 추가할 것입니다. 그리고 method = "lm"을 사용하여 선형 모형(linear model)에 기반한 최적 적합선을 그리도록 지정할 것입니다.
ggplot(
data = penguins,
mapping = aes(x = flipper_length_mm, y = body_mass_g, color = species)
) +
geom_point() +
geom_smooth(method = "lm")성공적으로 선을 추가했지만, 이 플롯은 각 펭귄 종에 대해 별도의 선이 있는 것이 아니라 전체 데이터셋에 대해 하나의 선만 있는 Section 1.2.2 의 플롯과 같아 보이지 않습니다.
심미적 매핑이 ggplot()에서 전역(global) 수준으로 정의되면 플롯의 후속 지옴 레이어 각각에 전달됩니다. 그러나 ggplot2의 각 지옴 함수는 mapping 인수를 취할 수 있으며, 이를 통해 전역 수준에서 상속된 것에 추가되는 로컬(local) 수준의 심미적 매핑을 허용합니다. 점은 종에 따라 색상을 지정하고 싶지만 선은 분리하고 싶지 않으므로 geom_point()에만 color = species를 지정해야 합니다.
ggplot(
data = penguins,
mapping = aes(x = flipper_length_mm, y = body_mass_g)
) +
geom_point(mapping = aes(color = species)) +
geom_smooth(method = "lm")짠! 우리의 최종 목표와 매우 비슷해 보이는 것이 생겼지만, 아직 완벽하지는 않습니다. 여전히 각 펭귄 종에 대해 다른 모양을 사용하고 레이블을 개선해야 합니다.
색맹이나 기타 색각 차이로 인해 사람들이 색을 다르게 인식하므로 플롯에서 색상만 사용하여 정보를 표현하는 것은 일반적으로 좋은 생각이 아닙니다. 따라서 색상 외에도 species를 shape 심미성에 매핑할 수 있습니다.
ggplot(
data = penguins,
mapping = aes(x = flipper_length_mm, y = body_mass_g)
) +
geom_point(mapping = aes(color = species, shape = species)) +
geom_smooth(method = "lm")범례가 점의 다른 모양도 반영하도록 자동으로 업데이트된다는 점에 유의하세요.
그리고 마지막으로, 새로운 레이어에서 labs() 함수를 사용하여 플롯의 레이블을 개선할 수 있습니다. labs()의 인수 중 일부는 설명이 필요 없을 수 있습니다: title은 제목을 추가하고 subtitle은 플롯에 부제를 추가합니다. 다른 인수들은 심미적 매핑과 일치합니다. x는 x축 레이블, y는 y축 레이블, color와 shape는 범례의 레이블을 정의합니다. 또한 ggthemes 패키지의 scale_color_colorblind() 함수를 사용하여 색맹에 안전하도록 색상 팔레트를 개선할 수 있습니다.
ggplot(
data = penguins,
mapping = aes(x = flipper_length_mm, y = body_mass_g)
) +
geom_point(aes(color = species, shape = species)) +
geom_smooth(method = "lm") +
labs(
title = "Body mass and flipper length",
subtitle = "Dimensions for Adelie, Chinstrap, and Gentoo Penguins",
x = "Flipper length (mm)", y = "Body mass (g)",
color = "Species", shape = "Species"
) +
scale_color_colorblind()드디어 우리의 “최종 목표”와 완벽하게 일치하는 플롯을 갖게 되었습니다!
1.2.5 연습문제
penguins에는 몇 개의 행이 있습니까? 열은 몇 개입니까?penguins데이터 프레임의bill_depth_mm변수는 무엇을 설명합니까??penguins도움말을 읽고 확인하세요.bill_depth_mm대bill_length_mm의 산점도를 만드세요. 즉, y축에bill_depth_mm, x축에bill_length_mm가 있는 산점도를 만드세요. 이 두 변수 사이의 관계를 설명하세요.species대bill_depth_mm의 산점도를 만들면 어떻게 됩니까? 더 나은 지옴 선택은 무엇일까요?-
다음이 오류를 발생시키는 이유는 무엇이며 어떻게 수정하겠습니까?
ggplot(data = penguins) + geom_point() geom_point()에서na.rm인수는 무엇을 합니까? 인수의 기본값은 무엇입니까? 이 인수를TRUE로 설정하여 성공적으로 사용하는 산점도를 만드세요.이전 연습문제에서 만든 플롯에 다음 캡션을 추가하세요: “Data come from the palmerpenguins package.” 힌트:
labs()문서를 살펴보세요.-
다음 시각화를 재현하세요.
bill_depth_mm은 어떤 심미성에 매핑되어야 합니까? 그리고 전역 수준에서 매핑되어야 할까요, 아니면 지옴 수준에서 매핑되어야 할까요? -
이 코드를 머릿속으로 실행하고 출력이 어떻게 보일지 예측해 보세요. 그런 다음 R에서 코드를 실행하고 예측을 확인하세요.
ggplot( data = penguins, mapping = aes(x = flipper_length_mm, y = body_mass_g, color = island) ) + geom_point() + geom_smooth(se = FALSE) -
이 두 그래프는 다르게 보일까요? 왜 그렇습니까/그렇지 않습니까?
ggplot( data = penguins, mapping = aes(x = flipper_length_mm, y = body_mass_g) ) + geom_point() + geom_smooth() ggplot() + geom_point( data = penguins, mapping = aes(x = flipper_length_mm, y = body_mass_g) ) + geom_smooth( data = penguins, mapping = aes(x = flipper_length_mm, y = body_mass_g) )
1.3 ggplot2 호출
이러한 소개 섹션에서 벗어나면서 ggplot2 코드를 더 간결하게 표현하는 방법으로 전환할 것입니다. 지금까지 우리는 매우 명시적이었으며, 이는 배울 때 도움이 됩니다:
ggplot(
data = penguins,
mapping = aes(x = flipper_length_mm, y = body_mass_g)
) +
geom_point()일반적으로 함수의 처음 한두 인수는 매우 중요해서 외워두는 것이 좋습니다. ggplot()의 처음 두 인수는 data와 mapping이며, 책의 나머지 부분에서는 이 이름들을 제공하지 않을 것입니다. 이렇게 하면 타이핑을 줄이고, 추가 텍스트의 양을 줄여서 플롯 간의 차이점을 더 쉽게 볼 수 있습니다. 이것은 Chapter 25 에서 다시 다룰 정말 중요한 프로그래밍 고려 사항입니다.
이전 플롯을 더 간결하게 다시 작성하면 다음과 같습니다:
ggplot(penguins, aes(x = flipper_length_mm, y = body_mass_g)) +
geom_point()나중에 파이프 |>에 대해 배우게 될 텐데, 이를 사용하면 다음과 같이 플롯을 만들 수 있습니다:
penguins |>
ggplot(aes(x = flipper_length_mm, y = body_mass_g)) +
geom_point()1.4 분포 시각화
변수의 분포를 시각화하는 방법은 변수의 유형(범주형 또는 수치형)에 따라 다릅니다.
1.4.1 범주형 변수
변수가 작은 집합의 값 중 하나만 취할 수 있는 경우 범주형(categorical) 입니다. 범주형 변수의 분포를 조사하려면 막대 차트를 사용할 수 있습니다. 막대의 높이는 각 x 값에서 얼마나 많은 관측값이 발생했는지 보여줍니다.
위의 펭귄 species와 같이 순서가 없는 수준을 가진 범주형 변수의 막대 플롯에서는 빈도에 따라 막대를 재정렬하는 것이 바람직한 경우가 많습니다. 그렇게 하려면 변수를 팩터(R이 범주형 데이터를 처리하는 방식)로 변환한 다음 해당 팩터의 수준을 재정렬해야 합니다.
ggplot(penguins, aes(x = fct_infreq(species))) +
geom_bar()Chapter 16 에서 팩터와 팩터를 다루는 함수(위에 표시된 fct_infreq() 등)에 대해 더 자세히 배우게 될 것입니다.
1.4.2 수치형 변수
변수가 광범위한 수치 값을 취할 수 있고 그 값으로 더하기, 빼기 또는 평균을 구하는 것이 합리적이라면 수치형(numerical) (또는 양적)입니다. 수치형 변수는 연속적이거나 이산적일 수 있습니다.
연속형 변수의 분포에 흔히 사용되는 시각화 중 하나는 히스토그램입니다.
ggplot(penguins, aes(x = body_mass_g)) +
geom_histogram(binwidth = 200)히스토그램은 x축을 등간격의 구간(bin)으로 나눈 다음 막대의 높이를 사용하여 각 구간에 속하는 관측값의 수를 표시합니다. 위의 그래프에서 가장 높은 막대는 39개의 관측값이 3,500에서 3,700그램 사이의 body_mass_g 값을 가짐을 보여주며, 이는 막대의 왼쪽과 오른쪽 가장자리입니다.
binwidth 인수를 사용하여 히스토그램의 간격 너비를 설정할 수 있으며, 이는 x 변수의 단위로 측정됩니다. 히스토그램으로 작업할 때는 항상 다양한 구간 너비를 탐색해야 합니다. 구간 너비가 다르면 다른 패턴이 드러날 수 있기 때문입니다. 아래 플롯에서 구간 너비 20은 너무 좁아서 막대가 너무 많아져 분포의 모양을 파악하기 어렵습니다. 마찬가지로 구간 너비 2,000은 너무 커서 모든 데이터가 단 3개의 막대로 묶여 버려, 역시 분포의 모양을 파악하기 어렵게 만듭니다. 구간 너비 200은 합리적인 균형을 제공합니다.
ggplot(penguins, aes(x = body_mass_g)) +
geom_histogram(binwidth = 20)
ggplot(penguins, aes(x = body_mass_g)) +
geom_histogram(binwidth = 2000)수치형 변수의 분포에 대한 대안적인 시각화는 밀도 플롯(density plot)입니다. 밀도 플롯은 히스토그램을 부드럽게 만든 버전이며, 특히 근본적으로 매끄러운 분포에서 나온 연속형 데이터에 실용적인 대안입니다. geom_density()가 밀도를 어떻게 추정하는지에 대해서는 자세히 설명하지 않겠지만(함수 문서에서 자세한 내용을 읽을 수 있습니다), 유추를 통해 밀도 곡선이 어떻게 그려지는지 설명해 보겠습니다. 나무 블록으로 만든 히스토그램을 상상해 보세요. 그런 다음 익힌 스파게티 면을 그 위에 떨어뜨린다고 상상해 보세요. 블록 위에 늘어진 스파게티가 취하는 모양을 밀도 곡선의 모양이라고 생각할 수 있습니다. 히스토그램보다 세부 사항은 적게 보여주지만 분포의 모양, 특히 최빈값과 왜도(skewness)와 관련하여 빠르게 파악하기 쉽게 해줍니다.
ggplot(penguins, aes(x = body_mass_g)) +
geom_density()
#> Warning: Removed 2 rows containing non-finite outside the scale range
#> (`stat_density()`).1.4.3 연습문제
penguins의species에 대한 막대 플롯을 만드는데,species를y심미성에 할당하세요. 이 플롯은 어떻게 다릅니까?-
다음 두 플롯은 어떻게 다릅니까? 막대의 색상을 변경하는 데
color와fill중 어떤 심미성이 더 유용합니까? geom_histogram()에서bins인수는 무엇을 합니까?tidyverse 패키지를 로드할 때 사용할 수 있는
diamonds데이터셋의carat변수에 대한 히스토그램을 만드세요. 다양한 구간 너비로 실험해 보세요. 어떤 구간 너비가 가장 흥미로운 패턴을 보여줍니까?
1.5 관계 시각화
관계를 시각화하려면 플롯의 심미성에 매핑된 변수가 적어도 두 개 있어야 합니다. 다음 섹션에서는 두 개 이상의 변수 간의 관계를 시각화하는 데 일반적으로 사용되는 플롯과 이를 생성하는 데 사용되는 지옴에 대해 배울 것입니다.
1.5.1 수치형 변수와 범주형 변수
수치형 변수와 범주형 변수 간의 관계를 시각화하기 위해 나란히 놓인 상자 그림(side-by-side box plots)을 사용할 수 있습니다. 상자 그림(boxplot) 은 분포를 설명하는 위치 측도(백분위수)에 대한 일종의 시각적 속기입니다. 잠재적인 이상치(outliers)를 식별하는 데도 유용합니다. Figure 1.1 에 표시된 것처럼 각 상자 그림은 다음으로 구성됩니다:
데이터의 중간 절반의 범위, 즉 사분위수 범위(IQR)로 알려진 거리를 나타내는 상자로, 분포의 25번째 백분위수에서 75번째 백분위수까지 뻗어 있습니다. 상자 가운데에는 분포의 중앙값, 즉 50번째 백분위수를 표시하는 선이 있습니다. 이 세 선은 분포의 퍼짐 정도와 분포가 중앙값을 중심으로 대칭인지 아니면 한쪽으로 치우쳐 있는지에 대한 감을 줍니다.
상자의 가장자리에서 1.5배의 IQR보다 더 멀리 떨어진 관측값을 표시하는 시각적 점들입니다. 이러한 이상 점들은 특이하므로 개별적으로 플롯됩니다.
상자의 각 끝에서 뻗어 나와 분포에서 이상치가 아닌 가장 먼 지점까지 가는 선(또는 수염)입니다.
geom_boxplot()을 사용하여 종별 체중 분포를 살펴봅시다:
ggplot(penguins, aes(x = species, y = body_mass_g)) +
geom_boxplot()대안으로 geom_density()를 사용하여 밀도 플롯을 만들 수 있습니다.
ggplot(penguins, aes(x = body_mass_g, color = species)) +
geom_density(linewidth = 0.75)배경에 비해 좀 더 눈에 띄게 만들기 위해 linewidth 인수를 사용하여 선의 두께를 사용자 지정했습니다.
또한 species를 color와 fill 심미성 모두에 매핑하고 alpha 심미성을 사용하여 채워진 밀도 곡선에 투명도를 추가할 수 있습니다. 이 심미성은 0(완전 투명)에서 1(완전 불투명) 사이의 값을 취합니다. 다음 플롯에서는 0.5로 설정 되어 있습니다.
ggplot(penguins, aes(x = body_mass_g, color = species, fill = species)) +
geom_density(alpha = 0.5)여기서 사용한 용어에 유의하세요:
- 시각적 속성이 변수의 값에 따라 달라지게 하려면 변수를 심미성에 매핑 합니다.
- 그렇지 않은 경우 심미성의 값을 설정 합니다.
1.5.2 두 개의 범주형 변수
누적 막대 플롯을 사용하여 두 범주형 변수 간의 관계를 시각화할 수 있습니다. 예를 들어, 다음 두 개의 누적 막대 플롯은 모두 island와 species 간의 관계를 표시하거나, 구체적으로 각 섬 내에서 species의 분포를 시각화합니다.
첫 번째 플롯은 각 섬에 있는 각 펭귄 종의 빈도를 보여줍니다. 빈도 플롯은 각 섬에 같은 수의 Adelie가 있음을 보여줍니다. 하지만 각 섬 내의 백분율 균형에 대한 좋은 감은 얻을 수 없습니다.
지옴에서 position = "fill"을 설정하여 생성된 상대 빈도 플롯인 두 번째 플롯은 섬 전체의 펭귄 수가 동일하지 않은 영향을 받지 않으므로 섬 전체의 종 분포를 비교하는 데 더 유용합니다. 이 플롯을 사용하면 Gentoo 펭귄은 모두 Biscoe 섬에 살고 해당 섬 펭귄의 약 75%를 차지하며, Chinstrap은 모두 Dream 섬에 살고 해당 섬 펭귄의 약 50%를 차지하며, Adelie는 세 섬 모두에 살고 Torgersen 섬 펭귄의 전체를 차지한다는 것을 알 수 있습니다.
이러한 막대 차트를 만들 때 막대로 구분될 변수를 x 심미성에 매핑하고, 막대 내부의 색상을 변경할 변수를 fill 심미성에 매핑합니다. 불행히도 ggplot2는 y축에 기본적으로 "count"라고 레이블을 지정하지만, 이는 y축 레이블을 "proportion"으로 지정하는 labs() 레이어를 추가하여 재정의할 수 있는 부분입니다.
1.5.3 두 개의 수치형 변수
지금까지 두 수치형 변수 간의 관계를 시각화하기 위해 산점도(geom_point()로 생성)와 매끄러운 곡선(geom_smooth()로 생성)에 대해 배웠습니다. 산점도는 아마도 두 수치형 변수 간의 관계를 시각화하는 데 가장 일반적으로 사용되는 플롯일 것입니다.
ggplot(penguins, aes(x = flipper_length_mm, y = body_mass_g)) +
geom_point()1.5.4 세 개 이상의 변수
Section 1.2.4 에서 보았듯이, 추가 심미성에 변수를 매핑하여 플롯에 더 많은 변수를 통합할 수 있습니다. 예를 들어, 다음 산점도에서 점의 색상은 종을 나타내고 점의 모양은 섬을 나타냅니다.
ggplot(penguins, aes(x = flipper_length_mm, y = body_mass_g)) +
geom_point(aes(color = species, shape = island))그러나 플롯에 심미적 매핑을 너무 많이 추가하면 어수선해지고 이해하기 어려워집니다. 특히 범주형 변수에 유용한 또 다른 방법은 플롯을 패싯(facets), 즉 데이터의 각 하위 집합을 표시하는 하위 플롯으로 나누는 것입니다.
단일 변수로 플롯을 패싯하려면 facet_wrap()을 사용하세요. facet_wrap()의 첫 번째 인수는 공식(formula)3으로, ~ 뒤에 변수 이름을 붙여 생성합니다. facet_wrap()에 전달하는 변수는 범주형이어야 합니다.
ggplot(penguins, aes(x = flipper_length_mm, y = body_mass_g)) +
geom_point(aes(color = species, shape = species)) +
facet_wrap(~island)Chapter 9 에서 변수의 분포와 변수 간의 관계를 시각화하기 위한 다른 많은 지옴에 대해 배울 것입니다.
1.5.5 연습문제
ggplot2 패키지에 번들로 제공되는
mpg데이터 프레임에는 38개의 자동차 모델에 대해 미국 환경 보호국이 수집한 234개의 관측값이 포함되어 있습니다.mpg의 어떤 변수가 범주형입니까? 어떤 변수가 수치형입니까? (힌트: 데이터셋에 대한 문서를 읽으려면?mpg를 입력하세요.)mpg를 실행할 때 이 정보를 어떻게 볼 수 있습니까?mpg데이터 프레임을 사용하여hwy대displ의 산점도를 만드세요. 다음으로, 세 번째 수치형 변수를color에 매핑하고, 그 다음엔size에, 그 다음엔color와size모두에, 그 다음엔shape에 매핑하세요. 이러한 심미성은 범주형 변수와 수치형 변수에 대해 어떻게 다르게 작동합니까?hwy대displ의 산점도에서 세 번째 변수를linewidth에 매핑하면 어떻게 됩니까?동일한 변수를 여러 심미성에 매핑하면 어떻게 됩니까?
bill_depth_mm대bill_length_mm의 산점도를 만들고species로 점의 색을 지정하세요. 종별로 색상을 추가하면 이 두 변수 간의 관계에 대해 무엇이 드러납니까?species로 패싯하는 것은 어떻습니까?-
다음이 두 개의 별도 범례를 생성하는 이유는 무엇입니까? 두 범례를 결합하려면 어떻게 수정해야 합니까?
ggplot( data = penguins, mapping = aes( x = bill_length_mm, y = bill_depth_mm, color = species, shape = species ) ) + geom_point() + labs(color = "Species") -
다음 두 개의 누적 막대 플롯을 만드세요. 첫 번째 플롯으로 어떤 질문에 답할 수 있습니까? 두 번째 플롯으로 어떤 질문에 답할 수 있습니까?
1.6 플롯 저장하기
플롯을 만든 후에는 다른 곳에서 사용할 수 있도록 이미지로 저장하여 R에서 내보내고 싶을 수 있습니다. 그것이 ggsave()의 역할이며, 가장 최근에 생성된 플롯을 디스크에 저장합니다:
ggplot(penguins, aes(x = flipper_length_mm, y = body_mass_g)) +
geom_point()
ggsave(filename = "penguin-plot.png")이렇게 하면 플롯이 작업 디렉터리에 저장되는데, 작업 디렉터리라는 개념은 Chapter 6 에서 더 자세히 배울 것입니다.
width와 height를 지정하지 않으면 현재 플롯 장치의 치수에서 가져옵니다. 재현 가능한 코드를 위해서는 지정하는 것이 좋습니다. 설명서에서 ggsave()에 대해 더 자세히 알아볼 수 있습니다.
그러나 일반적으로는 코드와 산문을 섞어서 작성하고 플롯을 자동으로 포함할 수 있는 재현 가능한 저작 시스템인 Quarto를 사용하여 최종 보고서를 작성하는 것을 권장합니다. Quarto에 대해서는 Chapter 28 에서 더 자세히 배울 것입니다.
1.6.1 연습문제
-
다음 코드 줄을 실행하세요. 두 플롯 중 어느 것이
mpg-plot.png로 저장됩니까? 그 이유는 무엇입니까? 위의 코드에서 플롯을 PNG 대신 PDF로 저장하려면 무엇을 변경해야 합니까?
ggsave()에서 어떤 유형의 이미지 파일이 작동하는지 어떻게 알 수 있습니까?
1.7 일반적인 문제
R 코드를 실행하기 시작하면 문제에 부딪힐 가능성이 큽니다. 걱정하지 마세요. 누구에게나 일어나는 일입니다. 우리 모두 수년 동안 R 코드를 작성해 왔지만, 매일 첫 시도에 작동하지 않는 코드를 여전히 작성합니다!
실행 중인 코드를 책의 코드와 주의 깊게 비교하는 것으로 시작하세요. R은 매우 까다로워서 문자가 잘못된 위치에 있으면 큰 차이가 날 수 있습니다. 모든 (가 )와 짝이 맞는지, 모든 "가 다른 "와 짝이 맞는지 확인하세요. 때로는 코드를 실행해도 아무 일도 일어나지 않을 수 있습니다. 콘솔 왼쪽을 확인하세요. +가 보이면 R이 완전한 표현식을 입력하지 않았다고 생각하고 완료하기를 기다리고 있다는 의미입니다. 이 경우 일반적으로 ESCAPE를 눌러 현재 명령 처리를 중단하고 처음부터 다시 시작하는 것이 쉽습니다.
ggplot2 그래픽을 만들 때 흔히 발생하는 문제 중 하나는 +를 잘못된 위치에 두는 것입니다. +는 줄의 시작이 아니라 끝에 와야 합니다. 즉, 실수로 다음과 같이 코드를 작성하지 않았는지 확인하세요:
ggplot(data = mpg)
+ geom_point(mapping = aes(x = displ, y = hwy))여전히 막힌다면 도움말을 시도해 보세요. 콘솔에서 ?function_name을 실행하거나 RStudio에서 함수 이름을 강조 표시하고 F1을 눌러 모든 R 함수에 대한 도움말을 얻을 수 있습니다. 도움말이 별로 도움이 되지 않는 것 같더라도 걱정하지 마세요. 대신 예제로 건너뛰어 시도하려는 것과 일치하는 코드를 찾아보세요.
그래도 해결되지 않으면 오류 메시지를 주의 깊게 읽으세요. 때로는 답이 거기에 묻혀 있을 수 있습니다! 하지만 R을 처음 접하는 경우 답이 오류 메시지에 있더라도 아직 이해하는 방법을 모를 수 있습니다. 또 다른 훌륭한 도구는 Google입니다. 오류 메시지를 구글링해 보세요. 다른 누군가가 같은 문제를 겪었고 온라인에서 도움을 받았을 가능성이 큽니다.
1.8 요약
이 장에서는 ggplot2를 사용한 데이터 시각화의 기본 사항을 배웠습니다. 우리는 ggplot2를 뒷받침하는 기본 아이디어인 시각화는 데이터의 변수에서 위치, 색상, 크기 및 모양과 같은 심미적 속성으로의 매핑이라는 것으로 시작했습니다. 그런 다음 레이어별로 플롯의 복잡성을 높이고 표현을 개선하는 것에 대해 배웠습니다. 또한 추가 심미적 매핑을 활용하거나 패싯을 사용하여 플롯을 작은 다중으로 분할함으로써 단일 변수의 분포를 시각화하고 두 개 이상의 변수 간의 관계를 시각화하는 데 일반적으로 사용되는 플롯에 대해 배웠습니다.
이 책 전체에서 시각화를 계속해서 사용할 것이며, 필요에 따라 새로운 기술을 소개하고 Chapter 9 부터 Chapter 11 까지 ggplot2로 시각화를 만드는 것에 대해 더 깊이 다룰 것입니다.
시각화의 기초를 익혔으므로 다음 장에서는 기어를 조금 바꿔서 실용적인 워크플로우 조언을 제공할 것입니다. 이 책의 이 파트 전반에 걸쳐 데이터 과학 도구와 함께 워크플로우 조언을 섞어서 제공하는 이유는 R 코드의 양이 늘어남에 따라 체계적으로 유지하는 데 도움이 되기 때문입니다.
conflicted 패키지를 사용하여 해당 메시지를 없애고 충돌 해결을 필요할 때 강제하도록 할 수 있습니다. 이는 더 많은 패키지를 로드할수록 중요해집니다. conflicted에 대한 자세한 내용은 https://conflicted.r-lib.org에서 확인할 수 있습니다.↩︎
Horst AM, Hill AP, Gorman KB (2020). palmerpenguins: Palmer Archipelago (Antarctica) penguin data. R package version 0.1.0. https://allisonhorst.github.io/palmerpenguins/. doi: 10.5281/zenodo.3960218.↩︎
여기서 “공식”은 “방정식”의 동의어가 아니라
~로 생성된 것의 이름입니다.↩︎