1. 데이터 APP 개발하기 : 확률 밀도 함수, 회귀 분석, 주성분 분석 추가까지
1) 라이브러리 준비, 마커 클러스터링 까지
# 배포판 작성
# 10_1, 10_2, 10_3, 10_4, 10_5 종합적으로 작성
# 통계분석 8장 - 확률밀도함수, 회귀선, 주성분석 결과를 하나의 탭으로 3개의 열을 나눠서 표현하는 것을 추가!
# 1. 라이브러리 준비
library(sp);library(sf);library(leaflet);library(tidyverse);library(raster);library(rgdal);library(tmap);library(mapedit);library(leafem);library(ggfortify);library(grid);library(shiny);library(DT)
# 2. 데이터 불러오기
# 아파트 가격
load("./data/apt_price.rdata")
# 서울시 그리드 데이터
grid <- st_read("./data/sigun_grid/seoul.shp")
# 서울시 경계선 데이터
bnd <- st_read("./data/sigun_bnd/seoul.shp")
# 최고가 레스터 데이터
load("./data/raster_high.rdata")
# 급등 지역 레스터 데이터
load("./data/raster_hot.rdata")
# 3. 그리드 데이터 전처리
# 그리드 데이터 sf형을 sp형으로 변환
grid <- as(grid, "Spatial")
# sfg: simple feature geometry
# sfc: simple feature columns (the coordinate)
# sf: simple feature columns + attribute (data.frame)
# 그리드 데이터를 sfc로 변환
grid <- as(grid, "sfc")
# 아파트 가격 데이터와 그리드 데이터 합치기
# 데이터가 있는 그리드만 추출
grid <- grid[which(sapply(st_contains(st_sf(grid), apt_price),length) > 0)]
# 4. 마커 클러스터
# 마커 클러스터링 옵션 설정
# 상위 10%, 하위 10%, 나머지
# q10 <- quantile(apt_price$py, probs = seq(.1, .9, by = .1))
# 상위 10% = 90%
pct_90 <- as.numeric(quantile(apt_price$py, probs = seq(.1, .9, by = .1))[9])
# 하위 10% = 10%
pct_10 <- as.numeric(quantile(apt_price$py, probs = seq(.1, .9, by = .1))[1])
# 마커 클러스터링 함수 불러오기
load("./data/circle_marker/circle_marker.rdata")
# 색상 설정
circle.colors <- c()
#
2) 반응형 지도 만들기
# 5. 반응형 지도 만들기 => shiny application 내부
# 6. shiny application
# ui
ui <- fluidPage(
# 상단: 좌 (반응형 지도)
column(width = 9,
selectModUI("selectmap"),
div(style = "height:40px;")),
# 상단: 우 (슬라이더-면적, 건축연도)
column(width = 3,
sliderInput(inputId = "range_area", # ID
"Area", # 제목
min = 0, max = 350, # 범위: 최소값, 최대값
value = c(0, 200)), # default 값
sliderInput(inputId = "range_con", # ID
"Construction year", # 제목
min = 1960, max = 2021, # 범위: 최소값, 최대값
value = c(2000, 2021))), # default 값
# 하단 : 탭 2개
# 첫번째 탭 : 시각화 3개
# 두번째 탭 : 입력값에 따른 데이터 테이블 형태로 출력
tabsetPanel(
tabPanel("Visualizations", # 탭에 노출되어 있는 이름
column(width = 4, h4("Price", align = "center"),
plotOutput("density", height = 250)), # 확률밀도함수
column(width = 4, h4("Price Trends", align = "center"),
plotOutput("regression", height = 250)), # 회귀선
column(width = 4,h4("PCA", align = "center"),
plotOutput("pca", height = 250)) # 주성분분석
),
tabPanel("Table", # 탭에 노출되어 있는 이름
dataTableOutput("table")) # output의 결과
)
)
지난번과 다르게 하단 탭이 추가 됨.
# 첫번째 탭 : 시각화 3개 (Price, Price Trends, PCA)
# 두번째 탭 : 입력값에 따른 데이터 테이블 형태로 출력 (기존 데이터)
3) 반응식 설정하기 (서버)
# server
server <- function(input, output, session){
# 반응식
# A: 면적과 건축연도 범위에 해당하는 데이터 추출
all <- reactive({
all <- subset(apt_price, area >= input$range_area[1] & area <= input$range_area[2] & con_year >= input$range_con[1] & con_year <= input$range_con[2])
return(all)})
4) 지도 입출력 모듈 설정하기 (서버와 이어짐)
# B: 선택된 그리드 저장
sel <- callModule(selectMod, "selectmap",
leaflet() %>%
addTiles(options = providerTileOptions(minZoom = 9, maxZoom = 18)) %>%
# 서울시 경계선
addPolygons(data = bnd,
color = "gold",
fill = NA,
weight = 5) %>%
# 최고가 레스터 데이터
addRasterImage(raster_high,
colors = colorNumeric(palette = c("blue","green","yellow","red"),
domain = values(raster_high),
na.color = "transparent"),
opacity = 0.4) %>%
# 급등 지역 레스터 데이터
addRasterImage(raster_hot,
colors = colorNumeric(palette = c("blue", "green", "yellow", "red"),
domain = values(raster_hot),
na.color = "transparent"),
opacity = 0.4) %>%
# 체크 박스
addLayersControl(baseGroups = c("High Price in 2021", "Hot Price in 2021"),
options = layersControlOptions(collapsed = F)) %>%
# 마커 클러스터링
addCircleMarkers(data = apt_price,
# 경도
lng = unlist(map(apt_price$geometry,1)),
# 위도
lat = unlist(map(apt_price$geometry,2)),
fillColor = circle.colors,
weight = apt_price$py,
clusterOptions = markerClusterOptions(iconCreateFunction=JS(avg.formula))) %>%
# 그리드 추가
addFeatures(st_sf(grid), layerId = ~seq_len(length(grid)), color = "gray")
)
5) 선택에 따른 반응 결과 저장하기
# A 와 B 교집합
# 초기 반응값 저장
rv <- reactiveValues(intersect = NULL, selectgrid = NULL)
# 반응 결과 저장
observe({
# grid selected => 선택된 그리드 저장
gs <- sel()
# 선택(selected == TRUE)된 그리드 ID를 숫자형으로 변환하여 그리드 데이터에서 가져오기(sf형으로 변환)
rv$selectgrid <- st_sf(grid[as.numeric(gs[which(gs$selected == TRUE),"id"])])
# 데이터가 있는 경우
if(length(rv$selectgrid) > 0){
# A & B 교집합
rv$intersect <- st_intersects(rv$selectgrid, all())
# geometry 삭제
# rv$intersect 결과는 리스트
rv$sel <- st_drop_geometry(apt_price[unlist(rv$intersect), ])
} else{ # 데이터가 없는 경우
rv$intersect <- NULL
}
})
6) 확률밀도함수 추가
# 확률밀도함수
output$density <- renderPlot({
# 데이터가 없는 경우
if(nrow(rv$intersect) == 0)
return(NULL)
# 데이터가 있는 경우
max_all <- density(all()$py)
max_sel <- density(rv$sel$py)
# x: 평당 가격
# y: 확률 = 상대도수
max_all <- max(max_all$y)
max_sel <- max(max_sel$y)
# y축 최대값 // y축에는 max_all 들어가야 함
plot_high <- max(max_all,max_sel)
# plot_high
# 평균값
avg_all <- mean(all()$py)
avg_sel <- mean(rv$sel$py)
# avg_all;avg_sel
#
#
# 서울시 전체 지역 확률밀도함수 그리기
plot(density(all()$py),
ylim = c(0,plot_high),
col = "blue",
lwd = 2,
main = NA
)
# 서울시 전체 지역 평균값 선 그리기
abline(v = avg_all,
col = "blue",
lwd = 2,
lty = 2)
# 평균값 텍스트 입력
text(avg_all + avg_all*0.2, # x축 위치
plot_high * 0.1, # y축 위치
round(avg_all))
#
#
# 선택 지역 확률밀도함수 그리기
lines(density(rv$sel$py),
ylim = c(0,plot_high),
col = "red",
lwd = 2
)
# 서울시 선택 지역 평균값 선 그리기
abline(v = avg_sel,
col = "red",
lwd = 2,
lty = 2)
# 평균값 텍스트 입력
text(avg_sel + avg_sel*0.2, # x축 위치
plot_high * 0.1, # y축 위치
round(avg_sel))
})
7) 회귀선 추가
# 회귀선
output$regression <- renderPlot({
# 데이터가 없는 경우
if(nrow(rv$intersect) == 0)
return(NULL)
# 거래 연월일 변수의 타입을 날짜로 변환
# str(all)
# all$ymd <- as.Date(all$ymd, "%Y-%m-%d")
# str(sel)
# sel$ymd <- as.Date(sel$ymd, "%Y-%m-%d")
# 월별 평균 가격
# 서울시 전체 지역
all <- aggregate(all()$py ~ lubridate::floor_date(all()$ymd, "month"), FUN = "mean")
# 선택 지역
sel <- aggregate(rv$sel$py ~ lubridate::floor_date(rv$sel$ymd, "month"), FUN = "mean")
# 변수 이름 변경
# head(all);head(sel)
colnames(all) <- c("month","all_py")
colnames(sel) <- c("month","sel_py")
# head(all);head(sel)
# 회귀 모델
# 서울시 전체 지역
fit_all <- lm(all$all_py~ all$month)
# 선택 지역
fit_sel <- lm(sel$sel_py~ sel$month)
# 회귀계수 = 기울기
coef_all <- summary(fit_all)$coefficients[2]
coef_sel <- summary(fit_sel)$coefficients[2]
# 1년의 변화량
coef_all <- round(coef_all,1)*365
coef_sel <- round(coef_sel,1)*365
# coef_all;coef_sel
# 서울시 선택 지역 주석 추가
# hjust(왼쪽, 오른쪽 0~1에 따라)
# vjust(위, 아래 0~1에 따라)
sel_grob <- grobTree(textGrob(paste0("Selected:", coef_sel),
x = 0.1,
y = 0.9,
hjust = 0,
gp = gpar(col = "red",
fontsize = 14,
fontface = "bold")))
# 서울시 전체 지역 주석 추가
all_grob <- grobTree(textGrob(paste0("All:", coef_all),
x = 0.1,
y = 0.85,
hjust = 0,
gp = gpar(col = "blue",
fontsize = 14,
fontface = "italic")))
# 선택 지역
sel_p <- ggplot(sel, aes(x = month, y = sel_py))+
geom_line(color = "red", size = 1) +
xlab("Month")+
ylab("Price")+
stat_smooth(method = "lm",
color = "gray",
linetype = "dashed" ) +
theme_bw()
# 주석 추가하여 다시 시각화 # 서울시 전체 지역
sel_p + geom_line(data = all, aes(x = month, y =all_py), color="blue", size = 1)+
annotation_custom(sel_grob)+
annotation_custom(all_grob)
})
8) PCA 추가
# PCA
output$pca <- renderPlot({
# 데이터가 없는 경우
if(nrow(rv$intersect) == 0)
return(NULL)
# 데이터가 있는 경우
# 아파트 별 평균값
pca_data <- aggregate(list(rv$sel$con_year, rv$sel$floor, rv$sel$py, rv$sel$area), by = list(rv$sel$apt_nm),mean)
# 변수 이름 변경
colnames(pca_data) <- c("apt_nm","con_year","floor","py","area")
# head(pca_data)
# 주성분 분석
pca <- prcomp(~ con_year + floor + py +area,
data = pca_data,
scale = T) # 표준화 = z점수 = 변동성 맞춰주는 작업
# 아파트 이름 추가
autoplot(pca,
loadings.label = T,
loadings.label.size = 5) +
geom_label(aes(label=pca_data$apt_nm),
size=5)
})
9) 반응 결과 렌더링 후 실행까지
# rv$sel => 데이터테이블로 저장(렌더링 함수 사용)
output$table <- DT::renderDataTable({
dplyr::select(rv$sel,
# 열 선택
ymd, addr_1, apt_nm, price, area, floor, py) %>%
arrange(desc(py))},
extensions = 'Buttons',
options = list(dom = 'Bfrtrip',
scrollY = 300,
scrollCollapse = T,
paging = T,
buttons = c('excel'))
)
}
# shinyApp
shinyApp(ui, server)
* 핵심
- 샤이니 앱을 만드는 구조는 똑같다.
라이브러리 준비, 마커클러스터링 - 반응형 지도 - 반응식 설정 (서버) - 지도 입출력 모듈 설정
- 선택에 따른 반응 결과 저장 - (확률밀도함수, 회귀선, PCA) - 반응 결과 렌더링 후 실행까지
- 다만 원하는 분석 결과 값을 어디에 출력할 것인지에 따라 분석하는 코드의 위치만 바뀐다.
728x90
반응형
LIST
'수업내용 정리' 카테고리의 다른 글
공공데이터 with 샤이니 12 정리 - 샤이니 app 활용 사례 (지진 발생 분석) (0) | 2023.03.15 |
---|---|
공공데이터 with 샤이니 10_2 정리 - 확률 밀도 함수, 회귀 분석, 주성분 분석 추가하기 (0) | 2023.03.14 |
공공데이터 with 샤이니 9,10 정리 - 샤이니 입문하기, 데이터 분석 app 개발하기 (0) | 2023.03.14 |
공공데이터 with 샤이니 7,8 정리 - 분석 주제를 지도로 시각화하기, 통계 분석과 시각화 (0) | 2023.03.09 |
공공데이터 with 샤이니 6,7 정리 - 지오 데이터프레임 만들기, 분석 주제를 지도로 시각화하기 (0) | 2023.03.08 |