데이터 엔지니어 이것저것

서울시 인구-CCTV 현황 산점도 본문

기타/데이터 분석 및 시각화

서울시 인구-CCTV 현황 산점도

pastime 2021. 6. 10. 20:34
728x90
# 라이브러리 임포트
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
import platform
from matplotlib import font_manager, rc   # 글꼴 처리 준비

# 주피터 노트북에서 시각화 결과가 직접 출력되도록 설정
%matplotlib inline

# 판다스 데이터프레임 실수 출력 형식 지정
pd.options.display.float_format = '{:,.1f}'.format  
# 운영체제에 적합한 한글 글꼴 지정

plt.rcParams['axes.unicode_minus'] = False                          # '-' 부호가 정상적으로 출력되도록 설정
if platform.system() == 'Darwin':                                   # 운영체제가 MAC OS X라면
    rc('font', family='AppleGothic')                                # 애플고딕 글꼴 지정
elif platform.system() == 'Windows':                                # 운영체제가 윈도우라면 
    path = "c:/Windows/Fonts/malgun.ttf"                            # 글꼴 파일 경로 지정
    font_name = font_manager.FontProperties(fname=path).get_name()  # 글꼴 이름 획득
    rc('font', family=font_name)                                    # 획득한 글꼴 이름으로 지정
else:                                                               # 기타 운영체제라면
    print('Unknown system... sorry~~~~')                            # 글꼴 설정 실패 메시지 출력
# 연도를 지정하여 인구 데이터를 입력하는 함수 실행
df_pop2015 = read_pop('2015')  # 1) 2015 인구 획득
df_pop2020 = read_pop('2020')  # 2) 2020 인구 획득

# 인구 데이터 병합
df_pop = pd.merge(        
    df_pop2015,df_pop2020,
    how='outer',
    on='자치구'
)

df_pop.head()

 

# CCTV 데이터 파일 읽어오기
df_cctv = pd.read_csv(
      'CCTV.csv',# 파일 경로 지정
      thousands = ',',# 천 단위 구분 쉼표 처리
      header = 1,# (0부터 시작하는 번호 기준으로) 1번 행을 헤더로 지정
      encoding='cp949' # 인코딩 방식 지정
)
    
# 열 이름 변경
df_cctv.rename(
    columns={
        '구분': '자치구',
        '2011년 이전' : '2010년 이전'
    },  # '구분'을 '자치구'로, '2011년 이전'을 '2010년 이전'으로 변경
    inplace=True    # 원본을 직접 수정
)  


lst_col = ['2010년 이전', '2011년', '2012년', '2013년', '2014년', '2015년']
df_cctv['CCTV 규모 2015'] = df_cctv[lst_col].sum(axis=1)
df_cctv

# 2015년 합계 계산
lst_col = ['2010년 이전', '2011년', '2012년', '2013년', '2014년', '2015년']
df_cctv['CCTV 규모 2015'] = df_cctv[lst_col].sum(axis=1)

# 2020년 합계 계산
lst_col = ['2010년 이전', '2011년', '2012년', '2013년', '2014년', '2015년', '2016년', '2017년', '2018년', '2019년', '2020년']
df_cctv['CCTV 규모 2020'] = df_cctv[lst_col].sum(axis=1)

 

rows = df_cctv.index[0]
df_cctv.drop(rows, inplace=True)  # 합계 행을 삭제

# 불필요한 열 삭제 ('자치구', 'CCTV 규모 2015', 'CCTV 규모 2020' 열만 남기고 다른 열은 제거)
cols = ['총계', '2010년 이전', '2011년', '2012년', '2013년', '2014년', '2015년', '2016년', '2017년', '2018년', '2019년', '2020년']
df_cctv.drop(cols, axis=1, inplace=True)

# '자치구' 열 내부의 공백 제거 (예를 들어 '중 구'를 '중구'로 수정)
df_cctv['자치구'] = df_cctv['자치구'].str.replace(' ','')
# 결과 출력
df_cctv.head()

# 인구와 CCTV 병합
cctv_with_pop = pd.merge(    # 병합 함수 호출
    df_pop,df_cctv, # 병합 대상은 인구 및 CCTV
    how='outer',# 외부 조인 방식
    on='자치구'# 조인 기준 열 지정
)

# 자치구를 인덱스로 설정
cctv_with_pop.set_index('자치구' , inplace=True)

# 병합 결과 출력
cctv_with_pop.head()

# 인구 백명당 CCTV 비율 계산
cctv_with_pop['CCTV 비율 2015'] = cctv_with_pop['CCTV 규모 2015'] / cctv_with_pop['인구 규모 2015'] * 100
cctv_with_pop['CCTV 비율 2020'] = cctv_with_pop['CCTV 규모 2020'] / cctv_with_pop['인구 규모 2020'] * 100

# 계산 결과 출력
cctv_with_pop.head()

# 회귀선 준비
pf2015 = np.polyfit(  # numpy.polyfit(x, y, 차수)
    cctv_with_pop['인구 규모 2015'], # 추정에 사용할 x 좌표 값
    cctv_with_pop['CCTV 규모 2015'], # 추정에 사용할 y 좌표 값
    1        # 1차원(직선 형태) 다항식
)
pf2020 = np.polyfit(  # numpy.polyfit(x, y, 차수)
    cctv_with_pop['인구 규모 2020'], # 추정에 사용할 x 좌표 값
    cctv_with_pop['CCTV 규모 2020'], # 추정에 사용할 y 좌표 값
    1        # 1차원(직선 형태) 다항식
)                               

# y 축
fy2015 = np.poly1d(pf2015)   # 2015년 회귀선 y 값 리스트 계산
fy2020 = np.poly1d(pf2020)   # 2020년 회귀선 y 값 리스트 계산

# x 축
fx2015 = np.linspace(100000, 700000, 100000)   # 2015년 회귀선 x 값 리스트 계산
fx2020 = np.linspace(100000, 700000, 100000)   # 2015년 회귀선 x 값 리스트 계산

# 오차
cctv_with_pop['오차 2015'] = np.abs(       # 2015년 오차 계산
    cctv_with_pop['CCTV 규모 2015'] - fy2015(cctv_with_pop['인구 규모 2015'])
)
cctv_with_pop['오차 2020'] = np.abs(       # 2020년 오차 계산
    cctv_with_pop['CCTV 규모 2020'] - fy2020(cctv_with_pop['인구 규모 2020'])
)
# 결과 출력
cctv_with_pop.head()

# 자치구마다 이동 거리 계산
cctv_with_pop['이동 거리'] = np.sqrt(
    ((1.0 * (cctv_with_pop['인구 규모 2020'] - cctv_with_pop['인구 규모 2015']))**2)    #   1배로 스케일링
    + (100*(cctv_with_pop['CCTV 규모 2020'] - cctv_with_pop['CCTV 규모 2015']))**2  # 100배로 스케일링
)

# 최대 이동 거리 확인
max_distance = cctv_with_pop['이동 거리'].max()

# 결과 출력
cctv_with_pop.head()

# 산점도 준비 및 y축 범위 설정
plt.figure(figsize=(14, 10))     # 그림 크기 (14, 10)으로 설정
plt.ylim(0,7000)            # y 축 범위 (0, 7000)으로 설정

# 산점도 시각화 함수 정의
def myScatter(year, alpha, fx, fy):
    # 1) 산점도 그리기
    plt.scatter(                       # 산점도 출력 함수
        x=cctv_with_pop[f'인구 규모 20{year}'],                   # x
        y=cctv_with_pop[f'CCTV 규모 20{year}'],                   # y
        c=cctv_with_pop[f'오차 20{year}'],                 # 마커 색상 (오차 데이터를 마커 색상으로 지정)
        s=cctv_with_pop[f'CCTV 비율 20{year}'] *1000,            # 마커 크기  (해당 연도 CCTV 비율의 제곱근에 비례)
        alpha=alpha              # 투명도
    )
    # 2) 추세선 그리기
    p = np.poly1d(np.polyfit(fx, fy,1))
    plt.plot(fx, p(fx), ls='-', lw=3, color='g', alpha=alpha)  # 추세선 출력 함수(실선, 두께 3, 색상 초록, 투명도 알파)
    # 3) 마커 레이블 출력
    for n in range(len(cctv_with_pop)): # df 행 개수만큼 반복
        plt.text(                        # 자치구 레이블 출력 함수
            fx[n],                       # 텍스트 위치 x (자치구 원 중심의 x 좌표 값)
            fy[n],                       # 텍스트 위치 y (자치구 원 중심의 y 좌표 값)
            f"{cctv_with_pop.index[n]}{str(year)}" ,                       # 텍스트 내용 (예: '강남구20' 또는 '강남구15')
            fontsize=12             # 폰트 크기
        )

# # 산점도 시각화 함수 실행
myScatter('15', 0.2, cctv_with_pop[f'인구 규모 2015'], cctv_with_pop[f'CCTV 규모 2015'])  # 2015년 산점도 시각화 함수 실행, 투명도는 0.2로 지정
myScatter('20', 0.9, cctv_with_pop[f'인구 규모 2020'], cctv_with_pop[f'CCTV 규모 2020'])  # 2020년 산점도 시각화 함수 실행, 투명도는 0.9로 지정

# 자치구 이동 궤적 출력
for i in range(len(cctv_with_pop)):                # df 행 개수만큼 반복
    distance = cctv_with_pop['이동 거리'][i]     # n번 자치구 이동 거리
    x_values = [    # n번 자치구의 x 좌표 리스트 시작
        cctv_with_pop['인구 규모 2015'][i],           # 2015년 x 좌표
        cctv_with_pop['인구 규모 2020'][i]            # 2020년 x 좌표
    ]               # n번 자치구의 x 좌표 리스트 종료
    y_values = [    # n번 자치구의 y 좌표 리스트 시작
        cctv_with_pop['CCTV 규모 2015'][i],           # 2015년 y 좌표
        cctv_with_pop['CCTV 규모 2020'][i]            # 2020년 y 좌표
    ]               # n번 자치구의 y 좌표 리스트 종료
    plt.plot(               # 2015년 위치에서 2020년 위치로 이동 궤적 출력 함수
        x_values,           # n번 자치구의 x 좌표 리스트
        y_values,           # n번 자치구의 y 좌표 리스트
        lw=(distance/max_distance)*10,     # 이동 궤적 두께를 최대 이동거리 대비 해당 자치구 이동거리 비율의 10배로 지정
        color='b',alpha=0.3      # 이동 궤적 색상의 blue 값을 최대 이동거리 대비 해당 자치구 이동거리 비율로, 투명도를 0.3으로 지정
    )

# 마무리
plt.colorbar()  # 색상 조견 막대 출력 함수 (수직 방향)
plt.title('2015-2020 인구 및 CCTV 규모 산점도')  # 산점도 제목 지정
plt.xlabel('인구 규모 (단위: 명)')  # x축 제목 지정
plt.ylabel('CCTV 설치 규모 (단위: 대)')  # y축 제목 지정
plt.grid()  # 눈금 그리드 보이도록 지정
plt.show()  # 시각화 결과 출력

 

 

 

728x90

'기타 > 데이터 분석 및 시각화' 카테고리의 다른 글

Lux  (0) 2021.11.18
타이타닉 분석  (0) 2021.09.11
판다스 프로파일링  (0) 2021.09.09
워드 클라우드  (0) 2021.06.03
비트코인 데이터 분석  (0) 2021.03.28