21  내보내기

21.1 그래프 저장하기

ggsave 명령어를 사용해서, ggplot2에서 생성한 가장 최신 도식화 결과물을 저장하는 방법을 이미 살펴봤다. 다시 상기시키기 위해 명령어를 적어보면 다음과 같다:

# eval: false
ggsave("My_most_recent_plot.pdf")

RStudio 내부에서 그림을 저장할 경우, ‘Plot’ 윈도우에서 ‘Export’ 버튼을 사용한다. 버튼을 클릭하면 .pdf, .png, .jpg 혹은 다른 이미지 형식으로 저장할지 선택 옵션이 제시된다.

종종 ‘Plot’ 윈도우에 먼저 찍어보지 말고 도표를 저장하고 싶을 때도 있다. 아마도 여러 페이지에 걸친 PDF 문서를 생성하고 싶을 것이다. 예를 들어, 각각은 다른 도표로 말이다. 혹은 다수 파일에서 부분집합으로 데이터를 뽑아내고, 각 하위 데이터에 대해 도식화를 하고, 결과물을 도표로 저장하고자 한다. 하지만 각각에 대해 ‘Export’ 버튼을 클릭하려고 루프를 중단할 수는 없는 노릇이다.

이런 경우 더 유연한 접근법을 사용할 수 있다. pdf 함수는 새로운 PDF 장치를 생성한다. pdf 함수에 여러 인자를 사용해서 크기와 해상도를 조절할 수 있다.

pdf("Life_Exp_vs_time.pdf", width=12, height=4)
ggplot(data=gapminder, aes(x=year, y=lifeExp, colour=country)) +
  geom_line() +
  theme(legend.position = "none")

# 그런 다음에는 반드시 PDF 장치를 끄는 것을 잊지 말아야 한다!
dev.off()

저장한 문서를 열어서 살펴본다.

도전과제

pdf 명령어를 다시 작성해서 PDF 파일에 두 번째 페이지를 생성한다. 각 창 패널에 대륙별로 데이터를 패싯 도표(힌트: facet_grid 사용)로 출력한다.

pdf("Life_Exp_vs_time.pdf", width = 12, height = 4)

p <- ggplot(data = gapminder, aes(x = year, y = lifeExp, colour = country)) +
  geom_line() +
  theme(legend.position = "none")
p

p + facet_grid(. ~continent)

dev.off()

jpeg, png 등의 명령어도 다양한 형식으로 문서를 저장하는 데 유사하게 사용할 수 있다.

21.2 데이터를 파일로 저장

어느 시점이 되면 R에서 데이터를 내보내서 파일로 저장하기도 한다. 이런 목적으로 write.table 함수를 사용하는데, 앞서 살펴본 read.table 함수와 매우 유사하다.

데이터 정제 스크립트를 생성하자. gapminder 데이터에서 Australia 호주만 집중한다:

aust_subset <- gapminder[gapminder$country == "Australia",]

write.table(aust_subset,
  file="cleaned-data/gapminder-aus.csv",
  sep=","
)

쉘로 다시 전환해서 모든 것이 정상인지 데이터를 살펴본다.

$ head cleaned-data/gapminder-aus.csv

음… 엄밀하게 보면 원하는 바는 아니다. 이 모든 인용부호는 어디서 왔을까? 또한 행 번호도 보이는데 무의미하다.

도움말 파일을 살펴보고 파일에 저장하는 방식을 변경해 본다.

? write.table

기본 설정으로 데이터를 파일로 저장할 때 R은 자동으로 인용부호로 문자 벡터를 감싼다. 행과 열 명칭도 파일에 저장한다.

다음과 같이 고쳐본다:

write.table(
  gapminder[gapminder$country == "Australia",],
  file="cleaned-data/gapminder-aus.csv",
  sep=",", quote=FALSE, row.names=FALSE
)

쉘 기술을 사용해서 다시 데이터를 살펴본다:

$ head cleaned-data/gapminder-aus.csv

훨씬 좋아 보인다!

도전과제

1990년 이후 수집된 데이터를 gapminder 데이터에서 부분집합으로 구성하는 데이터 정제 스크립트를 작성한다. 상기 스크립트를 사용해서 작업한 새로운 부분집합 데이터를 cleaned-data/ 디렉터리에 파일로 저장한다.

write.table(
   gapminder[gapminder$year > 1990, ],
   file = "cleaned-data/gapminder-after1990.csv",
   sep = ",", quote = FALSE, row.names = FALSE
)
# 수업 저장소에 이 디렉터리가 포함되는 것을 원치 않기 때문에,
# cleaned-data 디렉터리를 삭제한다.
unlink("cleaned-data", recursive=TRUE)

21.3 웹 앱

데이터 과학 프로젝트의 마지막 단계는 결과를 공유하거나 사업적 목적을 위해 사용자에게 제공하는 것이다. R 마크다운/쿼토 보고서, 그래프, 데이터 파일 저장하는 것 외에도 웹 앱(shiny)(Wickham 2021)을 만들어 사용자와 상호작용할 수 있다. 앞서 살펴본 펭귄 데이터에서 성별을 예측하는 통계 모형, 기계학습 모형을 개발한 후 웹앱을 제작하여 서비스 형태로 커뮤니케이션하는 것도 물론 가능하다.

펭귄의 부리 길이, 부리 깊이, 물갈퀴 길이, 체중 등의 신체 특징을 입력받아 기계학습 모형에 넣어 성별과 성별 확률을 추론하는 웹앱(Shiny)이다.

21.3.1 UI 뼈대설계

draw.io 웹사이트에서 wireframe 템플릿을 활용하여 대시보드 UI 뼈대 설계(wireframe)를 작성한다. 성별 예측 모형 변수 4개가 입력값으로 들어가고, 출력값으로는 펭귄의 성별과 예측 모형이 예측한 성별을 어느 정도 지지하는지 확률값을 함께 출력하도록 화면 설계를 한다.

그림 21.1: 펭귄 성별예측 웹앱 UI 뼈대설계

21.3.2 shiny 구현

shinydashboard UI를 앞서 정의한 wireframe에 맞춰 코딩한다. shinydashboard 레이아웃 문법에 맞춰 4개의 입력값을 받아 이를 제대로 전달되는지 확인한다. 본격적으로 예측 모형과 연결시키지 않고 단순히 펭귄 성별과 성별 예측 확률은 난수를 생성하여 기본 기능을 확인한 후 개발을 단계적으로 진행하는 것을 추천한다.

tidymodels로 개발한 예측 모형을 shinydashboard에 연결시키기 위해서는 입력변수가 reactive라는 특성을 갖기 때문에 이를 데이터프레임, 예측 모형 추론, 성별 예측과 성별 예측 확률에 연결시키는 점을 유념하여 개발한다.

그림 21.2: 펭귄 성별예측 웹앱

21.3.3 shiny 코드

UI에서 fluidPage를 사용하여 sidebarPanelmainPanel로 구성한다. sidebarPanel에는 펭귄 특징 입력을 위한 sliderInput이 있다. mainPanel에는 예측 결과와 입력값을 출력하는 verbatimTextOutputtextOutput을 위치시킨다.

서버에서는 입력받은 펭귄 특징을 바탕으로 성별을 예측한다. read_rds로 미리 학습된 예측 모형을 불러온다. reactive를 사용하여 입력값에 따라 예측 데이터프레임과 예측 결과를 생성한다. 예측 결과와 확률, 입력값을 출력하기 위해 renderPrint, renderText를 사용한다. 최종적으로 shinyApp 함수로 UI와 Server를 결합하여 Shiny 앱을 생성한다.

library(shiny)
library(tidyverse)
library(shinydashboard)
library(tidymodels)
ui <- fluidPage(
  titlePanel("펭귄 성별 예측 앱"),
  sidebarLayout(
    sidebarPanel(
      sliderInput('bill_length_mm', '부리 길이 (mm)',
                  min=32, max=50, value=43, step=1),
      sliderInput('bill_depth_mm', '부리 깊이 (mm)',
                  min=13, max=22, value=17, step=1),
      sliderInput('flipper_length_mm', '물갈퀴 길이 (mm)',
                  min=170, max=235, value=200, step=1),
      sliderInput('body_mass_g', '체중 (g)',
                  min=2700, max=6300, value=4200, step=100)
    ),
    mainPanel(
      h3("펭귄 성별 예측 결과"),
      h4("성별"),
      verbatimTextOutput("pred_sex"),
      h4("성별 확률"),
      textOutput("prob_sex"),
      h4("입력값"),
      textOutput("bill_length"),
      textOutput("bill_depth"),
      textOutput("flipper_length"),
      textOutput("body_mass")
    )
  )
)
server <- function(input, output, session) {

  penguin_predictvie_model <- read_rds(str_glue("{here::here()}/code/penguin_app/penguin_predictvie_model.rds"))

  penguin_df <- reactive({
    tibble(
      "species" = "Adelie",
      "bill_length_mm" = input$bill_length_mm,
      "bill_depth_mm" = input$bill_depth_mm,
      "flipper_length_mm" = input$flipper_length_mm,
      "body_mass_g" = input$body_mass_g
    )
  })

  pred_sex <- reactive({
    predict(penguin_predictvie_model, penguin_df()) %>%
      unlist() %>%
      as.character()
  })

  prob_sex <- reactive({
    predict(penguin_predictvie_model, penguin_df(), type="prob")[,1]
  })

  output$pred_sex <- renderPrint({ pred_sex() })

  output$prob_sex <- renderText({
    paste0("성별 확률: ", round(prob_sex()*100, 2), "%")
  })

  output$bill_length <- renderText({
    paste0("부리 길이 (mm): ", input$bill_length_mm)
  })

  output$bill_depth <- renderText({
    paste0("부리 깊이 (mm): ", input$bill_depth_mm)
  })

  output$flipper_length <- renderText({
    paste0("물칼퀴 길이 (mm): ", input$flipper_length_mm)
  })

  output$body_mass <- renderText({
    paste0("체중 (g): ", input$body_mass_g)
  })

}
shinyApp(ui, server)

21.4 요약

데이터 분석 결과를 효과적으로 공유하고 활용하기 위해서는 다양한 방법을 고려할 수 있다. 우선 ggsave 명령어나 RStudio의 ‘Export’ 기능을 사용하여 그래프를 이미지 파일로 저장할 수 있다. 또한 pdf 함수를 사용하면 여러 페이지로 구성된 PDF 문서를 생성할 수 있어 보다 체계적인 보고서 작성이 가능하다.

데이터 자체를 공유해야 하는 경우에는 write.table 함수를 사용하여 CSV 파일 등의 형식으로 저장할 수 있다. 파일 저장 옵션을 적절히 설정하여 불필요한 정보가 포함되지 않도록 주의해야 한다.

한편 웹 앱을 개발하면 사용자와의 상호작용을 통해 데이터 분석 결과를 더욱 효과적으로 전달할 수 있다. Shiny 패키지를 사용하면 R로 개발한 예측 모형을 웹 앱으로 구현할 수 있다. UI 설계를 통해 사용자 친화적인 인터페이스를 구성하고, 서버 측에서는 입력값에 따라 예측 결과를 반환하도록 로직을 구성한다.

데이터 분석 결과를 공유하는 것은 데이터 과학 프로젝트의 마지막 단계이자 가장 중요한 부분이다. 분석 결과를 단순히 저장하는 것에 그치지 않고, 적극적으로 활용될 수 있도록 다양한 방법을 모색해야 한다. 보고서, 대시보드, 웹 앱, API 등 상황에 맞는 최적의 공유 방식을 선택하여 데이터 분석의 가치를 극대화할 수 있다.