GT 패키지 소개

Page content

공지

본 블로그는 2020-04-08에 소개된 Great Looking Tables: gt(v0.2) by Richard Iannone 글을 번역한 것이다. 함수와 관련된 설명은 가급적 원어를 직접 인용 했으니, 영어로 직접 함수의 사용처를 음미하시길 바란다.

I. Intro

gt라는 이름은 grammar of tables즉 “테이블의 문법"의 줄임말이며 gt의 목표는 ggplot2와 비슷하게 운영하는 것이다. 특정 테이블을 쉽게 만들 수 있을 뿐만 아니라 다양한 문제를 해결하기 위해 서로 다른 방법으로 재결합할 수 있는 기본 구성요소 집합을 기술하는 역할을 한다.

R 사용자가 맞춤형(customized) 테이블을 R Markdown에서 만들 필요가 있다면, gt 패키지가 그 과정에 도움을 줄 것이다. 패키지를 설치해보자.

install.packages("gt")

표를 구성하고, 서식과 주석을 적용하며, 원하는 대로 스타일을 만드는 방법은 매우 다양하다. 현재 gt는 테이블을 HTML 출력 형식으로 렌더링하고 있으며 이미지 파일로 내보낼 수 있는 기능을 가지고 있다. 조만간 LaTeXRTF 출력 포맷도 지원할 계획이다.

gt 패키지의 Website에는 시작하기 위한 문서와 테이블 출력이 어떻게 나타나야 하는지를 보여주는 많은 예와 이미지가 있는 다양한 funciton reference section이 있다.

II. gt table structure

패키지의 저자가 고려한 것은 테이블의 구조를 파악해서 구조화 작업을 진행해야 하고, 이에 맞는 함수의 언어를 만드는 것이 필요했다. 아래 그림에서 보는 것처럼, 테이블(위에서 아래로 내려가면서)은 크게 Table Header, Column Lables, stub and stub head, table body, table footer이 포함된다. 이러한 각 구성 요소에는 하위 구성 요소가 있을 수 있다(예: table header에는 title & subtitle가 들어 있으며 table body에는 individual cells 을 포함하고 있다). 아래 그림을 통해서 다시한번 각 구조에 대해 파악하도록 한다.

새로운 문법을 배우는 것은 분명 고통이 수반된다. 그러나 ggplot2와 같이, 새로운 단어들은 익숙해지는 데 다소 시간이 걸리지만, 우리는 그것들을 배우는 것이 기존의 표를 분석하고 이해하는 능력을 향상시켜 gt로 성공적으로 재현할 것이라고 믿는다.

III. Sample

(1) Hello World

어느 문법이나 Hello World는 존재한다. exibble 데이터 집합은 gt에 포함되며, 다양한 데이터를 실험하기 위한 데이터셋을 준비했다.

library(gt)
library(dplyr)
glimpse(exibble)

8개의 row와 9개의 column으로 구성되어 있고, 숫자, 문자, 범주형, 날짜까지, 모든 종류의 데이터가 종류해도 무방하다. 기존의 데이터셋을 gt()로 바꿔주는 것이 중요하다. ggplot()에 데이터셋을 입력하는 형태를 기억해보자.

library(ggplot2)
data %>% 
  ggplot()

이 문법을 기억하면 좋을 것 같다.

exibble %>% gt()

사실 별거 아니지만, 조금 깔끔한 형태의 테이블이 출력되었다. ggplot() 함수가 ggplot2 그래프를 그리는 첫번째 starting point것처럼 gt() 함수는 gt 테이블을 만들기 위한 첫 번째 함수의 역할을 수행한다.

(2) Formatting data in columns

exibble 데이터셋에는 다양한 열 유형이 포함되어 있음을 기억하고 있다. 이것은 입력 데이터 값을 포맷하는 gtfmt_*() 함수를 활용하여 정리를 해보자. 가급적 원어를 그대로 정리하였다.

  • have num display numbers with exactly 2 decimal places using fmt_number()
  • show nicely formatted dates in date using date_style 6 (the m_day_year style) with fmt_date()
  • format the 24-h time values in time to time_style 4 (the hm_p style) with fmt_time()
  • make the datetimes in datetime formatted as such with the fmt_datetime() function
  • transform the currency column with fmt_currency() to show us values in the euro currency (currency = “KRW”)
exibble %>% 
  gt() %>% 
  fmt_number(columns = vars(num), decimals = 2) %>% 
  fmt_date(columns = vars(date), date_style = 6) %>% 
  fmt_time(columns = vars(time), time_style = 4) %>% 
  fmt_datetime(columns = vars(datetime), date_style = 6, time_style = 4) %>% 
  fmt_currency(columns = vars(currency), currency = "KRW")

번역시, 추가 내용, 위 함수에서 한국 사람들이 궁금해 할, KRW가 있는지 확인해봤다. 다행히 존재하였다. 확인하는 방법은 info_currencies(begins_with = "k")를 입력하면 k를 가지고 있는 다양한 나라의 화폐단위를 확인할 수 있고, 해당 문자열 코드를 KRW 대신 입력하면 된다.

info_currencies(begins_with = "k")

사용자들이 기대했던 것처럼, 전체 Column에는 매우 구체적인 방법으로 서식이 적용되었다. 좀 더 디테일한 수정도 가능하다. 특정 열에 있는 행의 하위 선택 유형을 지정할 수 있으며, 대상 행을 지정하는 방법(예: 행 색인, 스텁의 행 이름, 열 데이터에 기초한 조건문 등)이 상당히 많다.

조금 더 테이블을 입맛에 맞게 수정하고 싶다면, 더 많은 fmt_*() 함수가 있다. 물론, 이러한 함수들이 사용자나 또는 클라이언트의 요구에 정확히 부합하지 않는다면 사용자는 일반적인 fmt() 함수를 사용하고 특정 부분에는 사용자 정의 함수 또는 응용해서 가공할 수도 있다.

테이블에 몇몇 구성품을 추가할 수 있다. 제목(title)부제(subtitle)가 있는 머리글(header)소스 노트(source_note)가 있는 바닥글(footer)을 포함 할 수 있다. 이 때에는 tab_header()tab_source_note() 함수가 사용될 것이다.

exibble %>%
  gt() %>%
  tab_header(
    title = md("**gt** 패키지 내부에 있는 `exibble` 데이터셋  "),
    subtitle = "gt 패키지에는 6개의 데이터셋이 더 존재한다."
  ) %>%
  tab_source_note(md("More information is available at `?exibble`."))

기존 테이블에 parts 부분들이 들어간다면 tab_*() 함수가 사용되어질 것이다. 또한 Markdown 형태의 함수(md())가 사용되는 이유는 markdown style을 테이블에 적용하기 때문이다.

(4) Adding a stub and organizing rows into row groups

exibble 데이터셋은 행과 group columns로 이루어진 것을 알 수 있습니다. 이 때, gt(rowname_col = "row", groupname_col = "group")을 추가한다. 여기에서 stub이라는 문법은 조금 생소하기 때문에 아래 그림을 통해서 다시한번 확인하도록 한다.

부가적인 설명보다는 코드를 통해서 어떻게 결과값이 출력되는 확인해보자.

exibble %>%
  gt(rowname_col = "row", groupname_col = "group") %>%
  tab_header(
    title = md("**gt** 패키지 내부에 있는 `exibble` 데이터셋  "),
    subtitle = "gt 패키지에는 6개의 데이터셋이 더 존재한다."
  ) %>%
  tab_source_note(md("More information is available at `?exibble`."))

이러한 변화를 통해서 테이블 왼쪽의 영역(stub)에 행 레이블과 각 행 그룹위에 있는 행 그룹 레이블을 효과적으로 제공하는 것을 볼 수 있다. 만약에 행 그룹의 순서를 다시 정렬하고 싶다면, row_group_order() 함수를 사용하여 그룹화를 다시 정렬한다.

exibble %>%
  gt(rowname_col = "row", groupname_col = "group") %>%
  tab_header(
    title = md("**gt** 패키지 내부에 있는 `exibble` 데이터셋  "),
    subtitle = "gt 패키지에는 6개의 데이터셋이 더 존재한다."
  ) %>%
  tab_source_note(md("More information is available at `?exibble`.")) %>% 
  row_group_order(
    groups = c("grp_b", "grp_a")
  )

(5) Using spanner column labels

stub과 같이, 하나 이상의 열을 포함하는 spanner column 레이블을 통해 열 그룹화를 만들 수 있다. tab_spanner() 함수가 이러한 기능을 제공하는데, 라벨과 열 선택을 통해 새 라벨이 해당 열 위에 배치되고 관련 수평 규칙은 가로로 확장된다. 열이 서로 인접하지 않는다면 tab_spanner()가 자동으로 함께 모이게 된다.

exibble %>%
  gt(rowname_col = "row", groupname_col = "group") %>%
  tab_spanner(label = "날짜와 시간 Tab", columns = matches("date|time")) %>% 
  tab_header(
    title = md("**gt** 패키지 내부에 있는 `exibble` 데이터셋  "),
    subtitle = "gt 패키지에는 6개의 데이터셋이 더 존재한다."
  ) %>%
  tab_source_note(md("More information is available at `?exibble`.")) %>% 
  row_group_order(
    groups = c("grp_b", "grp_a")
  )

tab_spanner(label = "날짜와 시간 Tab", columns = matches("date|time")) 소스코드를 추가하면 되는데, matches("date|time") 문법은 dplyr 패키지내의 함수로 date 또는 time 열을 가져오라는 뜻이다.

IV. Much More

(1) gt Test Drive

짧게 소개하는 글이기 때문에, 이것으로 사용자의 만족을 기대할 수는 없다. RStudio Cloud 사용자라면 gt Test Drive에 있는 소스코드를 활용하는 것도 좋다.

(2) gt Dataset

  • gt 패키지 연습에 제공되는, 6개의 데이터셋을 패키지에 포함시켰다: countrypps, sza, gtcars, sp500, pizzaplace , exibble을 기념으로 로고를 만들었다 (굳이 이런걸..).

(3) 색상, 이미지 입히기 (소스코드 별도)

각주 삽입, 텍스트, 테두리 및 채우기 수정, 요약 행 추가와 같은 훨씬 더 유용한 작업을 수행할 수 있다. 본 블로그에서는 제공되지 않고, 대신 관련 코드를 제공한다. (available in this gist)

그러나, 번역시 가져왔다. 다행히 에러없이 잘 수행된다!

library(tidyverse)
library(paletteer)
library(gt)

pizzaplace %>%
  mutate(type = case_when(
    type == "chicken" ~ "chicken (pizzas with chicken as a major ingredient)",
    type == "classic" ~ "classic (classical pizzas)",
    type == "supreme" ~ "supreme (pizzas that try a little harder)",
    type == "veggie" ~ "veggie (pizzas without any meats whatsoever)",
  )) %>%
  mutate(size = factor(size, levels = c("S", "M", "L", "XL", "XXL"))) %>%
  dplyr::group_by(type, size) %>%
  dplyr::summarize(
    sold = n(),
    income = sum(price)
  ) %>%
  gt(rowname_col = "size") %>%
  tab_header(title = md("🍕 Pizzas Sold in 2015 🍕")) %>%
  fmt_number(
    columns = vars(sold),
    decimals = 0,
    use_seps = TRUE
  ) %>%
  fmt_currency(
    columns = vars(income),
    currency = "USD"
  ) %>%
  cols_align(align = "right", columns = TRUE) %>%
  data_color(
    columns = vars(sold, income),
    colors = scales::col_numeric(
      palette = paletteer::paletteer_d(
        palette = "ggsci::red_material"
      ) %>% as.character(),
      domain = NULL
    ),
    alpha = 0.8
  ) %>%
  summary_rows(
    groups = TRUE,
    columns = vars(sold),
    fns = list(TOTAL = "sum"),
    formatter = fmt_number,
    decimals = 0,
    use_seps = TRUE
  ) %>%
  summary_rows(
    groups = TRUE,
    columns = vars(income),
    fns = list(TOTAL = "sum"),
    formatter = fmt_currency,
    currency = "USD"
  ) %>%
  grand_summary_rows(
    columns = vars(sold),
    fns = list(`GRAND TOTAL` = "sum"),
    formatter = fmt_number,
    decimals = 0,
    use_seps = TRUE
  ) %>%
  grand_summary_rows(
    columns = vars(income),
    fns = list(`GRAND TOTAL` = "sum"),
    formatter = fmt_currency,
    currency = "USD"
  ) %>%
  tab_footnote(
    footnote = "The pizza category with the highest total sales.",
    locations = cells_row_groups("classic (classical pizzas)")
  ) %>%
  tab_footnote(
    footnote = md("Custom sizes for **The Greek** pizza."),
    locations = cells_stub(c("XL", "XXL"))
  ) %>%
  tab_footnote(
    footnote = md("This is a new record. Truly, 2015 was a **great** year for the `pizzaplace`."),
    locations = cells_grand_summary(columns = vars(sold))
  ) %>%
  tab_options(
    summary_row.background.color = "#ACEACE80",
    grand_summary_row.background.color = "#990000",
    row_group.background.color = "#FFEFDB80",
    heading.background.color = "#EFFBFC",
    column_labels.background.color = "#EFFBFC",
    stub.background.color = "#EFFBFC",
    table.font.color = "#323232",
    table_body.hlines.color = "#989898",
    table_body.border.top.color = "#989898",
    heading.border.bottom.color = "#989898",
    row_group.border.top.color = "#989898",
    row_group.border.bottom.style = "none",
    stub.border.style = "dashed",
    stub.border.color = "#989898",
    stub.border.width = "1px",
    summary_row.border.color = "#989898",
    table.width = "60%"
  ) %>%
  opt_all_caps()

결론: 번역 시, 느낀점.

grammar of tables 이 한 단어로 향후 R Studio에서 테이블 다루는 문법이 어떤 형태로 발전하는지 보여주는 좋은 예가 될 것 같다. 기존에는 굉장히 많은 종류의 테이블 관련 패키지가 존재하였지만, 문법적으로 일치가 되지는 않았다. ggplot2에서 발전한 다양한 extension 패키지가 있는 것을 고려하면, table 역시 그러한 형태로 발전할 것은 분명해 보인다.

더 보기

현재 gt 패키지에 Latex 지원이 되지 않는다. 따라서, 현재 모든 output은 html 코드로 출력되는 관련 파일 링크를 당분간 이 섹션에서 공유를 할 예정이니, 한번 더 아래 링크에서 확인하기를 바란다.

내용은 똑같다.