[Competition] Post Corona Data Visualization

Doyun’s Journey
Doyun’s Lab
Published in
19 min readAug 31, 2020

Subject : Our daily lives changed by Corona

Language : R

Data : ‘KT Corona’ Data

  • Part1 : 코로나 이후 서울시의 유입인구 변화 지도
  • Part2 : 서울시 구별 집단감염 여부 파악
  • Part3 : 배달 업종에서의 변화
  • Part4 : 재난 지원금이 없었더라면 ?
  • Part5 : 재난지원금 효과를 보지 못한 배달업종
  • Part6 : 지역별 상품 매출 추이
  • Part7 : 코로나가 만약 발병하지 않았더라면 ?

1. Data parsing

# Library
library(stringr)
library(dplyr)
library(ggplot2)
library(ggmap)
library(viridis)
library(lubridate)
library(gridExtra)
library(tidyverse)
library(forecast)
library(leaflet)
library(sf)
library(urca)
library(fpp2)
# 데이터 불러오기
raw_dliv <- read.csv('delivery.csv', encoding = 'UTF-8')
raw_popu <- read.csv('fpopl.csv', encoding= 'UTF-8')
raw_adstr <- read.csv('adstrd_master.csv', encoding='UTF-8')
raw_card <- read.csv('card.csv',encoding = 'UTF-8')
raw_index <- read.csv('index.csv', encoding = 'UTF-8')
raw_case <- read.csv('./COVID_19/Case.csv', encoding='UTF-8')
raw_TP <- read.csv('./COVID_19/TimeProvince.csv', encoding='UTF-8')

2. Analysis

  • Part 1
  • 코로나 이전에는 유입인구가 더 넓게 분포되어 있는 것을 확인 할 수 있었다
  • 코로나 이후에는 유입인구의 범위가 더 좁아진 형태를 파악하였다.
  • 사람들이 코로나 이후 구별 이동 반경이 줄어들었다.
# fpopl 데이터 집계
summarise_popu <- raw_popu %>%
group_by(base_ymd, adstrd_code) %>%
summarise(cascnt = mean(popltn_cascnt))

names(raw_adstr) <- c('adstrd_code','adstrd_nm','brtc_nm','signgu_nm')
fpopl_data <- merge(summarise_popu, raw_adstr, by ='adstrd_code')
fpopl_data$adstrd_nm <- as.factor(fpopl_data$adstrd_nm)
fpopl_data$ymd <- as.POSIXct(as.character(fpopl_data$base_ymd),format='%Y-%m-%d')

fpopl_data$adstrd_nm <- as.character(fpopl_data$adstrd_nm)
fpopl_data$brtc_nm <- as.character(fpopl_data$brtc_nm)
fpopl_data$signgu_nm <- as.character(fpopl_data$signgu_nm)

# 위경도(geocode)
register_google(key='AIzaSyA78Nuu9UJfhEw4KYX_Q8fLzLViGgdDiTk')
sigu <- unique(fpopl_data$signgu_nm)
latlon <- geocode(sigu)
latlon_data <- data.frame(sigu,latlon)
names(latlon_data) <- c('signgu_nm','lon','lat')

# 위경도 열 추가
fpopl_result <- merge(fpopl_data, latlon_data, by = 'signgu_nm')

# 서울 지도에 나타내기
seoul_lonlat = unlist(ggmap::geocode('seoul', source='google'))
seoul_map <- get_map("seoul", zoom=11, maptype="roadmap")

raw_TP$ymd <- as.POSIXct(as.character(raw_TP$X.U.FEFF.date),format='%Y-%m-%d')
raw_TP$province <- as.character(raw_TP$province)

# 전날 대비 확진자수 기준(0명 vs 이상) 평균 유동인구 밀도 그래프
seoul_TP <- subset(raw_TP,province == '서울')
seoul_TP$ymd <- as.POSIXct(as.character(seoul_TP$X.U.FEFF.date), format = '%Y-%m-%d')

seoul_TP$diff_confirmed <- c(0,diff(seoul_TP$confirmed))

summary(seoul_TP$diff_confirmed)

seoul_result <- merge(fpopl_result,seoul_TP, by ='ymd')
seoul_result[is.na(seoul_result)] <- 0

low_fpopl <- subset(seoul_result, diff_confirmed ==0 <=28)
high_fpopl <- subset(seoul_result, diff_confirmed != 0 >=2)

coro_low <- ggmap(seoul_map) + stat_density_2d(data=low_fpopl, aes(x=lon, y=lat, fill=cascnt), geom='polygon', bins=4, alpha=.4)
coro_hi <- ggmap(seoul_map) + stat_density_2d(data=high_fpopl, aes(x=lon, y=lat, fill=cascnt), geom='polygon',
grid.arrange(coro_low, coro_hi, ncol=2)
  • Part 2
  • 서울시는 집단감염의 수가 훨씬 많았다
  • 관악구, 구로구, 용산구는 규모가 큰 집단감염이 많았다는 것을 파악할 수 있었다.
# 서울 집단 감염 여부
raw_case %>%
filter(province == '서울') %>%
group_by(group) %>%
summarise(count_group = n()) %>%
ggplot(aes(x=group, y= count_group)) + geom_bar(stat='identity')
# 지역별 집단감염 확진자 수
raw_case %>%
filter(province == '서울' >%
ggplot(aes(x=city, y= confirmed)) + geom_bar(stat='identity')
  • Part 3

> 지역별 배달접수시간 편차가 큰 업종 : 도시락,배달전문업체,카페디저트

> 지역별 배달접수시간 편차가 작은 업종 : 대부분의 업종

> 변화를 많이 보인 업종 : 도시락, 배달전문업체, 야식, 심부름

  • 대부분 평균 시간이 빨라지고, 편차가 커짐(야식은 오히려 작아짐)
# 배달 데이터 전처리

grp_dliv <- raw_dliv[,c(-18,-19,-24,-25)] %>%
group_by(DLVR_STORE_SIDO,DLVR_STORE_INDUTY_NM)

grp_dliv$DLVR_RCEPT_HOUR <- as.integer(str_sub(grp_dliv$DLVR_RCEPT_TIME,12,13)) #시 추출
grp_dliv$DLVR_RCEPT_MINU <- as.integer(str_sub(grp_dliv$DLVR_RCEPT_TIME,15,16)) #분 추출
grp_dliv$PROCESS_DT <- as.POSIXct(grp_dliv$PROCESS_DT, format='%Y-%m-%d')


grp_dliv$DLVR_COMPT_TIME <- as.POSIXct(as.character(grp_dliv$DLVR_COMPT_TIME), format='%Y-%m-%d %H:%M:%S')
grp_dliv$DLVR_RCEPT_TIME <- as.POSIXct(as.character(grp_dliv$DLVR_RCEPT_TIME), format='%Y-%m-%d %H:%M:%S')


smr_dliv <- raw_dliv[,c(-18,-19,-24,-25)] %>%
group_by(DLVR_STORE_SIDO,DLVR_STORE_INDUTY_NM)

smr_dliv$DLVR_RCEPT_HOUR <- as.integer(str_sub(smr_dliv$DLVR_RCEPT_TIME,12,13)) #시 추출
smr_dliv$DLVR_RCEPT_MINU <- as.integer(str_sub(smr_dliv$DLVR_RCEPT_TIME,15,16)) #분 추출
smr_dliv$PROCESS_DT <- as.POSIXct(smr_dliv$PROCESS_DT, format='%Y-%m-%d')
# 코로나 이전
dliv_pre<- smr_dliv %>%
filter(month(PROCESS_DT) < 2 >%
summarise(MEAN_hour = mean(DLVR_RCEPT_HOUR)) %>%
ggplot(aes(DLVR_STORE_INDUTY_NM ,MEAN_hour, colour = DLVR_STORE_INDUTY_NM, group=DLVR_STORE_INDUTY_NM))+
geom_boxplot() +
theme(legend.position = "none")

# 코로나 이후
dliv_aft <- smr_dliv %>%
filter(month(PROCESS_DT) >= 2 >%
summarise(MEAN_hour = mean(DLVR_RCEPT_HOUR)) %>%
ggplot(aes(DLVR_STORE_INDUTY_NM ,MEAN_hour, colour = DLVR_STORE_INDUTY_NM, group=DLVR_STORE_INDUTY_NM))+
geom_boxplot() +
theme(legend.position = "none")
# 지역 및 업종별 평균 배달접수 시간
grid.arrange(dliv_pre, dliv_aft, ncol=1)
# 표본이 적어서 그런가?
smr_dliv %>% group_by(DLVR_STORE_INDUTY_NM) %>% filter(month(PROCESS_DT) >= 2 >% summarise(n = n()) %>% arrange(desc(n))
smr_dliv %>% group_by(DLVR_STORE_INDUTY_NM) %>% filter(month(PROCESS_DT) < 2 >% summarise(n = n())%>% arrange(desc(n))dlvr <- raw_dliv %>% select(PROCESS_DT, DLVR_REQUST_STTUS_VALUE, DLVR_STORE_INDUTY_NM, DLVR_STORE_SIGUNGU, DLVR_DSTN_SIGUNGU, DLVR_AMOUNT, GOODS_AMOUNT, SETLE_KND_VALUE, DLVR_RCEPT_TIME, DLVR_COMPT_TIME) colnames(dlvr) <- c("number", "state", "induty", "dl_region", "ds_region", "dl_amount", "amount", "kind_pay", "req_time", "comp_time")

dlvr$state <- ifelse(dlvr$state == 1, "complete", ifelse(dlvr$state == 2, "cancel", ifelse(dlvr$state == 3, "accident", "quest")))
dlvr$ym <- str_sub(dlvr$number, 1, 7) dlvr$amount <- as.numeric(dlvr$amount) dlvr <- na.omit(dlvr) dd <- dlvr %>% group_by(ym, induty) %>% summarise(amount_sum = sum(amount))
  • Part 4

> 재난 지원금이 없었더라면 카드 사용액이 감소했을 것으로 보임

  • 파란선은 재난 지원금을 받은 현재 카드사용액
  • ARIMA로 예측한 것과 NNAR로 예측한 결과는, 실제 값보다 카드사용액이 더 적을것으로 예상함
  • 즉, 재난 지원금이 소상공인들의 매출액에 큰 영향을 끼침. 매우 좋은 정책으로 생각 됨
# 재난지원금이 없었더라면 ? 
ddd <- dlvr %>% group_by(number) %>% summarise(amount_sum = sum(amount))
ggplot(ddd, aes(x = number, y = amount_sum, group = 1)) + geom_line() train <- ts(ddd$amount_sum, start = 1, end = 125)
test <- ts(ddd$amount_sum[125:165], start = 125, end = 165)
NNAR <- forecast(nnetar(train), h = 40)
ARIMA <- forecast(auto.arima(train, lambda = 0), h = 40)
autoplot(train) + autolayer(NNAR, series = "NNAR", PI = F) + autolayer(ARIMA, series = "ARIMA", PI = F) + autolayer(test) + geom_vline (xintercept = 125, color = "orange") + annotate("text", x = 145, y = 10^9*1.1, label = "Support payment", size = 4, color = "red")
  • Part 5

> 14개의 업종에 긍정적인 영향을 미쳤음(중식은 미미)

> 카페-디저트와 심부름 업종에 대해서는 영향을 미치지 않은 것으로 보임

# 재난지원금이 효과를 못받은 배달 업종
gr_id <- dlvr %>%
group_by(number, induty) %>%
summarise(amount_sum = sum(amount)) %>%
arrange(induty)
chan <- rbind(subset(dlvr, str_sub(dlvr$number, 7, 7) == 4),
subset(dlvr, str_sub(dlvr$number, 7, 7) == 5))
chan_sum <- chan %>%
group_by(number, induty) %>%
summarise(amount_sum = sum(amount))
chan_sum$ym <- str_sub(chan_sum$number, 1, 7)chan_f <- chan_sum %>%
group_by(ym, induty) %>%
summarise(amount_sum = sum(amount_sum)) %>%
arrange(induty)
result <- c(0)
for (x in 1:31) {
div <- (chan_f$amount_sum[x+1]/chan_f$amount_sum[x])
result <- c(result, div)
}
chan_f$variance <- result
chan_f$variance <- ifelse(chan_f$ym == "2020-04", 0, chan_f$variance)
visual2 <- chan_f[seq(2, 32, by = 2),] %>% arrange(desc(variance))ggplot(visual2, aes(x = reorder(induty, -variance), y = variance, fill = -amount_sum)) +
geom_bar(stat = "identity") +
coord_cartesian(ylim = c(0.7, 1.3)) +
labs(x = "Sectors", y = "Rate of change") +
theme(axis.text.x = element_text(angle = 45, hjust = 1)) +
theme(legend.position = "none") +
geom_hline(yintercept = 1, color = "red", linetype = "dashed", size = 1)
  • Part 6
# 어느지역에서 어떤 상품이 매출이 오르고 어떤 상품이 매출이 내렸을까
# 4월대비 5월
ab <- subset(raw_index,X.U.FEFF.period==202004)
ab <- ab %>% group_by(catm,sigungu)%>% summarise(n=mean(cgi)) %>% arrange(sigungu)ac <- subset(raw_index,X.U.FEFF.period==202005)ac <- ac %>% group_by(catm,sigungu)%>% summarise(n=mean(cgi)) %>% arrange(sigungu)ab <- cbind(ab = ab, nss = ac$n)
ab$diff <-ab$nss-ab$n
ggplot(ab,aes(x=sigungu,y=diff,fill=catm))+
geom_bar(stat='identity',position = 'dodge',colour='black')+
coord_cartesian(ylim = c(-30,20))+
geom_hline(yintercept=0, linetype='dashed', color='red', size=1)
  • Part 7
card <-rename(raw_card,'date'='X.U.FEFF.receipt_dttm')
#카드 사용량 예측(6월 14일 이후 30일 동안의 카드 사용량 예측)
card_pr <-subset(card,select = c('date','salamt'))
card_pr$salamt <-as.numeric(card_pr$salamt)
#카드 일 사용량 평균
card_pr <-card_pr %>% group_by(date) %>% summarise(use_card=mean(salamt))
ts_card <-msts(card_pr$use_card,
seasonal.periods=c(7,365.25),
start=c(2020,1,4))
summary(ur.kpss(ts_card))checkresiduals(ts_card)ETS <- forecast(ets(ts_card), h=30)
ARIMA <- forecast(auto.arima(ts_card, lambda=0), h=30)
NNAR <- forecast(nnetar(ts_card), h=30)
Combination <- (ETS[["mean"]] + ARIMA[["mean"]] + NNAR[["mean"]])/3
#
autoplot(ts_card) +
autolayer(ETS, series="ETS", PI=FALSE) +
autolayer(ARIMA, series="ARIMA", PI=FALSE) +
autolayer(NNAR, series="NNAR", PI=FALSE) +
autolayer(Combination, series="Combination") +
xlab("day") + ylab("use_card") +
ggtitle("Card Usage Forecast")

3. Conclusion

  • 코로나 이후 서울시-구 별 유입인구는 굉장히 적어졌다.

> 전체적인 유동인구 감소

  • 서울시 감염의 대부분은 집단 감염이 차지한다.

> 개인 감염은 거의 없다.

  • 코로나 이후 평균 배달 시간이 빨라지는 형태를 보임

> 또한, 배달시간의 편차가 커짐

> But, 야식은 편차가 엄청나게 줄어듬 (코로나 이후 특정 시간(18시)에 배달을 많이 시키는 것으로 파악)

  • 재난 지원금이 없었더라면 카드 사용액은 더 줄어들 것으로 예측됨
  • 지역별 효자 상품 도출

--

--