📚 R markdown
R markdown은 코드를 문서화하는데 아주 유용한 패키지이다.
Python의 경우 Jupyter notebook 같은 IDE를 이용해 markdown을 간편하게 사용이 가능하다.
물론 Jupyter notebook나 google colab으로 R을 사용할 수 있으나 R유저의 경우 익숙해져 버린 R studio(현 Posit)을 많이 사용할 것이다.
R markdown의 가장 큰 장점은 역시 다양한 패키지들이다. 이들을 사용하여 table을 꾸미는 방법을 소개하겠다.
패키지는 모두 r markdown환경에서 구동하며 코드는 각 패키지의 tutorial이나 cookbook을 참고하였다.
1. R markdown Basic
knitr::kable()
일단 R markdown은 만들어진 자료를 knitr package을 통해 하나로 엮어낸다.
knitr::kable()함수로 깔끔하게 프린트할 수 있다.
knitr::kable(
mtcars[1:5, ]
)
rmarkdown::paged_table()
rmarkdown::paged_table() 를 활용해 많은 양의 데이터를 페이지로 나누어서 보기 편하게 바꾸어 준다.
rmarkdown::paged_table(
mtcars[1:5, ]
)
tibble::as_tibble
rmarkdown::paged_table() 를 활용해 ## 으로 표시된 테이블을 얻을 수 있다.
짧은 데이터를 간략하게 보고 싶을 때 사용한다.
tibble::as_tibble(
mtcars[1:5, ]
)
2. DT package
- 공식 홈페이지 : https://rstudio.github.io/DT/
- data table의 약자로 table을 꾸미는 여러 함수를 담고 있다.
검색
가장 보편적인 기능은 데이터 검색 기능이다
library(DT)
datatable(mtcars, rownames = FALSE, filter="top", options = list(pageLength = 5, scrollX=T))
만약 3.6에 해당하는 값들을 찾고 싶다면 아래와 같이 검색해 준다
drat값이 3.6X인 두 row가 출력되었다
Cell 강조
df = as.data.frame(cbind(matrix(round(rnorm(50), 3), 10), sample(0:1, 10, TRUE)))
# create 19 breaks and 20 rgb color values ranging from white to red
brks <- quantile(df, probs = seq(.05, .95, .05), na.rm = TRUE)
clrs <- round(seq(255, 40, length.out = length(brks) + 1), 0) %>%
{paste0("rgb(255,", ., ",", ., ")")}
datatable(df) %>% formatStyle(names(df), backgroundColor = styleInterval(brks, clrs))
랜덤 하게 생성한 데이터에서 음의 값은 틴셀로, 양의 값은 값이 클수록 색이 진해지게 나타내었다.
Cell값 나타내기
# using styleColorBar
datatable(df) %>% formatStyle(names(df),
background = styleColorBar(range(df), 'lightblue'),
backgroundSize = '98% 88%',
backgroundRepeat = 'no-repeat',
backgroundPosition = 'center')
각 값들의 중앙값을 기준으로 상대적인 분포 크기를 나타낸다.
여러 기능 섞어 보기
datatable(iris) %>%
formatStyle('Sepal.Length', fontWeight = styleInterval(5, c('normal', 'bold'))) %>%
formatStyle(
'Sepal.Width',
color = styleInterval(c(3.4, 3.8), c('white', 'blue', 'red')),
backgroundColor = styleInterval(3.4, c('gray', 'yellow'))
) %>%
formatStyle(
'Petal.Length',
background = styleColorBar(iris$Petal.Length, 'steelblue'),
backgroundSize = '100% 90%',
backgroundRepeat = 'no-repeat',
backgroundPosition = 'center'
) %>%
formatStyle(
'Species',
transform = 'rotateX(45deg) rotateY(20deg) rotateZ(30deg)',
backgroundColor = styleEqual(
unique(iris$Species), c('lightblue', 'lightgreen', 'lightpink')
)
)
짜잔!.. 은 아니고 일단 여러 기능을 보여주기 위해서 이상하게 꾸며놓았지만..
formatstyle을 통해 각 column을 여러 스타일로 꾸밀 수 있다.
3. reactable package
- 각 테이블에 여러 그림을 넣고 꾸미고 싶을 때 사용할 수 있다.
- 이와 비슷한 reactablefmtr 패키지도 많이 쓰이고 있다
아이콘으로 표현하기
library(reactable)
data <- MASS::Cars93[1:5, c("Manufacturer", "Model", "Type", "AirBags", "Price")]
reactable(data, columns = list(
Model = colDef(html = TRUE, cell = JS('
function(cellInfo) {
// Render as a link
const url = `https://wikipedia.org/wiki/${cellInfo.row["Manufacturer"]}_${cellInfo.value}`
return `<a href="${url}" target="_blank">${cellInfo.value}</a>`
}
')),
AirBags = colDef(cell = JS("
function(cellInfo) {
// Render as an X mark or check mark
return cellInfo.value === 'None' ? '\u274c No' : '\u2714\ufe0f Yes'
}
")),
Price = colDef(cell = JS("
function(cellInfo) {
// Render as currency
return '$' + (cellInfo.value * 1000).toLocaleString()
}
"))
))
각 column을 눌러서 정렬도 가능하다
Cell 안에 그래프 그리기
data <- chickwts %>%
group_by(feed) %>%
summarise(weight = list(weight)) %>%
mutate(boxplot = NA, sparkline = NA)
reactable(data, columns = list(
weight = colDef(cell = function(values) {
sparkline(values, type = "bar", chartRangeMin = 0, chartRangeMax = max(chickwts$weight))
}),
boxplot = colDef(cell = function(value, index) {
sparkline(data$weight[[index]], type = "box")
}),
sparkline = colDef(cell = function(value, index) {
sparkline(data$weight[[index]])
})
))
heatmap 그리기
dimnames <- list(start(nottem)[1]:end(nottem)[1], month.abb)
temps <- matrix(nottem, ncol = 12, byrow = TRUE, dimnames = dimnames)
# ColorBrewer-inspired 3-color scale
BuYlRd <- function(x) rgb(colorRamp(c("#7fb7d7", "#ffffbf", "#fc8d59"))(x), maxColorValue = 255)
reactable(
temps,
defaultColDef = colDef(
style = function(value) {
if (!is.numeric(value)) return()
normalized <- (value - min(nottem)) / (max(nottem) - min(nottem))
color <- BuYlRd(normalized)
list(background = color)
},
format = colFormat(digits = 1),
minWidth = 50
),
columns = list(
.rownames = colDef(name = "Year", sortable = TRUE, align = "left")
),
bordered = TRUE
)
4. gtExtras package
- Cell을 꾸미는 데에 특화된 패키지이다
library(gtExtras)
mtcars %>%
head() %>%
dplyr::select(cyl, mpg) %>%
dplyr::mutate(mpg_pct_max = round(mpg/max(mpg) * 100, digits = 2),
mpg_scaled = mpg/max(mpg) * 100) %>%
dplyr::mutate(mpg_unscaled = mpg) %>%
gt::gt() %>%
gt_plt_bar_pct(column = mpg_scaled, scaled = TRUE) %>%
gt_plt_bar_pct(column = mpg_unscaled, scaled = FALSE, fill = "blue", background = "lightblue") %>%
gt::cols_align("center", contains("scale")) %>%
gt::cols_width(4 ~ px(125),
5 ~ px(125))
team_df <- readRDS(url("https://github.com/nflverse/nflfastR-data/raw/master/teams_colors_logos.rds"))
team_df %>%
dplyr::select(team_nick, team_abbr, team_conf, team_division, team_wordmark) %>%
head(8) %>%
gt::gt(groupname_col = "team_conf") %>%
gt_merge_stack(col1 = team_nick, col2 = team_division) %>%
gt_img_rows(team_wordmark)
5. formattable package
- 이 패키지의 장점은 다른 패키지보다 정형화된, 부드러운 시각화를 제공한다.
- 다른 패키지들은 호환성이 좋지만 간편하게 쓰고 싶다면 이 패키지를 추천한다.
library(formattable)
products <- data.frame(id = 1:5,
price = c(10, 15, 12, 8, 9),
rating = c(5, 4, 4, 3, 4),
market_share = percent(c(0.1, 0.12, 0.05, 0.03, 0.14)),
revenue = accounting(c(55000, 36400, 12000, -25000, 98100)),
profit = accounting(c(25300, 11500, -8200, -46000, 65000)))
products
sign_formatter <- formatter("span",
style = x ~ style(color = ifelse(x > 0, "green",
ifelse(x < 0, "red", "black"))))
sign_formatter(c(-1, 0, 1))
formattable(products, list(
price = color_tile("transparent", "lightpink"),
rating = color_bar("lightgreen"),
market_share = color_bar("lightblue"),
revenue = sign_formatter,
profit = sign_formatter))
- 그러나 단점으로는 numeric값에만 기본 적용되고, 나머지 character나 Logic값은 추가적인 변형이 필요하다
Example <- data.frame(
ID = 1:6,
Gender = c("F", "M", "M", "M", "F", "M"),
Type = c("A", "A", "B", "B", "C", "C"),
Value = c( 10, 8, 4, 12, 10, 6),
Log = c( F, T, T, T, T, F)
)
Example %>% formattable(
list(
ID = color_tile("transparent", "#FFCCCC"),
Value = color_bar("lightblue")
)
) %>%
as.datatable()
이제 우리는 category로 나누어진 gender, type, logic데이터에도 이와 같은 변형을 추가하려고 한다.
Example %>% formattable(
list(
ID = color_tile("transparent", "#FFCCCC"),
Value = color_bar("lightblue"),
Log = color_bar_factor
)
) %>%
as.datatable()
하지만 이렇게 Log값에 색을 적용할 순 없다. 아래와 같은 수정이 필요하다
# https://github.com/renkun-ken/formattable/issues/108
color_bar_factor <- formatter("span",
style = function(x) style(
display = "block",
color = "white",
border.radius = "4px",
background = c("red","green")[factor(as.character(x))]))
Example %>% formattable(
list(
ID = color_tile("transparent", "#FFCCCC"),
Value = color_bar("lightblue"),
Log = color_bar_factor,
Gender = color_bar_factor
)
) %>%
as.datatable()
이렇게 추가적인 변형이 필요하지만 개인적으로 reactable 보다 코드가 깔끔해서 추천한다
6. flextable package
- 이 친구도 다양한 기능을 제공하는데 거의 위 패키지들과 중복이 많다
library(flextable)
flextable( head(iris, n = 10 )) %>%
mk_par(j = 1,
value = as_paragraph(
linerange(value = Sepal.Length, max = max(Sepal.Length), height = .15)
),
part = "body")
7. rhandsontable package
- 다른 패키지와 다르게 table을 print 한 후에도 수정이 가능하게 해주는 패키지이다
- 나중에 웹으로 업로드하거나, 대시보드 서비스를 만들 때 사용하면 유용할 듯하다
library(rhandsontable)
DF = data.frame(integer = 1:10,
numeric = rnorm(10),
logical = rep(TRUE, 10),
character = LETTERS[1:10],
factor = factor(letters[1:10], levels = letters[10:1],
ordered = TRUE),
factor_allow = factor(letters[1:10], levels = letters[10:1],
ordered = TRUE),
date = seq(from = Sys.Date(), by = "days", length.out = 10),
stringsAsFactors = FALSE)
rhandsontable(DF, width = 600, height = 300) %>%
hot_col("factor_allow", allowInvalid = TRUE)
위의 테이블에서 각 cell을 클릭하면 내용 수정이 가능하다
이 패키지도 cell안에 그림을 그려 넣을 수 있다
DF = data.frame(val = 1:10, bool = TRUE, big = LETTERS[1:10],
small = letters[1:10],
dt = seq(from = Sys.Date(), by = "days", length.out = 10),
stringsAsFactors = FALSE)
DF$chart = c(sapply(1:5,
function(x) jsonlite::toJSON(list(values=rnorm(10),
options = list(type = "bar")))),
sapply(1:5,
function(x) jsonlite::toJSON(list(values=rnorm(10),
options = list(type = "line")))))
rhandsontable(DF, rowHeaders = NULL, width = 550, height = 300) %>%
hot_col("chart", renderer = htmlwidgets::JS("renderSparkline"))
🟦 추가적으로 읽어볼 글
https://towardsdatascience.com/ten-awesome-r-markdown-tricks-56ef6d41098
https://towardsdatascience.com/top-7-packages-for-making-beautiful-tables-in-r-7683d054e541
https://rfortherestofus.com/2019/11/how-to-make-beautiful-tables-in-r/
🟦 그래서 이 패키지가 왜 필요한가?
단순한 이유는 이쁘니까.. 써보고 싶어서 찾아본 것이고
차후를 생각해서 만약 웹에 어떤 서비스를 만들 때 잘 사용할 수 있을 것 같다
그러나 패키지를 넘어서 더 다양하게 꾸미기 위해서는 css나 html에 대한 이해가 조금 필요할 것 같기도 하다..