본문 바로가기

배운 책들 정리/공공데이터로 배우는 R 데이터 분석 with 샤이니

공공데이터 with 샤이니 12 - 샤이니 app 활용 사례 (지진 발생 분석)

1. 샤이니 app 활용 사례

 

1) 라이브러리, 지진 데이터 불러오기

 

# 지진 발생 분석 웹 어플리케이션 개발
# 0315_12_app3.R
# 데이터 준비
load("./data/earthquake/earthquake_16_21.rdata")
head(quakes)
# sn 연번, year 연도, month 월, day 일, mag 진도, depth 깊이, lat 위도, lon 경도, location 위치
library(shiny);library(leaflet);library(ggplot2);library(ggpmisc)

# install.packages(c("shiny","leaflet","ggplot2","ggpmisc"))

# ui
# bootstrapPage(): html,css,js 기능을 쉽게 사용, 웹 어플리케이션 좀 더 쉽게 개발할 수 있도록 지원. 트위터에서 개발

 

2) ui 만들기

 

ui <- bootstrapPage(
  # tags$style = 부트스트랩에 적용할 영역과 대상을 선정
  # 사용자 화면 페이지 스타일 설정
  tags$style(type = "text/css", "html, body {width:100%;height:100%"),
  
  # 지도 생성
  leafletOutput(
    outputId = "map", 
    width="100%",
    height = "100%"),
  
  # 메뉴 패널 (입출력 패널)
  absolutePanel(top = 10, 
                right = 10,
                # 슬라이드 입력(진도)
                sliderInput(
                  inputId = "range", # 입력  아이디
                  label = "Magnitude", # 라벨
                  min = min(quakes$mag), # 선택 범위 최솟값
                  max = max(quakes$mag), # 선택 범위 최댓값
                  value = range(quakes$mag), # 기본 선택 범위
                  step = 0.5 # 단계
                ),
                # 슬라이드 입력(기간)
                sliderInput(
                  inputId = "time",
                  label = "Period",
                  sep = "",
                  min = min(quakes$year), # 선택 범위 최솟값
                  max = max(quakes$year), # 선택 범위 최댓값
                  value = range(quakes$year), # 기본 선택 범위
                  step = 1 # 단계
                ),
                # 출력 : 빈도 히스토그램
                plotOutput("histCentile", height = 230),
                # 출력 : 빈도 - 깊이 산점도
                plotOutput("depth", height = 230),
                p(span("자료 출처: 기상청", align = "center",
                       style = "color:black;background-color:white"
                ))
                
  )
  
)

 

3) 서버 구현부터 실행까지

 

  # 히스토그램
  output$histCentile <-  renderPlot({
    # 데이터 없는 경우
    if(nrow(filteredData()) == 0)
      return(NULL)
    centileBreaks <- hist(plot = F, filteredData()$mag, breaks = 20)$breaks
    hist(filteredData()$mag,
         breaks = centileBreaks,
         main = "지진 발생 정보", xlab = "진도", ylab = "빈도",
         col = 'blue', border = 'grey')
  })
  
  # 산점도 + 회귀선
  output$depth <-  renderPlot({
    ggplot(filteredData(), aes(x=mag, y=-depth)) +
      geom_point(size=3, col="red")+
      geom_smooth(method = "lm", col="blue")+
      xlab('진도')+ylab('깊이')+
      stat_poly_eq(aes(label=paste(..eq.label..)),
                   label.x = "right", label.y = "top",
                   formula = y ~ x, parse = T, size = 5, col ="black")
    
  })


  # 입력값 변경에 따른 지도 업데이트
  observe({
    leafletProxy("map", data = filteredData()) %>%  
      clearShapes() %>% 
      addCircles(
        radius = ~log((quakes$mag))^20,
        weight = 1, color = "grey70",
        fillColor = "red", fillOpacity = 0.6, popup = ~paste(mag)
      )
  })
  
}

shinyApp(ui, server)

 

초기 화면

 

지정값을 달리 했을 때

 

col = rainbow (10) 으로 변경한 모습

 

 

샤이니 퍼블리싱까지 완료

 

4) 알록달록 꾸며보기 : 코드

 

# 지진 발생 분석 웹 어플리케이션 개발
# 0315_12_app3.R
# 데이터 준비
load("./data/earthquake/earthquake_16_21.rdata")
head(quakes)
# sn 연번, year 연도, month 월, day 일, mag 진도, depth 깊이, lat 위도, lon 경도, location 위치
library(shiny);library(leaflet);library(ggplot2);library(ggpmisc)

# install.packages(c("shiny","leaflet","ggplot2","ggpmisc"))

# ui
# bootstrapPage(): html,css,js 기능을 쉽게 사용, 웹 어플리케이션 좀 더 쉽게 개발할 수 있도록 지원. 트위터에서 개발

ui <- bootstrapPage(
  # tags$style = 부트스트랩에 적용할 영역과 대상을 선정
  # 사용자 화면 페이지 스타일 설정
  tags$style(type = "text/css", "html, body {width:100%;height:100%"),
  
  tags$h1("한국 5년간 지진 동향", style = "font-size: 20pt; font-weight: bold; text-align: center;"),
  # 지도 생성
  leafletOutput(
    outputId = "map", 
    width="100%",
    height = "100%"),
  
  # 메뉴 패널 (입출력 패널)
  absolutePanel(top = 120, 
                right = 30,
                # 슬라이드 입력(진도)
                sliderInput(
                  
                  inputId = "range", # 입력  아이디
                  label = tags$span("지진 규모로 찾기", style = "color: white;"), # 라벨
                  min = min(quakes$mag), # 선택 범위 최솟값
                  max = max(quakes$mag), # 선택 범위 최댓값
                  value = range(quakes$mag), # 기본 선택 범위
                  step = 0.5 # 단계
                ),
                # 슬라이드 입력(기간)
                sliderInput(
                  
                  inputId = "time",
                  label = tags$span("지진 연도로 찾기", style = "color: white;"),
                  sep = "",
                  min = min(quakes$year), # 선택 범위 최솟값
                  max = max(quakes$year), # 선택 범위 최댓값
                  value = range(quakes$year), # 기본 선택 범위
                  step = 1 # 단계
                ),
                # 출력 : 빈도 히스토그램
                plotOutput("histCentile", height = 230),
                # 출력 : 빈도 - 깊이 산점도
                plotOutput("depth", height = 230),
                p(span("자료 출처: 기상청", align = "center",
                       style = "color:white;font-weight: bold;"
                ))
                
  )
  
)



server <- function(input, output, session){
  # 반응식
  filteredData <- reactive({
    quakes[quakes$mag >= input$range[1] & quakes$mag <= input$range [2] &
             quakes$year >= input$time[1] & quakes$year <= input$time[2],]
  })
  # 지도
  output$map <- renderLeaflet({
    leaflet(quakes) %>% 
      addProviderTiles("NASAGIBS.ViirsEarthAtNight2012") %>%
      fitBounds(~min(lon), ~min(lat), ~max(lon), ~max(lat))
  })
  
  
  # 지도 샘플 1
  # 지도
  # output$map <- renderLeaflet({
  #   leaflet(quakes) %>% addTiles() %>%
  #     setView(lng = 128, lat = 36, zoom = 6.5)
  
  
  
  
  
  # 히스토그램
  output$histCentile <-  renderPlot({
    # 데이터 없는 경우
    if(nrow(filteredData()) == 0)
      return(NULL)
    # 데이터가 있는 경우
    centileBreaks <- hist(plot = F, filteredData()$mag, breaks = 20)$breaks
    
    hist(filteredData()$mag,
         breaks = centileBreaks,
         main = "지진 발생 정보", xlab = "진도", ylab = "빈도",
         col = rainbow(10), border = 'grey')
  })
  
  # 산점도 + 회귀선
  output$depth <-  renderPlot({
    
    # 데이터 없는 경우
    if(nrow(filteredData()) == 0)
      return(NULL)
    ggplot(filteredData(), aes(x=mag, y=-depth)) +
      geom_point(size=3, col="black")+
      geom_smooth(method = "lm", col="blue")+
      xlab('진도')+ylab('깊이')+
      stat_poly_eq(aes(label=paste(..eq.label..)),
                   label.x = "right", label.y = "top",
                   formula = y ~ x, parse = T,
                   size = 5, col ="black",
      )
    
  })
  
  
  # 입력값 변경에 따른 지도 업데이트
  observe({
    leafletProxy("map", data = filteredData()) %>%  
      clearShapes() %>%  # 지도 위에 표시된 데이터 정리
      addCircles(
        # 로그 변환을 통해 변동성을 낮춤 (큰 숫자를 작은 숫자로 변환)
        # radius = ~filteredData()$mag^6.5  # 원의 크기를 정해주는 것
        radius = ~log((quakes$mag))^20,
        
        weight = 1,  # 원의 두께
        color = "grey70", # 원의 둘레 색상
        fillColor = "white", # 원의 내부 색상
        fillOpacity = 0.6,  # 투명도
        popup = paste0("Time : ", filteredData()$year, "-", filteredData()$month, "-", filteredData()$day, "<br/>Place : ", filteredData()$location, "<br/>magnitude : ", filteredData()$mag)
        
      )
  })
  
}

shinyApp(ui, server)

 

 

https://441w9m-0-0.shinyapps.io/shiny01/

 

441w9m-0-0.shinyapps.io

 

 

 

* 핵심

 

- 변동성을 낮춰주는 함수 :

radius = ~log((quakes$mag))^20,

 

- 데이터 없는 경우 빈값을 표현하는 함수 : 

    if(nrow(filteredData()) == 0)
      return(NULL)

 

- 입력값 변경에 따른 데이터 업데이트 해주는 함수 :

observe({})

 

728x90
반응형
LIST