-
파이썬으로 데이터 주무르기 - 챕터 1 실습코딩 및 데이터분석/데이터 분석(pandas, numpy 등) 2023. 7. 23. 21:35
책 정보: https://www.yes24.com/Product/Goods/57670268
나는 실습 위주로 부딪히면서 배우는 걸 좋아하는 편이라 기초부터 가르치는 책이 별로 재미가 없었다.
그런데 이 책은 바로 실전으로 들어가서 너무 좋다.
2017년 기준으로 작성된 것 같은데, 지금 기준 데이터랑 다른 점이 있어서 그걸 해결하면서 하다보니 한 줄 한 줄 이해도 더 잘 되는 느낌이다.
나 같은 성향을 지닌 분들에게는 강추하는 책.
이걸 보면서 절실히 느낀 건.... 고등학생 때 수학 공부 좀 열심히 할 걸... 후회된다는 거
※ 주의 : 아래 내용은 저 나름대로 코드를 수정하면서 진행했기 때문에 교재 내용이랑 살짝 차이가 있습니다.
import pandas as pd import numpy as np import matplotlib.pyplot as plt import matplotlib.font_manager as fm import platform
서울시 각 지역구 CCTV 현황
In [153]:#csv 파일 읽기 -- 인코딩은 utf-8가 안 돼서 cp949로 인코딩 CCTV_Seoul = pd.read_csv("C:\\Users\\phant\\Downloads\\CCTV_in_Seoul.csv", encoding='cp949') #읽는 범위에 결측치밖에 없는 컬럼이 모두 포함되는 바람에 16384개 컬럼이 읽힘. 이에 결측치만 있는 컬럼을 모두 제거 CCTV_Seoul=CCTV_Seoul.dropna(axis=1) #1열 컬럼명을 '지역구'로 변경 CCTV_Seoul.rename(columns={CCTV_Seoul.columns[0] : '지역구'}, inplace=True) #3열 컬럼명을 '2013년 이전 CCTV'로 변경 CCTV_Seoul.rename(columns={CCTV_Seoul.columns[2] : '2013년 미만'}, inplace=True) #오름차순, 내림차순 정렬 ## '총계' 컬럼 기준 오름차순 정렬 --최소 3개구 종로구, 도봉구, 중구 ;; 최다 3개구 강남구, 관악구, 서초구 CCTV_Seoul.sort_values(by='총계', ascending=True) #기타 특수 문자 제거 후 정수형으로 바꾸기. fillna(0)은 결측치를 NaN이 아닌 0으로 처리하라는 것 ## 셀값을 object로 인식하고 있어서 집계 함수를 쓸 수가 없기 때문에 데이터타입을 바꿔주는 것 CCTV_Seoul.loc[CCTV_Seoul.any(axis=1)] = CCTV_Seoul.replace('[\$,]', '', regex=True).astype(int, errors='ignore').fillna(0) #2013년 이후 증가한 CCTV 합계 컬럼 생성 ## [: , 3: ]에서 왼쪽 콜론만 있는 것은 전체 행을 의미. '3: '은 4번째 열부터 마지막 열까지를 의미 CCTV_Seoul['2013년 이후 증가량'] = CCTV_Seoul.iloc[:, 3:].sum(axis=1) #2013년 이전 CCTV 갯수 대비 2013년 이후 증가한 CCTV 갯수의 비율 구하기 ## 2013년 이전 CCTV가 0인 행이 포함되어 있어 'ZeroDivisionError' 에러가 나기에, '2013년 미만' 컬럼에서 값이 0인 곳을 제외하고 나눗셈 ## 값이 0인 곳은 결과를 NaN으로 처리 CCTV_Seoul['2013년 이후 CCTV증가율'] = np.where(CCTV_Seoul['2013년 미만'] != 0, (CCTV_Seoul['2013년 미만'] / CCTV_Seoul['2013년 이후 증가량']) * 100, np.nan) #첫번째 '계' 행 제거 CCTV_Seoul.drop([0], inplace=True) CCTV_Seoul
Out[153]:지역구총계2013년 미만2013년2014년2015년2016년2017년2018년2019년2020년2021년2022년2013년 이후 증가량2013년 이후 CCTV증가율12345678910111213141516171819202122232425종로구 1980 36 540 107 161 131 158 152 69 250 85 291 1944.0 1.851852 중 구 2584 130 87 77 236 240 372 386 155 361 403 137 2454.0 5.297474 용산구 2847 44 50 68 83 295 491 115 322 623 422 334 2803.0 1.569747 성동구 4047 58 99 110 366 279 945 459 647 485 367 232 3989.0 1.453998 광진구 3480 507 82 84 64 21 465 443 709 172 662 271 2973.0 17.053481 동대문구 2759 1 4 12 107 802 711 201 218 223 221 259 2758.0 0.036258 중랑구 4193 224 331 104 145 153 170 215 1074 976 507 294 3969.0 5.643739 성북구 4842 137 170 229 322 594 811 867 714 253 407 338 4705.0 2.911796 강북구 3321 0 21 16 68 210 4 375 963 569 298 797 3321.0 NaN 도봉구 2247 103 2 79 72 103 117 200 202 183 600 586 2144.0 4.804104 노원구 2617 77 153 75 510 329 172 216 326 387 220 152 2540.0 3.031496 은평구 4653 17 44 332 329 555 403 635 1057 288 471 522 4636.0 0.366695 서대문구 3445 312 144 127 77 254 524 389 344 587 207 480 3133.0 9.958506 마포구 2628 21 40 109 170 458 376 368 494 298 177 117 2607.0 0.805524 양천구 3851 197 139 128 239 422 563 830 311 337 338 347 3654.0 5.391352 강서구 3265 67 59 202 193 168 506 259 458 359 439 555 3198.0 2.095059 구로구 4693 37 361 193 269 353 563 545 794 651 684 243 4656.0 0.794674 금천구 2636 0 0 43 361 133 196 539 367 513 207 277 2636.0 NaN 영등포구 4553 109 81 295 360 285 422 803 153 1357 329 359 4444.0 2.452745 동작구 2671 66 25 502 127 256 271 300 307 432 30 355 2605.0 2.533589 관악구 5398 362 244 302 537 607 676 662 890 404 550 164 5036.0 7.188245 서초구 4995 74 86 70 559 518 1054 426 344 422 566 876 4921.0 1.503759 강남구 7243 59 62 573 826 1295 989 744 790 923 514 468 7184.0 0.821269 송파구 3203 84 78 88 221 163 256 516 1081 224 139 353 3119.0 2.693171 강동구 3190 82 195 56 174 226 351 379 362 635 301 429 3108.0 2.638353 서울시 인구
In [152]:# 서울시 인구 csv 파일 읽기 -- 이 파일은 원래 utf-8로 인코딩되었었나보다. 이 파일은 오히려 cp949가 안 먹힌다 ## header는 2번째 행(즉 1)부터 읽으라는 뜻 ### (인덱스 포함) 1, 3, 4, 5, 7번째 컬럼만 읽기 pop_Seoul=pd.read_csv("C:\\Users\\phant\\Downloads\\Population_in_Seoul.csv", header = 1, usecols = [1,3,4,5,7], encoding='utf-8') pop_Seoul=pop_Seoul.dropna(axis=1) #컬럼명을 보다 직관적으로 변경 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.drop([0], inplace=True) #유니크로 원하는 컬럼의 행에 무슨 값 들어 있는지 알아보기 # pop_Seoul['지역구'].unique() #외국인 비율과 고령자 비율 컬럼 추가. 외국인 인구수/지역구 인구수 , 고령자 인구수/지역구 인구수를 백분율로 pop_Seoul['외국인 비율'] = pop_Seoul['외국인'] / pop_Seoul['인구수'] * 100 pop_Seoul['고령자 비율'] = pop_Seoul['고령자'] / pop_Seoul['인구수'] * 100 pop_Seoul
Out[152]:지역구인구수한국인외국인고령자외국인 비율고령자 비율12345678910111213141516171819202122232425종로구 152212 141060 11152 28265 7.326623 18.569495 중구 131390 120963 10427 25353 7.935916 19.295989 용산구 232482 217756 14726 39478 6.334254 16.981100 성동구 287240 280240 7000 48238 2.436986 16.793622 광진구 350925 336801 14124 54854 4.024792 15.631260 동대문구 354884 337574 17310 65154 4.877650 18.359239 중랑구 389928 385003 4925 76116 1.263054 19.520527 성북구 441855 430100 11755 78427 2.660375 17.749488 강북구 296934 292762 4172 67056 1.405026 22.582796 도봉구 312858 310509 2349 68114 0.750820 21.771539 노원구 506989 502515 4474 93279 0.882465 18.398624 은평구 468766 464871 3895 90556 0.830905 19.317954 서대문구 321966 308437 13529 56785 4.201996 17.636955 마포구 376542 365570 10972 56582 2.913885 15.026743 양천구 442345 439219 3126 73522 0.706688 16.620963 강서구 573711 568287 5424 98659 0.945424 17.196637 구로구 417983 395183 22800 77259 5.454767 18.483766 금천구 242467 229307 13160 43772 5.427543 18.052766 영등포구 398999 376614 22385 66014 5.610290 16.544904 동작구 390377 380201 10176 68766 2.606711 17.615280 관악구 502628 487815 14813 83420 2.947110 16.596767 서초구 408979 404831 4148 63328 1.014233 15.484414 강남구 537817 532798 5019 83097 0.933217 15.450795 송파구 663704 658006 5698 105161 0.858515 15.844563 강동구 464027 459982 4045 79706 0.871717 17.177018 데이터 병합
In [188]:# 왼쪽 CCTV_Seoul과 오른쪽 pop_Seoul 데이터를 '지역구' 컬럼을 중심으로 merge(SQL의 join) data_result=pd.merge(CCTV_Seoul, pop_Seoul, on='지역구') # 2013년 이후 CCTV 증가율과 총계만 남기고 나머지 CCTV 컬럼 삭제 data_result.drop(['2013년 미만', '2013년', '2014년', '2015년', '2016년', '2017년', '2018년', '2019년', '2020년', '2021년', '2022년', '2013년 이후 증가량'], axis=1, inplace=True) #지역구를 index로 설정 data_result.set_index('지역구', inplace=True) # object로 되어 있는 컬럼을 int or float으로 변경 ## astype을 사용할 때는 dtype 바꾼 결과를 다시 원본 df 변수로 새로 할당해줘야 변경이 반경됨 data_result=data_result.astype({'총계':'int64'}) data_result=data_result.astype({'2013년 이후 CCTV증가율':'float64'}) data_result.dtypes
Out[188]:총계2013년 이후 CCTV증가율인구수한국인외국인고령자외국인 비율고령자 비율지역구종로구용산구성동구광진구동대문구중랑구성북구강북구도봉구노원구은평구서대문구마포구양천구강서구구로구금천구영등포구동작구관악구서초구강남구송파구강동구1980 1.851852 152212 141060 11152 28265 7.326623 18.569495 2847 1.569747 232482 217756 14726 39478 6.334254 16.981100 4047 1.453998 287240 280240 7000 48238 2.436986 16.793622 3480 17.053481 350925 336801 14124 54854 4.024792 15.631260 2759 0.036258 354884 337574 17310 65154 4.877650 18.359239 4193 5.643739 389928 385003 4925 76116 1.263054 19.520527 4842 2.911796 441855 430100 11755 78427 2.660375 17.749488 3321 NaN 296934 292762 4172 67056 1.405026 22.582796 2247 4.804104 312858 310509 2349 68114 0.750820 21.771539 2617 3.031496 506989 502515 4474 93279 0.882465 18.398624 4653 0.366695 468766 464871 3895 90556 0.830905 19.317954 3445 9.958506 321966 308437 13529 56785 4.201996 17.636955 2628 0.805524 376542 365570 10972 56582 2.913885 15.026743 3851 5.391352 442345 439219 3126 73522 0.706688 16.620963 3265 2.095059 573711 568287 5424 98659 0.945424 17.196637 4693 0.794674 417983 395183 22800 77259 5.454767 18.483766 2636 NaN 242467 229307 13160 43772 5.427543 18.052766 4553 2.452745 398999 376614 22385 66014 5.610290 16.544904 2671 2.533589 390377 380201 10176 68766 2.606711 17.615280 5398 7.188245 502628 487815 14813 83420 2.947110 16.596767 4995 1.503759 408979 404831 4148 63328 1.014233 15.484414 7243 0.821269 537817 532798 5019 83097 0.933217 15.450795 3203 2.693171 663704 658006 5698 105161 0.858515 15.844563 3190 2.638353 464027 459982 4045 79706 0.871717 17.177018 상관분석
In [189]:# numpy의 corrcoef (상관계수 계산) 명령어로 각 컬럼 간 상관계수를 계산 ## 상관계수 x는 ''-1 <= x <= 1'의 범주를 지님 ### 0에 가까울수록 약한 상관관계. -1에 가까울수록 역 상관관계(음의 상관관계), 1에 가까울수록 정 상관관계(양의 상관관계) np.corrcoef(data_result['고령자 비율'], data_result['총계']) # 고령자 비율과 CCTV 갯수 사이의 관계는 약 -0.32로 약한 음의 상관관계
Out[189]:array([[ 1. , -0.31958939], [-0.31958939, 1. ]])
In [190]:np.corrcoef(data_result['외국인 비율'], data_result['총계']) # 외국인 비율과 CCTV 갯수 사이의 관계는 약 -0.25로 약한 음의 상관관계
Out[190]:array([[ 1. , -0.25159909], [-0.25159909, 1. ]])
In [192]:np.corrcoef(data_result['인구수'], data_result['총계']) # 인구수와 CCTV 갯수 사이의 관계는 약 0.44로 약한 양의 상관관계
Out[192]:array([[1. , 0.44096629], [0.44096629, 1. ]])
그래프 그리기
In [201]:# matplotlib 한글 표기 처리 import matplotlib.font_manager as fm plt.rcParams['axes.unicode_minus'] = False path = 'C:\\Windows\\Fonts\\malgun.ttf' font_name = fm.FontProperties(fname=path).get_name() plt.rc('font', family=font_name)
Out[201]:총계2013년 이후 CCTV증가율인구수한국인외국인고령자외국인 비율고령자 비율지역구종로구용산구성동구광진구동대문구중랑구성북구강북구도봉구노원구은평구서대문구마포구양천구강서구구로구금천구영등포구동작구관악구서초구강남구송파구강동구1980 1.851852 152212 141060 11152 28265 7.326623 18.569495 2847 1.569747 232482 217756 14726 39478 6.334254 16.981100 4047 1.453998 287240 280240 7000 48238 2.436986 16.793622 3480 17.053481 350925 336801 14124 54854 4.024792 15.631260 2759 0.036258 354884 337574 17310 65154 4.877650 18.359239 4193 5.643739 389928 385003 4925 76116 1.263054 19.520527 4842 2.911796 441855 430100 11755 78427 2.660375 17.749488 3321 NaN 296934 292762 4172 67056 1.405026 22.582796 2247 4.804104 312858 310509 2349 68114 0.750820 21.771539 2617 3.031496 506989 502515 4474 93279 0.882465 18.398624 4653 0.366695 468766 464871 3895 90556 0.830905 19.317954 3445 9.958506 321966 308437 13529 56785 4.201996 17.636955 2628 0.805524 376542 365570 10972 56582 2.913885 15.026743 3851 5.391352 442345 439219 3126 73522 0.706688 16.620963 3265 2.095059 573711 568287 5424 98659 0.945424 17.196637 4693 0.794674 417983 395183 22800 77259 5.454767 18.483766 2636 NaN 242467 229307 13160 43772 5.427543 18.052766 4553 2.452745 398999 376614 22385 66014 5.610290 16.544904 2671 2.533589 390377 380201 10176 68766 2.606711 17.615280 5398 7.188245 502628 487815 14813 83420 2.947110 16.596767 4995 1.503759 408979 404831 4148 63328 1.014233 15.484414 7243 0.821269 537817 532798 5019 83097 0.933217 15.450795 3203 2.693171 663704 658006 5698 105161 0.858515 15.844563 3190 2.638353 464027 459982 4045 79706 0.871717 17.177018 In [205]:# 수평 막대 그래프, 내림차순 정렬로 지역구 별 CCTV 갯수 그래프 그리기 data_result['총계'].sort_values().plot(kind='barh', grid=True, figsize=(10,10)) # 원본 데이터에 '인구 수 대비 cctv 비율' 컬럼을 만들어서 총계 대신 인구 대비 cctv 비율로 그래프 그리기 data_result['CCTV비율'] = data_result['총계'] / data_result['인구수'] * 100 data_result['CCTV비율'].sort_values().plot(kind='barh', grid=True, figsize=(10,10)) plt.xlabel('CCTV비율') plt.show()
In [218]:# x축 인구수, y축 총계로 두고 지역구별 scatter 그래프 그리기 # plt.figure(figsize=(6,6)) # plt.scatter(data_result['인구수'], data_result['총계'], s=30) # plt.ylabel('CCTV 갯수') # plt.grid() # 인구수와 CCTV 갯수 사이 양의 상관관계를 대표하는 직선 긋기 fp1= np.polyfit(data_result['인구수'], data_result['총계'], 1) ## y축 데이터 f1 = np.poly1d(fp1) ## x축 데이터 fx = np.linspace(100000, 700000, 100) ### 아까 그린 그래프 위에 직선 추가 # plt.figure(figsize=(10,10)) # plt.scatter(data_result['인구수'], data_result['총계'], s=30) # plt.plot(fx, f1(fx), ls='dashed', lw=3, color='g' ) # plt.ylabel('CCTV 갯수') # plt.grid() # plt.show() # 해당 직선 기울기 상 x축(인구수)에 따른 y값(CCTV)과 실제 설치된 CCTV수의 차이를 절대값으로 나타내는 컬럼을 추가한 뒤 ## 그 컬럼 기준으로 내림차 순 정렬. 즉 서울시 지역구 별 인구수 대비 CCTV 예상 설치 수 (직선)와 scatter plot에 찍힌 실제 값 사이의 오차가 큰 순서대로 정렬 data_result['오차']=np.abs(data_result['총계']-f1(data_result['인구수'])) df_sort=data_result.sort_values(by='오차', ascending=False) df_sort
Out[218]:총계2013년 이후 CCTV증가율인구수한국인외국인고령자외국인 비율고령자 비율CCTV비율오차지역구강남구송파구노원구서초구강서구관악구도봉구동작구마포구성북구구로구성동구영등포구강동구동대문구은평구종로구중랑구금천구용산구서대문구강북구양천구광진구7243 0.821269 537817 532798 5019 83097 0.933217 15.450795 1.346741 2899.991076 3203 2.693171 663704 658006 5698 105161 0.858515 15.844563 0.482595 1717.918763 2617 3.031496 506989 502515 4474 93279 0.882465 18.398624 0.516185 1584.486730 4995 1.503759 408979 404831 4148 63328 1.014233 15.484414 1.221334 1243.448079 3265 2.095059 573711 568287 5424 98659 0.945424 17.196637 0.569102 1242.787621 5398 7.188245 502628 487815 14813 83420 2.947110 16.596767 1.073955 1216.533327 2247 4.804104 312858 310509 2349 68114 0.750820 21.771539 0.718217 1063.288950 2671 2.533589 390377 380201 10176 68766 2.606711 17.615280 0.684210 995.155662 2628 0.805524 376542 365570 10972 56582 2.913885 15.026743 0.697930 974.643285 4842 2.911796 441855 430100 11755 78427 2.660375 17.749488 1.095835 939.524125 4693 0.794674 417983 395183 22800 77259 5.454767 18.483766 1.122773 900.113389 4047 1.453998 287240 280240 7000 48238 2.436986 16.793622 1.408926 854.315682 4553 2.452745 398999 376614 22385 66014 5.610290 16.544904 1.141106 847.263296 3190 2.638353 464027 459982 4045 79706 0.871717 17.177018 0.687460 814.260944 2759 0.036258 354884 337574 17310 65154 4.877650 18.359239 0.777437 744.217837 4653 0.366695 468766 464871 3895 90556 0.830905 19.317954 0.992606 626.983714 1980 1.851852 152212 141060 11152 28265 7.326623 18.569495 1.300817 592.810862 4193 5.643739 389928 385003 4925 76116 1.263054 19.520527 1.075327 528.905564 2636 NaN 242467 229307 13160 43772 5.427543 18.052766 1.087158 351.144769 2847 1.569747 232482 217756 14726 39478 6.334254 16.981100 1.224611 94.306599 3445 9.958506 321966 308437 13529 56785 4.201996 17.636955 1.069989 92.898927 3321 NaN 296934 292762 4172 67056 1.405026 22.582796 1.118430 83.813406 3851 5.391352 442345 439219 3126 73522 0.706688 16.620963 0.870587 53.725320 3480 17.053481 350925 336801 14124 54854 4.024792 15.631260 0.991665 5.043244 In [255]:#오차를 기준으로 그래프 다시 그리기 ## 책 저자는 직선에서 멀리 떨어진(즉 오차가 큰) 지역구 10개만 이름을 표기한다고 했는데, 나는 전체적인 게 궁금해서 그냥 다 표기함 ### 그래서 사실상 오차를 구할 필요가 없었다는...ㅋㅋㅋ plt.figure(figsize=(14,10)) plt.scatter(data_result['인구수'], data_result['총계'], c=data_result['오차'], s=50) plt.plot(fx, f1(fx), ls='dashed', lw=2, color='black' ) for n in range(0, 24): plt.text(df_sort['인구수'][n]*1.01, df_sort['총계'][n]*1.01, df_sort.index[n], fontsize=9.5) plt.ylabel('CCTV 갯수') plt.xlabel('인구수') plt.colorbar() plt.viridis() plt.grid() plt.show()
'코딩 및 데이터분석 > 데이터 분석(pandas, numpy 등)' 카테고리의 다른 글
[유저 이탈 지표 구축 프로젝트]K-means Clustering 예제 (1) 2024.01.26 [유저 이탈 지표 구축 프로젝트][스크랩] (0) 2024.01.24 [네트워크 분석] Pyvis 패키지를 이용한 유저 간 거래 네트워크 분석 (1) (2) 2023.12.20 [스크랩] 250+ Python and Data Science Tips — Covering Pandas, NumPy, ML Basics, Skl (0) 2023.09.20