R로 그래프 플로팅을 하기 위한 몇 가지 팁

이 글은 R기반의 데이터 시각화라는 주제로 dbguide.net에 기고했던 시리즈 물의 외전격의 원고이다. 이전 시각화에 대한 글은 이곳에 링크가 걸려있으니 참고하기 바란다.

금번 회에서는 R로 그래프 플로팅을 하는 몇 가지 팁을 올리도록 하겠다. 사실 R로 그래프를 플로팅 하는 방법도 중요하지만 이를 목적에 맞게 적절히 제공하는 것도 무엇보다 중요하다. 이를 위해 꼭 알아둬야 할 것들 몇 가지를 정리하겠다.

1. 웹으로 게시할 그래프에 J(E)PG를 사용하지 말자.

대부분 많은 사용자들이 디지털카메라를 사용하면서 JPG라는 이미지 포맷을 익숙하게 사용한다. 따라서 아무 의심없이 R에서는 그래프 플로팅을 파일로 뽑아낼 때 JPG를 사용할 수 있는데, 이는 JPG라는 포맷의 성격을 잘못 알고 사용하는 것이다. JPEG포맷은 이미지나, 색상의 경계들에 대해서 양자화 압축을 수행하는데, 이 부분 때문에 그래프에서 라인과 같은 성분의 경우 흐릿하게 보일 수 있기 때문이다.

아래는 JPG와 PNG를 동일 크기의 이미지로 출력하기 위한 예제 코드이다.

library(ggplot2)

jpeg(width = 500, height = 500, filename = "jpg.jpg")
ggplot(diamonds, aes(carat, price)) + geom_line(aes(colour = clarity))
dev.off()

png(width = 500, height = 500, filename = "png.png")
ggplot(diamonds, aes(carat, price)) + geom_line(aes(colour = clarity))
dev.off()

cairo_pdf(filename = "pdf.pdf")
ggplot(diamonds, aes(carat, price)) + geom_line(aes(colour = clarity))
dev.off()

그리고 실제 그래프를 뽑아본 뒤 이를 같은 배율로 확대한 화면을 캡처한 화면이다(왼쪽이 JPG이고 오른쪽이 PNG이다).

JPG 파일에서 경계부분의 색상이 이상하며, 라인 색상 역시 바랜듯한 빛을 띠고 있다. 바로 이런 이유 때문에 그래프 출력에 JPG를 사용하지 말라는 것이다. 그리고 JPG, PNG 모두 계단 현상이 있는 것을 볼 수 있을 것이다. 이는 레스터 이미지(Raster Graphics)의 표현방식 때문이다. 이런 계단현상은 이미지를 확대할 경우 더욱 부각되는 단점이 있다. 따라서 PNG에 만족하지 못한다면, PDF방식의 이미지 출력을 사용할 것을 추천한다. PDF로 출력할 경우 같은 그래프가 아래와 같은 방식으로 표현된다.

이 경우에는 아무래 확대해도 계단현상이 일어나지 않는다.

2. anti-aliasing을 활성화 하라.

대부분의 시스템은 anti-aliasing을 활성화 해놓지만, 몇몇 시스템의 경우 이 부분을 확인해야 될 경우가 있다. 이 기술은 경사방향의 선분이나 경계선에서 생기는 계단형의 모양을 제거하는 기술로서 픽셀로 표현된 기울어진 선분이 자연스럽게 연결되게 한다.
아래의 첫 번째 그래프가 기본 옵션으로 플로팅한 결과이며 두 번째는 cairo 라이브러리를 활용한 subpixel anti-aliasing을 준 옵션이다. 물론 둘 다 PNG로 뽑은 결과인데, 두 번째 그래프가 선분을 표현한 부분에서 좀더 자연스러운 이미지를 제공하는 것을 볼 수 있다.

최근의 R 버전에는 cairo가 기본 탑재가 되어 있어서 아래와 같은 명령으로 위와 같은 anti-aliasing기능을 사용할 수 있다.

# 윈도우 맑은 고딕 폰트를 R 폰트시스템에 연동
windowsFonts(malgun = windowsFont("맑은 고딕"))

png(width = 400, height = 400, filename = "pngnaa.jpg", family = "malgun")
ggplot(diamonds, aes(carat, price), family = "malgun") + geom_line(aes(colour = clarity)) + 
    ggtitle("none 안티얼라이어싱")
dev.off()

png(width = 400, height = 400, filename = "pngaa.jpg", type = "cairo", antialias = "subpixel", 
    family = "malgun")
ggplot(diamonds, aes(carat, price), family = "malgun") + geom_line(aes(colour = clarity)) + 
    ggtitle("안티얼라이어싱")
dev.off()

3. 정확한 디바이스 드라이버를 사용해 그래프를 저장하라

윈도우에서 R 기본 GUI를 사용하거나, RStudio를 사용하거나 했을 때, 기본적인 그래프를 저장할 옵션이 존재한다. 사실 그런 옵션을 사용해서 저장하는 건 추천하지 않는다. 왜냐면 GUI자체가 상세한 옵션을 제공해 주지 않기 때문이다. 따라서 GUI에서 제공하는 출력은 그래프가 의도하는 바대로 데이터를 잘 반영하고 있는지 확인하는 용도로 사용하고, 이를 가지고 웹에 게시하거나 출력물을 만드는 행위는 지양하기 바란다.
그렇다면 어떤 방식으로 출력물을 만드는지 궁금하다 할 수 있겠는데, 1, 2번 항목에서 예제코드를 출력한 방식으로 출력 파일을 생성하면 된다.
예를 들자면 아래와 같다.

png(…)    # jpeg(..), cairo_pdf(…), tiff(…)
…..   #출력하고자 하는 그래프 함수들을 실행한다. 
dev.off()

4. 필요시, 고해상도 이미지로 출력하라

R에서 그래프는 항상 출력물 기준으로 표현된다. 그리고 출력물의 기본 사이즈는 인치(inch)로 표현되는데, 이 때문에 png()나 jpeg()와 같은 명령어들에서 res라는 파라메터가 중요하게 된다. res에 들어가는 숫자의 단위는 pixel per inch로서 1인치를 몇 픽셀로 표현할 것인지를 결정하게 되는 옵션이다. 이 res는 또한 이 강좌에서는 dpi(dot per inch)하고 구분하지 않겠다. 도트는 프린터 출력의 표현 기준이 되며 픽셀은 모니터 출력의 기준이 되기 때문인데 어차피 같은 이미지를 무엇으로 표현하느냐의 문제니 구분을 두지 않겠다는 이야기다.

여기서 가정을 하자면, 5*5 인치 크기의 이미지를 파워포인트 슬라이드에 넣고자 하며 220 ppi가 지원되는 파워포인트 2010 슬라이드에 첨부하고자 한다(일반적으로 220을 지원한다).
R의 모든 플로팅은 72ppi로 맞춰져 있다. 따라서 5인치를 표현하기 위해서는 가로, 세로 360(5 * 72)픽셀로 정해져야 된다. 따라서 아래와 같은 그래프 생성 코드를 만들 수 있다.

png(width = 360, height = 360, filename = "pngaa360.jpg", type = "cairo", antialias = "subpixel", 
    family = "malgun")  # res = 72 옵션은 생략
ggplot(diamonds, aes(carat, price), family = "malgun") + geom_line(aes(colour = clarity)) + 
    ggtitle("72 ppi(dpi)")
dev.off()

그리고 220ppi를 지원하는 같은 내용의 이미지를 만들기 위한 코드를 아래와 같은데, 이 이미지는 가로, 세로 1100(220 * 5)픽셀로 계산된다.

png(width = 1100, height = 1100, filename = "pngaa1100.jpg", type = "cairo", 
    antialias = "subpixel", family = "malgun", res = 220)
ggplot(diamonds, aes(carat, price), family = "malgun") + geom_line(aes(colour = clarity)) + 
    ggtitle("220 ppi(dpi)")
dev.off()

실제 모니터에서 해상도의 차이를 볼 수 있는 방법은 이 두 파일을 파워포인트나 pdf를 통해 보는 것이다.
아래는 이 두 파일을 파워포인트 슬라이드쇼를 통해 출력한 결과이다.

사실 이렇게 봐서는 차이가 크게 나지 않을 것이다.
하지만 이 문서를 pdf로 출력 후 확대해서 보면 아래와 같은 차이가 있다.