R) ggplot2 - y축 절단(1편)

R) ggplot2 - y축 절단(1편)

ggplot2로 y축이 튀는 경우 그 그래프에 절사 표기를 하는 방법을 알아보자.

개요

막대그래프의 경우 특정 값이 매우 커서 이를 시각화 하는 경우 상대적으로 값이 작은 다른 변수가 제대로 보이지 않는 경우가 발생한다. 이를 위해서 보통 상용로그(log10) 또는 그에 준하는 처리 방법을 활용하여 값을 어느 정도 맞춰준다. 하지만 이 마저도 통하지 않을 때가 있다. 그래서 임의의 값으로 가장 큰 값을 조정해서 그림을 그린 다음 MS의 Power Point나 각종 그래픽 툴로 그래프를 후처리 하는데 이를 R의 ggplot2를 사용하여 구현해보려고 한다.

데이터 준비

패키지와 데이터는 다음과 같다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
library("ggplot2")
library("reshape2")

set.seed(123)
df = data.frame(A = sample(1000:300, size = 20),
B = sample(100:300, size = 20),
C = sample(100:300, size = 20),
D = sample(100:300, size = 20),
E = sample(100:300, size = 20))

df_melt = melt(data = df)
df_agg = aggregate(data = df_melt, value ~ variable, FUN = "sum")
head(df_agg)
## variable value
## 1 A 13106
## 2 B 4049
## 3 C 3707
## 4 D 3514
## 5 E 4292

시각화

1차

우선 상기 샘플을 가지고 그래프를 그리면 다음과 같다.

1
2
3
ggplot(data = df_agg,
aes(x = variable, y = value)) +
geom_col()

1차 기준 그래프

이제 상기 그래프 막대 중에서 가장 y값이 큰 A 막대를 분리하여 별도의 표시를 할 예정이다. 우선 막대를 분리해야 하고 각 막대 사이의 공간을 띄워야 한다.

2차

다음의 그래프는 대략 5000 부터 최대값을 기준으로 분위수를 잡고 막대를 나누어보았다. 그리고 분리한 막대를 annotate() 함수로 구현하기 위해서 각 사각형의 꼭지점을 지정하였고 이 역시 분위수와 기존 df_agg 객체의 최대값을 활용하였다.

1
2
3
4
5
6
7
8
9
10
11
12
13
vec_diff = 5000:max(df_agg$value)
df_agg2 = df_agg

df_agg2[1, "value"] = quantile(vec_diff, probs = 0.45)

ggplot(data = df_agg2,
aes(x = variable, y = value)) +
geom_col() +
scale_y_continuous(limits = c(0, max(df_agg$value))) +
annotate(geom = "rect",
xmin = 0.5, xmax = 1.5,
ymin = quantile(vec_diff, probs = 0.55), ymax = max(df_agg$value),
fill = "#FF0000")

2차 분리 그래프
보기 쉽게 빨간색으로 포인트를 줬으나 현재 막대가 A를 기준으로 +0.5, -0.5가 아닌지 annotate() 함수로 만든 사각형이 조금더 너비가 넓다. 일반적으로 ggplot2 막대그래프의 경우 막대의 너비를 수정하지 않지만 향후 자유도를 위해서 이 부분도 고려하여 별도 변수로 조정 가능하게끔 해야한다.

3차

앞에서 확인했던 너비 조정 문제와 막대 분리시 그 간격을 조정하는 코드는 다음과 같다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
max_pos = which.max(df_agg$value)
max_value = df_agg[max_pos, "value"]
not_max_value_mean = mean(df_agg[-max_pos, "value"])

space_y = 0.05
space_x = 0.05

bar_xmin = max_pos - 0.5 + space_x
bar_xmax = max_pos + 0.5 - space_x

cut_value_mid = quantile(c(max_value, not_max_value_mean),
probs = c(0.5 - space_y, 0.5, 0.5 + space_y))
df_agg_cut = df_agg
df_agg_cut[max_pos, "value"] = cut_value_mid[1]

ggplot(data = df_agg_cut,
aes(x = variable, y = value)) +
geom_col() +
scale_y_continuous(limits = c(0, max(df_agg$value))) +
annotate(geom = "rect",
xmin = bar_xmin, xmax = bar_xmax,
ymin = cut_value_mid[3], ymax = max_value)

3차 분리 그래프

일단 “cut_value_mid” 객체에 분위수 값을 3개 주고 그에 맞게 나눴는데 현재 “space_x”, “space_y” 객체의 값이 간격조정의 모든 값에 할당되고 있어 현재 그래프는 딱 맞을지 몰라도 향후 커스터마이징을 더 하게되면 일그러질 가능성이 있다. 향후 수정해야 한다.

4차

아무튼 선분을 긋기 위한 annotate() 파티를 하였다. 대각선으로 표기하기 위해 “space_diag_x_ratio”와 “space_diag_y_ratio”를

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
space_diag_x_ratio = 0.05
space_diag_y_ratio = 0.05

ggplot(data = df_agg_cut,
aes(x = variable, y = value)) +
geom_col() +
scale_y_continuous(limits = c(0, max(df_agg$value))) +
annotate(geom = "rect",
xmin = bar_xmin, xmax = bar_xmax,
ymin = cut_value_mid[3], ymax = max_value) +
annotate(geom = "segment",
size = 2, color = "#FF0000",
x = bar_xmin - space_diag_y_ratio,
xend = bar_xmin + space_diag_y_ratio,
y = cut_value_mid[1] * (1 - space_diag_y_ratio), yend = cut_value_mid[1] * (1 + space_diag_y_ratio)) +
annotate(geom = "segment",
size = 2, color = "#FF0000",
x = bar_xmax - space_diag_y_ratio,
xend = bar_xmax + space_diag_y_ratio,
y = cut_value_mid[1] * (1 - space_diag_y_ratio), yend = cut_value_mid[1] * (1 + space_diag_y_ratio)) +
annotate(geom = "segment",
size = 2, color = "#FF0000",
x = bar_xmin - space_diag_y_ratio,
xend = bar_xmin + space_diag_y_ratio,
y = cut_value_mid[3] * (1 - space_diag_y_ratio), yend = cut_value_mid[3] * (1 + space_diag_y_ratio)) +
annotate(geom = "segment",
size = 2, color = "#FF0000",
x = bar_xmax - space_diag_y_ratio,
xend = bar_xmax + space_diag_y_ratio,
y = cut_value_mid[3] * (1 - space_diag_y_ratio), yend = cut_value_mid[3] * (1 + space_diag_y_ratio))

4차 분리 그래프

한계점

현재 막대그래프는 각 값의 차이가 매우 크지 않은 상황이다. 그리하여 이상치에 해당하는 값을 사용자가 지정하는 경우 임의로 막대의 높이를 낮추고 그 막대를 분리하는 작업이 필요하다. 이렇게 되면 y 축의 값 표기가 올바르지 않은데 이를 위해서 scale_y_continuous() 함수의 “limits” 인자에 할당하는 값을 별도의 변수로 지정하고 이상치 값 기반으로 현산을 하게 해야한다. 그리고 단순하게 선이 아니라 물결표시를 구현해야 하는데 이는 삼각함수를 기반으로 구현하는 것도 있겠고, 직선이 아닌 도형을 구현하고자 할 경우 geom_path() 또는 geom_polygon() 함수로 다각형 표현을 해야한다. 다음 포스팅에서 이 문제를 해결하도록 하겠다.

Your browser is out-of-date!

Update your browser to view this website correctly. Update my browser now

×