본문 바로가기
Python

Python 구글맵 API, 피벗테이블, 데이터정규화를 이용하여 서울시 범죄 데이터 분석하기

by ssury94 2025. 1. 24.
 
 
 

데이터 분석 개요 🧮

서울시 5대 범죄(강간, 강도, 살인, 절도, 폭력) 데이터를 기반으로, 각 구별 범죄 발생과 검거율 데이터를 분석하고 시각화합니다.
이 글에서는 데이터 처리, 변환, 그리고 원하는 데이터 분석을 얻기 위한 주요 기술과 실습 내용을 공유합니다. 📊✨

 

 

데이터 로드와 초기 처리 🗂️

숫자 데이터 처리

CSV 파일에서 쉼표(,)로 구분된 숫자를 바로 정수(int)로 변환하려면 pandas의 read_csv 함수에서 thousands=',' 옵션을 사용합니다.
이를 통해 숫자 데이터를 정수형으로 처리할 수 있습니다. ✅

import pandas as pd

df = pd.read_csv('crime_in_Seoul.csv', encoding='euc-kr', thousands=',')

 

아래 방법으로도 인트형 변환 가능합니다.

df['절도 발생']=df['절도 발생'].str.replace(',','').astype(int)

 

 

 

관서명이 아닌 지역구별로 컬럼을 보여주기위해 구글API를 이용하겠습니다.

 

구글맵 API 활용: 주소 데이터 처리 📍

지역구별 데이터로 통합하기 위해, 경찰서의 주소에서 구를 추출해야 합니다. 이를 위해 Google Maps API를 활용할 수 있습니다. Python 라이브러리 googlemaps를 사용하면 간단히 주소 데이터를 처리할 수 있습니다. 🗺️

Google Maps API 사용 방법

1. 구글 API에서 Geocoding API 신청

https://console.cloud.google.com/

 

Google 클라우드 플랫폼

로그인 Google 클라우드 플랫폼으로 이동

accounts.google.com

 

Google Cloud Console에서 프로젝트 생성하는 법

 

 

2. 구글맵 라이브러리 설치: 

! pip install googlemaps

import googlemaps

gmaps_key = 구글 서비스키 # 자신의 key를 사용합니다.
gmaps = googlemaps.Client(key=gmaps_key)

gmaps.geocode('서울중부경찰서', language='ko')

 

gmaps.geocode 실행시 서울중부경찰서의 주소데이터를 리스트로 가져옵니다.

 

 

결과출력 : 

[{'address_components': [{'long_name': '67',
    'short_name': '67',
    'types': ['premise']},
   {'long_name': '퇴계로',
    'short_name': '퇴계로',
    'types': ['political', 'sublocality', 'sublocality_level_4']},
   {'long_name': '중구',
    'short_name': '중구',
    'types': ['political', 'sublocality', 'sublocality_level_1']},
   {'long_name': '서울특별시',
    'short_name': '서울특별시',
    'types': ['administrative_area_level_1', 'political']},
   {'long_name': '대한민국',
    'short_name': 'KR',
    'types': ['country', 'political']},
   {'long_name': '04529', 'short_name': '04529', 'types': ['postal_code']}],
  'formatted_address': '대한민국 서울특별시 중구 퇴계로 67',
  'geometry': {'location': {'lat': 37.55990389999999, 'lng': 126.9794911},
   'location_type': 'ROOFTOP',
   'viewport': {'northeast': {'lat': 37.5612528802915,
     'lng': 126.9808400802915},
    'southwest': {'lat': 37.5585549197085, 'lng': 126.9781421197085}}},
  'place_id': 'ChIJc-9q5uSifDURLhQmr5wkXmc',
  'plus_code': {'compound_code': 'HX5H+XQ 대한민국 서울특별시',
   'global_code': '8Q98HX5H+XQ'},
  'types': ['establishment', 'point_of_interest']}]

 

여기서 필요한 부분은 'formatted_address': '대한민국 서울특별시 중구 퇴계로 67'의 '중구' 입니다.

result=gmaps.geocode('서울중부경찰서', language='ko')
result[0]['formatted_address']

#출력
'대한민국 서울특별시 중구 퇴계로 67'


result[0]['formatted_address'].split()[2]
#출력
'중구'

 

station_addreess 에 저장된 주소에서, 구만 추출하여 컬럼 만들기

관서명만 가지고는 구글 주소로 찾을 수 없습니다.

단어 앞에 '서울', 맨 끝 글자인 '서'를 떼어내고 '경찰서'를 붙여 이름을 다시 만듭니다.

df_name='서울'+df['관서명'].str[0:-2+1]+'경찰서'

#출력결과:
0      서울중부경찰서
1      서울종로경찰서
2     서울남대문경찰서
3     서울서대문경찰서
4      서울혜화경찰서
...

 

 

준비 된 df_name으로 주소를 반환하겠습니다.

# 함수를 하나 만든다.
def get_address(name):
    result=gmaps.geocode(name, language='ko')
    return result[0]['formatted_address']
    
    
#df_name을 get_address()로 싹 실행해라
df_address=df_name.apply(get_address)

#전체 주소 중 구만 추출하기 위해 스플릿해서 컬럼저장
df['구별']=df_address.str.split().str[2]

 

 

하나의 구에 경찰서가 2개 이상인 곳도 있네요.

구 내의 범죄는 합치도록 하겠습니다.

데이터 변환: 피벗 테이블 🔄

피벗 테이블 생성

피벗테이블이란?

pivot_table은 판다스(Pandas) 라이브러리의 중요한 기능 중 하나로, 데이터를 재구성하고 요약하는 데 매우 유용한 기능입니다.

  • 데이터 재구성: 세로 데이터를 가로 데이터로 변환
  • 데이터 요약: 지정된 기준에 따라 데이터 집계 가능
  • 멀티인덱스 생성: 복잡한 데이터 구조 표현 가능
pd.pivot_table(df, values='Score', index=['Subject'], columns=['Name'], aggfunc='mean', fill_value=0)

기본 구성

  • values: 집계할 데이터 열
  • index: 행 인덱스로 사용할 열
  • columns: 열 인덱스로 사용할 열
  • aggfunc: 데이터 집계 함수 (기본값: 'mean')

💡 TIP: values 옵션에는 숫자형 컬럼만 사용할 수 있습니다.
문자열 데이터가 포함되지 않은 경우 values를 생략해도 무방합니다.

다양한 집계 함수 활용

  • 기본값: 평균 (mean) ➡️ 범죄 데이터를 비교할 때 적합.
  • 합계: 'sum' ➡️ 전체 데이터를 집계할 때 적합.
  • 최소값: 'min' ➡️ 최저 수치를 확인할 때 유용.
#관서명은 이제 필요없다. 삭제
df.drop('관서명',axis=1,inplace=True)

pd.pivot_table(df,index=['구별'],aggfunc='sum')

 

검거율 계산 및 정규화 🧮⚖️

검거율 계산

5대 범죄별 검거율을 계산한 뒤, 기존 데이터프레임에 새로운 컬럼으로 추가합니다.

df['강간검거율'] = (df['강간검거'] / df['강간발생']) * 100
df['강도검거율'] = (df['강도검거'] / df['강도발생']) * 100
# 다른 범죄들도 동일한 방식으로 계산

정규화(Normalization)

사람은 퍼센티지로 비교를 하지만, 컴퓨터는 정규화 혹은 표준화된 데이터로 비교가 가능합니다.

정규화는 서로 다른 범위를 가진 데이터를 동일한 스케일로 변환하는 과정입니다. 이를 통해 데이터 비교가 더 쉬워집니다. ⚙️

from sklearn.preprocessing import MinMaxScaler, StandardScaler 
#싸이킥런 #전처리 = 정규화,표준화 처리
#강간, 강도, 살인, 절도, 폭력 을 노멀라이징

#1. 정규화 MinMaxScaler
scaler = MinMaxScaler()
scaler.fit_transform(df.loc[ : ,'강간':'폭력'])
df.loc[ : ,'강간':'폭력'])=scaler.fit_transform(df.loc[ : ,'강간':'폭력']

#2. 표준화 StandardScaler()
scaler2=StandardScaler()
scaler2.fit_transform(df.loc[ : ,'강간':'폭력'])

 

 

⚡ 정규화 vs 표준화:

  • 정규화(Normalization): 데이터를 [0, 1] 범위로 변환. ➡️ 상대적 비교를 위해 유용.
  • 표준화(Standardization): 데이터 평균이 0, 표준편차가 1이 되도록 변환. ➡️ 통계 분석에 적합.

 

데이터 시각화: Seaborn Heatmap 🖼️

히트맵을 활용한 데이터 시각화

Seaborn의 heatmap을 사용하면 데이터의 패턴과 관계를 직관적으로 확인할 수 있습니다. 🎨

 

5대범죄검거율의 값을 모두 더하고 검거라는 컬럼을 만들어넣기.

df['검거']=df.loc[ : , '강간검거율':'폭력검거율'].sum(axis=1)

 

import seaborn as sb
import matplotlib.pyplot as plt

df1=df.sort_values('검거',ascending=False).loc[ : ,'강간검거율':'폭력검거율']

plt.figure(figsize=(10,8))
sb.heatmap(data=df1,cmap='RdPu',annot=True,linewidths=0.8,fmt='.1f') #소수점 뒤로 1자리만
plt.show()

색이 진할수록 검거율이 높다.

 

결론: 데이터 분석의 가치 💡

이 프로젝트에서는 범죄 데이터를 다양한 관점에서 분석하고 시각화했습니다. 주요 학습 내용은 다음과 같습니다:

  1. Google Maps API를 활용한 구별 데이터 통합. 🌐
  2. 피벗 테이블과 집계 함수로 데이터 변환 및 요약. 📊
  3. 검거율 계산 및 정규화로 데이터 간 비교 가능성 향상. 📏
  4. Seaborn 히트맵을 통한 데이터 시각화. 🎨