1 데이터

미세먼지 주범 석탄화력발전소, 대선 후보 입장은? 한겨례 신문 2017년 기사를 보면 석탄화력발전소에 대한 구체적인 정보가 담겨있다. World Resources Institute Global Power Plant Database 에서 전세계 화력발전소 중 한국만 추려 본다.

library(tidyverse)

plant_df <- read_csv("data/globalpowerplantdatabasev120/global_power_plant_database.csv")

plant_tbl <- plant_df %>% 
  filter(country == "KOR") %>% 
  select("name", "capacity_mw", "latitude", "longitude", fuel = "primary_fuel", year = "commissioning_year", "owner", "url", 
generation_gwh = "estimated_generation_gwh")

plant_tbl %>% 
  reactable::reactable()

2 현황분석

2.1 회사별

회사별 용량과 발전양 순위는 다음과 같다.

library(reactable)

plant_tbl %>% 
  group_by(owner) %>% 
  summarise(capacity_mw    = sum(capacity_mw, na.rm = TRUE),
            generation_gwh = sum(generation_gwh, na.rm = TRUE)) %>% 
  arrange(desc(generation_gwh)) %>% 
  reactable::reactable(  columns = list(
    owner          = colDef(name = "소유 회사", minWidth = 100),
    capacity_mw    = colDef(name = "용량(MW)",  format = colFormat(digits = 0, separators = TRUE)),
    generation_gwh = colDef(name = "발전(GWh)",  format = colFormat(digits = 0, separators = TRUE))
  ))

2.2 연료별

발전에는 다양한 연료가 투입되는데 국내에 소재한 발전소 용량과 발전에 대해 살펴보자.

plant_tbl %>% 
  group_by(fuel) %>% 
  summarise(capacity_mw    = sum(capacity_mw, na.rm = TRUE),
            generation_gwh = sum(generation_gwh, na.rm = TRUE)) %>% 
  ungroup() %>% 
  arrange(desc(generation_gwh)) %>% 
  mutate(용량비율 = capacity_mw / sum(capacity_mw),
         발전비율 = generation_gwh / sum(generation_gwh)) %>% 
  bind_rows(summarise(.,
                    across(where(is.numeric), sum),
                    across(where(is.character), ~"합계"))) %>% 
  reactable::reactable( defaultPageSize = 11,
    columns = list(
      fuel           = colDef(name = "연료", minWidth = 70),
      capacity_mw    = colDef(name = "용량(MW)",  format = colFormat(digits = 0, separators = TRUE)),
      용량비율       = colDef(name = "용량비율",  format = colFormat(percent = TRUE, digits = 1)),
      generation_gwh = colDef(name = "발전(GWh)",  format = colFormat(digits = 0, separators = TRUE)),
      발전비율       = colDef(name = "발전비율",  format = colFormat(percent = TRUE, digits = 1))
  ))

2.3 회사 X 연료

소유 회사와 발전에 사용되는 연료를 교차 분석하여 보자.

2.3.1 용량

먼저 용량에 대해 석탄을 기준으로 의존도가 높은 회사를 살펴보자.

plant_tbl %>% 
  group_by(fuel, owner) %>% 
  summarise(capacity_mw    = sum(capacity_mw, na.rm = TRUE)) %>% 
  pivot_wider(names_from = fuel, values_from = capacity_mw, values_fill  = 0) %>% 
  arrange(desc(Coal)) %>% 
  reactable::reactable( defaultPageSize = 10,
    columns = list(
      owner   = colDef(name = "회사", minWidth = 150),
      Biomass    = colDef(name = "바이오",  format = colFormat(digits = 0, separators = TRUE)),      
      Coal    = colDef(name = "석탄",  format = colFormat(digits = 0, separators = TRUE)),
      Gas     = colDef(name = "가스",  format = colFormat(digits = 0, separators = TRUE)),
      Hydro   = colDef(name = "수력",  format = colFormat(digits = 0, separators = TRUE)),
      Nuclear = colDef(name = "원자력",  format = colFormat(digits = 0, separators = TRUE)),
      Oil     = colDef(name = "원유",  format = colFormat(digits = 0, separators = TRUE)),
      Solar   = colDef(name = "태양열",  format = colFormat(digits = 0, separators = TRUE)),
      Waste   = colDef(name = "폐열",  format = colFormat(digits = 0, separators = TRUE)),
      `Wave and Tidal`   = colDef(name = "해수",  format = colFormat(digits = 0, separators = TRUE)),
      Wind    = colDef(name = "풍력",  format = colFormat(digits = 0, separators = TRUE))
  ))  

2.3.2 발전

앞서 용량(Capacity)에 대해 살펴봤다면 발전(Generation)에 대해 살펴보자.

plant_tbl %>% 
  group_by(fuel, owner) %>% 
  summarise(generation_gwh    = sum(generation_gwh, na.rm = TRUE)) %>% 
  pivot_wider(names_from = fuel, values_from = generation_gwh, values_fill  = 0) %>% 
  arrange(desc(Coal)) %>% 
  reactable::reactable( defaultPageSize = 10,
    columns = list(
      owner   = colDef(name = "회사", minWidth = 150),
      Biomass = colDef(name = "바이오",  format = colFormat(digits = 0, separators = TRUE)),      
      Coal    = colDef(name = "석탄",  format = colFormat(digits = 0, separators = TRUE)),
      Gas     = colDef(name = "가스",  format = colFormat(digits = 0, separators = TRUE)),
      Hydro   = colDef(name = "수력",  format = colFormat(digits = 0, separators = TRUE)),
      Nuclear = colDef(name = "원자력",  format = colFormat(digits = 0, separators = TRUE)),
      Oil     = colDef(name = "원유",  format = colFormat(digits = 0, separators = TRUE)),
      Solar   = colDef(name = "태양열",  format = colFormat(digits = 0, separators = TRUE)),
      Waste   = colDef(name = "폐열",  format = colFormat(digits = 0, separators = TRUE)),
      `Wave and Tidal`   = colDef(name = "해수",  format = colFormat(digits = 0, separators = TRUE)),
      Wind    = colDef(name = "풍력",  format = colFormat(digits = 0, separators = TRUE))
  ))  

3 전체 발전소 위치

국내 전체 발전소 위경도 정보가 있어 이를 반영하여 발전소 위치를 지도에 시각화 시켜보자.

library(glue)
library(leaflet)

plant_tbl %>% 
  leaflet() %>% 
    addProviderTiles(provider = providers$OpenStreetMap) %>% 
    addMarkers(lng=~longitude, lat = ~latitude, clusterOptions = markerClusterOptions(),
               popup = ~ as.character(glue("<strong> {name} </strong> <br>
                                           &middot; 회사: {owner}<br>
                                           &middot; 용량: {scales::comma(capacity_mw, accuracy = 1) } MW<br>
                                           &middot; 발전: {scales::comma(generation_gwh, accuracy = 1)} GWh<br>
                                           &middot; 연료: {fuel}")))

3.1 석탄 발전소 위치

석탄 발전소만 추려 지도에 시각화 시켜보자.

plant_tbl %>% 
  filter(fuel == "Coal") %>% 
  leaflet() %>% 
    addProviderTiles(provider = providers$OpenStreetMap) %>% 
    addMarkers(lng=~longitude, lat = ~latitude, clusterOptions = markerClusterOptions(),
               popup = ~ as.character(glue("<strong> {name} </strong> <br>
                                           &middot; 회사: {owner}<br>
                                           &middot; 용량: {scales::comma(capacity_mw, accuracy = 1) } MW<br>
                                           &middot; 발전: {scales::comma(generation_gwh, accuracy = 1)} GWh<br>
                                           &middot; 연료: {fuel}")))

3.2 석탄 발전소 용량

석탄 발전소 용량을 기준으로 시도 경계를 추가하여 가시성을 높여보자.

library(sf)

korea_sf <- st_read("data/map/HangJeongDong_ver20201001.geojson")
Reading layer `1' from data source `/Users/kwangchunlee/swc/esg/data/map/HangJeongDong_ver20201001.geojson' using driver `GeoJSON'
Simple feature collection with 3492 features and 8 fields
geometry type:  MULTIPOLYGON
dimension:      XY
bbox:           xmin: 124.6097 ymin: 33.11187 xmax: 131.8713 ymax: 38.61695
geographic CRS: WGS 84
sido_sf <- korea_sf %>% 
  group_by(sido, sidonm) %>% 
  summarise(cnt = n()) %>% 
  ungroup() %>% 
  select(-cnt)

plant_tbl %>% 
  filter(fuel == "Coal") %>% 
  leaflet() %>% 
    ## 석탄 화력 발전소 위치 --------------------------------------
    addProviderTiles(provider = providers$OpenStreetMap) %>% 
    addMarkers(lng=~longitude, lat = ~latitude, clusterOptions = markerClusterOptions(),
               popup = ~ as.character(glue("<strong> {name} </strong> <br>
                                           &middot; 회사: {owner}<br>
                                           &middot; 용량: {scales::comma(capacity_mw, accuracy = 1) } MW<br>
                                           &middot; 발전: {scales::comma(generation_gwh, accuracy = 1)} GWh<br>
                                           &middot; 연료: {fuel}"))) %>% 
    addCircleMarkers( radius = ~ (capacity_mw)^(1/2),
                      stroke = FALSE, fillOpacity = 0.5) %>% 
    ## 시도 경계 shapefile --------------------------------------
    addPolygons(data = sido_sf, opacity = 1.0, fillOpacity = 0.1,
            weight = 1,
            highlightOptions = highlightOptions(color = "black", weight = 3,  bringToFront = TRUE),
            labelOptions = labelOptions(
            style = list("font-weight" = "normal", padding = "3px 8px"),
            textsize = "15px",
            direction = "auto"))

3.3 석탄 발전소 발전량

추정 발전량을 기준으로 시도 경계를 포함시켜 지도 위에 시각화를 해 보자.

plant_tbl %>% 
  filter(fuel == "Coal") %>% 
  leaflet() %>% 
    ## 석탄 화력 발전소 위치 --------------------------------------
    addProviderTiles(provider = providers$OpenStreetMap) %>% 
    addMarkers(lng=~longitude, lat = ~latitude, clusterOptions = markerClusterOptions(),
               popup = ~ as.character(glue("<strong> {name} </strong> <br>
                                           &middot; 회사: {owner}<br>
                                           &middot; 용량: {scales::comma(capacity_mw, accuracy = 1) } MW<br>
                                           &middot; 발전: {scales::comma(generation_gwh, accuracy = 1)} GWh<br>
                                           &middot; 연료: {fuel}"))) %>% 
    addCircleMarkers( radius = ~ (generation_gwh)^(1/3),
                      stroke = FALSE, fillOpacity = 0.5) %>% 
    ## 시도 경계 shapefile --------------------------------------
    addPolygons(data = sido_sf, opacity = 1.0, fillOpacity = 0.1,
            weight = 1,
            highlightOptions = highlightOptions(color = "black", weight = 3,  bringToFront = TRUE),
            labelOptions = labelOptions(
            style = list("font-weight" = "normal", padding = "3px 8px"),
            textsize = "15px",
            direction = "auto"))

4 Many Models

발전소 용량(capacity)과 발전량(generation)과의 관계를 발전소가 나름 어느 정도 있는 석탄, 가스, 수력을 추려 회귀모형을 만들어보자.

library(broom)

plant_nest_tbl <- plant_tbl %>% 
  filter(fuel %in% c("Coal", "Gas", "Hydro")) %>% 
  na.omit() %>% 
  select(name, fuel, capacity_mw, generation_gwh) %>% 
  group_by(fuel) %>% 
  nest() %>% 
  ungroup()

generation_model <- function(df) {
  lm(generation_gwh ~ capacity_mw, data = df)
}

plant_nest_tbl <- plant_nest_tbl %>% 
  mutate(model        = map(data, generation_model),
         model_glance = map(model, glance),
         rsquare      = map_dbl(model_glance, ~.$r.squared))

plant_nest_tbl
# A tibble: 3 x 5
  fuel  data                  model  model_glance           rsquare
  <chr> <list>                <list> <list>                   <dbl>
1 Gas   <tibble[,3] [18 × 3]> <lm>   <tibble[,12] [1 × 12]>       1
2 Coal  <tibble[,3] [10 × 3]> <lm>   <tibble[,12] [1 × 12]>       1
3 Hydro <tibble[,3] [7 × 3]>  <lm>   <tibble[,12] [1 × 12]>       1

회귀식과 발전 원료를 나눠 용량과 발전량과의 상관 관계를 살펴보자.

library(rbokeh)
library(trelliscopejs)

# 시각화 -----
coal_plot <- function(data, model) {
  figure(xlim = c(0, 3000), ylim=c(0, 15000), tools = NULL) %>% 
    ly_points(capacity_mw, generation_gwh, data=data, hover=data) %>% 
    ly_abline(model)
}

# trelliscope 그래프
plant_nest_tbl <- plant_nest_tbl %>% 
  mutate(plot = map2_plot(data, model, coal_plot)) %>% 
  arrange(rsquare)

plant_nest_tbl %>% 
  trelliscope(name = "power_plant", nrow=1, ncol=3, path="coal_power_plant")
 

데이터 과학자 이광춘 저작

kwangchun.lee.7@gmail.com