16  부록: 본문에 사용된 분석 코드

이 부록은 교재 학습 후, 앞서 분석에 사용된 R코드들을 실전에서 쉽게 재사용할 수 있도록 모아 제공하기 위한 목적으로 쓰여졌습니다. 기본적인 패키지와 데이터를 불러오는 다음 코드는 이 장에 수록된 전체 코드를 실행하기 위해 공통적으로 필요합니다.

library(tidyverse)
library(lubridate)
library(httr)
library(jsonlite)
library(ggtext)
library(glue)
library(raster)
#library(extrafont)
library(ggthemes)
library(sf)

options(scipen=9999)
Sys.setlocale("LC_ALL", "korean")
[1] "LC_COLLATE=Korean_Korea.949;LC_CTYPE=Korean_Korea.949;LC_MONETARY=Korean_Korea.949;LC_NUMERIC=C;LC_TIME=Korean_Korea.949"
#font_import()
projects <- read_csv("data/projects.csv")
donations <- read_csv("data/donations.csv")
data <- read_csv("data/august_october_2020.csv")

해당 데이터는 교재 본문에서 안내한대로 Kaggle 홈페이지에서 다운 받을 수 있습니다.

16.1 데이터 요약: 주별, 연도별 평균

projects |>
    mutate(date_posted = as.character(date_posted),
           year = year(date_posted)) |>
    group_by(school_state, year) |>
    summarise(avg_size = mean(total_price_excluding_optional_support))
`summarise()` has grouped output by 'school_state'. You can override using the
`.groups` argument.
# A tibble: 455 x 3
# Groups:   school_state [52]
   school_state  year avg_size
   <chr>        <dbl>    <dbl>
 1 AK            2007     380.
 2 AK            2008     414.
 3 AK            2009     412.
 4 AK            2010     419.
 5 AK            2011     442.
 6 AK            2012     462.
 7 AK            2013     577.
 8 AK            2014     522.
 9 AL            2003     339.
10 AL            2004     546.
# i 445 more rows
projects |> 
    mutate(year = year(date_posted)) |>
    group_by(school_state, year) |>
    summarise(avg_size = mean(total_price_excluding_optional_support)) |> 
    filter(school_state %in% c("HI", "NY")) |>
    ggplot() +
    aes(x=year, y=avg_size, color=school_state) +
    geom_line() +
    geom_point() +
    labs(x="연도", y="평균 목표액", color="주(State)") +
    scale_x_continuous(breaks=2002:2014, labels=2002:2014) + 
    scale_colour_economist(labels=c("하와이", "뉴욕")) +
    theme_economist() +
    ggtitle("donorschoose.org 연도별 평균 목표액")

16.2 Facet 계층과 색상을 동시에 이용한 다차원 시각화

projects |>
    mutate(optional = total_price_including_optional_support - total_price_excluding_optional_support) |>
    filter(total_price_excluding_optional_support < 1000 & students_reached < 500) |>
    sample_n(2000) |>
    ggplot() +
    aes(x=students_reached, y=total_price_excluding_optional_support, 
        size=optional,
        color=poverty_level) +
    geom_point(alpha=0.3, stroke=NA) +
    facet_wrap(vars(poverty_level))

16.3 두 개 이상의 테이블을 결합한 시각화

yearly_goals <- projects |>
    mutate(year_posted = year(date_posted)) |>
    group_by(year_posted) |>
    summarise(total_goal = sum(total_price_excluding_optional_support))

yearly_donations <- donations |>
    mutate(year_donated = year(ymd_hms(donation_timestamp))) |>
    group_by(year_donated) |>
    summarise(total_donations = sum(donation_to_project))

yearly_goals |>
    left_join(yearly_donations, by=c("year_posted"="year_donated")) |>
    pivot_longer(!year_posted, names_to="var", values_to="val") |>
    ggplot() +
    aes(x=year_posted, y=val, color=var) +
    geom_line() +
    geom_point()

16.4 데이터 스토리텔링

data <- data |>
    rename(country = X.1, august = `Total Agree - August 2020`,
           october = `Total Agree - October 2020`)

data %>%
    pivot_longer(!country, names_to="month", values_to="agree") |>
    filter(country != "Total") |>
    # 한국을 다른 색상으로 표시하기 위한 변수
    mutate(highlight = ifelse(country=="South Korea", TRUE, FALSE)) |>   
    ggplot(aes(x=month, y=agree, color=highlight)) +
    # 색상 범례(legend) 삭제
    geom_point(show.legend=FALSE) +
    # 색상 범례(legend) 삭제
    geom_line(aes(group=country, size=highlight), show.legend = FALSE) + 
    # 가로-세로 비율 조정
    coord_fixed(ratio=0.03) +                                            
    labs(x="",
         y="백신 투약 의사 비율",
         title="<span style='color: blue'>한국</span>의 백신 투약 의사는<br>오히려 상승") +
    scale_color_manual(breaks=c(TRUE, FALSE),
                         values=c("blue", "grey")) +
    scale_size_manual(breaks=c(TRUE, FALSE),
                      values=c(1, 0.5)) +
    scale_x_discrete(expand=c(0,0.1), 
                     breaks=c("august", "october"),labels=c("8월", "10월")) +
    theme(panel.background = element_rect(fill="white"),
          axis.title.y = element_text(size=15),
          # ggtext 사용
          plot.title = element_markdown(size=18, color="grey"),
          plot.margin = unit(c(1,1,1,1), "cm"),
          plot.title.position="plot")

data %>%
    # 나라 이름을 한글로 변환
    mutate(country = case_when(country=="India"~"인도", country=="China"~"중국", 
                               country=="South Korea"~"한국", country=="Barzil"~"브라질", 
                               country=="Australia"~"호주", country=="United Kingdom"~"영국", 
                               country=="Mexico"~"멕시코", country=="Canada"~"캐나다", 
                               country=="Germany"~"독일", country=="Japan"~"일본", 
                               country=="South Africa"~"남아공", country=="Italy"~"이탈리아", 
                               country=="Spain"~"스페인", country=="United States"~"미국", 
                               country=="France"~"프랑스")) |>
    pivot_longer(!country, names_to="month", values_to="agree") |>
    filter(country != "Total") |>
    group_by(country) %>%
    # 그래프상 수치 표현을 위한 위치 지정 
    mutate(increase = ifelse(agree == max(agree), TRUE, FALSE),
           bump = ifelse(increase, agree + 2, agree - 2)) |>
    ggplot(aes(x=agree, y=country, color=month)) +
    geom_line(aes(group=country), size=1.75, color="#e6e6e6", show.legend = FALSE) +
    geom_point(size=2, show.legend = FALSE) +
    # glue를 이용해 수치 뒤에 %를 삽입
    geom_text(aes(label=glue("{agree}%"), x=bump), show.legend=FALSE) +
    scale_color_manual(name=NULL,
                     breaks=c("august", "october"),
                     values=c("#727272", "#15607a")) +
    scale_x_continuous(limits=c(50, 100),
                     breaks=seq(50, 100, by=5),
                     labels=glue("{seq(50, 100, 5)}%")) +
    # 세로 여백을 확보
    scale_y_discrete(expand=c(0, 1.5)) +
    labs(x=NULL, y=NULL,
       title="코로나 백신 접종이 시작되면 접종을 받겠다",
       caption="<i>15개 국가 16-74세 사이 18,526명에 대한 온라인 조사</i><br>출처: Ipsos") +
    theme(plot.title.position = "plot",
          plot.title = element_text(face="bold", margin= margin(b=20), size=20),
          # ggtext이용해 HTML 문법 사용함 주의
          plot.caption = element_markdown(hjust=0, color="darkgray"),
          plot.caption.position = "plot",
          panel.background = element_blank(),
          axis.ticks = element_blank(),
          axis.text.x = element_text(color="darkgray"),
          panel.grid.major.x = element_line(color="gray", size=0.1),
          panel.grid.major.y = element_line(color="gray", size=0.1, linetype="dotted")) +
    # 적절한 위치에 '8월', '10월' 표시
    geom_richtext(x=79, y=14.7, label="<span style='color:#15607a'> 10월 </span>", 
                  fill = NA, label.color = NA, size=4, show.legend = FALSE) +
    geom_richtext(x=88, y=14.7, label="<span style='color:#727272'> 8월 </span>", 
                  fill = NA, label.color = NA, size=4, show.legend = FALSE)

16.5 API와 지도를 이용한 공간정보 활용

getApt <- function(cd, ymd){
    res <- GET(url="http://openapi.molit.go.kr:8081/OpenAPI_ToolInstallPackage/service/rest/RTMSOBJSvc/getRTMSDataSvcAptTrade",
           query=list(LAWD_CD=cd,
                      DEAL_YMD=ymd,
                      serviceKey="LGvIXqsO1eCAKjgMBQWd5QYDOJ77cd4Tq/ea2CQUTUuisLvxlxaGm0YTp4f+89FfhplvwiQIe0cngpybTkdDHQ=="))
    dtJSON <- httr::content(res, as = "text", encoding="UTF-8")
    data <- fromJSON(dtJSON)
    df <- data$response$body$items$item

    return(df)
}
datalist <- list()   # 데이터를 차례차례 입력할 빈 리스트 생성
counter <- 1         # 리스트 안의 위치를 지정해주기 위한 카운터
for (cd in cds){
    for (ymd in ymds) {
        #dt <- getApt(cd=cd, ymd=ymd)
        #print(dt)
        datalist[[counter]] <- getApt(cd=cd, ymd=ymd)
        counter <- counter + 1
    }
}

dtApt <- bind_rows(datalist)
Rows: 18970 Columns: 16
-- Column specification --------------------------------------------------------
Delimiter: ","
chr (7): 거래유형, 법정동, 아파트, 중개사소재지, 지번, 해제사유발생일, 해제여부
dbl (7): 건축년도, 년, 월, 일, 전용면적, 지역코드, 층
num (1): 거래금액
lgl (1): 등기일자

i Use `spec()` to retrieve the full column specification for this data.
i Specify the column types or set `show_col_types = FALSE` to quiet this message.
aggApt <- dtApt %>%
    mutate(거래금액 = as.numeric(gsub(",", "", 거래금액)),
           #yearmonth = 년*100 + 월,
           district = case_when(지역코드 == 27260 ~ "수성구",
                                지역코드 == 27140 ~ "동구",
                                지역코드 == 27290 ~ "달서구",
                                지역코드 == 27110 ~ "중구",
                                지역코드 == 27170 ~ "서구",
                                지역코드 == 27200 ~ "남구",
                                지역코드 == 27230 ~ "북구", 
                                지역코드 == 27710 ~ "달성군")) %>%
    group_by(district, 월) %>%
    summarise(avgP = mean(거래금액)) 
`summarise()` has grouped output by 'district'. You can override using the
`.groups` argument.
aggApt %>%
    ggplot(aes(as.integer(월), avgP, color=district)) +
    geom_line() +
    facet_wrap(~ district, nrow = 2) +
    theme_classic() +
    ylab("아파트 가격") + 
    xlab("월") +
    scale_x_continuous(breaks=seq(1,12,by=2)) +
    scale_y_continuous(breaks=c(25000, 35000, 45000)) +
    theme(legend.position = "none")

korea <- getData('GADM', country='kor', level=2)
daegu_gadm <- korea[korea$NAME_1 == 'Daegu', ]
daegu <- st_as_sf(daegu_gadm) |>
    rename(id = "NL_NAME_2")

daegu <- daegu %>%
    mutate(district = case_when(id == "남구 | 南區" ~ "남구",
                                id == "달서구 | 達西區" ~ "달서구",
                                id == "달성군 | 達城郡" ~ "달성군",
                                id == "동구 | 東區" ~ "동구",
                                id == "북구 | 北區" ~ "북구", 
                                id == "서구 | 西區" ~ "서구",
                                id == "수성구 |  壽城區" ~ "수성구",
                                id == "중구| 中區" ~ "중구"))
growth <- aggApt %>%
    group_by(district) %>%
    summarise(growth = (last(avgP) - first(avgP)) / first(avgP) * 100)
daegu %>%
    left_join(growth, by="district") %>%
    ggplot() +
    geom_sf(aes(fill=growth), color="black") +
    #geom_polygon(aes(x=long, y=lat, group=group, fill=growth), color="black") +
    labs(title="대구 연평균 아파트 가격 상승폭") +
    theme_void() +
    theme(legend.position = "bottom", 
          plot.title = element_text(size=18, face="bold.italic", hjust=0.5)) +
    scale_fill_continuous(name="연간 가격 성장률(%)") +
    scale_fill_gradient2()
Scale for fill is already present.
Adding another scale for fill, which will replace the existing scale.