데이터분석/다섯째주

EDA : 서울시 범죄 현황(1) 데이터 정리

핑크댕댕이 2023. 11. 4. 19:28
728x90

목차

     

    데이터 과학

    • 현상, 인식, 가정을 확인하는 도구로 사용할 수 있다.
    • 데이터 과학의 목적 :  가정 (혹은 인식) 을 검증하고 표현하는 것 

     

      [ 기사 ] '부자동네'  서울 강남 3구(강남,서초,송파)의 체감안전도가 대체로 높은것으로 나타났다.

      [ 검증 ] 강남 3구가 실제로 범죄로 부터 안전하다고 할 수 있는지 확인해보자.  

     

     

    서울시 범죄 현황

    • 데이터 : 공공데이터포털,  2020년도 데이터
      파일명 : crime_in_Seoul.csv

     

     

    <데이터 확인해보기>

    데이터 불러오기 : read_csv( )

    thousands 옵션에 ',' 을 설정하여 천단위 구분자인 콤마를 제외하고 숫자형으로 읽어들인다.

    import pandas as pd
    import numpy as np
    
    # csv파일에서 숫자값들에 콤마(,)를 사용하고 있어서 문자로 인식될 수 있음
    # 천단위 구분(thousands=',')을 설정하여 콤마를 제고하고 숫자형으로 읽어들인다.
    crime_raw_data = pd.read_csv('경로~/crime_in_Seoul.csv', thousands=',', encoding='euc-kr')
    crime_raw_data.head(3)

    -- 출력 --

     

    데이터 개요 확인 : info( )

    crime_raw_data.info()

    -- 출력 --

    RangeIndex가 65534인데, 데이터는 310개이다...

     

     

    '죄종' 컬럼의 데이터 고유값 확인: unique( )

    crime_raw_data['죄종'].unique()

    -- 출력 --

    array( [ '살인', '강도', '강간', '절도', '폭력', nan ], dtype=object )

     

     

    NaN 데이터가 확인되어, NaN 만 추출해보기: isnull( )

    crime_raw_data[ crime_raw_data['죄종'].isnull() ]

    -- 출력 --

    엑셀에서 읽을때는 정상이지만, 실제 데이터들 그 후에 엄청 많은 nan 데이터가 보인다.
    그래서 RangeIndex가 65534의 크기를 가지게 되면서, 실제 value와의 크기 차이가 발생하였다.

     

    NaN이 아닌 데이터만 다시 가져오기: notnull( )

    crime_raw_data = crime_raw_data[ crime_raw_data['죄종'].notnull() ]
    crime_raw_data.info()

    -- 출력 --

     

    • 데이터 확인하기
    crime_raw_data.head()

    -- 출력 --

    이 형태로의 데이터는 의미가 없다.
    형태가 세로축에 서울시 구이름, 가로축에 5개 범죄 수치가 있게 변경하고자 한다

     

     

    데이터 재정렬하기: pivot table

    경찰서 이름을 index로, 피벗데이블의 기본설정은 평균이므로 aggfunc 옵션에 sum 설정하여 합계 출력

    crime_station = crime_raw_data.pivot_table(
    			crime_raw_data, index=['구분'], columns=['죄종', '발생검거'], aggfunc=np.sum
    			)
    crime_station.head()

    -- 출력 --

    깔끔하게 데이터 재정렬되었으나, column이 multi로 잡히고 있다.

     

    컬럼 확인하기: columns

    crime_station.columns

    -- 출력 --

    MultiIndex([('sum', '건수', '강간', '검거'),
                      ('sum', '건수', '강간', '발생'),
                      ('sum', '건수', '강도', '검거'),
                      ('sum', '건수', '강도', '발생'),
                      ('sum', '건수', '살인', '검거'),
                      ('sum', '건수', '살인', '발생'),
                      ('sum', '건수', '절도', '검거'),
                      ('sum', '건수', '절도', '발생'),
                      ('sum', '건수', '폭력', '검거'),
                      ('sum', '건수', '폭력', '발생'),

                      names=[None, None, '죄종', '발생검거' ])

    pivot_table을 적용하면 column이나 index가 다중으로 잡힌다.

     

    Multi Index에 대한 접근

    crime_station['sum','건수','강도','건수']

    -- 출력 --

    구분
    강남          26.0
    강동          13.0
    강북            4.0
    강서          10.0
    관악          10.0
    광진            6.0
    구로          13.0
    금천            7.0
    남대문        4.0
    노원            9.0
    도봉            5.0
    동대문      12.0
    동작            7.0
    마포            7.0
    방배            5.0
    서대문        4.0
    서부            2.0
    서초            5.0
    성동            5.0

     

     

    다중컬럼에서 특정 컬럼 제거: droplovel( )

    crime_station.columns = crime_station.columns.droplevel([0, 1])
    crime_station.columns

    -- 출력 --

    MultiIndex([('강간', '검거'),
                      ('강간', '발생'),
                      ('강도', '검거'),
                      ('강도', '발생'),
                      ('살인', '검거'),
                      ('살인', '발생'),
                      ('절도', '검거'),
                      ('절도', '발생'),
                      ('폭력', '검거'),
                      ('폭력', '발생'),
                      names=[ '죄종', '발생검거' ])

     

    crime_station.head()

    -- 출력 --

     

     

    인덱스 확인하기: index

    crime_station.index

    -- 출력 --

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

    index는 경찰서 이름으로 되어있다. 경찰서 이름으로 구이름을 알아야한다.

     

     

    <Google Maps를 이용한 데이터 정리>

    # 주소, 위도, 경도 데이터를 넣을 컬럼 추가
    crime_station['구별'] = np.nan
    crime_station['lat'] = np.nan
    crime_station['lng'] = np.nan
    crime_station.head()

    -- 출력 --

     

    구글지도 검색하여 위치정보 얻기 : geocode( )

    #구글맵 import
    import googlemaps
    
    gmaps_key = '발급받은 API Key 입력'
    gmaps = googlemaps.Client(key=gmaps_key)
    
    for idx, rows in crime_station.iterrows():
    	name = '서울' + str(idx) + '경찰서'		# 구글 검색에 용이하게 하기 위해서~
    	tmp = gmaps.geocode(name, language='ko')
        
    	tmp_gu = tmp[0].get('formatted_address')
    	lat = tmp[0].get('geometry')['location']['lat']
    	lng = tmp[0].get('geometry')['location']['lng']
    
    	crime_station.loc[idx, 'lat'] = lat
    	crime_station.loc[idx, 'lng'] = lng
    	crime_station.loc[idx, '구별'] = tmp_gu.split()[2]
        
    crime_station.head()

    -- 출력 --

     

     

    멀티 컬럼을 하나로 합치기 : columns.get_level_values( )

    tmp = [
    	crime_station.columns.get_level_values(0)[i] + crime_station.columns.get_level_values(1)[i]
    	for i in range( len(crime_station.columns.get_level_values(0)) )
    	]
    
    crime_station.columns = tmp
    crime_station.head()

    -- 출력 --

     

     

    데이터를 CSV파일로 저장: to_csv( )

    crime_station.to_csv('./crime_in_seoul_raw.csv', sep=',', encoding='utf-8')

     

    구별로 데이터 재정렬하면서 합계 출력하기: pd.pivot_table( )

    crime_anal = pd.read_csv('./crime_in_seoul_raw.csv', index_col=0, encoding='utf-8')
    crime_anal.head()
    
    crime_anal_gu = pd.pivot_table( crime_anal, index='구별', aggfunc=np.sum )
    
    #불필요한 컬럼 제거
    del crime_anal_gu['lat']
    del crime_anal_gu['lng']
    
    crime_anal_gu.head()

    -- 출력 --

     

     

    <검거율 데이터 추가하기>

    #검거율 = 검거 수 / 발생 수
    
    #crime_anal_gu['강도검거'] / crime_anal_gu['강도발생']
    #crime_anal_gu[['강도검거','살인검거']].div( crime_anal_gu['강도발생'], axis=0 )	#계산오류, 강도검거와 살인검거 모두 강도발생 데이터로 나뉘어짐
    
    num = ['강간검거', '강도검거', '살인검거', '절도검거', '폭력검거']
    den = ['강간발생', '강도발생', '살인발생', '절도발생', '폭력발생']
    
    display(crime_anal_gu[num].div( crime_anal_gu[den].values ))

    -- 출력 --

     

     

    target = ['강간검거율', '강도검거율', '살인검거율', '절도검거율', '폭력검거율']
    
    crime_anal_gu[target] = crime_anal_gu[num].div(crime_anal_gu[den].values) * 100
    crime_anal_gu.head()

    -- 출력 --

     

     

     

    불필요한 컬럼 제거: del

    del crime_anal_gu['강간검거']
    del crime_anal_gu['강도검거']
    del crime_anal_gu['살인검거']
    del crime_anal_gu['절도검거']
    del crime_anal_gu['폭력검거']
    
    crime_anal_gu.head()

    -- 출력 --

    검거율이 100이 넘는 데이터가 있다.

    전년도 발생 범죄를 당해 년도에 검거하여 검거 수에 포함되었기 때문이다.

    이에 대해서 발생 년도와 검거 년도를 구분하여 분석해야 한다.

    하지만 그런 디테일까지 목표가 아니므로, 강제로 100이상의 수치는 100으로 조정한다.

     

     

    이상치 데이터 강제로 조정하기

    crime_anal_gu[ crime_anal_gu[target] >= 100 ] = 100
    
    #상기 코드가 오류가 발생할시 대체할 코드
    # for col in crime_anal_gu.columns:
    # 	crime_anal_gu.loc[ crime_anal_gu[target] >= 100, col ] = 100
    
    crime_anal_gu.head()

    -- 출력 --

     

     

    컬럼명 간결하게 변경하기: rename( )

    crime_anal_gu.rename(
    		columns = {'강간발생':'강간', '강도발생':'강도', '살인발생':'살인', '절도발생':'절도', '폭력발생':'폭력'}
    		, inplace=True
    	)
    crime_anal_gu.head()

    -- 출력 --

    검거율의 데이터는 잘 정리되었다.

    그러나 범죄 유형(강간, 강도, 살인, 절도, 폭력)의 경중에 따라 발생 건수의 차이가 너무 크다.

    살인은 한자리 발생 수일때, 절도는 네자리 발생수이다.

     

     

     

     

    < 데이터 정규화 >

    범죄 유형별 발생건수를 정규화 데이터로 만들기: 최대값으로 나누기(Min Max 스케일러)

    col = ['살인', '강도', '강간', '절도', '폭력']
    
    #정규화
    #최고값은 1로 두고, 최소값은 0으로..
    crime_anal_norm = crime_anal_gu[col] / crime_anal_gu[col].max()
    display(crime_anal_norm)

    -- 출력 --

     

     

    검거율 데이터 추가하기

    col2 = ['강간검거율', '강도검거율', '살인검거율', '절도검거율', '폭력검거율']
    
    #데이터 추가하기
    crime_anal_norm [col2] = crime_anal_gu[col2]
    crime_anal_norm.head()

    -- 출력 --

     

     

     

    <인구과 CCTV 데이터 추가>

    구별 CCTV자료에서 인구수와 CCTV수 가져오기

    result_CCTV = pd.read_csv('./CCTV_result.csv', encoding='utf-8', index_col='구별')
    
    crime_anal_norm[['인구수', 'CCTV']] = result_CCTV[['인구수', 'CCTV']]
    crime_anal_norm.head()

    -- 출력 --

     

     

     

    <지표 데이터 추가>

    정규화된 범죄 발생건수 전체의 평균을 범죄 대표값으로 추출하기: np.mean( )

    crime_anal_norm['범죄'] = np.mean( crime_anal_norm[col], axis=1 )
    crime_anal_norm.head()

    -- 출력 --

     

     

    검거율의 평균을 검거의 대표값으로 추출하기

    crime_anal_norm['검거'] = np.mean( crime_anal_norm[col2], axis=1 )
    crime_anal_norm.head(25)

    -- 출력 --

     

     

     

     

     

     

    반응형