데이터분석

[23.06.28] 데이터 시각화(CCTV) - 19(2)

gmwoo 2023. 6. 28. 22:26

 

 

In [1]:
import pandas as pd
In [2]:
# 서울 열린광장 데이터 cctv
CCTV_Seoul = pd.read_csv('../../data/01. CCTV_in_Seoul.csv', encoding='utf-8')
CCTV_Seoul.head()
Out[2]:
  기관명 소계 2013년도 이전 2014년 2015년 2016년
0 강남구 2780 1292 430 584 932
1 강동구 773 379 99 155 377
2 강북구 748 369 120 138 204
3 강서구 884 388 258 184 81
4 관악구 1496 846 260 390 613
In [3]:
CCTV_Seoul.columns[0]
Out[3]:
'기관명'
 

pandas dataframe의 칼럼 이름 변경

  • df.rename(columns = {'기존 칼럼' : '바꿀 칼럼',}, inplace =True)
  • df.columns = ['바꿀 칼럼1', '바꿀 칼럼2']
In [4]:
CCTV_Seoul.rename(columns={CCTV_Seoul.columns[0]: '구별'}, inplace=True)
CCTV_Seoul.head()
Out[4]:
  구별 소계 2013년도 이전 2014년 2015년 2016년
0 강남구 2780 1292 430 584 932
1 강동구 773 379 99 155 377
2 강북구 748 369 120 138 204
3 강서구 884 388 258 184 81
4 관악구 1496 846 260 390 613
 

엑셀 파일 읽기 - 서울시 인구 현황

In [5]:
pop_Seoul = pd.read_excel('../../data/01. population_in_Seoul.xls ', 
                         header = 2,
                         usecols = 'B, D, G, J, N') # parse_cols
pop_Seoul.head()
Out[5]:
  자치구 계.1 계.2 65세이상고령자
0 합계 10197604.0 9926968.0 270636.0 1321458.0
1 종로구 162820.0 153589.0 9231.0 25425.0
2 중구 133240.0 124312.0 8928.0 20764.0
3 용산구 244203.0 229456.0 14747.0 36231.0
4 성동구 311244.0 303380.0 7864.0 39997.0
In [6]:
pop_Seoul.rename(columns={pop_Seoul.columns[0] : '구별',
                          pop_Seoul.columns[1] : '인구수',
                          pop_Seoul.columns[2] : '한국인',
                          pop_Seoul.columns[3] : '외국인',
                          pop_Seoul.columns[4] : '고령자'}, inplace = True)
pop_Seoul.head()
Out[6]:
  구별 인구수 한국인 외국인 고령자
0 합계 10197604.0 9926968.0 270636.0 1321458.0
1 종로구 162820.0 153589.0 9231.0 25425.0
2 중구 133240.0 124312.0 8928.0 20764.0
3 용산구 244203.0 229456.0 14747.0 36231.0
4 성동구 311244.0 303380.0 7864.0 39997.0
 

데이터 파악하기

  • 데이터 정제
In [7]:
CCTV_Seoul.sort_values(by='소계', ascending=False).head(5)
Out[7]:
  구별 소계 2013년도 이전 2014년 2015년 2016년
0 강남구 2780 1292 430 584 932
18 양천구 2034 1843 142 30 467
14 서초구 1930 1406 157 336 398
21 은평구 1873 1138 224 278 468
20 용산구 1624 1368 218 112 398
In [8]:
# 최근 3년간 증가율 = (2016+2015+2014) / 2013년도 이전 * 100
CCTV_Seoul['최근증가율'] = (CCTV_Seoul['2016년'] + CCTV_Seoul['2015년'] + \
                      CCTV_Seoul['2014년']) / CCTV_Seoul['2013년도 이전'] * 100

CCTV_Seoul.sort_values(by='최근증가율', ascending=False).head(5)
Out[8]:
  구별 소계 2013년도 이전 2014년 2015년 2016년 최근증가율
22 종로구 1002 464 314 211 630 248.922414
9 도봉구 485 238 159 42 386 246.638655
12 마포구 574 314 118 169 379 212.101911
8 노원구 1265 542 57 451 516 188.929889
1 강동구 773 379 99 155 377 166.490765
 

4. 서울시 인구 데이터 파악하기

In [9]:
pop_Seoul.head()
Out[9]:
  구별 인구수 한국인 외국인 고령자
0 합계 10197604.0 9926968.0 270636.0 1321458.0
1 종로구 162820.0 153589.0 9231.0 25425.0
2 중구 133240.0 124312.0 8928.0 20764.0
3 용산구 244203.0 229456.0 14747.0 36231.0
4 성동구 311244.0 303380.0 7864.0 39997.0
In [10]:
pop_Seoul.drop([0], inplace = True) # 합계 행 삭제
pop_Seoul.head()
Out[10]:
  구별 인구수 한국인 외국인 고령자
1 종로구 162820.0 153589.0 9231.0 25425.0
2 중구 133240.0 124312.0 8928.0 20764.0
3 용산구 244203.0 229456.0 14747.0 36231.0
4 성동구 311244.0 303380.0 7864.0 39997.0
5 광진구 372164.0 357211.0 14953.0 42214.0
In [11]:
# 구 이름이 아닌 nan이 보임
pop_Seoul['구별'].unique()
Out[11]:
array(['종로구', '중구', '용산구', '성동구', '광진구', '동대문구', '중랑구', '성북구', '강북구',
       '도봉구', '노원구', '은평구', '서대문구', '마포구', '양천구', '강서구', '구로구', '금천구',
       '영등포구', '동작구', '관악구', '서초구', '강남구', '송파구', '강동구', nan],
      dtype=object)
In [12]:
# isnull 명령어로 누락값 데이터 추출
pop_Seoul[pop_Seoul['구별'].isnull()]
Out[12]:
  구별 인구수 한국인 외국인 고령자
26 NaN NaN NaN NaN NaN
In [13]:
pop_Seoul.tail()
Out[13]:
  구별 인구수 한국인 외국인 고령자
22 서초구 450310.0 445994.0 4316.0 51733.0
23 강남구 570500.0 565550.0 4950.0 63167.0
24 송파구 667483.0 660584.0 6899.0 72506.0
25 강동구 453233.0 449019.0 4214.0 54622.0
26 NaN NaN NaN NaN NaN
In [14]:
pop_Seoul.drop([26], inplace=True)
pop_Seoul.tail()
Out[14]:
  구별 인구수 한국인 외국인 고령자
21 관악구 525515.0 507203.0 18312.0 68082.0
22 서초구 450310.0 445994.0 4316.0 51733.0
23 강남구 570500.0 565550.0 4950.0 63167.0
24 송파구 667483.0 660584.0 6899.0 72506.0
25 강동구 453233.0 449019.0 4214.0 54622.0
In [15]:
pop_Seoul['외국인비율'] = pop_Seoul['외국인'] / pop_Seoul['인구수'] * 100
pop_Seoul['고령자비율'] = pop_Seoul['고령자'] / pop_Seoul['인구수'] * 100
pop_Seoul.head()
Out[15]:
  구별 인구수 한국인 외국인 고령자 외국인비율 고령자비율
1 종로구 162820.0 153589.0 9231.0 25425.0 5.669451 15.615404
2 중구 133240.0 124312.0 8928.0 20764.0 6.700690 15.583909
3 용산구 244203.0 229456.0 14747.0 36231.0 6.038828 14.836427
4 성동구 311244.0 303380.0 7864.0 39997.0 2.526635 12.850689
5 광진구 372164.0 357211.0 14953.0 42214.0 4.017852 11.342849
In [16]:
# 인구수가 많은 순으로 정렬
pop_Seoul.sort_values(by='인구수', ascending=False).head(5)
Out[16]:
  구별 인구수 한국인 외국인 고령자 외국인비율 고령자비율
24 송파구 667483.0 660584.0 6899.0 72506.0 1.033584 10.862599
16 강서구 603772.0 597248.0 6524.0 72548.0 1.080540 12.015794
23 강남구 570500.0 565550.0 4950.0 63167.0 0.867660 11.072217
11 노원구 569384.0 565565.0 3819.0 71941.0 0.670725 12.634883
21 관악구 525515.0 507203.0 18312.0 68082.0 3.484582 12.955291
In [17]:
pop_Seoul.sort_values(by='외국인', ascending=False).head(5)
Out[17]:
  구별 인구수 한국인 외국인 고령자 외국인비율 고령자비율
19 영등포구 402985.0 368072.0 34913.0 52413.0 8.663598 13.006191
17 구로구 447874.0 416487.0 31387.0 56833.0 7.007998 12.689506
18 금천구 255082.0 236353.0 18729.0 32970.0 7.342345 12.925255
21 관악구 525515.0 507203.0 18312.0 68082.0 3.484582 12.955291
6 동대문구 369496.0 354079.0 15417.0 54173.0 4.172440 14.661322
In [18]:
pop_Seoul.sort_values(by='외국인비율', ascending=False).head(5)
Out[18]:
  구별 인구수 한국인 외국인 고령자 외국인비율 고령자비율
19 영등포구 402985.0 368072.0 34913.0 52413.0 8.663598 13.006191
18 금천구 255082.0 236353.0 18729.0 32970.0 7.342345 12.925255
17 구로구 447874.0 416487.0 31387.0 56833.0 7.007998 12.689506
2 중구 133240.0 124312.0 8928.0 20764.0 6.700690 15.583909
3 용산구 244203.0 229456.0 14747.0 36231.0 6.038828 14.836427
In [19]:
pop_Seoul.sort_values(by='고령자', ascending=False).head(5)
Out[19]:
  구별 인구수 한국인 외국인 고령자 외국인비율 고령자비율
16 강서구 603772.0 597248.0 6524.0 72548.0 1.080540 12.015794
24 송파구 667483.0 660584.0 6899.0 72506.0 1.033584 10.862599
12 은평구 494388.0 489943.0 4445.0 72334.0 0.899091 14.631019
11 노원구 569384.0 565565.0 3819.0 71941.0 0.670725 12.634883
21 관악구 525515.0 507203.0 18312.0 68082.0 3.484582 12.955291
 

5. CCTV 데이터와 인구 데이터 합치고 분석

In [20]:
data_result = pd.merge(CCTV_Seoul, pop_Seoul, on = '구별')
data_result.head()
Out[20]:
  구별 소계 2013년도 이전 2014년 2015년 2016년 최근증가율 인구수 한국인 외국인 고령자 외국인비율 고령자비율
0 강남구 2780 1292 430 584 932 150.619195 570500.0 565550.0 4950.0 63167.0 0.867660 11.072217
1 강동구 773 379 99 155 377 166.490765 453233.0 449019.0 4214.0 54622.0 0.929765 12.051638
2 강북구 748 369 120 138 204 125.203252 330192.0 326686.0 3506.0 54813.0 1.061806 16.600342
3 강서구 884 388 258 184 81 134.793814 603772.0 597248.0 6524.0 72548.0 1.080540 12.015794
4 관악구 1496 846 260 390 613 149.290780 525515.0 507203.0 18312.0 68082.0 3.484582 12.955291
In [21]:
# drop - 행 방향 삭제, del - 열 삭제
del data_result['2013년도 이전']
del data_result['2014년']
del data_result['2015년']
del data_result['2016년']
data_result.head()
Out[21]:
  구별 소계 최근증가율 인구수 한국인 외국인 고령자 외국인비율 고령자비율
0 강남구 2780 150.619195 570500.0 565550.0 4950.0 63167.0 0.867660 11.072217
1 강동구 773 166.490765 453233.0 449019.0 4214.0 54622.0 0.929765 12.051638
2 강북구 748 125.203252 330192.0 326686.0 3506.0 54813.0 1.061806 16.600342
3 강서구 884 134.793814 603772.0 597248.0 6524.0 72548.0 1.080540 12.015794
4 관악구 1496 149.290780 525515.0 507203.0 18312.0 68082.0 3.484582 12.955291
In [22]:
data_result.set_index('구별', inplace=True)
data_result.head()
Out[22]:
  소계 최근증가율 인구수 한국인 외국인 고령자 외국인비율 고령자비율
구별                
강남구 2780 150.619195 570500.0 565550.0 4950.0 63167.0 0.867660 11.072217
강동구 773 166.490765 453233.0 449019.0 4214.0 54622.0 0.929765 12.051638
강북구 748 125.203252 330192.0 326686.0 3506.0 54813.0 1.061806 16.600342
강서구 884 134.793814 603772.0 597248.0 6524.0 72548.0 1.080540 12.015794
관악구 1496 149.290780 525515.0 507203.0 18312.0 68082.0 3.484582 12.955291
In [23]:
# CCTV 개수와 고령자 비율은 약한 음의 상관관계
import numpy as np

np.corrcoef(data_result['고령자비율'], data_result['소계'])
Out[23]:
array([[ 1.        , -0.28078554],
       [-0.28078554,  1.        ]])
In [24]:
# CCTV 개수와 외국인 비율과는 큰 의미가 없음
np.corrcoef(data_result['외국인비율'], data_result['소계'])
Out[24]:
array([[ 1.        , -0.13607433],
       [-0.13607433,  1.        ]])
In [25]:
data_result.sort_values(by='인구수', ascending=False).head()
Out[25]:
  소계 최근증가율 인구수 한국인 외국인 고령자 외국인비율 고령자비율
구별                
송파구 618 104.347826 667483.0 660584.0 6899.0 72506.0 1.033584 10.862599
강서구 884 134.793814 603772.0 597248.0 6524.0 72548.0 1.080540 12.015794
강남구 2780 150.619195 570500.0 565550.0 4950.0 63167.0 0.867660 11.072217
노원구 1265 188.929889 569384.0 565565.0 3819.0 71941.0 0.670725 12.634883
관악구 1496 149.290780 525515.0 507203.0 18312.0 68082.0 3.484582 12.955291
 

6. CCTV와 인구 현황 그래프로 분석

In [26]:
import matplotlib.pyplot as plt
import platform
from matplotlib import font_manager, rc

plt.rcParams['axes.unicode_minus'] = False   # - 기호 깨지는 것 방지
# f_path = "/Library/Fonts/AppleGothic.ttf"   -> MAC
f_path = "C:/Windows/Fonts/malgun.ttf"
font_name = font_manager.FontProperties(fname=f_path).get_name()
rc('font', family=font_name)
In [27]:
data_result.head()
Out[27]:
  소계 최근증가율 인구수 한국인 외국인 고령자 외국인비율 고령자비율
구별                
강남구 2780 150.619195 570500.0 565550.0 4950.0 63167.0 0.867660 11.072217
강동구 773 166.490765 453233.0 449019.0 4214.0 54622.0 0.929765 12.051638
강북구 748 125.203252 330192.0 326686.0 3506.0 54813.0 1.061806 16.600342
강서구 884 134.793814 603772.0 597248.0 6524.0 72548.0 1.080540 12.015794
관악구 1496 149.290780 525515.0 507203.0 18312.0 68082.0 3.484582 12.955291
In [28]:
plt.figure()
data_result['소계'].plot(kind='barh', grid=True, figsize=(10,10))
plt.show()
 
In [29]:
# CCTV 갯수가 많은 순으로 정렬하여 plot
data_result['소계'].sort_values().plot(kind='barh',
                                    grid=True, figsize=(10,10))
Out[29]:
<Axes: ylabel='구별'>
 
In [30]:
# 인구 대비 CCTV 비율이 높은 순으로 정렬 후 plot
data_result['CCTV_비율'] = data_result['소계'] / data_result['인구수'] * 100
data_result['CCTV_비율'].sort_values().plot(kind='barh',
                                          grid=True, figsize=(10,10))
Out[30]:
<Axes: ylabel='구별'>
 
In [31]:
import seaborn as sns
colors = list('rgby')
data_result['CCTV_비율'].sort_values().plot(kind='barh',
                                          grid=True, figsize=(10,10), 
                                          color = colors)
plt.show()
 
In [32]:
# sns.color_palette("atrribue") -> seaborn.pydata.org
colors = sns.color_palette("viridis", len(data_result.index))
data_result['CCTV_비율'].sort_values().plot(kind='barh',
                                          grid=True, figsize=(10,10), 
                                          color = colors)
plt.show()
 
In [33]:
plt.figure(figsize=(6,6))
plt.scatter(data_result['인구수'], data_result['소계'],
            s=70, color = colors)
plt.xlabel('인구수')
plt.ylabel('CCTV')
plt.grid()
plt.show()
 
 

polyfit 함수

  • y = ax + b (a: 기울기, b: 절편)
In [34]:
# 기울기, 절편, 1차원
fp1 = np.polyfit(data_result['인구수'], data_result['소계'], 1)
fp1
Out[34]:
array([1.30916415e-03, 6.45066497e+02])
In [35]:
f1 = np.poly1d(fp1)   # 매개변수로부터 모델 생성
fx = np.linspace(100000, 700000, 100)  # 시작, 끝(포함), 갯수
 

attribute

  • ls : linestyle
  • lw : 선 굵기
  • s : 점(scatter) size
In [36]:
plt.figure(figsize=(10, 10))
plt.scatter(data_result['인구수'], data_result['소계'],
            s=70, color = colors)
plt.plot(fx, f1(fx), ls = 'dotted', lw=3, color='r')
plt.xlabel('인구수')
plt.ylabel('CCTV')
plt.grid()
plt.show()
 
 

7. 설득력 있는 자료 만들기

In [37]:
fp1 = np.polyfit(data_result['인구수'], data_result['소계'], 1)
f1 = np.poly1d(fp1)
fx = np.linspace(100000, 700000, 100)

data_result['오차'] = np.abs(data_result['소계'] - f1(data_result['인구수']))

df_sort = data_result.sort_values(by='오차', ascending=False)
df_sort.head()
Out[37]:
  소계 최근증가율 인구수 한국인 외국인 고령자 외국인비율 고령자비율 CCTV_비율 오차
구별                    
강남구 2780 150.619195 570500.0 565550.0 4950.0 63167.0 0.867660 11.072217 0.487292 1388.055355
송파구 618 104.347826 667483.0 660584.0 6899.0 72506.0 1.033584 10.862599 0.092587 900.911312
양천구 2034 34.671731 479978.0 475949.0 4029.0 52975.0 0.839413 11.036964 0.423769 760.563512
서초구 1930 63.371266 450310.0 445994.0 4316.0 51733.0 0.958451 11.488308 0.428594 695.403794
용산구 1624 53.216374 244203.0 229456.0 14747.0 36231.0 6.038828 14.836427 0.665020 659.231690
In [38]:
plt.figure(figsize=(10, 10))
plt.scatter(data_result['인구수'], data_result['소계'],
            c=data_result['오차'], s=70)
plt.plot(fx, f1(fx), ls = 'dotted', lw=3, color='r')

for n in range(10):
    plt.text(df_sort['인구수'][n]*1.02, df_sort['소계'][n]*0.98,
            df_sort.index[n], fontsize=15)

plt.xlabel('인구수')
plt.ylabel('CCTV')
plt.colorbar()
plt.grid()
plt.show()
 
In [ ]:
 
반응형