#######################################################
# 원본: http://highthroughput.org/wp/ (Hyeshik Chang 장혜식님)
# 원본참고: http://pinkwink.kr/1005
#######################################################
2곳의 원본을 참고하여 대한민국 지도에
인구 분포도를 그려주는 코드를 한줄한줄 따라가면서
numpy, pandas, matplotlib에 대한 사용법을 익혀보았다.
필요한 csv 파일과 소스코드는 핑크윙크에 가면 있다.
각 라인별로 주석을 달아두었다.
drawKorea 함수만 이해해도 왠만한건 할 수 있을것 같다.
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
import numpy as np
import matplotlib.pyplot as plt
import pandas as pd
BORDER_LINES = [
[(3, 2), (5, 2), (5, 3), (9, 3), (9, 1)], # 인천
[(2, 5), (3, 5), (3, 4), (8, 4), (8, 7), (7, 7), (7, 9), (4, 9), (4, 7), (1, 7)], # 서울
[(1, 6), (1, 9), (3, 9), (3, 10), (8, 10), (8, 9),
(9, 9), (9, 8), (10, 8), (10, 5), (9, 5), (9, 3)], # 경기도
[(9, 12), (9, 10), (8, 10)], # 강원도
[(10, 5), (11, 5), (11, 4), (12, 4), (12, 5), (13, 5),
(13, 4), (14, 4), (14, 2)], # 충청남도
[(11, 5), (12, 5), (12, 6), (15, 6), (15, 7), (13, 7),
(13, 8), (11, 8), (11, 9), (10, 9), (10, 8)], # 충청북도
[(14, 4), (15, 4), (15, 6)], # 대전시
[(14, 7), (14, 9), (13, 9), (13, 11), (13, 13)], # 경상북도
[(14, 8), (16, 8), (16, 10), (15, 10),
(15, 11), (14, 11), (14, 12), (13, 12)], # 대구시
[(15, 11), (16, 11), (16, 13)], # 울산시
[(17, 1), (17, 3), (18, 3), (18, 6), (15, 6)], # 전라북도
[(19, 2), (19, 4), (21, 4), (21, 3), (22, 3), (22, 2), (19, 2)], # 광주시
[(18, 5), (20, 5), (20, 6)], # 전라남도
[(16, 9), (18, 9), (18, 8), (19, 8), (19, 9), (20, 9), (20, 10)], # 부산시
]
"""
# ziped BORDER_LINES 확인
>>> for path in BORDER_LINES:
>>> ys, xs = zip(*path)
(3, 5, 5, 9, 9) (2, 2, 3, 3, 1)
(2, 3, 3, 8, 8, 7, 7, 4, 4, 1) (5, 5, 4, 4, 7, 7, 9, 9, 7, 7)
(1, 1, 3, 3, 8, 8, 9, 9, 10, 10, 9, 9) (6, 9, 9, 10, 10, 9, 9, 8, 8, 5, 5, 3)
(9, 9, 8) (12, 10, 10)
(10, 11, 11, 12, 12, 13, 13, 14, 14) (5, 5, 4, 4, 5, 5, 4, 4, 2)
(11, 12, 12, 15, 15, 13, 13, 11, 11, 10, 10) (5, 5, 6, 6, 7, 7, 8, 8, 9, 9, 8)
(14, 15, 15) (4, 4, 6)
(14, 14, 13, 13, 13) (7, 9, 9, 11, 13)
(14, 16, 16, 15, 15, 14, 14, 13) (8, 8, 10, 10, 11, 11, 12, 12)
(15, 16, 16) (11, 11, 13)
(17, 17, 18, 18, 15) (1, 3, 3, 6, 6)
(19, 19, 21, 21, 22, 22, 19) (2, 4, 4, 3, 3, 2, 2)
(18, 20, 20) (5, 5, 6)
(16, 18, 18, 19, 19, 20, 20) (9, 9, 8, 8, 9, 9, 10)
# 읽어들였던 data_draw_korea.csv 엑셀 파일의 일부
,인구수,shortName,x,y,면적,광역시도,행정구역
0,202520,강릉,11,4,1040.07,강원도,강릉시
1,25589,고성(강원),9,0,664.19,강원도,고성군
2,86747,동해,11,5,180.01,강원도,동해시
3,63986,삼척,11,8,1185.8,강원도,삼척시
4,76733,속초,9,1,105.25,강원도,속초시
... 후략...
"""
def drawKorea(target_data, blocked_map, d1, d2, cmapname):
vmin = min(blocked_map[target_data]) # target_data의 최소값 (인구수가 가장 적은 값)
vmax = max(blocked_map[target_data]) # target_data의 최대값 (인구수가 가장 많은 값)
white_label_min = (vmax - vmin) * 0.25 + vmin
# .csv에서 받아온 정보에서 사용하기 좋게 pivot
map_data = blocked_map.pivot(index='y', columns='x', values=target_data)
# np.isnan(map_data)는 Nan이면 True, 나머지는 False 리턴해주고
# 이것을 바탕으로 마스킹 배열 생성
masked_mapdata = np.ma.masked_where(condition=np.isnan(map_data), a=map_data, copy=True) # numpy masked array
plt.figure(figsize=(8, 13)) # width, height tuple in inches
# create a pseudocolor plot of a 2-D array.
# plt.pcolor 대신에 pcolormesh 사용함.
plt.pcolormesh(masked_mapdata, vmin=vmin, vmax=vmax, cmap=cmapname, edgecolor='#aaaaaa', linewidth=0.5)
for idx, row in blocked_map.iterrows(): # rows에 대한 반복 처리
# 인구수: row[target_data]
annocolor = 'white' if row[target_data] > white_label_min else 'black'
# 광역시도: row[d1]
# 행정구역: row[d2]
# 광역시는 구 이름이 겹치는 경우가 많아서 시단위 이름도 같이 표시한다. (중구, 서구)
# XX시로 끝나지만 세종시가 아닌 것들만 예외처리
if row[d1].endswith('시') and not row[d1].startswith('세종'):
# row[d1] : 서울특별시, 부산광역시... row[d1][:2] : 서울, 부산...
# row[d2] : 서대문구, 서초구, 중구 ... row[d2][:-1] : 서대문, 서초, 중 ...
dispname = '{}\n{}'.format(row[d1][:2], row[d2][:-1])
if len(row[d2]) <= 2:
# 중, 서, ... -> 중구, 서구, ...
dispname += row[d2][-1]
else:
# 제주특별자치도, 전라남도, 경상북도, ...
dispname = row[d2][:-1]
# 서대문구, 서귀포시 같이 이름이 3자 이상인 경우에 작은 글자로 표시한다.
if len(dispname.splitlines()[-1]) >= 3:
fontsize, linespacing = 9.5, 1.5
else:
fontsize, linespacing = 11, 1.2
# dispname '서울/n강남'에 대한 annotating
plt.annotate(s=dispname,
xy=(row['x'] + 0.5, row['y'] + 0.5),
fontweight='bold', fontsize=fontsize,
horizontalalignment='center',
verticalalignment='center',
color=annocolor,
linespacing=linespacing)
for path in BORDER_LINES:
ys, xs = zip(*path)
# plot xs, ys
plt.plot(xs, ys, color='black', linewidth=4)
plt.gca().invert_yaxis() # x, y 뒤집어야 함
plt.gca().set_aspect(aspect='equal')
plt.axis('off')
# 우측에 컬러바를 통해서 색상에 따라 수치를 파악할 수 있음
# shirink: 컬러바의 전반적인 크기 조절
# aspect: 컬러바의 폭 조절
cb = plt.colorbar(shrink=0.1, aspect=10)
cb.set_label(label=target_data)
plt.tight_layout()
plt.show()
def main():
plt.rcParams["font.family"] = "AppleGothic" # font setting
# http://en.wikipedia.org/wiki/Plus_and_minus_signs#Character_codes
plt.rcParams['axes.unicode_minus'] = False # use hypen '-' (True: use unicode minus)
data_draw_korea = pd.read_csv('./data_draw_korea.csv', index_col=0, encoding='UTF-8')
data_draw_korea.head() # return first row
drawKorea('인구수', data_draw_korea, '광역시도', '행정구역', 'Blues')
if __name__ == '__main__':
main()
...
...
반응형
'Language > PYTHON' 카테고리의 다른 글
[라즈베리파이3] 조도센서로 값 받아서 서버의 DB로 데이터 전송해서 저장하기 (9) | 2017.04.21 |
---|---|
[Redis] redis-cli로 ip, port, password 입력하여 접속하기 (9) | 2017.04.06 |
[라즈베리파이3] 블루투스 키보드 자동 연결하기 (8) | 2017.04.05 |
[라즈베리파이3] redis 설치하기 (8) | 2017.04.05 |
[라즈베리파이3] 조도센서로 빛에 대한 값 가져오기 (소스코드 분석 및 동영상) (8) | 2017.04.04 |