본문 바로가기
데이터분석/R

[R 데이터분석 with 샤이니] 분석 주제를 지도로 시각화하기

by 버섯도리 2022. 7. 5.

> ## 07-1 어느 지역이 제일 비쌀까?

> # Step 1 : 지역별 평균 가격 구하기

> setwd(dirname(rstudioapi::getSourceEditorContext()$path))
> load("./06_geodataframe/06_apt_price.rdata")
> library(sf)
> grid <- st_read("./01_code/sigun_grid/seoul.shp") # 서울시 1km 그리드 불러오기
Reading layer `seoul' from data source `D:\1_Study\1_BigData\01_R\02_Doit_R_Shiny\01_code\sigun_grid\seoul.shp' using driver `ESRI Shapefile'
Simple feature collection with 694 features and 2 fields
Geometry type: MULTIPOLYGON
Dimension:     XY
Bounding box:  xmin: 126.7645 ymin: 37.42899 xmax: 127.1835 ymax: 37.70146
Geodetic CRS:  GCS_unknown
> apt_price <- st_join(apt_price, grid, join = st_intersects) # 실거래+그리드 결합
> head(apt_price, 2)
Simple feature collection with 2 features and 15 fields
Geometry type: POINT
Dimension:     XY
Bounding box:  xmin: 126.9674 ymin: 37.57442 xmax: 126.9689 ymax: 37.58621
CRS:           +proj=longlat +datum=WGS84 +no_defs
         ymd         ym year  code    addr_1           apt_nm                                    juso_jibun  price
1 2021-01-14 2021-01-01 2021 11110 서울_종로         청운현대       서울특별시 종로구 청운동 56-45 청운현대 130000
2 2021-01-07 2021-01-01 2021 11110 서울_종로 광화문스페이스본 서울특별시 종로구 사직동 9-1 광화문스페이스본 150000
  con_year area floor   py cnt    ID SIG_CD                  geometry
1     2000  130     2 3300   1 83556   1100 POINT (126.9674 37.58621)
2     2008  145     6 3414   1 83095   1100 POINT (126.9689 37.57442)

> kde_high <- aggregate(apt_price$py, by=list(apt_price$ID), mean)
> colnames(kde_high) <- c("ID", "avg_price")
> head(kde_high, 2)
     ID avg_price
1 79520  1506.167
2 79765  3600.258

> # Step 2 : 평균 가격 정보 표시하기

> kde_high <- merge(grid, kde_high, by="ID")
> library(ggplot2)
> library(dplyr)
> kde_high %>% ggplot(aes(fill = avg_price)) +
+   geom_sf() +
+   scale_fill_gradient(low = "white", high = "red")

> # Step 3 : 지도 경계 그리기

> library(sp)
> kde_high_sp <- as(st_geometry(kde_high), "Spatial")
> x <- coordinates(kde_high_sp)[,1]
> y <- coordinates(kde_high_sp)[,2]

> l1 <- bbox(kde_high_sp)[1,1] - (bbox(kde_high_sp)[1,1] * 0.0001)
> l2 <- bbox(kde_high_sp)[1,2] + (bbox(kde_high_sp)[1,2] * 0.0001)
> l3 <- bbox(kde_high_sp)[2,1] - (bbox(kde_high_sp)[2,1] * 0.0001)
> l4 <- bbox(kde_high_sp)[2,2] + (bbox(kde_high_sp)[1,1] * 0.0001)

> install.packages("spatstat")

> library(spatstat)

> win <- owin(xrange = c(l1,l2), yrange = c(l3,l4))
plot(win)

> rm(list = c("kde_high_sp", "apt_price", "l1", "l2", "l3", "l4"))

> # Step 4 : 밀도 그래프 표시하기

# 포인트 패턴 분석
> p <- ppp(x, y, window = win) # 경계선 위에 좌표값 포인트 생성
> d <- density.ppp(p, weights = kde_high$avg_price, # 커널 밀도 함수로 변환
+                  sigma = bw.diggle(p),
+                  kernel = 'gaussian')
plot(d) # 밀도 그래프 확인

> rm(list = c("x", "y", "win", "p"))

> # Step 5 : 래스터 이미지로 변환하기

> # 래스터 이미지 : 일반적으로 직사각형 격자의 화소, 색의 점을 모니터, 종이 등의 매체에 표시하는 자료 구조
> # 커널 밀도 추정에서 의미 있는 데이터는 보통 상위 20~30% 범위 내에 있다.
> # 여기에서는 전체 데이터의 25% 지점만 의미 있는 신호라고 가정.
> d[d < quantile(d)[4] + (quantile(d)[4] * 0.1)] <- NA

> library(raster)
> raster_high <- raster(d) # 래스터 변환
plot(raster_high)

> # Step 6 : 불필요한 부분 자르기

> bnd <- st_read("./01_code/sigun_bnd/seoul.shp") # 서울시 경계선 불러오기
Reading layer `seoul' from data source `D:\1_Study\1_BigData\01_R\02_Doit_R_Shiny\01_code\sigun_bnd\seoul.shp' using driver `ESRI Shapefile'
Simple feature collection with 1 feature and 1 field
Geometry type: POLYGON
Dimension:     XY
Bounding box:  xmin: 126.7645 ymin: 37.42899 xmax: 127.1835 ymax: 37.70146
Geodetic CRS:  GCS_unknown
> raster_high <- crop(raster_high, extent(bnd)) # 외곽선 자르기
crs(raster_high) <- sp::CRS("+proj=longlat +datum=WGS84 +no_defs +ellps=WGS84 +towgs84=0,0,0") # 좌표계 정의
plot(raster_high)
plot(bnd, col=NA, border="red", add=TRUE)

> # Step 7 : 지도 그리기

> install.packages("rgdal")
> library(rgdal)
> library(leaflet)
> leaflet() %>%
+   #---# 기본 지도 불러오기
+   addProviderTiles(providers$CartoDB.Positron) %>%
+   #---# 서울시 경계선 불러오기
+   addPolygons(data = bnd, weight = 3, color = "red", fill = NA) %>%
+   #---# 래스터 이미지 불러오기
+   addRasterImage(raster_high,
+                  colors = colorNumeric(c("blue", "green", "yellow", "red"),
+                                        values(raster_high), na.color = "transparent"),
+                  opacity = 0.4)

Warning messages:
1: In wkt(projfrom) : CRS object has no comment
2: In showSRID(uprojargs, format = "PROJ", multiline = "NO", prefer_proj = prefer_proj) :
  Discarded ellps WGS 84 in Proj4 definition: +proj=merc +a=6378137 +b=6378137 +lat_ts=0 +lon_0=0 +x_0=0 +y_0=0 +k=1 +units=m +nadgrids=@null +wktext +no_defs +type=crs
3: In showSRID(uprojargs, format = "PROJ", multiline = "NO", prefer_proj = prefer_proj) :
  Discarded datum World Geodetic System 1984 in Proj4 definition
4: In wkt(pfrom) : CRS object has no comment
5: In rgdal::rawTransform(projfrom, projto, nrow(xy), xy[, 1], xy[,  :
  Using PROJ not WKT2 strings
6: In wkt(pfrom) : CRS object has no comment
7: In rgdal::rawTransform(projfrom, projto, nrow(xy), xy[, 1], xy[,  :
  Using PROJ not WKT2 strings
8: In rgdal::rawTransform(projto_int, projfrom, nrow(xy), xy[, 1],  :
  Using PROJ not WKT2 strings
9: In wkt(pfrom) : CRS object has no comment
10: In rgdal::rawTransform(projfrom, projto, nrow(xy), xy[, 1], xy[,  :
  Using PROJ not WKT2 strings

> # Step 8 : 평균 가격 정보 저장하기

> dir.create("07_map")
> save(raster_high, file = "./07_map/07_kde_high.rdata")


> ## 07-2 요즘 뜨는 지역이 어디일까?

> # Step 1 : 데이터 준비하기

> setwd(dirname(rstudioapi::getSourceEditorContext()$path))
> load("./06_geodataframe/06_apt_price.rdata")
> grid <- st_read("./01_code/sigun_grid/seoul.shp") # 서울시 1km 그리드 불러오기
Reading layer `seoul' from data source `D:\1_Study\1_BigData\01_R\02_Doit_R_Shiny\01_code\sigun_grid\seoul.shp' using driver `ESRI Shapefile'
Simple feature collection with 694 features and 2 fields
Geometry type: MULTIPOLYGON
Dimension:     XY
Bounding box:  xmin: 126.7645 ymin: 37.42899 xmax: 127.1835 ymax: 37.70146
Geodetic CRS:  GCS_unknown
> apt_price <- st_join(apt_price, grid, join = st_intersects) # 실거래+그리드 결합
> head(apt_price, 2)
Simple feature collection with 2 features and 15 fields
Geometry type: POINT
Dimension:     XY
Bounding box:  xmin: 126.9674 ymin: 37.57442 xmax: 126.9689 ymax: 37.58621
CRS:           +proj=longlat +datum=WGS84 +no_defs
         ymd         ym year  code    addr_1           apt_nm                                    juso_jibun  price
1 2021-01-14 2021-01-01 2021 11110 서울_종로         청운현대       서울특별시 종로구 청운동 56-45 청운현대 130000
2 2021-01-07 2021-01-01 2021 11110 서울_종로 광화문스페이스본 서울특별시 종로구 사직동 9-1 광화문스페이스본 150000
  con_year area floor   py cnt    ID SIG_CD                  geometry
1     2000  130     2 3300   1 83556   1100 POINT (126.9674 37.58621)
2     2008  145     6 3414   1 83095   1100 POINT (126.9689 37.57442)

> # Step 2 : 이전/이후 데이터 세트 만들기

> kde_before <- subset(apt_price, ymd < '2021-07-01')
> kde_before <- aggregate(kde_before$py, by=list(kde_before$ID), mean)
> colnames(kde_before) <- c("ID", "before")

> kde_after <- subset(apt_price, ymd >= '2021-07-01')
> kde_after <- aggregate(kde_after$py, by=list(kde_after$ID), mean)
> colnames(kde_after) <- c("ID", "after")

> kde_diff <- merge(kde_before, kde_after, by = "ID")
> kde_diff$diff <- round( ((kde_diff$after - kde_diff$before) / kde_diff$before) * 100, 0) # 변화율 계산
> head(kde_diff, 2)
     ID   before   after diff
1 79520 1451.000 1517.20    5
2 79765 3545.216 3681.72    4

> # Step 3 : 가격이 오른 지역 찾기

> library(sf)
> kde_diff <- kde_diff[kde_diff$diff > 0, ]
> kde_hot <- merge(grid, kde_diff, by = "ID") # 그리드에 상승 지역 결합
> library(ggplot2)
> library(dplyr)
> kde_hot %>%
+   ggplot(aes(fill = diff)) +
+   geom_sf() +
+   scale_fill_gradient(low = "white", high = "red")

> # Step 4 : 지도 경계 그리기

> library(sp)
> kde_hot_sp <- as(st_geometry(kde_hot), "Spatial")
> x <- coordinates(kde_hot_sp)[,1]
> y <- coordinates(kde_hot_sp)[,2]

> l1 <- bbox(kde_hot_sp)[1,1] - (bbox(kde_hot_sp)[1,1] * 0.0001)
> l2 <- bbox(kde_hot_sp)[1,2] + (bbox(kde_hot_sp)[1,2] * 0.0001)
> l3 <- bbox(kde_hot_sp)[2,1] - (bbox(kde_hot_sp)[2,1] * 0.0001)
> l4 <- bbox(kde_hot_sp)[2,2] + (bbox(kde_hot_sp)[1,1] * 0.0001)

> library(spatstat)
> win <- owin(xrange = c(l1,l2), yrange = c(l3,l4))
plot(win)

> rm(list = c("kde_hot_sp", "apt_price", "l1", "l2", "l3", "l4"))

> # Step 5 : 밀도 그래프 표시하기

> # 포인트 패턴 분석
> p <- ppp(x, y, window = win) # 경계선 위에 좌표값 포인트 생성
> d <- density.ppp(p, weights = kde_hot$diff, # 커널 밀도 함수로 변환
+                  sigma = bw.diggle(p),
+                  kernel = 'gaussian')
plot(d) # 밀도 그래프 확인

> rm(list = c("x", "y", "win", "p"))

> # Step 6 : 래스터 이미지로 변환하기

> d[d < quantile(d)[4] + (quantile(d)[4] * 0.1)] <- NA
> library(raster)
> raster_hot <- raster(d) # 래스터 변환
> plot(raster_hot)


> # Step 7 : 불필요한 부분 자르기

> bnd <- st_read("./01_code/sigun_bnd/seoul.shp") # 서울시 경계선 불러오기
Reading layer `seoul' from data source `D:\1_Study\1_BigData\01_R\02_Doit_R_Shiny\01_code\sigun_bnd\seoul.shp' using driver `ESRI Shapefile'
Simple feature collection with 1 feature and 1 field
Geometry type: POLYGON
Dimension:     XY
Bounding box:  xmin: 126.7645 ymin: 37.42899 xmax: 127.1835 ymax: 37.70146
Geodetic CRS:  GCS_unknown
> raster_hot <- crop(raster_hot, extent(bnd)) # 외곽선 자르기
crs(raster_hot) <- sp::CRS("+proj=longlat +datum=WGS84 +no_defs +ellps=WGS84 +towgs84=0,0,0") # 좌표계 정의
plot(raster_hot)
plot(bnd, col=NA, border="red", add=TRUE)

> # Step 8 : 지도 그리기

> library(leaflet)
leaflet() %>%
+   #---# 기본 지도 불러오기
+   addProviderTiles(providers$CartoDB.Positron) %>%
+   #---# 서울시 경계선 불러오기
+   addPolygons(data = bnd, weight = 3, color = "red", fill = NA) %>%
+   #---# 래스터 이미지 불러오기
+   addRasterImage(raster_hot,
+                  colors = colorNumeric(c("blue", "green", "yellow", "red"),
+                                        values(raster_hot), na.color = "transparent"),
+                  opacity = 0.4)

Warning messages:
1: In showSRID(uprojargs, format = "PROJ", multiline = "NO", prefer_proj = prefer_proj) :
  Discarded ellps WGS 84 in Proj4 definition: +proj=merc +a=6378137 +b=6378137 +lat_ts=0 +lon_0=0 +x_0=0 +y_0=0 +k=1 +units=m +nadgrids=@null +wktext +no_defs +type=crs
2: In showSRID(uprojargs, format = "PROJ", multiline = "NO", prefer_proj = prefer_proj) :
  Discarded datum World Geodetic System 1984 in Proj4 definition

> # Step 9 : 평균 가격 변화율 정보 저장하기

> save(raster_hot, file = "./07_map/07_kde_hot.rdata")



> ## 07-3 우리 동네가 옆 동네보다 비쌀까?

> # Step 1 : 데이터 준비하기

> setwd(dirname(rstudioapi::getSourceEditorContext()$path))
> load("./06_geodataframe/06_apt_price.rdata")
> load("./07_map/07_kde_high.rdata")
> load("./07_map/07_kde_hot.rdata")

> library(sf)
> bnd <- st_read("./01_code/sigun_bnd/seoul.shp")
Reading layer `seoul' from data source `D:\1_Study\1_BigData\01_R\02_Doit_R_Shiny\01_code\sigun_bnd\seoul.shp' using driver `ESRI Shapefile'
Simple feature collection with 1 feature and 1 field
Geometry type: POLYGON
Dimension:     XY
Bounding box:  xmin: 126.7645 ymin: 37.42899 xmax: 127.1835 ymax: 37.70146
Geodetic CRS:  GCS_unknown
> grid <- st_read("./01_code/sigun_grid/seoul.shp")
Reading layer `seoul' from data source `D:\1_Study\1_BigData\01_R\02_Doit_R_Shiny\01_code\sigun_grid\seoul.shp' using driver `ESRI Shapefile'
Simple feature collection with 694 features and 2 fields
Geometry type: MULTIPOLYGON
Dimension:     XY
Bounding box:  xmin: 126.7645 ymin: 37.42899 xmax: 127.1835 ymax: 37.70146
Geodetic CRS:  GCS_unknown

> # Step 2 : 마커 클러스터링 옵션 설정하기

> #---# 마커 클러스터링 함수(javascript avg.fomula) 등록
> load("./01_code/circle_marker/circle_marker.rdata")
> #---# 마커 클러스터링 색상 설정 : 상, 중, 하
> circle.colors <- sample(x=c("red","green","blue"), size = 1000, replace = TRUE)

> # Step 3 : 마커 클러스터링 시각화하기

> # install.packages("purrr")
> library(purrr)
> library(leaflet)
> leaflet() %>%
+   #---# 오픈스트리트맵 불러오기
+   addTiles() %>%
+   #---# 서울시 경계선 불러오기
+   addPolygons(data = bnd, weight = 3, color = "red", fill = NA) %>%
+   #---# 최고가 래스터 이미지 불러오기
+   addRasterImage(raster_high,
+                  colors = colorNumeric(c("blue", "green", "yellow", "red"), values(raster_high)
+                                        , na.color = "transparent"), opacity = 0.4, group = "2021 최고가") %>%
+   #---# 급등지 래스터 이미지 불러오기
+   addRasterImage(raster_hot,
+                  colors = colorNumeric(c("blue", "green", "yellow", "red"), values(raster_hot)
+                                        , na.color = "transparent"), opacity = 0.4, group = "2021 급등지") %>%
+   #---# 최고가/급등지 선택 옵션 추가하기
+   addLayersControl(baseGroups = c("2021 최고가", "2121 급등지"),
+                    options = layersControlOptions(collapsed = FALSE)) %>%
+   #---# 마커 클러스터링 불러오기
+   addCircleMarkers(data = apt_price, lng = unlist(map(apt_price$geometry,1)),
+                    lat = unlist(map(apt_price$geometry,2)), radius = 10, stroke = FALSE,
+                    fillOpacity = 0.6, fillColor = circle.colors, weight = apt_price$py,
+                    clusterOptions = markerClusterOptions(iconCreateFunction=JS(avg.formula)))

Warning messages:
1: In showSRID(uprojargs, format = "PROJ", multiline = "NO", prefer_proj = prefer_proj) :
  Discarded ellps WGS 84 in Proj4 definition: +proj=merc +a=6378137 +b=6378137 +lat_ts=0 +lon_0=0 +x_0=0 +y_0=0 +k=1 +units=m +nadgrids=@null +wktext +no_defs +type=crs
2: In showSRID(uprojargs, format = "PROJ", multiline = "NO", prefer_proj = prefer_proj) :
  Discarded datum World Geodetic System 1984 in Proj4 definition
3: In wkt(projfrom) : CRS object has no comment
4: In wkt(pfrom) : CRS object has no comment
5: In rgdal::rawTransform(projfrom, projto, nrow(xy), xy[, 1], xy[,  :
  Using PROJ not WKT2 strings
6: In wkt(pfrom) : CRS object has no comment
7: In rgdal::rawTransform(projfrom, projto, nrow(xy), xy[, 1], xy[,  :
  Using PROJ not WKT2 strings
8: In rgdal::rawTransform(projto_int, projfrom, nrow(xy), xy[, 1],  :
  Using PROJ not WKT2 strings
9: In wkt(pfrom) : CRS object has no comment
10: In rgdal::rawTransform(projfrom, projto, nrow(xy), xy[, 1], xy[,  :
  Using PROJ not WKT2 strings


 

 

 

 

 

 

 

출처 : 김철민, ⌜공공데이터로 배우는 R 데이터분석 with 샤이니⌟, 이지스퍼블리싱, 2022