데이터 시각화

저자

이상일, 전혜민, 김세창, 김우형

공개

2025년 8월 1일

1 ggplot2 패키지

ggplot2 패키지는 R에서 시각화를 위해 가장 널리 사용되는 패키지 중 하나로, 그래픽 문법(Grammar of Graphics)을 바탕으로 유연하고 직관적인 그래프 생성을 가능하게 한다. 심미성 요소(aesthetics)와 기하적 요소(geometry)를 명확히 분리하고, 각 요소를 레이어(layer) 형태로 구성해 +로 연결함으로써 복잡한 시각화를 논리적으로 표현할 수 있다.

출처: https://r.qcbs.ca/workshop03/book-en/grammar-of-graphics-gg-basics.html

각 레이어에 대한 설명은 다음과 같다.

구성요소 설명
데이터(Data) 시각화할 원데이터
심미성(Aesthetics) 변수와 시각적 속성 간의 연결(x, y, color, size 등)
기하(Geometries) 실제 그래프 형태
면 분할(Facets) 데이터를 하위 집합으로 나누어 여러 개의 소형 그래프로 표현
통계 변환(statistics) 데이터에 대한 요약 또는 변환
좌표계(Coordinates) 그래프의 좌표체계를 설정
테마(Theme) 배경, 폰트, 제목, 축 등 비데이터적 시각 요소

2 핵심 레이어: 심미성과 기하

7개의 구성요소 중 가장 중요한 것은 심미성(aesthetics) 혹은 심미성 매핑(aesthetic mapping)기하(geometries) 혹은 기하 객체(geometric objects) 이다. 심미성은 전체적인 시각적 속성을 결정하고 기하는 그래프의 유형을 결정한다. 두 가지는 독립적인 요소이지만 어느 정도 관련되며, 특정 기하는 특정 심미성과만 결합할 수 있기도 하다. 예를 들어 포인트 기하 객체(geom_point())는 크기(size) 심미성과 관련되지만, 라인폭(linewidth) 심미성과는 관련이 없다.

2.1 기초 예제

기초 예제에서 다룰 데이터는 iris 데이터이다. 그려볼 그래프는 꽃잎의 길이와 너비 사이의 관계를 보여줄 수 있는 산점도를 그려보고자 한다. 이 때 종별로 심미성 요소를 다르게 부여하여 시각적으로 구분하고자 한다.

iris 데이터를 불러와 확인해보자. 꽃잎의 길이는 Petal.Length, 꽃잎의 너비는 Petal.Width, 종에 대한 정보는 Species에 나타나있다.

head(iris)
  Sepal.Length Sepal.Width Petal.Length Petal.Width Species
1          5.1         3.5          1.4         0.2  setosa
2          4.9         3.0          1.4         0.2  setosa
3          4.7         3.2          1.3         0.2  setosa
4          4.6         3.1          1.5         0.2  setosa
5          5.0         3.6          1.4         0.2  setosa
6          5.4         3.9          1.7         0.4  setosa

ggplot2 패키지는 tidyverse 패키지 내에 속해 있다. tidyverse 패키지를 로드하자.

── Attaching core tidyverse packages ──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── tidyverse 2.0.0 ──
✔ dplyr     1.1.4     ✔ readr     2.1.5
✔ forcats   1.0.0     ✔ stringr   1.5.1
✔ ggplot2   3.5.1     ✔ tibble    3.2.1
✔ lubridate 1.9.3     ✔ tidyr     1.3.1
✔ purrr     1.0.4     
── 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 errors

먼저 그래프를 그릴 데이터를 추가한다.

ggplot(data = iris)

데이터만 추가했을 때는 x축과 y축에 어떤 변수가 들어갈지 정해지지 않아서 빈 화면만 출력되는 것을 볼 수 있다. 이제 x축과 y축을 지정해보자. x축과 y축 지정은 심미성 매핑에 해당하므로 aes() 함수 안에서 지정한다. 꾳잎의 길이와 꽃잎의 너비의 관계를 보고 싶으므로 x축에 꾳잎의 길이, y축에 꽃잎의 너비를 지정해보자.

ggplot(data = iris, aes(x = Petal.Length, y = Petal.Width))

아직 기하 객체가 없기 때문에 그래프가 나타나지 않는다. geom_point()를 사용하여 점 객체를 그려 산점도를 완성해보자.

ggplot(data = iris, aes(x = Petal.Length, y = Petal.Width)) + 
    geom_point()

드디어 산점도가 그려졌다. 하지만 우리가 원하는 최종적인 그래프는 그래프 내에서 종이 시각적으로 구분되는 것이다. 이를 위해서는 추가적인 심미성 매핑이 필요하다.

2.2 심미성 매핑

심미성 매핑(aesthetic mapping)은 데이터의 변수를 시각적 속성에 연결하는 과정을 의미하며, 앞서 보았듯이 aes() 함수 안에서 지정된다. x축과 y축을 지정하거나, 변수에 따라서 색상, 크기, 투명도 등을 다르게 부여하는 것이 대표적인 심미성 매핑의 예시이다. 앞서 그린 그래프를 종에 따라서 점 색상이 다르게 나타나도록 해보자. color 속성을 이용하면 된다.

# 심미성: 색상
ggplot(data = iris, aes(x = Petal.Length, y = Petal.Width, color = Species)) +
    geom_point()

점의 형태나 크기, 투명도도 종에 따라 다르게 부여할 수 있다.

# 심미성: 형태
ggplot(data = iris, aes(x = Petal.Length, y = Petal.Width, shape = Species)) +
    geom_point()

# 심미성: 크기
ggplot(data = iris, aes(x = Petal.Length, y = Petal.Width, size = Species)) +
    geom_point()
Warning: Using size for a discrete variable is not advised.

# 심미성: 투명도
ggplot(data = iris, aes(x = Petal.Length, y = Petal.Width, alpha = Species)) +
    geom_point()
Warning: Using alpha for a discrete variable is not advised.

심미성 요소는 ggplot() 내에서 글로벌하게 적용할 수도 있고, 기하 객체별로 로컬하게 적용할 수도 있다. 위에서 그린 산점도를 보면 꽃잎 길이와 너비 사이의 정적(positive) 관계를 확인할 수 있는데, 이를 표현하는 추세선을 그리고자 한다. 이때 color 심미성 요소를 글로벌하게 적용하느냐, 로컬하게 적용하느냐에 따라 다른 결과를 얻게 된다. 추세선은 geom_smooth()를 이용하여 그릴 수 있으며, 하나의 그래프에 복수의 기하 객체를 담고 싶다면 +로 연결해주면 된다.

# 글로벌하게 적용
ggplot(data = iris, aes(x = Petal.Length, y = Petal.Width, color = Species)) +
    geom_point() +
    geom_smooth()

# 로컬하게 적용
ggplot(data = iris, aes(x = Petal.Length, y = Petal.Width)) +
    geom_point(aes(color = Species)) +
    geom_smooth()
`geom_smooth()` using method = 'loess' and formula = 'y ~ x'

2.3 기하 객체

기하 객체(geometric objects)는 데이터가 시각적으로 표현되는 구체적인 형태를 의미하는데, ggplot2 패키지에서는 geom_으로 시작하는 함수로 나타낸다. 점, 선, 막대, 히스토그램, 박스플롯 등 다양한 기하 객체가 존재하며 사용자가 그리고자 하는 그래프 유형에 따라 적절하게 선택할 수 있다. 위에서 사용한 geom_point()도 점을 표현하는 기하 객체이다.

geom_line()을 사용하면 선을 표현할 수 있다. 관측 순서에 따른 꽃잎 길이에 대한 꺾은선 그래프를 그려보자.

ggplot(data = iris, aes(x = 1:150, y = Petal.Length, color = Species)) + 
    geom_line() +
    labs(
        x = "Observed Index",
        y = "Length of Petal"
    )

geom_bar()geom_col()은 막대 그래프를 그린다. 여기서는 geom_col()를 사용해 종별 평균 꽃잎 길이를 나타내는 그래프를 그려보자.

# 종별 평균 꽃잎 길이 구하기
iris_avg <- iris |> 
    group_by(Species) |> 
    summarise(avg_Petal_Length = mean(Petal.Length))

# 종별 평균 꽃잎 길이 
ggplot(data = iris_avg, aes(x = Species, y = avg_Petal_Length)) +
    geom_col()

geom_histogram()는 히스토그램을 그린다. 꽃잎 길이에 대한 히스토그램을 그려보자. binwidth 옵션은 히스토그램의 구간을 설정한다.

ggplot(data = iris, aes(x = Petal.Length)) + 
    geom_histogram(binwidth = 0.1)

geom_boxplot()은 박스플롯을 그린다. 종별 꽃잎 길이에 대한 박스플롯을 그려보자. 종의 구분은 색상으로 한다.

ggplot(data = iris, aes(x = Petal.Length, color = Species)) +
    geom_boxplot()

3 추가 레이어

3.1 스케일

스케일(scale)은 심미성을 세부적으로 조정하는 역할을 하며, scale_<aesthetic>_<type>() 형태로 사용된다.

  • <aesthetic> : x, y, color, fill, size, shape

  • <type> : continuous, discrete, manual, gradient, log10, 등

앞서 그린 꽃잎의 길이와 너비에 대한 산점도를 다시 살펴보자.

ggplot(data = iris, aes(x = Petal.Length, y = Petal.Width, color = Species)) +
    geom_point()

이는 아래 코드처럼 ggplot2가 자동으로 적용한 세 가지 스케일 설정에 의거해 만들어진 것이다.

ggplot(data = iris, aes(x = Petal.Length, y = Petal.Width, color = Species)) +
    geom_point() +
    scale_x_continuous() +
    scale_y_continuous() +
    scale_color_discrete()

스케일 함수의 아규먼트를 수정하면 보다 세밀한 심미성 조정이 가능하다.

ggplot(data = iris, aes(x = Petal.Length, y = Petal.Width, color = Species)) +
    geom_point() +
    scale_x_continuous(labels = NULL) +  # x축 표시하지 않음
    scale_y_continuous(breaks = seq(0, 3, by = 1)) + # y축 구간 설정
    scale_color_brewer(palette = "Set2") # 컬러 팔레트 설정

ColorBrewer

scale_color_brewer() 함수는 ColorBrewer 컬러 스케일을 사용한 것인데 익히고 있으면 많은 도움이 된다. 살펴보면 양적인 변수에 적용하기 좋은 팔레트가 있고, 질적인 변수에 적용하기 좋은 팔레트도 있다. 마음에 드는 팔레트를 골라 그래프의 색상 팔레트를 바꿔보자.

직접 색상 지정하기

내가 원하는 색상을 골라 직접 지정하는 방법도 있다. scale_color_brewer() 대신 scale_color_manual() 함수를 사용하면 된다. 또한 RGB 색상에 대한 html 코드를 사용해도 되고, R에서 부여한 657개의 이름 중에서 골라 사용해도 된다. 색상 이름 및 html 코드는 다음 사이트를 참고하라.

ggplot(data = iris, aes(x = Petal.Length, y = Petal.Width, color = Species)) +
    geom_point() +
    scale_x_continuous(labels = NULL) +  
    scale_y_continuous(breaks = seq(0, 3, by = 1)) + 
    scale_color_manual(
        values = c(
            "setosa" = "Tomato",
            "versicolor" = "#3498DB",
            "virginica" = "#F1C40F"
            ) # 컬러 팔레트 직접 설정
        )

3.2 좌표

좌표(coordinates) 레이어 혹은 좌표계(coordinate systems)는 그래픽 요소들의 위치 결정에 기준이 되는 준거체계이다. coord_flip() 함수가 대표적이다.

coord_flip()은 축을 전환한다. 종별 꽃잎 길이에 대한 박스플롯의 축을 바꿔 보다 보기 편하게 그려보자.

ggplot(data = iris, aes(x = Petal.Length, color = Species)) +
    geom_boxplot() +
    coord_flip()

3.3 테마

ggplot2 패키지는 다양한 complete 테마를 제공하고 있으며 theme_*() 함수를 이용해 설정 가능하다. 여기에서 확인한 후 마음에 드는 테마로 설정해보자.

ggplot(data = iris, aes(x = Petal.Length, y = Petal.Width, color = Species)) +
    geom_point() +
    theme_minimal()

theme() 함수를 사용하여 요소별로 하나씩 수정할 수도 있다. 수정 가능한 요소는 여기를 확인하라. 아래는 범례의 배열 방식과 위치를 수정한 것이다.

ggplot(data = iris, aes(x = Petal.Length, y = Petal.Width, color = Species)) +
    geom_point() +
    theme_minimal() +
    theme(
        legend.direction = "horizontal",
        legend.position = "bottom"
    )

4 세부 설정

4.1 라벨

labs() 함수를 이용하면 그래프에 다양한 종류의 라벨을 설정할 수 있다.

ggplot(data = iris, aes(x = Petal.Length, y = Petal.Width, color = Species)) +
    geom_point() +
    labs(
        title = "Relationship Between Petal Length and Width", # 그래프 제목
        subtitle = "Visualizing Morphological Differences Among Iris Setosa, Versicolor, and Virginica", # 부제
        x = "Petal Length (cm)", # x축 제목
        y = "Petal Width (cm)", # y축 제목
        color = "Species", # 범례 제목
        caption = "Source: Fisher's Iris Dataset (1936)" # 주석
    )

4.2 그래프 저장

두 가지 방식이 있다. 첫 번째 방식은 Output 창의 Plots 탭에 있는 Export 버튼은 이용하는 것이다. 다양한 그래픽 포멧 뿐만 아니라 pdf 형식으로도 저장할 수 있다. 저장 경로와 저장명, 그래프의 폭과 너비를 설정 후 save 버튼을 누르면 이미지가 저장된다.

두 번째 방식ggplot2 패키지의 ggsave() 함수를 이용하는 것이다. 결과물의 가로-세로 비, 해상도 등을 종합적으로 고려하여 최적의 세팅값을 찾아야 한다. 디바이스에 따라 동일한 세팅값이 다른 결과를 산출하기도 한다.

my_plot <- ggplot(data = iris, aes(x = Petal.Length, y = Petal.Width, color = Species)) +
    geom_point() +
    labs(
        title = "Relationship Between Petal Length and Width", # 그래프 제목
        subtitle = "Visualizing Morphological Differences Among Iris Setosa, Versicolor, and Virginica", # 부제
        x = "Petal Length (cm)", # x축 제목
        y = "Petal Width (cm)", # y축 제목
        color = "Species", # 범례 제목
    ) +
    theme_minimal() 

ggsave(filename = "my_plot.png", plot = my_plot, width = 8, height = 6, dpi = 600)