티스토리 뷰

이제 첫 작업이다. 설레는 마음을 잠시 누르면서 먼저 목표에 대해 이야기 하겠다. 이번에는 서울시의 구별 CCTV 현황을 분석한다. 단순히 어디에 CCTV가 많이 설치 되었는지부터 시작해서 구별 인구 대비 비율을 확인하는 것까지 진행한다. 특히 인구 현황을 보면서 구별 인구 현황에 대해서도 확인해볼까 한다. 그리고 구별 CCTV 현황을 시각화하는 부분도 이야기하려고 한다.

 

 

1. CCTV 현황과 인구 현황 데이터 구하기

 

서울시의 CCTV 현황은 구글 검색만으로 쉽게 얻을 수 있다. '서울시 자치구 연도별 CCTV 설치 현황'을 검색하면 '서울 열린데이터 광장' 사이트의 해당 페이지로 갈 수 있다. 페이지 하단으로 가서 sheet 탭을 클릭하면 된다. 그러면 나타나는 화면에서 CSV를 클릭하면 다운 받을수 있다.

 

CSV 파일은 콤마로 구분된 텍스트 파일이다. 우리나라에서는 잘 통용되지 않지만 외국 자료를 검색하다 보면 자주 나타난다. 텍스트 파일이기 때문에 메모장에서도 열린다. 그런데 만약 CSV 파일에 한글이 UTF-8로 인코딩되어 있다면 엑셀에서 열었을 때 한글이 깨져 보일 수 있다. 그것은 MS 오피스 제품들이 UTF-8을 지원하지 않기 때문이며 파일에 문제가 있는 것은 아니다.

 

이제 '서울시 서울통계'라는 사이트를 찾아서, '서울인구'를 클릭해 다운 받는다. 이렇게 받은 두 파일을 data폴더에 옮긴다.

 

 

2. 파이썬에서 텍스트 파일과 엑셀 파일 읽기 - pandas

 

우리는 CSV 파일과 엑셀 파일을 하나씩 얻었다. 파이썬에서는 이런 종류의 파일을 아주 손쉽게 읽을 수 있는 모듈이 있다. 바로 pandas라는 모듈이다. Anaconda에서는 기본으로 설치되므로 그냥 사용하면 된다.

 

 In:

import pandas as pd

아마 앞으로도 위 코드처럼 대부분의 시작은 import로 하게 될 것이다. 파이썬에서 원하는 모듈을 불러오는 명령이 import이다. 그 뒤에 원하는 모듈명을 넣게 된다. 모듈명이 다음의 as는 as 뒤에 붙는 pd라는 짧은 단어로 pandas를 대체하겠다는 뜻이다. 즉, pandas의 read_csv라는 의미로 pandas.read_csv라고 하지 않고 pd.read_csv라고 쓰겠다는 의미이다. pandas에서 csv 파일을 읽는 명령은 read_csv이다. 그 안에 한글을 사용하는 경우는 인코딩에 신경 써야 합니다. 우리가 받은 CCTV 데이터는 UTF-8로 인코딩되어 있어서 read_csv 명령을 쓸 때 encoding 옵션에 UTF-8이라고 지정한다. 이때 사용된 head() 명령은 pandas 데이터의 첫 5행만 보여달라는 것이다.

 

 In:

CCTV_Seoul = pd.read_csv('C:/myPyCode/data/CCTV_in_Seoul.csv', encoding='utf-8')
CCTV_Seoul.head()

Out:

 

 

 

 

 

 

 

기관명 소계 2013년도 이전 2014년 2015년 2016년
0 강남구 3238 1292 430 584 932
1 강동구 1010 379 99 155 377
2 강북구 831 369 120 138 204
3 강서구 911 388 258 184 81
4 관악구 2109 846 260 390 613

pandas 데이터는 제일 첫 줄에 보이는 것이 해당하는 열을 대표하는 일종의 제목이다. 그래서 첫 줄을 특별히 column이라고 한다. 데이터 뒤에 columns라고 하면 column의 이름들이 반환된다.

 

 In:

 

CCTV_Seoul.rename(columns={CCTV_Seoul.columns[0] : '구별'}, inplace=True)
CCTV_Seoul.head()

Out: 

 

 

 

 

 

 

 

구별 소계 2013년도 이전 2014년 2015년 2016년
0 강남구 3238 1292 430 584 932
1 강동구 1010 379 99 155 377
2 강북구 831 369 120 138 204
3 강서구 911 388 258 184 81
4 관악구 2109 846 260 390 613

이제 두 번째 받은 엑셀 파일을 읽어야 한다. 그 명령은 read_excel이다. 그냥 인코딩만 지정하고 읽었더니 약간 이상하다. 그것은 원본 엑셀 파일이 그림 1-8에서 보는 것처럼 첫 세줄이 열의 제목처럼 되어 있기 때문이다.

 

 In:

 

pop_seoul = pd.read_excel('C:/myPyCode/data/population_in_seoul.xls', encoding = 'utf-8')
pop_seoul.head()

Out:

 

 

 

 

 

 

 

 

 

 

 

 

 

 

기간

자치구

세대

인구

인구.1

인구.2

인구.3

인구.4

인구.5

인구.6

인구.7

인구.8

세대당인구 65세이상고령자
0 기간 자치구 세대 합계 합계 합계 한국인 한국인 한국인 등록외국인 등록외국인 등록외국인 세대당인구 65세이상고령자
1 기간 자치구 세대 남자 여자 남자 여자 남자 여자 세대당인구 65세이상고령자
2 2018.3/4 합계 4254018 10068381 4922959 5145422 9793003 4789821 5003182 275378 133138 142240 2.3 1405404
3 2018.3/4 종로구 73696 163086 79301 83785 153396 75024 78372 9690 4277 5413 2.08 26622
4 2018.3/4 중구 61232 135258 66554 68704 125815 62063 63752 9443 4491 4952 2.05 21902

그래서 엑셀 파일을 읽을 때는 옵션을 좀 더 많이 적용하도록 하겠다. 일단 엑셀을 읽는 read_excel 명령 안에서 세 번째 줄부터 읽으라는 header=2라는 옵션을 걸고, B, D, G, J, N 열만 읽도록 parse_cols='B, D, G, J, N'이라는 옵션을 넣었다.

 

 In:

 

 

 

 

pop_seoul = pd.read_excel('C:/myPyCode/data/population_in_seoul.xls',
                          header = 2,
                          parse_cols = 'B, D, G, J, N',
                          encoding='utf-8')
pop_seoul.head()

Out: 

 

 

 

 

 

 

 

 

자치구

계.1

계.2

65세이상고령자
0 합계 10068381 9793003 275378 1405404
1 종로구 163086 153396 9690 26622
2 중구 135258 125815 9443 21902
3 용산구 245087 229391 15696 37443
4 성동구 317197 309251 7946 42581

출력된 모습도 간결하게 되었다. 그러나 컬럼의 이름에 문제가 있는 듯 하다. rename 명령을 사용해서 컬럼의 이름을 변경한다.

 

 In:

 

 

 

 

 

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: 

 

 

 

 

 

 

 

구별 인구수 한국인 외국인 고령자
0 합계 10068381 9793003 275378 1405404
1 종로구 163086 153396 9690 26622
2 중구 135258 125815 9443 21902
3 용산구 245087 229391 15696 37443
4 성동구 317197 309251 7946 42581

이제 어느정도 정리된 것 같다 CCTV_Seoul이라는 변수에는 '구별 CCTV 현황'을, pop_Seoul이라는 변수에는 '구별 인구 현황'을 저장했다. pandas에서 몇 줄 입력하지 않았는데 우리는 두 종류의 파일을 보기 좋게 읽게 되었다.

 

 

3. pandas를 이용해서 CCTV와 인구 현황 데이터 파악하기

 

 In:

CCTV_Seoul.head()

Out: 

 

 

 

 

 

 

 

구별 소계 2013년도 이전 2014년 2015년 2016년
0 강남구 3238 1292 430 584 932
1 강동구 1010 379 99 155 377
2 강북구 831 369 120 138 204
3 강서구 911 388 258 184 81
4 관악구 2109 846 260 390 613

구별 CCTV 데이터에서 CCTV 전체 개수인 소계로 정렬했다.

 

 In:

CCTV_Seoul.sort_values(by='소계', ascending=True).head(5)

Out: 

 

 

 

 

 

 

 

구별 소계 2013년도 이전 2014년 2015년 2016년
9 도봉구 825 238 159 42 386
2 강북구 831 369 120 138 204
5 광진구 878 573 78 53 174
3 강서구 911 388 258 184 81
24 중랑구 916 509 121 177 109

CCTV의 전체 개수가 가장 작은 구는 '도봉구', '강북구', '광진구', '강서구', '중랑구'라는 것을 알 수 있다.

 

 In:

CCTV_Seoul.sort_values(by='소계', ascending=False).head(5)

Out:

 

 

 

 

 

 

 

구별 소계 2013년도 이전 2014년 2015년 2016년
0 강남구 3238 1292 430 584 932
18 양천구 2482 1843 142 30 467
14 서초구 2297 1406 157 336 398
4 관악구 2109 846 260 390 613
21 은평구 2108 1138 224 278 468

그리고 CCTV가 가장 많은 구는 '강남구', '양천구', '서초구', '관악구', '은평구'로 나타난다. 특히 2014년부터 2016년까지 최근 3년간 CCTV 수를 더하고 2013년 이전 CCTV 수로 나눠서 최근 3년간 CCTV 증가율을 계산하겠다.

 

 In:

 

 

CCTV_Seoul['최근증가율'] = (CCTV_Seoul['2016년'] + CCTV_Seoul['2015년'] +
                       CCTV_Seoul['2016년']) / CCTV_Seoul['2013년도 이전'] * 100
CCTV_Seoul.sort_values(by='최근증가율', ascending=False).head(5)

Out: 

 

 

 

 

 

 

 

구별 소계 2013년도 이전 2014년 2015년 2016년 최근증가율
9 도봉구 825 238 159 42 386 342.016807
22 종로구 1619 464 314 211 630 317.025862
12 마포구 980 314 118 169 379 295.222930
8 노원구 1566 542 57 451 516 273.616236
1 강동구 1010 379 99 155 377 239.841689

그 결과를 보면 최근 3년간 CCTV가 그 이전 대비 많이 증가한 구는 '도봉구', '종로구', '마포구', '노원구', '강동구'라는 것도 알 수 있다. 이제 서울시 인구 현황을 정리해 보겠다.

 

 In:

pop_Seoul.head()

Out: 

 

 

 

 

 

 

 

구별 인구수 한국인 외국인 고령자
0 합계 10068381 9793003 275378 1405404
1 종로구 163086 153396 9690 26622
2 중구 135258 125815 9443 21902
3 용산구 245087 229391 15696 37443
4 성동구

먼저 pop_Seoul 변수를 확인했더니 0번 행에 합계가 보인다. 아마 서울시 전체 합계를 넣어둔 것 같은데 우리에게는 필요가 없다. 이럴 때는 행을 지우는 drop 명령을 사용해서 지우도록 한다.

 

 In:

 

pop_Seoul.drop([0], inplace=True)

pop_Seoul.head()

Out: 

구별 인구수 한국인 외국인 고령자
1 종로구 163086 153396 9690 26622
2 중구 135258 125815 9443 21902
3 용산구 245087 229391 15696 37443
4 성동구 317197 309251 7946 42581
5 광진구 369999 355032 14967 45202

 

그리고 pop_Seoul 데이터의 '구별'컬럼의 unique를 조사한다. 유니크 조사는 반복된 데이터는 하나로 나타내서 한 번 이상 나타난 데이터를 확인하는 것이다.

 

 

 In:

pop_Seoul['구별'].unique()

Out: 

 

 

array(['종로구', '중구', '용산구', '성동구', '광진구', '동대문구', '중랑구', '성북구', '강북구',
       '도봉구', '노원구', '은평구', '서대문구', '마포구', '양천구', '강서구', '구로구', '금천구',
       '영등포구', '동작구', '관악구', '서초구', '강남구', '송파구', '강동구'], dtype=object)

 

이제 각 구별 전체 인구를 이용해서 구별 '외국인비율'과 '고령자비율'을 계산하겠다.

 

 In:

 

 

pop_Seoul['외국인비율']=pop_Seoul['외국인']/pop_Seoul['인구수']*100
pop_Seoul['고령자비율']=pop_Seoul['고령자']/pop_Seoul['인구수']*100
pop_Seoul.head()

Out: 

 

 

 

 

 

 

구별 인구수 한국인 외국인 고령자 외국인비율 고령자비율
1 종로구 163086 153396 9690 26622 5.941650 16.323903
2 중구 135258 125815 9443 21902 6.981472 16.192758
3 용산구 245087 229391 15696 37443 6.404256 15.277432
4 성동구 317197 309251 7946 42581 2.505068 13.424150
5 광진구 369999 355032 14967 45202 4.045146 12.216790

인구수로 정렬했더니 '종로구', '중구', '용산구', '성동구', '광진구' 순으로 인구가 많았다.

 

 In:

pop_Seoul.sort_values(by='인구수', ascending=False).head(5)

Out: 

 

 

 

 

 

 

 

구별 인구수 한국인 외국인 고령자 외국인비율 고령자비율
24 송파구 673582 666892 6690 80220 0.993198 11.909463
16 강서구 605068 598416 6652 78814 1.099381 13.025643
11 노원구 551069 546911 4158 76466 0.754533 13.875939
23 강남구 549255 544257 4998 66412 0.909960 12.091287
21 관악구 521685 503956 17729 71840 3.398411 13.770762

 In:

pop_Seoul.sort_values(by='외국인', ascending=False).head(5)

Out: 

 

 

 

 

 

 

 

구별 인구수 한국인 외국인 고령자 외국인비율 고령자비율
19 영등포구 403724 368577 35147 55364 8.705700 13.713329
17 구로구 439234 406144 33090 61293 7.533570 13.954521
18 금천구 252359 232760 19599 35299 7.766317 13.987613
21 관악구 521685 503956 17729 71840 3.398411 13.770762
3 용산구 245087 229391 15696 37443 6.404256 15.277432

 In:

pop_Seoul.sort_values(by='외국인비율', ascending=False).head(5)

Out: 

 

 

 

 

 

 

 

구별 인구수 한국인 외국인 고령자 외국인비율 고령자비율
19 영등포구 403724 368577 35147 55364 8.705700 13.713329
18 금천구 252359 232760 19599 35299 7.766317 13.987613
17 구로구 439234 406144 33090 61293 7.533570 13.954521
2 중구 135258 125815 9443 21902 6.981472 16.192758
3 용산구 245087 229391 15696 37443 6.404256 15.277432

외국인 숫자가 많은 구는 '영등포', '구로구', '금천구', '관악구', '용산구'입니다만, 외국인 비율이 높은 구는 '영등포', '금천구', '구로구', '중구', '용산구'로 조금 바뀌는 것을 알 수 있다.

 

 In:

pop_Seoul.sort_values(by='고령자', ascending=False).head(5)

Out: 

 

 

 

 

 

 

 

구별 인구수 한국인 외국인 고령자 외국인비율 고령자비율
24 송파구 673582 666892 6690 80220 0.993198 11.909463
16 강서구 605068 598416 6652 78814 1.099381 13.025643
12 은평구 487849 483417 4432 76643 0.908478 15.710394
11 노원구 551069 546911 4158 76466 0.754533 13.875939
21 관악구 521685 503956 17729 71840 3.398411 13.770762

 In:

pop_Seoul.sort_values(by='고령자비율', ascending=False).head(5)

Out:

 

 

 

 

 

 

 

구별 인구수 한국인 외국인 고령자 외국인비율 고령자비율
9 강북구 324276 320576 3700 57741 1.141003 17.806128
1 종로구 163086 153396 9690 26622 5.941650 16.323903
2 중구 135258 125815 9443 21902 6.981472 16.192758
10 도봉구 342990 340876 2114 55499 0.616344 16.180938
12 은평구 487849 483417 4432 76643 0.908478 15.710394

고령자가 많은 구는 '송파구', '강서구', '은평구', '노원구', '관악구'입니다만, 고령자 비율이 높은 구는 '강북구', '종로구', '중구', 도봉구', '은평구'로 차이가 좀 났다. 아무튼 이렇게 비록 한정된 내용이지만 뭔가 데이터들을 확인했다. 지금까지 확인한 내용도 중요하지만 여전히 우리는 CCTV의 현황을 완전히 파악한 것 같지는 않았다. 인구 대비 CCTV 현황 같은 내용을 확인하고 싶은데, 그러기 위해서는 두 데이터를 병합해야 한다.

 

4. CCTV 데이터와 인구 현황 데이터를 합치고 분석하기

 

merge 명령을 이용하여 합치도록 하겠다. 두 데이터의 공통된 컬럼인 '구별'로 merge하면 된다.

 

 In:

 

data_result = pd.merge(CCTV_Seoul, pop_Seoul, on='구별')
data_result.head()

Out: 

 

 

 

 

 

 

 

 

 

 

구별 소계 2013년도 이전 2014년 2015년 2016년 최근증가율 인구수 한국인 외국인 고령자 외국인비율 고령자비율
0 강남구 3238 1292 430 584 932 189.473684 549255 544257 4998 66412 0.909960 12.091287
1 강동구 1010 379 99 155 377 239.841689 434992 430705 4287 58280 0.985535 13.397948
2 강북구 831 369 120 138 204 147.967480 324276 320576 3700 57741 1.141003 17.806128
3 강서구 911 388 258 184 81 89.175258 605068 598416 6652 78814 1.099381 13.025643
4 관악구 2109 846 260 390 613 191.016548 521685 503956 17729 71840 3.398411 13.770762

그리고 이제부터 의미 없는 컬럼들을 지우도록 하겠다. 행 방향으로 삭제하는 것은 drop이고, 열을 삭제하는 명령은 del이다.

 

 In:

 

 

 

del data_result['2013년도 이전']
del data_result['2014년']
del data_result['2015년']
del data_result['2016년']

data_result.head()

Out: 

 

 

 

 

 

 

 

구별 소계 최근증가율 인구수 한국인 외국인 고령자 외국인비율 고령자비율
0 강남구 3238 189.473684 549255 544257 4998 66412 0.909960 12.091287
1 강동구 1010 239.841689 434992 430705 4287 58280 0.985535 13.397948
2 강북구 831 147.967480 324276 320576 3700 57741 1.141003 17.806128
3 강서구 911 89.175258 605068 598416 6652 78814 1.099381 13.025643
4 관악구 2109 191.016548 521685 503956 17729 71840 3.398411 13.770762

나중에 그래프 그릴 것을 생각하면 index는 구 이름이 되면 여러모로 유리하다. 그렇게 설정하는 명령이 set_index이다.

 

 In:

 

data_result.set_index('구별', inplace=True)
data_result.head()

Out: 

 

 

 

 

 

 

 

 

 

소계 최근증가율 인구수 한국인 외국인 고령자 외국인비율 고령자비율
구별
강남구 3238 189.473684 549255 544257 4998 66412 0.909960 12.091287
강동구 1010 239.841689 434992 430705 4287 58280 0.985535 13.397948
강북구 831 147.967480 324276 320576 3700 57741 1.141003 17.806128
강서구 911 89.175258 605068 598416 6652 78814 1.099381 13.025643
관악구 2109 191.016548 521685 503956 17729 71840 3.398411 13.770762

상관계수의 절대값이 0.1 이하면 거의 무시, 0.3 이하면 약한 상관관계, 0.7 이하면 뚜렷한 상관관계라고 한다. 그럼 다수의 데이터 중 상관계수가 가장 큰 값인 데이터를 비교하자. 먼저 상관계수를 어떻게 계산하는지 알아보자. 그 명령은 numpy에 있는 corrcoef 명령이다. 단, 이 명령의 결과는 행렬로 나타난다. 주 대각선을 기준으로 대칭인 행렬이고 대각선 빼고 다른 값을 읽으면 된다.

 

 In:

import numpy as np

 In:

np.corrcoef(data_result['고령자비율'],data_result['소계'])

Out:

 

array([[ 1.        , -0.27533083],
       [-0.27533083,  1.        ]])

 In:

np.corrcoef(data_result['외국인비율'],data_result['소계'])

Out: 

 

array([[ 1.        , -0.04796912],
       [-0.04796912,  1.        ]])

 In:

np.corrcoef(data_result['인구수'],data_result['소계'])

Out: 

 

array([[1.       , 0.2242953],
       [0.2242953, 1.       ]])

조사해보니 CCTV와 고령자비율은 약한 음의 상관관계고, 외국인비율과는 큰 의미가 없다고 할 수 있다. 그런데 인구수와는 상관계수가 0.3이어서 약한 상관관계가 있다고 볼 수 있다. 그러면 CCTV와 인구수의 관계를 조금 더 들여다보겠다.

 

 In:

data_result.sort_values(by='소계', ascending=False).head(5)

Out:

 

 

 

 

 

 

 

 

소계 최근증가율 인구수 한국인 외국인 고령자 외국인비율 고령자비율
구별
강남구 3238 189.473684 549255 544257 4998 66412 0.909960 12.091287
양천구 2482 52.306023 469945 466121 3824 57442 0.813712 12.223132
서초구 2297 80.512091 441539 437407 4132 54887 0.935818 12.430838
관악구 2109 191.016548 521685 503956 17729 71840 3.398411 13.770762
은평구 2108 106.678383 487849 483417 4432 76643 0.908478 15.710394

위에서 봤듯이 CCTV가 많이 설치된 구와

 

 In:

data_result.sort_values(by='인구수', ascending=False).head(5)

Out:

 

 

 

 

 

 

 

 

소계 최근증가율 인구수 한국인 외국인 고령자 외국인비율 고령자비율
구별
송파구 1081 187.901701 673582 666892 6690 80220 0.993198 11.909463
강서구 911 89.175258 605068 598416 6652 78814 1.099381 13.025643
노원구 1566 273.616236 551069 546911 4158 76466 0.754533 13.875939
강남구 3238 189.473684 549255 544257 4998 66412 0.909960 12.091287
관악구 2109 191.016548 521685 503956 17729 71840 3.398411 13.770762

인구수가 많은 구를 시각적으로 비교하면 좋을 것 같다. 다음 절에서는 파이썬의 시각화 도구인 Matplotlib을 이용해 보겠다.

 

5. CCTV 현황 그래프로 분석하기

 

matplotlib이 기본적으로 가진 폰트는 한글을 지원하지 않기 때문에 matplotlib의 폰트를 변경할 필요가 있다.

 

 

 In:

 

 

 

 

 

 

import matplotlib.font_manager as fm

 

 

font_location = 'C:/windows/Fonts/H2GTRM.TTF' 

                    # ex - 'C:/asiahead4.ttf'

font_name = fm.FontProperties(fname = font_location).get_name()

matplotlib.rc('font', family = font_name)

다시 한번 결과 변수인 data_result를 확인해보자.

 

 In:

data_result.head()

Out: 

 

 

 

 

 

 

 

 

소계 최근증가율 인구수 한국인 외국인 고령자 외국인비율 고령자비율
구별
강남구 3238 189.473684 549255 544257 4998 66412 0.909960 12.091287
강동구 1010 239.841689 434992 430705 4287 58280 0.985535 13.397948
강북구 831 147.967480 324276 320576 3700 57741 1.141003 17.806128
강서구 911 89.175258 605068 598416 6652 78814 1.099381 13.025643
관악구 2109 191.016548 521685 503956 17729 71840 3.398411 13.770762

pandas 데이터 뒤에 plot 명령어를 붙여서 그래프를 그려주자

 

 In:

data_result['소계'].plot(kind='barh', grid=True, figsize=(10,10))

Out: 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

여기서는 kind='barh'로 지정해서 수평바(bar)로 그리도록 했다. grid=True로 grid를 그리라고 했고 figsize로 그림 크기도 지정했다. 그러나 figsize를 지정했어도 표현되는 곳이 웹 브라우저이기 때문에 항상 그 크기를 유지하는 것은 아닐 수 있다. 유저가 창의 크기를 줄이거나 하면 변하게 될 것이다.

 

위 그림은 큰 의미를 찾기 어렵다. 사실 수평바 그래프는 데이터가 정렬되어 잇을 때 좀 더 보기 좋다.

 

 In:

 

data_result['소계'].sort_values().plot(kind='barh', grid=True, figsize=(10,10))

plt.show()

Out: 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

이제 보니 CCTV 개수에서는 강남구가 월등하다는 것을 알 수 있다. 그 뒤를 이어서 양천구, 서초구, 관악구가 꽤 많은 CCTV가 설치됐다는 것을 알 수 있다. 그리고 하위 그룹이 얼마나 적은 수의 CCTV를 가지고 있는지도 확인할 수 있다. 여기에 인구 대비 CCTV 비율을 계산해서 정렬하고 그려보겠습니다.

 

 In:

 

 

data_result['CCTV비율'] = data_result['소계'] / data_result['인구수'] * 100
data_result['CCTV비율'].sort_values().plot(kind='barh', grid=True, figsize=(10,10))

plt.show()

Out:

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

인구 대비 CCTV 수를 보니 이번에는 종로구와 용산구가 월등이 높다. 그런데 강서구는 인구 대비로 봐도 CCTV 비율이 낮습니다.

 

이번에는 scatter 함수를 사용해보자. 그리고 s=50로 마커의 크기를 잡고 그려보자.

 

 In:

 

 

 

 

 

plt.figure(figsize=(6,6))
plt.scatter(data_result['인구수'], data_result['소계'], s=50)
plt.xlabel('인구수')
plt.ylabel('CCTV')
plt.grid()
plt.show()

Out: 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

이제 저 데이터를 대표하는 직선을 하나 그려보자.

 

 In:

 

fp1 = np.polyfit(data_result['인구수'], data_result['소계'], 1)
fp1

Out: 

array([1.08180882e-03, 1.07963746e+03])

 In:

 

f1 = np.poly1d(fp1)
fx = np.linspace(100000, 700000, 100)

여기서 numpy의 polyfit 명령으로 손쉽게 직선을 만들 수 있다. 그리고 이를 그리기 위해 x축과 y축 데이터를 얻어야 하는데, x축 데이터는 numpy의 linspace로 만들고, y축은 poly1d로 만들 수 있다.


 In:

 

 

 

 

 

 

plt.figure(figsize=(10,10))
plt.scatter(data_result['인구수'], data_result['소계'], s=50)
plt.plot(fx, f1(fx), ls='dashed', lw=3, color='g')
plt.xlabel('인구수')
plt.ylabel('CCTV')
plt.grid()
plt.show()

Out: 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

여기서 두 가지 장치를 넣어보자. 하나는 직선이 이 전체 데이터의 대표 값 역할을 한다면, 즉 인구수가 300000일 때는 CCTV는 1100 정도여야 한다는 개념을 이야기하는 거라면 그 경향에서 멀리 있는 구는 이름이 같이 나타나도록 하고 싶다는 것과 직선에서 멀어질수록 다른 색을 나타내도록 하고 싶다는 것이다.

그래서 오차를 계산할 수 있는 코드를 만들고 오차가 큰 순으로 데이터를 정렬해서 다시 저장했다.

 

 In:

 

 

 

 

 

 

 

 

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:

 

 

 

 

 

 

 

 

 

소계 최근증가율 인구수 한국인 외국인 고령자 외국인비율 고령자비율 CCTV비율 오차
구별
강남구 3238 189.473684 549255 544257 4998 66412 0.909960 12.091287 0.589526 1564.173631
양천구 2482 52.306023 469945 466121 3824 57442 0.813712 12.223132 0.528147 893.971889
강서구 911 89.175258 605068 598416 6652 78814 1.099381 13.025643 0.150562 823.205365
용산구 2096 66.374269 245087 229391 15696 37443 6.404256 15.277432 0.855207 751.225257
서초구 2297 80.512091 441539 437407 4132 54887 0.935818 12.430838 0.520226 739.701750

이제 텍스트와 color map을 입히겠다.

 

 In:

 

 

 

 

 

 

 

 

 

 

plt.figure(figsize=(14,10))
plt.scatter(data_result['인구수'], data_result['소계'],
            c=data_result['오차'], s=50)
plt.plot(fx, f1(fx), ls='dashed', lw=3, color='g')

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('인구당비율')

plt.colorbar()
plt.grid
plt.show

Out: 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

일단 직선을 기준으로 위에 있는 '강남구', '양천구', 서초구', '용산구'는 서울시 전체 지역의 일반적인 경향보다 CCTV가 많이 설치된 지역이다. 그리고 '송파구', '강서구', '중랑구', '광진구', '도봉구', '강북구'는 일반적인 경향보다 CCTV가 적게 설치된 지역이다. 특히 '강남구'는 월등히 많은 CCTV가 설치됐지만, '송파구'는 인구수에 비해 너무나도 적은 수의 CCTV를 가지고 있다.

 

 

 

 

출처: 파이썬으로 데이터 주무르기

공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
링크
«   2024/12   »
1 2 3 4 5 6 7
8 9 10 11 12 13 14
15 16 17 18 19 20 21
22 23 24 25 26 27 28
29 30 31
글 보관함