안녕하세요?
오늘은 구름 인공지능 교육에서 배운 서울시 범죄현황 통계자료 분석 활동을 복습하는 시간을 갖도록 하겠습니다.
오늘 실습에 필요한 ‘서울시 관서별 5대범죄 발생 및 검거’데이터와 서울시 구별 인구수 데이터는 공공데이터포털에서 다운받으실 수 있습니다.
오늘은 데이터 전처리를 중심으로 하는 시간이 되겠습니다.
* 중요시 볼 것 : get함수, lambda함수, apply함수, pivot_table함수, aggfunc함수, masking기법
먼저 필요한 라이브러리들을 불러오겠습니다.
import numpy as np
import pandas as pd
import seaborn as sns
import matplotlib.pyplot as plt
from matplotlib import font_manager, rc
다음으로 엑셀파일로 되어있는 데이터를 파이썬에 데이터프레임 형식으로 불러오도록 하겠습니다.
df = pd.read_excel(‘관서별 5대범죄 발생 및 검거.xlsx’)
데이터를 불러왔으면, 먼저 데이터가 어떤 형식으로 되어있나를 살펴보는 것이 좋습니다.
type(df)
df.head()
df.tail()
df.describe() #describe는 0행의 값이 포함되어 있는 값을 보여주므로 따로 df_temp = df.drop([0])을 해주시고 보는 것이 좋습니다.(특히 평균값에 영향을 많이 미침)
df.info() #데이터에 null값은 없는지, 무슨자료형인지 확인
다음으로 서울시의 경찰서를 관서별이아닌, 구별로 정리해서 보도록 하겠습니다.
police_to_gu = {'서대문서': '서대문구', '수서서': '강남구', '강서서': '강서구', '서초서': '서초구',
'서부서': '은평구', '중부서': '중구', '종로서': '종로구', '남대문서': '중구',
'혜화서': '종로구', '용산서': '용산구', '성북서': '성북구', '동대문서': '동대문구',
'마포서': '마포구', '영등포서': '영등포구', '성동서': '성동구', '동작서': '동작구',
'광진서': '광진구', '강북서': '강북구', '금천서': '금천구', '중랑서': '중랑구',
'강남서': '강남구', '관악서': '관악구', '강동서': '강동구', '종암서': '성북구',
'구로서': '구로구', '양천서': '양천구', '송파서': '송파구', '노원서': '노원구',
'방배서': '서초구', '은평서': '은평구', '도봉서': '도봉구'}
df[‘구별’] = df[‘관서명’].apply(lambda x : police_to_gu.get(x, ‘구 없음‘))
df.head(3)
여기서 apply함수, lambda함수, get함수를 쓴 것을 중요하게 봐주시기 바랍니다.
1. dict.get(key,~) -> value값 return, 키에 해당하는 밸류가 없으면 ~로 return
2. lambda ㅁㅁㅁ : ㅇㅇㅇ -> 람다함수는 ‘ : ’ 를 기준으로 봐주시면 됩니다. ‘ : ’ 전은 적용할 변수들, 후는 그 변수들에 적용할 연산이나 함수들
3. dict[칼럼명].apply(칼럼 내 데이터마다 적용할 함수)
다음으로 관서별로 정렬되어있는 데이터를 구별 데이터로 변경하겠습니다. (같은 구의 경우에는 sum을 적용)
gu_df = pd.pivot_table(df, index=‘구별‘, aggfunc=np.sum)
gu_df
여기서는 pivot_table과 aggfunc을 중요하게 봐주시기 바랍니다.
1. 관서별 데이터의 index를 구별로 바꾸고 싶을 때는 df.set_index(‘구별‘)을 사용해도 되지만, set_index는 데이터들을 강제로 인덱스 고정시키기 때문에 구별로 정렬이 안되어있어 데이터가 중구난방으로 보입니다.(set_index의 한계라고 할 수 있죠.)
2. aggfunc은 피벗테이블내의 집계함수로 index를 바꿀 때 중복되는 데이터들을 원하는 연산(sum,mean등)으로 집계시켜 자동으로 정렬시키게 해줍니다.
다음으로 gu_df의 ‘구없음’행은 없애주고, 범죄별 검거율을 계산후 필요없는 열들은 삭제하도록 하겠습니다.
gu_df = gu_df.drop([‘구 없음‘])
gu_df['강간검거율'] = gu_df['강간(검거)']/gu_df['강간(발생)']*100
gu_df['강도검거율'] = gu_df['강도(검거)']/gu_df['강도(발생)']*100
gu_df['살인검거율'] = gu_df['살인(검거)']/gu_df['살인(발생)']*100
gu_df['절도검거율'] = gu_df['절도(검거)']/gu_df['절도(발생)']*100
gu_df['폭력검거율'] = gu_df['폭력(검거)']/gu_df['폭력(발생)']*100
gu_df['검거율'] = gu_df['소계(검거)']/gu_df['소계(발생)']*100
del gu_df['강간(검거)']
del gu_df['강도(검거)']
del gu_df['살인(검거)']
del gu_df['절도(검거)']
del gu_df['폭력(검거)']
del gu_df['소계(발생)']
del gu_df['소계(검거)']
gu_df.head()
이제 데이터를 확인해보시면 이상한 점이 몇가지 보이실 것입니다.
첫번째로는 검거율이 100이 넘어가는 데이터들을 보실 수 있으실텐데, 이는 검거건수에 해당하는 범죄가 작년에 일어났거나 하는 등의 문제로 생기는 값입니다.
이런 데이터들은 당해 검거율을 100%으로 바꿔주도록 하겠습니다.
두번째로는 도봉구의 살인검거율이 결측치로 뜨는 것입니다.
이는 도봉구의 경우 2020년 살인 발생 건수가 0이기 때문에 검거율 계산시 9으로 나누는 계산으로 인해 살인검거율 값이 NaN(Not a number)으로 되는 것입니다.
이 결측치 또한 검거율을 100으로 간주하고 채워주겠습니다.
gu_df[gu_df[ [‘강간검거율‘, ’강도검거율‘, ’살인검거율‘, ’절도검거율‘, ’폭력검거율‘] ] > 100] = 100
gu_df.head(10)
print(gu_df[gu_df[‘살인(발생)‘]==0])
gu_df[‘살인검거율’] = gu_df[‘살인검거율‘].fillna(100)
gu_df[gu_df[‘살인(발생)’]==0]
여기서 중요하게 보실 점은 바로 ‘Masking 기법’(‘체’ 활용 기법) 입니다.
gu_df[???] -> 데이터프레임 코드에서 key값을 넣는 자리에 Ture 와 False로 이루어진 ‘데이터프레임’을 넣으면 Ture에 해당하는 애들만 포착되어 나타나집니다.
이렇게 Masking 기법을 활용하시면 이중 for문과, if문을 쓸 필요 없이 바로 boolean체크 후 값 대입 적용이 가능해집니다. 코드 이해가 잘 되시지 않는다면, gu_df[ [‘강간검거율‘, ’강도검거율‘, ’살인검거율‘, ’절도검거율‘, ’폭력검거율‘] ] > 100을 먼저 찍어보시고 전체 코드를 찍어보시기 바랍니다.
Masking 기법을 이용하면 여러 필터링이 가능해지는데, 이때 판단기준들과 사이사이에는 비트 논리 연산자를 사용해주셔야 합니다. (and는 ’&‘, or는 ’|‘, not은 ’~‘)
(ex. gu_df[(gu_df[‘살인 (발생)‘ > 7) & (gu_df[‘폭력(발생)’]>2000)]
마지막으로 데이터프레임의 열 이름들을 보기쉽게 바꿔주도록 하겠습니다.
gu_df.rename(columns = {'강간(발생)':'강간',
'강도(발생)':'강도',
'살인(발생)':'살인',
'절도(발생)':'절도',
'폭력(발생)':'폭력'}, inplace=True) # inplace 옵션 == 덮어쓰기 여부
gu_df.head()
이제 할 것은 서울시 구별 인구수 데이터를 불러와 gu_df 데이터프레임과 합쳐줄 것입니다.
활동을 이어나가기 앞서, 데이터프레임 두개 A, B가 있을 때 두 데이터프레임을 합치는 방법을 잠시 요약해보겠습니다.
1. A.join(B)
- 위아래로 섞여있어도 제자리로 잘 합쳐줍니다.
- 단, A와 B 데이터프레임의 index column이 동일해야합니다.
2. pd.merge(A, B, left_on=‘??’, right_on=‘??’, how=‘??’)
- 위아래로 섞여있어도 제자리로 잘 합쳐줍니다.
- 기준이 되는 열이 서로 다른 이름을 가지고 있어도 괜찮습니다.
- 설정값이 다양해서 많은 것을 할 수 있음 ex. how=‘inner’, ‘left’ 등등..)
3. pd.concat([A, B], axis=???)
- 위아래로 섞여있으면 그대로 합쳐줍니다.
- 행방향 or 열방향 적용이 가능합니다.
서울시 구별 인구수 데이터를 불러와주도록 하겠습니다.
구별 index를 기준으로 데이터를 merge할 것이기 때문에 데이터를 불러올 때 데이터프레임의 index를 바로 구별로 셋팅하도록 하겠습니다.
popul_df = pd.read_csv(‘pop_kor.csv’, encoding=‘utf-8’, index_col=‘구별‘)
popul_df.head()
바로 두 데이터를 합쳐주도록 하겠습니다.
gu_df = gu_df.join(popul_df)
gu_df.head()
여기까지 한 데이터들을 한번 시각화해서 보고 마무리하는 시간을 가지겠습니다.
%matplotlib inline
from matplotlib import font_manager, rc
font_name = font_manager.FontProperties(fname=“c:/Windows/Fonts/malgun.ttf”).get_name()
rc(‘font’, family=font_name)
import seaborn as sns
sns.heatmap(gu_df[[‘강간’,‘강도’,‘살인’,‘절도’,‘폭력’]])
한글폰트를 적용시켜주고 seaborn의 heatmap으로 각 범죄 발생 건수를 시각화 해보았습니다. (코랩, mac에서의 한글포트 설정은 나중에 정리하겠습니다.)
우선 먼저 heatmap을 살펴보면 강도와 살인의 범죄건수가 절도, 폭력에 비해 색깔이 매우 새까만 것을 볼 수 있습니다.
이는 강도, 살인이 절도, 폭력에 비해 발생 건수가 현저히 적기 때문입니다.
실제로 이렇게 각 데이터의 단위 차이가 크게날 때를 데이터 feature의 scale이 엉망인 상태라고 합니다.
이렇게 feature scale이 엉망이면 데이터 시각화에도 문제가 있지만, 나중에 머신러닝이나 딥러닝의 여러 모델들을 돌릴 때에도 문제가 발생 할 수 있습니다.
다음시간에는 범죄별 발생 건수를 정규화하는 것(feature scale을 맞추는 것)부터 시작해보도록 하겠습니다.
감사합니다.
서울시 범죄현황 통계자료 분석 및 시각화 (4) (0) | 2023.01.10 |
---|---|
서울시 범죄현황 통계자료 분석 및 시각화 (3) (0) | 2023.01.08 |
서울시 범죄현황 통계자료 분석 및 시각화 (2) (0) | 2023.01.08 |