데이터 사이언스 05 지도데이터 처리2

게시: by Creative Commons Licence

1. 19대 대선 후보의 지역별 투표율

1) 데이터 얻기

  • 중앙선거관리위원회 선거통계시스템 : http://info.nec.go.kr/
  • 크롬 개발자 도구 > Source > 위 사이트는 3개의 프레임 (= index + left + main)으로 구성되어 있다. 프레임에 맞춰 selenium 작업을 해야한다.

png

(1) selenium.webdriver.common.action_chains.ActionChains

  • 마우스 over(hovering 상태)에서 관련 태그를 클릭하는 연속 동작을 구현하기
from selenium import  webdriver
import time
# 크롬 창 열기
driver = webdriver.Chrome('../crawler/driver/chromedriver')
# 중앙선거관리위원회 선거통계시스템 열기
driver.get('http://info.nec.go.kr/')
# 크롬 개발자 도구 > Source
driver.switch_to_default_content()
driver.switch_to_frame('main')
# 투/개표 텝 열기  -  JavaScript
## xpath   //*[@id="topmenu"]/ul/li[4]/a
driver.find_element_by_xpath("""//*[@id="topmenu"]/ul/li[4]/a""").click()
# 투/개표 텝 >> 역대선거 탭
driver.find_element_by_xpath("""//*[@id="header"]/div[1]/ul[1]/li[2]/a""").click()
# 투/개표 >> 역대선거  >>  투개표 : hover 상태
element01 = driver.find_element_by_xpath('''//*[@id="presubmu"]/li[3]/a''')
# 투/개표 >> 역대선거  >>  투개표 : hover 상태 >  개표현황 : 클릭
element02 = driver.find_element_by_xpath('''//*[@id="preallmenu"]/div/div/ul/li[3]/dl/dd[4]/a''')
webdriver.common.action_chains.ActionChains(driver).move_to_element(element01).move_to_element(element02).click().perform()
# 투/개표 >> 역대선거  >>  투개표 >  역대선거실시상황 >> 대통령선거
driver.find_element_by_xpath('''//*[@id="electionType1"]''').click()
# ~ 대통령선거 >> 선택
driver.find_element_by_xpath('''//*[@id="electionName"]''').click()
# ~ 대통령선거 >> 선택 >> 제19대
driver.find_element_by_xpath('''//*[@id="electionName"]/option[2]''').click()
time.sleep(2)
# ~ 대통령선거 >> 선택 >> 제19대 >> 선거 선택 >> 대통령선거
driver.find_element_by_xpath('''//*[@id="electionCode"]/option[2]''').click()
# 시도 선택
time.sleep(2)
sido_list_raw = driver.find_element_by_xpath("""//*[@id="cityCode"]""")
sido_list = sido_list_raw.find_elements_by_tag_name("option")
time.sleep(2)
sido_names_values_raw = [ option.text for option in sido_list ]

sido_names_values_raw[:5]
['▽ 선 택', '▷ 전 체', '서울특별시', '부산광역시', '대구광역시']
sido_names_values = sido_names_values_raw[2:]

sido_names_values
['서울특별시',
 '부산광역시',
 '대구광역시',
 '인천광역시',
 '광주광역시',
 '대전광역시',
 '울산광역시',
 '세종특별자치시',
 '경기도',
 '강원도',
 '충청북도',
 '충청남도',
 '전라북도',
 '전라남도',
 '경상북도',
 '경상남도',
 '제주특별자치도']
# 서울특별시  >> 검색 버튼 클릭
driver.find_element_by_id("cityCode").send_keys(sido_names_values[0])
driver.find_element_by_xpath('''//*[@id="searchBtn"]''').click()
# 서울특별시  >> 검색 버튼 클릭 >>  개표현황 표
from bs4 import BeautifulSoup

html = driver.page_source
soup = BeautifulSoup(html, 'lxml')
tmp = soup.find('table').find('tbody').find_all('tr')
tmp[0]
<tr>
<td class="alignL" width="70">합계</td>
<td class="alignR">8,382,999<br/>(867,043)</td>
<td class="alignR">6,590,646<br/>(843,459)</td>
<td class="alignR">2,781,345<br/>(42.34)</td><td class="alignR">1,365,285<br/>(20.78)</td><td class="alignR">1,492,767<br/>(22.72)</td><td class="alignR">476,973<br/>(7.26)</td><td class="alignR">425,459<br/>(6.47)</td><td class="alignR">9,987<br/>(0.15)</td><td class="alignR">789<br/>(0.01)</td><td class="alignR">3,554<br/>(0.05)</td><td class="alignR">1,938<br/>(0.02)</td><td class="alignR">3,416<br/>(0.05)</td><td class="alignR">1,277<br/>(0.01)</td><td class="alignR">2,177<br/>(0.03)</td><td class="alignR">3,950<br/>(0.06)</td><td class="alignR">6,568,917</td>
<td class="alignR">21,729</td>
<td class="alignR">1,792,353</td>
</tr>
tmp=tmp[1:]
tmp[0]
<tr>
<td class="alignL">종로구</td>
<td class="alignR">133,769<br/>(15,511)</td>
<td class="alignR">102,566<br/>(14,822)</td>
<td class="alignR">42,512<br/>(41.59)</td><td class="alignR">22,325<br/>(21.84)</td><td class="alignR">22,313<br/>(21.83)</td><td class="alignR">7,412<br/>(7.25)</td><td class="alignR">7,113<br/>(6.95)</td><td class="alignR">228<br/>(0.22)</td><td class="alignR">5<br/>(0.00)</td><td class="alignR">78<br/>(0.07)</td><td class="alignR">31<br/>(0.03)</td><td class="alignR">63<br/>(0.06)</td><td class="alignR">26<br/>(0.02)</td><td class="alignR">47<br/>(0.04)</td><td class="alignR">49<br/>(0.04)</td><td class="alignR">102,202</td>
<td class="alignR">364</td>
<td class="alignR">31,203</td>
</tr>
# 구 이름
tmp_gu_si_gun_name =tmp[0].find_all("td")[0]
tmp_gu_si_gun_name.get_text()
'종로구'
# 투표수
tmp_gu_si_gun_num_of_votes = tmp[1].find_all("td")[2]
t1 = tmp_gu_si_gun_num_of_votes.text
tmp_votes = t1[: t1.find('(')].replace(',', "")
tmp_votes
'82852'
# 문재인 득표수/득표율
tmp_gu_si_gun_Moon = tmp[1].find_all("td")[3]
tm = tmp_gu_si_gun_Moon.text
tmp_Moon_votes = tm[ : tm.find('(')].replace(',', "")
tmp_Moon_rate = tm[ tm.find('(') +1 : -1 ]
tm, tmp_Moon_votes, tmp_Moon_rate
('34,062(41.23)', '34062', '41.23')
# 홍준표 득표수/득표율
tmp_gu_si_gun_Hong = tmp[1].find_all("td")[4]
tH =  tmp_gu_si_gun_Hong.text
tmp_Hong_votes = tH[ : tH.find('(')].replace(',', "")
tmp_Hong_rate = tH[ tH.find('(') +1 : -1 ]
tH, tmp_Hong_votes, tmp_Hong_rate
('17,901(21.67)', '17901', '21.67')
# 안철수 득표수/득표율
tmp_gu_si_gun_Ahn = tmp[1].find_all("td")[5]
tA = tmp_gu_si_gun_Ahn.text
tmp_Ahn_votes = tA[ : tA.find('(')].replace(',', "")
tmp_Ahn_rate = tA[ tA.find('(') +1 : -1 ]
tH, tmp_Ahn_votes, tmp_Ahn_rate
('17,901(21.67)', '19372', '23.45')
from bs4 import BeautifulSoup
import time

name_si_do = []
name_gu_si_gun = []
num_votes = []
num_Moon = []
rate_Moon = []
num_Hong = []
rate_Hong = []
num_Ahn = []
rate_Ahn = []

for sido_name in sido_names_values:
    # 시도  >> 검색 버튼 클릭
    driver.find_element_by_id("cityCode").send_keys(sido_name)
    time.sleep(1)
    driver.find_element_by_xpath('''//*[@id="searchBtn"]''').click()
    # 시도  >> 검색 버튼 클릭 >>  개표현황 표
    html = driver.page_source
    time.sleep(1)
    soup = BeautifulSoup(html, 'lxml')
    table_tr = soup.find('table').find('tbody').find_all('tr')
    table_tr = table_tr[1:]

    for tmp in table_tr:
        # 구 이름
        tmp_gu_si_gun_name =tmp.find_all("td")[0]
        gu_si_gun_name = tmp_gu_si_gun_name.get_text()

        # 투표수
        tmp_gu_si_gun_num_of_votes = tmp.find_all("td")[2]
        t1 = tmp_gu_si_gun_num_of_votes.text
        tmp_votes = t1[: t1.find('(')].replace(',', "")

        # 문재인 득표수/득표율
        tmp_gu_si_gun_Moon = tmp.find_all("td")[3]
        tm = tmp_gu_si_gun_Moon.text
        tmp_Moon_votes = tm[ : tm.find('(')].replace(',', "")
        tmp_Moon_rate = tm[ tm.find('(') +1 : -1 ]

        # 홍준표 득표수/득표율
        tmp_gu_si_gun_Hong = tmp.find_all("td")[4]
        tH =  tmp_gu_si_gun_Hong.text
        tmp_Hong_votes = tH[ : tH.find('(')].replace(',', "")
        tmp_Hong_rate = tH[ tH.find('(') +1 : -1 ]

        # 안철수 득표수/득표율
        tmp_gu_si_gun_Ahn = tmp.find_all("td")[5]
        tA = tmp_gu_si_gun_Ahn.text
        tmp_Ahn_votes = tA[ : tA.find('(')].replace(',', "")
        tmp_Ahn_rate = tA[ tA.find('(') +1 : -1 ]

        time.sleep(1)
        name_si_do.append(sido_name)
        name_gu_si_gun.append(gu_si_gun_name)
        num_votes.append(tmp_votes)
        num_Moon.append(tmp_Moon_votes)
        rate_Moon.append(tmp_Moon_rate)
        num_Hong.append(tmp_Hong_votes)
        rate_Hong.append(tmp_Hong_rate)
        num_Ahn.append(tmp_Ahn_votes)
        rate_Ahn.append(tmp_Ahn_rate)
import pandas as pd

election_result = pd.DataFrame(
    {
        '광역시도' : name_si_do,
        '시군' : name_gu_si_gun,
        '투표수' : num_votes,
        '문재인 득표' : num_Moon,
        '문재인 득표율' : rate_Moon,
        '홍준표 득표': num_Hong,
        '홍준표 득표율' : rate_Hong,
        '안철수 득표' : num_Ahn,
        '안철수 득표율' : rate_Ahn
    },
    columns=['광역시도', '시군', '투표수', '문재인 득표', '문재인 득표율', '홍준표 득표', '홍준표 득표율', '안철수 득표', '안철수 득표율' ]
)

election_result.tail()
광역시도 시군 투표수 문재인 득표 문재인 득표율 홍준표 득표 홍준표 득표율 안철수 득표 안철수 득표율
245 경상남도 산청군 24513 6561 27.00 12544 51.63 2753 11.33
246 경상남도 거창군 41325 11256 27.48 19976 48.78 4923 12.02
247 경상남도 합천군 33021 7143 21.83 19699 60.22 3077 9.40
248 제주특별자치도 제주시 273163 125717 46.25 48027 17.67 55971 20.59
249 제주특별자치도 서귀포시 101296 43776 43.50 20036 19.91 21890 21.75
election_result.to_csv(
    './data_ouput/05.election_result.csv',
    encoding='utf-8',
    sep=','
)

2. 지역별 ID 생성 코드

1) draw_Korea의 ID와 일치시키기

  • 정규표현식
  • 세종시를 제외한 광역시 + 일반 도 + 세종시
import re
# 예제
example = '수원시장안구'
re.split('시|구', example)
['수원', '장안', '']
election_result = pd.read_csv(
    './data_ouput/05.election_result.csv',
    encoding='utf-8',
    index_col=0
)

# 데이터 유형 파악
election_result.info()
<class 'pandas.core.frame.DataFrame'>
Int64Index: 250 entries, 0 to 249
Data columns (total 9 columns):
광역시도       250 non-null object
시군         250 non-null object
투표수        250 non-null int64
문재인 득표     250 non-null int64
문재인 득표율    250 non-null float64
홍준표 득표     250 non-null int64
홍준표 득표율    250 non-null float64
안철수 득표     250 non-null int64
안철수 득표율    250 non-null float64
dtypes: float64(3), int64(4), object(2)
memory usage: 19.5+ KB
import re


ID = []

for n in election_result.index:

    # '세종특별시'를 제외한 광역시
    if ( election_result['광역시도'][n][-1] == '시'  ) & ( election_result['광역시도'][n] != '세종특별자치시' ):
        #  광역시가 다르지만 이름이 같은 구 이름의 중복을 막기 위한 코드
        ## 구/시/군 이름의 길이가 2자인 경우
        if len(election_result['시군'][n]) == 2:
            ID.append( election_result['광역시도'][n][:2] + ' ' + election_result['시군'][n]  )
        ## 구/시/군 이름의 길이가 2자 이상인  경우
        else:
            ID.append( election_result['광역시도'][n][:2] + ' ' + election_result['시군'][n][:-1] )

   # '도'             
    elif ( election_result['광역시도'][n][-1] == '도' ):
        tmp = election_result['시군'][n]

        # 정규표현식 :  '시', '군' 없애기
        if tmp[0] not in ['시', '군']:
            tmp2 = re.split('시|군', tmp)
        else:
            tmp2 = [ tmp[:-1], '' ]

         # 구/시/군 이름 2자  >>  전남 고흥(군)
        if len( tmp2[1] ) == 2:
            tmp3 = tmp2[0] + ' ' + tmp2[1]
        # 구/시/군 이름 3자 이상  >> 경북 미리내(시)
        elif len(tmp2[1]) >= 3:
            tmp3 = tmp2[0] + ' ' + tmp2[1][:-1]
        else:
            tmp3 = tmp2[0]
        ID.append(tmp3)

    # 세종시
    else:
        ID.append('세종')
len(ID), len(election_result)
(250, 250)
election_result['ID'] = ID

election_result.head()
광역시도 시군 투표수 문재인 득표 문재인 득표율 홍준표 득표 홍준표 득표율 안철수 득표 안철수 득표율 ID
0 서울특별시 종로구 102566 42512 41.59 22325 21.84 22313 21.83 서울 종로
1 서울특별시 중구 82852 34062 41.23 17901 21.67 19372 23.45 서울 중구
2 서울특별시 용산구 148157 58081 39.33 35230 23.85 32109 21.74 서울 용산
3 서울특별시 성동구 203175 86686 42.80 40566 20.03 45674 22.55 서울 성동
4 서울특별시 광진구 240030 105512 44.10 46368 19.38 52824 22.08 서울 광진

2) 표 데이터 분석

  • 문재인 후보에 대한 지지 비율이 높은 지역
  • 홍준표 후보에 대한 지지 비율이 높은 지역
# 문재인 후보에 대한 지지 비율이 높은 지역
election_result.sort_values( ['문재인 득표율'], ascending=False ).head()
광역시도 시군 투표수 문재인 득표 문재인 득표율 홍준표 득표 홍준표 득표율 안철수 득표 안철수 득표율 ID
182 전라남도 순천시 181451 122595 67.81 4525 2.50 40429 22.36 순천
175 전라북도 장수군 16079 10714 67.06 717 4.48 3353 20.98 장수
166 전라북도 전주시덕진구 187921 125375 66.89 5183 2.76 40188 21.44 전주 덕진
165 전라북도 전주시완산구 236383 157637 66.89 7003 2.97 50506 21.43 전주 완산
173 전라북도 진안군 18107 11918 66.17 819 4.54 3904 21.67 진안
# 홍준표 후보에 대한 지지 비율이 높은 지역
election_result.sort_values( ['홍준표 득표율'], ascending=False ).head()
광역시도 시군 투표수 문재인 득표 문재인 득표율 홍준표 득표 홍준표 득표율 안철수 득표 안철수 득표율 ID
219 경상북도 군위군 17627 2251 12.83 11651 66.43 1939 11.05 군위
220 경상북도 의성군 37855 5365 14.27 23790 63.30 4767 12.68 의성
223 경상북도 영덕군 26125 3786 14.61 16314 62.96 3231 12.47 영덕
247 경상남도 합천군 33021 7143 21.83 19699 60.22 3077 9.40 합천
216 경상북도 고령군 22396 3754 16.90 13248 59.65 2600 11.70 고령

3. Square tile grid Map 시각화

1) 기존 지도 데이터 가져오기

draw_korea = pd.read_csv(
    './data_ouput/05.draw_korea.csv',
    encoding='utf-8',
    index_col=0
)

draw_korea.head()
y x ID
0 0 7 철원
1 0 8 화천
2 0 9 양구
3 0 10 고성(강원)
4 1 3 양주

2) draw_korea / election_result 두 데이터의 ID 일치 여부 확인하기

set( draw_korea['ID'].unique() )  -  set( election_result['ID'].unique() )
{'고성(강원)', '고성(경남)', '부천 소사', '부천 오정', '부천 원미', '창원 합포', '창원 회원'}
set( election_result['ID'].unique() ) - set( draw_korea['ID'].unique() )
{'고성', '부천', '창원 마산합포', '창원 마산회원'}
# 강원 고성과  경남 고성  서로 구분하기

election_result[ election_result['ID'] == '고성' ]
광역시도 시군 투표수 문재인 득표 문재인 득표율 홍준표 득표 홍준표 득표율 안철수 득표 안철수 득표율 ID
125 강원도 고성군 18692 5664 30.58 6511 35.15 3964 21.40 고성
233 경상남도 고성군 34603 9848 28.67 16797 48.91 4104 11.95 고성
election_result.loc[125, 'ID'] = '고성(강원)'
election_result.loc[233, 'ID'] = '고성(경남)'

election_result[ election_result['시군'] == '고성군' ]
광역시도 시군 투표수 문재인 득표 문재인 득표율 홍준표 득표 홍준표 득표율 안철수 득표 안철수 득표율 ID
125 강원도 고성군 18692 5664 30.58 6511 35.15 3964 21.40 고성(강원)
233 경상남도 고성군 34603 9848 28.67 16797 48.91 4104 11.95 고성(경남)
election_result.tail()
광역시도 시군 투표수 문재인 득표 문재인 득표율 홍준표 득표 홍준표 득표율 안철수 득표 안철수 득표율 ID
245 경상남도 산청군 24513 6561 27.00 12544 51.63 2753 11.33 산청
246 경상남도 거창군 41325 11256 27.48 19976 48.78 4923 12.02 거창
247 경상남도 합천군 33021 7143 21.83 19699 60.22 3077 9.40 합천
248 제주특별자치도 제주시 273163 125717 46.25 48027 17.67 55971 20.59 제주
249 제주특별자치도 서귀포시 101296 43776 43.50 20036 19.91 21890 21.75 서귀포
#  창원시의 긴 이름의 구  검색하기

election_result[ election_result['광역시도'] == '경상남도' ][:7]
광역시도 시군 투표수 문재인 득표 문재인 득표율 홍준표 득표 홍준표 득표율 안철수 득표 안철수 득표율 ID
226 경상남도 창원시의창구 164047 60757 37.22 56887 34.85 22830 13.98 창원 의창
227 경상남도 창원시성산구 153327 63717 41.74 42052 27.54 22923 15.01 창원 성산
228 경상남도 창원시마산합포구 119281 35592 29.99 54488 45.91 14686 12.37 창원 마산합포
229 경상남도 창원시마산회원구 136757 45014 33.07 56340 41.39 17744 13.03 창원 마산회원
230 경상남도 창원시진해구 114779 41249 36.11 40049 35.06 17435 15.26 창원 진해
231 경상남도 진주시 222813 73929 33.35 93751 42.30 26687 12.04 진주
232 경상남도 통영시 82855 25477 30.94 36128 43.87 10738 13.04 통영
election_result.loc[228, 'ID'] = '창원 합포'
election_result.loc[229, 'ID'] = '창원 회원'
election_result[ election_result['광역시도'] == '경상남도' ][:5]
광역시도 시군 투표수 문재인 득표 문재인 득표율 홍준표 득표 홍준표 득표율 안철수 득표 안철수 득표율 ID
226 경상남도 창원시의창구 164047 60757 37.22 56887 34.85 22830 13.98 창원 의창
227 경상남도 창원시성산구 153327 63717 41.74 42052 27.54 22923 15.01 창원 성산
228 경상남도 창원시마산합포구 119281 35592 29.99 54488 45.91 14686 12.37 창원 합포
229 경상남도 창원시마산회원구 136757 45014 33.07 56340 41.39 17744 13.03 창원 회원
230 경상남도 창원시진해구 114779 41249 36.11 40049 35.06 17435 15.26 창원 진해
# 부천
# 2016년 7월 4일 부터 구제를 폐지하고 10개의 행정복지센터(책임동)으로 전환 운영한다.

set( draw_korea['ID'].unique() )  -  set( election_result['ID'].unique() )
{'부천 소사', '부천 오정', '부천 원미'}
set( election_result['ID'].unique() ) - set( draw_korea['ID'].unique() )
{'부천'}
# 부천은 1/3씩 나눠서 처리

election_result[ election_result['시군'] == '부천시' ]
광역시도 시군 투표수 문재인 득표 문재인 득표율 홍준표 득표 홍준표 득표율 안철수 득표 안철수 득표율 ID
85 경기도 부천시 543777 239697 44.23 100544 18.55 128297 23.67 부천
votes_부천 = election_result.loc[ 85, '투표수' ] /3
Moon_votes = election_result.loc[ 85, '문재인 득표' ] /3
Moon_rate = election_result.loc[ 85, '문재인 득표율' ]
Hong_votes = election_result.loc[ 85, '홍준표 득표' ] /3
Hong_rate = election_result.loc[ 85, '홍준표 득표율' ]
Ahn_votes = election_result.loc[ 85, '안철수 득표' ] /3
Ahn_rate = election_result.loc[ 85, '안철수 득표율' ]

election_result.loc[250] = ['경기도', '부천시', votes_부천, Moon_votes, Moon_rate, Hong_votes, Hong_rate, Ahn_votes, Ahn_rate, '부천 소사' ]
election_result.loc[251] = ['경기도', '부천시', votes_부천, Moon_votes, Moon_rate, Hong_votes, Hong_rate, Ahn_votes, Ahn_rate, '부천 오정' ]
election_result.loc[252] = ['경기도', '부천시', votes_부천, Moon_votes, Moon_rate, Hong_votes, Hong_rate, Ahn_votes, Ahn_rate, '부천 원미' ]
election_result[ election_result['시군'] == '부천시' ]
광역시도 시군 투표수 문재인 득표 문재인 득표율 홍준표 득표 홍준표 득표율 안철수 득표 안철수 득표율 ID
85 경기도 부천시 543777.0 239697.0 44.23 100544.000000 18.55 128297.000000 23.67 부천
250 경기도 부천시 181259.0 79899.0 44.23 33514.666667 18.55 42765.666667 23.67 부천 소사
251 경기도 부천시 181259.0 79899.0 44.23 33514.666667 18.55 42765.666667 23.67 부천 오정
252 경기도 부천시 181259.0 79899.0 44.23 33514.666667 18.55 42765.666667 23.67 부천 원미
election_result.drop( [85], inplace=True  )
election_result[ election_result['시군'] == '부천시' ]
광역시도 시군 투표수 문재인 득표 문재인 득표율 홍준표 득표 홍준표 득표율 안철수 득표 안철수 득표율 ID
250 경기도 부천시 181259.0 79899.0 44.23 33514.666667 18.55 42765.666667 23.67 부천 소사
251 경기도 부천시 181259.0 79899.0 44.23 33514.666667 18.55 42765.666667 23.67 부천 오정
252 경기도 부천시 181259.0 79899.0 44.23 33514.666667 18.55 42765.666667 23.67 부천 원미
# 일치 여부 최종 확인

set( draw_korea['ID'].unique() )  -  set( election_result['ID'].unique() )
set()
set( election_result['ID'].unique() )  -  set( draw_korea['ID'].unique() )
set()

3) draw_korea / election_result 두 데이터를 합치기 : merge

draw_korea_election = pd.merge(
    election_result,
    draw_korea,
    how='left',
    on=['ID']
)

draw_korea_election.head()
광역시도 시군 투표수 문재인 득표 문재인 득표율 홍준표 득표 홍준표 득표율 안철수 득표 안철수 득표율 ID y x
0 서울특별시 종로구 102566.0 42512.0 41.59 22325.0 21.84 22313.0 21.83 서울 종로 4 6
1 서울특별시 중구 82852.0 34062.0 41.23 17901.0 21.67 19372.0 23.45 서울 중구 5 6
2 서울특별시 용산구 148157.0 58081.0 39.33 35230.0 23.85 32109.0 21.74 서울 용산 6 6
3 서울특별시 성동구 203175.0 86686.0 42.80 40566.0 20.03 45674.0 22.55 서울 성동 5 7
4 서울특별시 광진구 240030.0 105512.0 44.10 46368.0 19.38 52824.0 22.08 서울 광진 6 7

4) 문재인 vs 홍준표, 문재인 vs 안철수, 홍준표 vs 안철수 득표율 정의하기

draw_korea_election['문vs홍'] = draw_korea_election['문재인 득표율']  -  draw_korea_election['홍준표 득표율']
draw_korea_election['문vs안'] = draw_korea_election['문재인 득표율']  -  draw_korea_election['안철수 득표율']
draw_korea_election['홍vs안'] = draw_korea_election['홍준표 득표율']  -  draw_korea_election['안철수 득표율']
draw_korea_election.tail()
광역시도 시군 투표수 문재인 득표 문재인 득표율 홍준표 득표 홍준표 득표율 안철수 득표 안철수 득표율 ID y x 문vs홍 문vs안 홍vs안
247 제주특별자치도 제주시 273163.0 125717.0 46.25 48027.000000 17.67 55971.000000 20.59 제주 25 5 28.58 25.66 -2.92
248 제주특별자치도 서귀포시 101296.0 43776.0 43.50 20036.000000 19.91 21890.000000 21.75 서귀포 26 5 23.59 21.75 -1.84
249 경기도 부천시 181259.0 79899.0 44.23 33514.666667 18.55 42765.666667 23.67 부천 소사 4 2 25.68 20.56 -5.12
250 경기도 부천시 181259.0 79899.0 44.23 33514.666667 18.55 42765.666667 23.67 부천 오정 6 2 25.68 20.56 -5.12
251 경기도 부천시 181259.0 79899.0 44.23 33514.666667 18.55 42765.666667 23.67 부천 원미 5 2 25.68 20.56 -5.12

5) draw_Korea 함수 가져오기

def draw_Korea(target_data, blocked_map, c_map_name):
    gamma = 0.75
    white_label_min = (
        max(blocked_map[target_data]) - min(blocked_map[target_data]) * 0.25 + min( blocked_map[target_data] )        
    )
    data_label = target_data
    vmin = min( blocked_map[target_data] )
    vmax = max( blocked_map[target_data] )

    BORDER_LINES = [
        # 인천
        [(5, 1), (5,2), (7,2), (7,3), (11,3), (11,0)],
        # 서울
        [(5,4), (5,5), (2,5), (2,7), (4,7), (4,9), (7,9), (7,7), (9,7), (9,5), (10,5), (10,4), (5,4)],
        # 경기도
        [(1,7), (1,8), (3,8), (3,10), (10,10), (10,7), (12,7), (12,6), (11,6), (11,5), (12, 5), (12,4), (11,4), (11,3)],
        # 강원도
        [(8,10), (8,11), (6,11), (6,12)],
        # 충청북도
        [(12,5), (13,5), (13,4), (14,4), (14,5), (15,5), (15,4), (16,4), (16,2)],
        # 전라북도
        [(16,4), (17,4), (17,5), (16,5), (16,6), (19,6), (19,5), (20,5), (20,4), (21,4), (21,3), (19,3), (19,1)],
        # 대전시
        [(13,5), (13,6), (16,6)],
        #세종시
        [(13,5), (14,5)],
        #광주
        [(21,2), (21,3), (22,3), (22,4), (24,4), (24,2), (21,2)],
        #전라남도
        [(20,5), (21,5), (21,6), (23,6)],
        #충청북도
        [(10,8), (12,8), (12,9), (14,9), (14,8), (16,8), (16,6)],
        #경상북도
        [(14,9), (14,11), (14,12), (13,12), (13,13)],
        #대구
        [(15,8), (17,8), (17,10), (16,10), (16,11), (14,11)],
        #부산
        [(17,9), (18,9), (18,8), (19,8), (19,9), (20,9), (20,10), (21,10)],
        #울산
        [(16,11), (16,13)],
        # [(9,14), (9,15)],
        [(27,5), (27,6), (25,6)],
    ]

    map_data = blocked_map.pivot_table(
        index='y',
        columns='x',
        values=target_data
        )
    masked_map_data = np.ma.masked_where(
        np.isnan(map_data),
        map_data
        )
    plt.figure( figsize=(9, 11) )
    plt.pcolor(masked_map_data, vmin=vmin, vmax=vmax, cmap=c_map_name, edgecolor='#aaaaaa', linewidth=0.5 )

    # 지역 이름 표시
    for idx, row in blocked_map.iterrows():
        # 광역시는 구 이름이 겹치는 경우가 많아서 시단위 이름을 같이 표시한다.
        if len( row['ID'].split() ) ==2:
            display_name = '{}\n{}'.format(row['ID'].split()[0], row['ID'].split()[1])
        elif row['ID'][:2] =='고성':
            display_name = '고성'
        else:
            display_name = row['ID']
        # 서대문구, 서귀포시 같이 이름이 3자 이상인 경우에 작은 글자로 표시한다
        if len(display_name.splitlines()[-1]) >= 3:
            fontsize, linespacing = 9.5, 1.5
        else:
            fontsize, linespacing = 11, 1.2
        annocolor = 'white' if row[target_data] > white_label_min else 'black'
        plt.annotate(
            display_name,
            (row['x']+0.5, row['y']+0.5),
            weight='bold',
            fontsize=fontsize,
            ha='center',
            va='center',
            color=annocolor,
            linespacing=linespacing
        )

        # 시도 경계를 그린다.
        for path in BORDER_LINES:
            ys, xs = zip(*path)
            plt.plot(xs, ys, c='black', lw=2)

    plt.gca().invert_yaxis()
    plt.axis('off')
    plt.tight_layout()
    plt.show()
import numpy as np

import matplotlib.pyplot as plt
%matplotlib inline

# 한글폰트 적용
import matplotlib.font_manager as fm

font_location = '/usr/share/fonts/truetype/nanum/NanumBarunGothic.ttf'
font_name = fm.FontProperties(fname=font_location).get_name()

from matplotlib import rc
rc('font', family=font_name)
draw_Korea('문vs홍', draw_korea_election, 'Blues' )

png

#  대비 효과를 높이기 위한 draw_Korea 함수 수정


def draw_Korea(target_data, blocked_map, c_map_name):
    gamma = 0.75
#     white_label_min = (
#         max(blocked_map[target_data]) - min(blocked_map[target_data]) * 0.25 + min( blocked_map[target_data] )        
#     )
    white_label_min = 20.

    data_label = target_data
#     vmin = min( blocked_map[target_data] )
    vmin = -50
#     vmax = max( blocked_map[target_data] )
    vmax = 50

    BORDER_LINES = [
        # 인천
        [(5, 1), (5,2), (7,2), (7,3), (11,3), (11,0)],
        # 서울
        [(5,4), (5,5), (2,5), (2,7), (4,7), (4,9), (7,9), (7,7), (9,7), (9,5), (10,5), (10,4), (5,4)],
        # 경기도
        [(1,7), (1,8), (3,8), (3,10), (10,10), (10,7), (12,7), (12,6), (11,6), (11,5), (12, 5), (12,4), (11,4), (11,3)],
        # 강원도
        [(8,10), (8,11), (6,11), (6,12)],
        # 충청북도
        [(12,5), (13,5), (13,4), (14,4), (14,5), (15,5), (15,4), (16,4), (16,2)],
        # 전라북도
        [(16,4), (17,4), (17,5), (16,5), (16,6), (19,6), (19,5), (20,5), (20,4), (21,4), (21,3), (19,3), (19,1)],
        # 대전시
        [(13,5), (13,6), (16,6)],
        #세종시
        [(13,5), (14,5)],
        #광주
        [(21,2), (21,3), (22,3), (22,4), (24,4), (24,2), (21,2)],
        #전라남도
        [(20,5), (21,5), (21,6), (23,6)],
        #충청북도
        [(10,8), (12,8), (12,9), (14,9), (14,8), (16,8), (16,6)],
        #경상북도
        [(14,9), (14,11), (14,12), (13,12), (13,13)],
        #대구
        [(15,8), (17,8), (17,10), (16,10), (16,11), (14,11)],
        #부산
        [(17,9), (18,9), (18,8), (19,8), (19,9), (20,9), (20,10), (21,10)],
        #울산
        [(16,11), (16,13)],
        # [(9,14), (9,15)],
        [(27,5), (27,6), (25,6)],
    ]

    map_data = blocked_map.pivot_table(
        index='y',
        columns='x',
        values=target_data
        )
    masked_map_data = np.ma.masked_where(
        np.isnan(map_data),
        map_data
        )
    plt.figure( figsize=(9, 11) )
    plt.pcolor(masked_map_data, vmin=vmin, vmax=vmax, cmap=c_map_name, edgecolor='#aaaaaa', linewidth=0.5 )

    # 지역 이름 표시
    for idx, row in blocked_map.iterrows():
        # 광역시는 구 이름이 겹치는 경우가 많아서 시단위 이름을 같이 표시한다.
        if len( row['ID'].split() ) ==2:
            display_name = '{}\n{}'.format(row['ID'].split()[0], row['ID'].split()[1])
        elif row['ID'][:2] =='고성':
            display_name = '고성'
        else:
            display_name = row['ID']
        # 서대문구, 서귀포시 같이 이름이 3자 이상인 경우에 작은 글자로 표시한다
        if len(display_name.splitlines()[-1]) >= 3:
            fontsize, linespacing = 9.5, 1.5
        else:
            fontsize, linespacing = 11, 1.2
#         annocolor = 'white' if row[target_data] > white_label_min else 'black'
        annocolor = 'white' if np.abs( row[target_data] )  > white_label_min else 'black'
        plt.annotate(
            display_name,
            (row['x']+0.5, row['y']+0.5),
            weight='bold',
            fontsize=fontsize,
            ha='center',
            va='center',
            color=annocolor,
            linespacing=linespacing
        )

        # 시도 경계를 그린다.
        for path in BORDER_LINES:
            ys, xs = zip(*path)
            plt.plot(xs, ys, c='black', lw=2)

    plt.gca().invert_yaxis()
    plt.axis('off')
    plt.tight_layout()
    plt.show()

6) 지도 그리기

(1) Sequential ColorMaps

  • Greys / Purples / Blues / Greens / Oranges / Reds / YlOrBr / YlOrRd / OrRd / RdPu / BuPu / GnBu / YlGnBu / PuBuGn / BuGn / YlGn

(2) Diverging ColorMaps

  • PiYG / PRGn / BrBG / PuOr / RdGy / RdBu / RdYlBu / RdYlGn / Spectral / coolwarm / bwr / seismic
draw_Korea('문vs홍', draw_korea_election, 'RdBu' )

png

draw_Korea('문vs안', draw_korea_election, 'BrBG' )

png

draw_Korea('홍vs안', draw_korea_election, 'BrBG' )

png