- 국가 통계 포털에서 인구 정보 내려받기
http://kosis.kr/statHtml/statHtml.do?orgId=101&tblId=DT_1IN1509&
KOSIS
kosis.kr
기본코드
import pandas as pd
import numpy as np
import platform
import matplotlib.pyplot as plt
%matplotlib inline
path = "c:/Windows/Fonts/malgun.ttf"
from matplotlib import font_manager, rc
if platform.system() == 'Darwin':
rc('font', family='AppleGothic')
elif platform.system() == 'Windows':
font_name = font_manager.FontProperties(fname=path).get_name()
rc('font', family=font_name)
else:
print('알 수 없는 시스템')
plt.rcParams['axes.unicode_minus'] = False
1. 데이터 읽어오기
population = pd.read_excel('./data/population.xlsx', header=1)
population
![]() |
2. 데이터 전처리
#결측치를 앞의 데이터로 채우기
population.fillna(method='ffill', inplace=True)
#컬럼이름 변경하기
population.rename(columns = {'행정구역별(시군구)(1)':'광역시도',
'행정구역별(시군구)(2)':'시도',
'합계':'인구수'}, inplace=True)
population.head()
![]() |
# 소계를 제외한 데이터만 가져오기
population = population[(population['시도'] != '소계')]
population.head()
![]() |
# 컬럼 이름 변경
population.is_copy = False
population.rename(columns = {'성별(1)':'구분'}, inplace=True)
population.loc[population['구분'] == '계', '구분'] = '합계'
population.head()
![]() |
# 청년과 노년 구분
population['20-39세'] = population['20~24세'] + population['25~29세'] + \
population['30~34세'] + population['35~39세']
population['65세이상'] = population['65~69세'] + population['70~74세'] + \
population['75~79세'] + population['80~84세'] + \
population['85세이상']
population.head(5)
![]() |
# 피벗 테이블 생성
pop = pd.pivot_table(population,
index = ['광역시도', '시도'],
columns = ['구분'],
values = ['인구수', '20-39세', '65세이상'])
pop.head()
![]() |
# 새로운 컬럼 추가 - 소멸 비율
pop['소멸비율'] = pop['20-39세','합계'] / (pop['65세이상','합계'] / 2)
pop.head()
![]() |
# 새로운 컬럼 추가 - 소멸 위기 지역
pop['소멸위기지역'] = pop['소멸비율'] < 1.0
pop.head()
![]() |
print(pop[pop['소멸위기지역']==True].index.get_level_values(1))
Index(['영월군', '남해군', '산청군', '의령군', '하동군', '함양군', '합천군', '군위군', '면부', '봉화군', '영덕군', '영양군', '의성군', '청도군', '청송군', '강화군', '면부', '강진군', '고흥군', '곡성군', '구례군', '면부', '보성군', '신안군', '완도군', '장흥군', '진도군', '함평군', '해남군', '고창군', '면부', '무주군', '부안군', '순창군', '임실군', '장수군', '진안군', '부여군', '서천군', '청양군', '괴산군', '단양군', '보은군'], dtype='object', name='시도') |
# 인덱스 초기화
pop.reset_index(inplace=True)
pop.head()
![]() |
3. 시도 이름 합치기
# 새로운 컬럼 추가 - 첫번째와 두번째 컬럼 이름 합치기
tmp_coloumns = [pop.columns.get_level_values(0)[n] + \
pop.columns.get_level_values(1)[n]
for n in range(0,len(pop.columns.get_level_values(0)))]
pop.columns = tmp_coloumns
pop.head()
![]() |
# 데이터 확인
pop.info()
<class 'pandas.core.frame.DataFrame'> RangeIndex: 303 entries, 0 to 302 Data columns (total 13 columns): # Column Non-Null Count Dtype --- ------ -------------- ----- 0 광역시도 303 non-null object 1 시도 303 non-null object 2 20-39세남자 303 non-null int64 3 20-39세여자 303 non-null int64 4 20-39세합계 303 non-null int64 5 65세이상남자 303 non-null int64 6 65세이상여자 303 non-null int64 7 65세이상합계 303 non-null int64 8 인구수남자 303 non-null int64 9 인구수여자 303 non-null int64 10 인구수합계 303 non-null int64 11 소멸비율 303 non-null float64 12 소멸위기지역 303 non-null bool dtypes: bool(1), float64(1), int64(9), object(2) memory usage: 28.8+ KB |
# 시도 고유값 확인
print(pop['시도'].unique())
['강릉시' '고성군' '동부' '동해시' '면부' '삼척시' '속초시' '양구군' '양양군' '영월군' '원주시' '읍부' '인제군' '정선군' '철원군' '춘천시' '태백시' '평창군' '홍천군' '화천군' '횡성군' '가평군' '고양시' '과천시' '광명시' '광주시' '구리시' '군포시' '권선구' '기흥구' '김포시' '남양주시' '단원구' '덕양구' '동두천시' '동안구' '만안구' '부천시' '분당구' '상록구' '성남시' '수원시' '수정구' '수지구' '시흥시' '안산시' '안성시' '안양시' '양주시' '양평군' '여주시' '연천군' '영통구' '오산시' '용인시' '의왕시' '의정부시' '이천시' '일산동구' '일산서구' '장안구' '중원구' '처인구' '파주시' '팔달구' '평택시' '포천시' '하남시' '화성시' '거제시' '거창군' '김해시' '남해군' '마산합포구' '마산회원구' '밀양시' '사천시' '산청군' '성산구' '양산시' '의령군' '의창구' '진주시' '진해구' '창녕군' '창원시' '통영시' '하동군' '함안군' '함양군' '합천군' '경산시' '경주시' '고령군' '구미시' '군위군' '김천시' '남구' '문경시' '봉화군' '북구' '상주시' '성주군' '안동시' '영덕군' '영양군' '영주시' '영천시' '예천군' '울릉군' '울진군' '의성군' '청도군' '청송군' '칠곡군' '포항시' '광산구' '동구' '서구' '달서구' '달성군' '수성구' '중구' '대덕구' '유성구' '강서구' '금정구' '기장군' '동래구' '부산진구' '사상구' '사하구' '수영구' '연제구' '영도구' '해운대구' '강남구' '강동구' '강북구' '관악구' '광진구' '구로구' '금천구' '노원구' '도봉구' '동대문구' '동작구' '마포구' '서대문구' '서초구' '성동구' '성북구' '송파구' '양천구' '영등포구' '용산구' '은평구' '종로구' '중랑구' '세종시' '울주군' '강화군' '계양구' '남동구' '미추홀구' '부평구' '연수구' '옹진군' '강진군' '고흥군' '곡성군' '광양시' '구례군' '나주시' '담양군' '목포시' '무안군' '보성군' '순천시' '신안군' '여수시' '영광군' '영암군' '완도군' '장성군' '장흥군' '진도군' '함평군' '해남군' '화순군' '고창군' '군산시' '김제시' '남원시' '덕진구' '무주군' '부안군' '순창군' '완산구' '완주군' '익산시' '임실군' '장수군' '전주시' '정읍시' '진안군' '서귀포시' '제주시' '계룡시' '공주시' '금산군' '논산시' '당진시' '동남구' '보령시' '부여군' '서북구' '서산시' '서천군' '아산시' '예산군' '천안시' '청양군' '태안군' '홍성군' '괴산군' '단양군' '보은군' '상당구' '서원구' '영동군' '옥천군' '음성군' '제천시' '증평군' '진천군' '청원구' '청주시' '충주시' '흥덕구'] |
# 시도 고유 이름
si_name = [None] * len(pop)
#광역시가 아닌 곳 중에서 구를 가지고 있는 시도들의 구이름 디셔너리 생성
tmp_gu_dict = {'수원':['장안구', '권선구', '팔달구', '영통구'],
'성남':['수정구', '중원구', '분당구'],
'안양':['만안구', '동안구'],
'안산':['상록구', '단원구'],
'고양':['덕양구', '일산동구', '일산서구'],
'용인':['처인구', '기흥구', '수지구'],
'청주':['상당구', '서원구', '흥덕구', '청원구'],
'천안':['동남구', '서북구'],
'전주':['완산구', '덕진구'],
'포항':['남구', '북구'],
'창원':['의창구', '성산구', '진해구', '마산합포구', '마산회원구'],
'부천':['오정구', '원미구', '소사구']}
for n in pop.index:
#고성이 2곳이므로 도를 추가
if pop['광역시도'][n][-3:] not in ['광역시', '특별시', '자치시']:
if pop['시도'][n][:-1]=='고성' and pop['광역시도'][n]=='강원도':
si_name[n] = '고성(강원)'
elif pop['시도'][n][:-1]=='고성' and pop['광역시도'][n]=='경상남도':
si_name[n] = '고성(경남)'
#그 이외의 지역은 마지막 한글자를 제거해서 군 이나 시 글자를 제거
else:
si_name[n] = pop['시도'][n][:-1]
for keys, values in tmp_gu_dict.items():
if pop['시도'][n] in values:
if len(pop['시도'][n])==2:
si_name[n] = keys + ' ' + pop['시도'][n]
elif pop['시도'][n] in ['마산합포구','마산회원구']:
si_name[n] = keys + ' ' + pop['시도'][n][2:-1]
else:
si_name[n] = keys + ' ' + pop['시도'][n][:-1]
#세종은 이름을 수정
elif pop['광역시도'][n] == '세종특별자치시':
si_name[n] = '세종'
else:
if len(pop['시도'][n])==2:
si_name[n] = pop['광역시도'][n][:2] + ' ' + pop['시도'][n]
else:
si_name[n] = pop['광역시도'][n][:2] + ' ' + pop['시도'][n][:-1]
print(si_name)
['강릉', '고성(강원)', '동', '동해', '면', '삼척', '속초', '양구', '양양', '영월', '원주', '읍', '인제', '정선', '철원', '춘천', '태백', '평창', '홍천', '화천', '횡성', '가평', '고양', '과천', '광명', '광주', '구리', '군포', '수원 권선', '용인 기흥', '김포', '남양주', '안산 단원', '고양 덕양', '동두천', '동', '안양 동안', '안양 만안', '면', '부천', '성남 분당', '안산 상록', '성남', '수원', '성남 수정', '용인 수지', '시흥', '안산', '안성', '안양', '양주', '양평', '여주', '연천', '수원 영통', '오산', '용인', '읍', '의왕', '의정부', '이천', '고양 일산동', '고양 일산서', '수원 장안', '성남 중원', '용인 처인', '파주', '수원 팔달', '평택', '포천', '하남', '화성', '거제', '거창', '고성(경남)', '김해', '남해', '동', '창원 합포', '창원 회원', '면', '밀양', '사천', '산청', '창원 성산', '양산', '읍', '의령', '창원 의창', '진주', '창원 진해', '창녕', '창원', '통영', '하동', '함안', '함양', '합천', '경산', '경주', '고령', '구미', '군위', '김천', '포항 남구', '동', '면', '문경', '봉화', '포항 북구', '상주', '성주', '안동', '영덕', '영양', '영주', '영천', '예천', '울릉', '울진', '읍', '의성', '청도', '청송', '칠곡', '포항', '광주 광산', '광주 남구', '광주 동구', '광주 북구', '광주 서구', '대구 남구', '대구 달서', '대구 달성', '대구 동구', '대구 동부', '대구 면부', '대구 북구', '대구 서구', '대구 수성', '대구 읍부', '대구 중구', '대전 대덕', '대전 동구', '대전 서구', '대전 유성', '대전 중구', '부산 강서', '부산 금정', '부산 기장', '부산 남구', '부산 동구', '부산 동래', '부산 동부', '부산 면부', '부산 부산진', '부산 북구', '부산 사상', '부산 사하', '부산 서구', '부산 수영', '부산 연제', '부산 영도', '부산 읍부', '부산 중구', '부산 해운대', '서울 강남', '서울 강동', '서울 강북', '서울 강서', '서울 관악', '서울 광진', '서울 구로', '서울 금천', '서울 노원', '서울 도봉', '서울 동대문', '서울 동작', '서울 마포', '서울 서대문', '서울 서초', '서울 성동', '서울 성북', '서울 송파', '서울 양천', '서울 영등포', '서울 용산', '서울 은평', '서울 종로', '서울 중구', '서울 중랑', '세종', '세종', '세종', '세종', '울산 남구', '울산 동구', '울산 동부', '울산 면부', '울산 북구', '울산 울주', '울산 읍부', '울산 중구', '인천 강화', '인천 계양', '인천 남동', '인천 동구', '인천 동부', '인천 면부', '인천 미추홀', '인천 부평', '인천 서구', '인천 연수', '인천 옹진', '인천 읍부', '인천 중구', '강진', '고흥', '곡성', '광양', '구례', '나주', '담양', '동', '면', '목포', '무안', '보성', '순천', '신안', '여수', '영광', '영암', '완도', '읍', '장성', '장흥', '진도', '함평', '해남', '화순', '고창', '군산', '김제', '남원', '전주 덕진', '동', '면', '무주', '부안', '순창', '전주 완산', '완주', '읍', '익산', '임실', '장수', '전주', '정읍', '진안', '동', '면', '서귀포', '읍', '제주', '계룡', '공주', '금산', '논산', '당진', '천안 동남', '동', '면', '보령', '부여', '천안 서북', '서산', '서천', '아산', '예산', '읍', '천안', '청양', '태안', '홍성', '괴산', '단양', '동', '면', '보은', '청주 상당', '청주 서원', '영동', '옥천', '음성', '읍', '제천', '증평', '진천', '청주 청원', '청주', '충주', '청주 흥덕'] |
#도시 이름을 DataFrame에 추가하고 불필요한 데이터 삭제
pop['ID'] = si_name
del pop['20-39세남자']
del pop['65세이상남자']
del pop['65세이상여자']
pop.head()
![]() |
3. Cartogram을 위한 엑셀 파일
# 엑셀 파일 읽기
draw_korea_raw = pd.read_excel('./data/draw_korea_raw.xlsx')
draw_korea_raw
![]() |
4. stack으로 묶기
# 컬럼 이름을 인덱스로 만들기
draw_korea_raw_stacked = pd.DataFrame(draw_korea_raw.stack())
draw_korea_raw_stacked
![]() |
# 인덱스 초기화
draw_korea_raw_stacked.reset_index(inplace=True)
draw_korea_raw_stacked
![]() |
# 컬럼 이름 변경
draw_korea_raw_stacked.rename(columns={'level_0':'y', 'level_1':'x', 0:'ID'},
inplace=True)
draw_korea_raw_stacked
![]() |
5. cartogram의 기본이 되는 지도 그리기
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)],
]
# 지역 이름 표시
plt.figure(figsize=(8, 11))
# 지역 이름 표시
for idx, row in draw_korea_raw_stacked.iterrows():
# 광역시는 구 이름이 겹치는 경우가 많아서 시단위 이름도 같이 표시
# (중구, 서구)
if len(row['ID'].split())==2:
dispname = '{}\n{}'.format(row['ID'].split()[0], row['ID'].split()[1])
elif row['ID'][:2]=='고성':
dispname = '고성'
else:
dispname = row['ID']
# 시도 경계 그리기
for path in BORDER_LINES:
ys, xs = zip(*path)
plt.plot(xs, ys, c='black', lw=1.5)
#y축의 위아래 변경
plt.gca().invert_yaxis()
#plt.gca().set_aspect(1)
#축과 라벨 제거
plt.axis('off')
#자동 레이아웃 설정
plt.tight_layout()
plt.show()
![]() |
# draw_korea_raw_stacked와 pop의 도시이름 일치시키기
#draw_korea_raw_stacked 와 pop 의 도시이름 비교
print(set(draw_korea_raw_stacked['ID'].unique()) - set(pop['ID'].unique()))
print(set(pop['ID'].unique()) - set(draw_korea_raw_stacked['ID'].unique()))
#일치하지 않는 데이터 삭제
tmp_list = list(set(pop['ID'].unique()) - set(draw_korea_raw_stacked['ID'].unique()))
for tmp in tmp_list:
pop = pop.drop(pop[pop['ID']==tmp].index)
print(set(pop['ID'].unique()) - set(draw_korea_raw_stacked['ID'].unique()))
{'부천 원미', '부천 오정', '부천 소사', '인천 남구'} {'포항', '부산 면부', '대구 읍부', '성남', '용인', '인천 면부', '대구 동부', '인천 미추홀', '인천 동부', '안산', '부산 동부', '수원', '창원', '울산 면부', '읍', '인천 읍부', '부산 읍부', '고양', '천안', '동', '청주', '울산 읍부', '대구 면부', '안양', '전주', '면', '울산 동부', '부천'} set() |
pop = pd.merge(pop, draw_korea_raw_stacked, how='left', on=['ID'])
pop.head()
![]() |
# 좌표와 인구수 이용해서 피벗 테이블 생성
mapdata = pop.pivot_table(index='y', columns='x', values='인구수합계')
mapdata
![]() |
# Nan이 아닌 데이터 확인
![]() ![]() |
6. Cartogram 그리기
# DataFrame과 컬럼 이름 및 색상 값을 받아서 Cartogram을 그려주는 함수
def drawKorea(targetData, blockedMap, cmapname):
gamma = 0.75
#인구수 데이터의 크고 낮음을 분류하기 위한 값 만들기
whitelabelmin = (max(blockedMap[targetData]) -
min(blockedMap[targetData]))*0.25 + \
min(blockedMap[targetData])
#컬럼이름을 대입하기
datalabel = targetData
#최대값과 최소값 구하기
vmin = min(blockedMap[targetData])
vmax = max(blockedMap[targetData])
#x 와 y를 가지고 피봇 테이블 만들기
mapdata = blockedMap.pivot_table(index='y', columns='x', values=targetData)
#데이터가 존재하는 것 골라내기
masked_mapdata = np.ma.masked_where(np.isnan(mapdata), mapdata)
#그래프 영역 크기 만들기
plt.figure(figsize=(9, 11))
#색상 설정
plt.pcolor(masked_mapdata, vmin=vmin, vmax=vmax, cmap=cmapname,
edgecolor='#aaaaaa', linewidth=0.5)
# 지역 이름 표시
for idx, row in blockedMap.iterrows():
# 광역시는 구 이름이 겹치는 경우가 많아서 시단위 이름도 같이 표시
#(중구, 서구)
if len(row['ID'].split())==2:
dispname = '{}\n{}'.format(row['ID'].split()[0], row['ID'].split()[1])
elif row['ID'][:2]=='고성':
dispname = '고성'
else:
dispname = row['ID']
# 서대문구, 서귀포시 같이 이름이 3자 이상인 경우에 작은 글자로 표시
if len(dispname.splitlines()[-1]) >= 3:
fontsize, linespacing = 10.0, 1.1
else:
fontsize, linespacing = 11, 1.
#글자색상 만들기
annocolor = 'white' if row[targetData] > whitelabelmin else 'black'
#텍스트 출력하기
plt.annotate(dispname, (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')
cb = plt.colorbar(shrink=.1, aspect=10)
cb.set_label(datalabel)
plt.tight_layout()
plt.show()
# 인구수 합계로 Catrogram 그리기
drawKorea('인구수합계', pop, 'Blues')
![]() |
#소멸위기지역으로 Cartogram 그리기
pop['소멸위기지역'] = [1 if con else 0 for con in pop['소멸위기지역']]
drawKorea('소멸위기지역', pop, 'Reds')
![]() |
# 여성 비율로 Catrogram 그리기
pop['여성비'] = (pop['인구수여자']/pop['인구수합계'])*100
drawKorea('여성비', pop, 'RdBu')
![]() |
# 청년 층의 여성 비율을 이용한 Cartogram 그리기
pop['2030여성비'] = (pop['20-39세여자']/pop['20-39세합계'])*100
drawKorea('2030여성비', pop, 'RdBu')
![]() |
# 지역 이름을 index로 설정
import folium
import json
import warnings
warnings.simplefilter(action='ignore', category=FutureWarning)
pop_folium = pop.set_index('ID')
pop_folium.head()
![]() |
#인구 수 합계를 이용한 단계구분도
#한국 지도 시도 경계 좌표를 가진 파일을 가져오기
geo_path = './data/korea_geo_simple.json'
geo_str = json.load(open(geo_path, encoding='utf-8'))
map = folium.Map(location=[36.2002, 127.054], zoom_start=7)
folium.Choropleth(geo_data = geo_str,
data = pop_folium['인구수합계'],
columns = [pop_folium.index, pop_folium['인구수합계']],
fill_color = 'YlGnBu',
key_on = 'feature.id').add_to(map)
map
![]() |
map = folium.Map(location=[36.2002, 127.054], zoom_start=7)
folium.Choropleth(geo_data = geo_str,
data = pop_folium['소멸위기지역'],
columns = [pop_folium.index, pop_folium['소멸위기지역']],
fill_color = 'YlOrRd',
key_on = 'feature.id').add_to(map)
map
![]() |
'Python > Python 실전편' 카테고리의 다른 글
[딥러닝] CNN _ 도로 교통 표지판 인식 (0) | 2024.03.22 |
---|---|
[딥러닝] CNN _ 패션 이미지 분류 (1) | 2024.03.22 |
[전처리] 서울시 범죄 현황 시각화 (0) | 2024.03.20 |
[전처리] 지도 출력 Choropleth (0) | 2024.03.20 |
[전처리] 서울시 구별 CCTV와 인구 관계 분석 (0) | 2024.03.20 |