범죄현황 분석 (서울시) - 재 업로드
실제 강남3구가 범죄로부터 안전하다고 말할 수 있는지 검증
필요한 모듈 import
import numpy as np
import pandas as pd
데이터 읽기
crime_raw_data = pd.read_csv("../data/02. crime_in_Seoul.csv", thousands=",", encoding="euc-kr") # thousands 숫자값을 문자로 인식할 수 있어서 설정
crime_raw_data.head()

데이터 기본 정보
crime_raw_data.info()

특정 column에서 uniqe 확인
ime_raw_data["죄종"].unique()

nan값 확인
crime_raw_data[crime_raw_data["죄종"].isnull()].head()

데이터 정리 (nan값 아닌 값들만 다시 저장)
crime_raw_data = crime_raw_data[crime_raw_data["죄종"].notnull()]
다시 기본 정보 확인 (index와 데이터 값 310개 동일)
crime_raw_data.info()

crime_raw_data.head()

Pivot 테이블로 정리
crime_station = crime_raw_data.pivot_table(
crime_raw_data,
index="구분",
columns=["죄종", "발생검거"],
aggfunc=[np.sum])
crime_station.head()

현재 Multi-index로 되어있음
crime_station.columns

특정 column 값 확인
crime_station["sum", "건수", "강도", "검거"][:5]

Multi-index에서 불필요한 column 제거
crime_station.columns = crime_station.columns.droplevel([0, 1])
crime_station.columns

다시 데이터 확인
crime_station.head()

현재 경찰서 이름으로 index 설정되어있음 -> 경찰서 이름으로 구를 알아내야 함
crime_station.index

import googlemaps
gmaps_key = "개인마다 다름 (unique key provied from google)"
gmaps = googlemaps.Client(key=gmaps_key)
gmaps.geocode("서울영등포경찰서", language="ko") # 테스트 코드

tmp = gmaps.geocode("서울영등포경찰서", language="ko")
len(tmp)

데이터 형태 확인(type)
type(tmp[0].get("geometry")["location"])

해당 경찰서의 위도와 경도 확인
print(tmp[0].get("geometry")["location"]["lat"])
print(tmp[0].get("geometry")["location"]["lng"])

이름 확인
tmp[0].get("formatted_address").split()[2]

기존 데이터에 구별,위도,경도 column 추가
crime_station["구별"] = np.nan
crime_station["lat"] = np.nan
crime_station["lng"] = np.nan
crime_station.head()

iterrows() 반복문 사용해서 경찰서 이름에서 구이름, 위도, 경도 얻기
count = 0
for idx, rows in crime_station.iterrows():
station_name = "서울" + str(idx) + "경찰서"
tmp = gmaps.geocode(station_name, language="ko")
tmp[0].get("formatted_address")
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]
print(count)
count = count + 1
다시 데이터 확인
crime_station.head()

특정 column 확인
crime_station.columns.get_level_values(0)[2] + crime_station.columns.get_level_values(1)[2]

0,1 column 합치기
tmp = [
crime_station.columns.get_level_values(0)[n] + crime_station.columns.get_level_values(1)[n]
for n in range(0, len(crime_station.columns.get_level_values(0)))
]
tmp

데이터에 업데이트 하기
crime_station.columns = tmp
crime_station.head()

1차 저장
crime_station.to_csv("../data/02. crime_in_Seuol_raw.csv", sep=",", encoding="utf-8")
index_col "구분"을 인덱스 컬럼으로 설정
crime_anal_station = pd.read_csv("../data/02. crime_in_Seoul_raw.csv", index_col=0, encoding="utf-8")
crime_anal_station.head()

Pivot 테이블을 이용해서 구별로 정리, func은 Sum 그리고 불필요 column 제거
crime_anal_gu = pd.pivot_table(crime_anal_station, index="구별", aggfunc=np.sum)
del crime_anal_gu["lat"]
crime_anal_gu.drop("lng", axis=1, inplace=True)
crime_anal_gu.head()

검거률 column 생성
target = ["강간검거율", "강도검거율", "살인검거율", "절도검거율", "폭력검거율"]
num = ["강간검거", "강도검거", "살인검거", "절도검거", "폭력검거"]
den = ["강간발생", "강도발생", "살인발생", "절도발생", "폭력발생"]
crime_anal_gu[target] = crime_anal_gu[num].div(crime_anal_gu[den].values) * 100
crime_anal_gu.head()

불필요한 column 제거
del crime_anal_gu["강간검거"]
del crime_anal_gu["강도검거"]
crime_anal_gu.drop(["살인검거", "절도검거", "폭력검거"], axis=1, inplace=True)
crime_anal_gu.head()

데이터에 100보다 큰 값이 존재하기에 찾아서 100으로 바꾸기 (율이기 때문에 100을 넘길 수 없다)
crime_anal_gu[crime_anal_gu[target] > 100] = 100
crime_anal_gu.head()

가시성을 높이기위해 column이름 짧게 줄이기
crime_anal_gu.rename(columns={"강간발생": "강간", "강도발생": "강도", "살인발생": "살인", "절도발생": "절도", "폭력발생": "폭력"},
inplace=True)
crime_anal_gu.head()

정규화에서 1은 최고 값, 0은 최소 값
# 특정 column 정규값 확인
crime_anal_gu["강도"] / crime_anal_gu["강도"].max()

모든 범죄 column의 정규화 값 계산
col = ["살인", "강도", "강간", "절도", "폭력"]
crime_anal_norm = crime_anal_gu[col] / crime_anal_gu[col].max()
crime_anal_norm.head()

정규화된 데이터 값에 위에서 만든 검거율 추가
col2 = ["강간검거율", "강도검거율", "살인검거율", "절도검거율", "폭력검거율"]
crime_anal_norm[col2] = crime_anal_gu[col2]
crime_anal_norm.head()

기존에 만들었던 "서울 구별 CCTV" 데이터 가져오기
result_CCTV = pd.read_csv("../data/01. CCTV_result.csv", index_col="구별", encoding="utf-8")
result_CCTV.head()

데이터 "서울 구별 CCTV"에서 인구수와 CCTV수 추가
crime_anal_norm[["인구수", "CCTV"]] = result_CCTV[["인구수", "소계"]]
crime_anal_norm.head()

정규화된 범죄발생 건수 전체의 평균을 구하고 범죄 column 만든 후 대표값으로 사용
col = ["강간", "강도", "살인", "절도", "폭력"]
crime_anal_norm["범죄"] = np.mean(crime_anal_norm[col], axis=1)
crime_anal_norm.head()

검거율 전체 평균을 구하고 검거 column 만든 후 대표값으로 사용
col = ["강간검거율", "강도검거율", "살인검거율", "절도검거율", "폭력검거율"]
crime_anal_norm["검거"] = np.mean(crime_anal_norm[col], axis=1) # axis=1 행을 따라서 연산하는 옵션
crime_anal_norm.head()

전체 데이터 확인
crime_anal_norm

중간 저장
crime_anal_norm.to_csv("../data/02. crime_in_Seoul_final.csv", sep=",", encoding="utf-8")
Seaborn
import matplotlib.pyplot as plt
%matplotlib inline
import seaborn as sns
from matplotlib import rc
rc("font", family="Malgun Gothic")
pairplot 사용 (강도, 살인, 폭력에 대한 상관관계 확인)
sns.pairplot(data=crime_anal_norm, vars=["살인", "강도", "폭력"], kind="reg", height=3);

해석1-1 : 폭력 사건이 살인으로 이어지는 경우가 강도 사건이 살인으로 이어지는 것 보다 더 많다.
해석2-1 : 강도와 폭력은 상관관계가 매우 높다.
pairplot 사용 (인구수, CCTV와 살인, 강도의 상관관계 확인)
def drawGraph():
sns.pairplot(
data=crime_anal_norm,
x_vars=["인구수", "CCTV"],
y_vars=["살인", "강도"],
kind="reg",
height=4
)
plt.show()
drawGraph()

해석1-1 : 인구수가 증가하는 것에 비해 강도가 많이 증가한다고 볼 수는 없다. (아웃라이어도 존재하며, 이를 제외하면 증가폭은 더욱 줄어들 것이다)
해석1-2 : 강남3구가 인구수가 많은 곳이라면, 강도 발생 비율이 낮으니까 안전하다고 느낄 수 있지 않을까?
해석2-1 : 인구수가 증가함에 따라 살인은 증가하는 경향을 보인다.
해석3-1 : CCTV가 많이 설치되어있을 수록 강도 사건이 많이 일어난다? 이는 해석의 오류. 그렇다면 CCTV가 많아서 강도사건이 많이 발생하니까, CCTV를 줄여야한다 라고 연결될 수 있다.
해석3-2 : 강도 사건이 많이 발생하는 곳에 CCTV를 많이 설치한 것일 수도 있다.
해석3-3 : 아웃라이어를 제외하면, 회귀선이 조금 더 내려가서 해석을 달리 할 수 있는 여지가 있다.
pairplot 사용 (인구수, CCTV와 살인검거율, 폭력검거율의 상관관계 확인)
def drawGraph():
sns.pairplot(
data=crime_anal_norm,
x_vars=["인구수", "CCTV"],
y_vars=["살인검거율", "폭력검거율"],
kind="reg",
height=4
)
plt.show()
drawGraph()
해석1-1 : 인구수가 증가할 수록 폭력검거율이 떨어진다.
해석2-1 : 인구수와 살인검거율은 조금 높아지는 것 같은 느낌?
해석3-1 : CCTV와 살인검거율은 해석하기 애매(100에 모여있는 이유는, 검거율은 100으로 제한했기 때문)
해석4-1 : CCTV가 증가할수록 폭력검거율이 약간 하향세를 보인다.
pairplot 사용 (인구수, CCTV와 절도검거율, 강도검거율의 상관관계 확인)
def drawGraph():
sns.pairplot(
data=crime_anal_norm,
x_vars=["인구수", "CCTV"],
y_vars=["절도검거율", "강도검거율"],
kind="reg",
height=4
)
plt.show()
drawGraph()

heatmap 사용 (검거율) - 검거 column을 기준으로 정렬
def drawGraph():
target_col = ["강간검거율", "강도검거율", "살인검거율", "절도검거율", "폭력검거율", "검거"]
crime_anal_norm_sort = crime_anal_norm.sort_values(by="검거", ascending=False)
plt.figure(figsize=(10, 10))
sns.heatmap(
data=crime_anal_norm_sort[target_col],
annot=True, # 데이터값 표현
fmt="f", # d: 정수, f: 실수
linewidths=0.5, # 간격설정
cmap="RdPu",
)
plt.title("범죄 검거 비율(정규화된 검거의 합으로 정렬")
plt.show()
drawGraph()

heatmap 사용 (범죄 건수) - 범죄 column을 기준으로 정렬
def drawGraph():
target_col = ["살인", "강도", "강간", "절도", "폭력", "범죄"]
crime_anal_norm_sort = crime_anal_norm.sort_values(by="범죄", ascending=False)
plt.figure(figsize=(10, 10))
sns.heatmap(
data=crime_anal_norm_sort[target_col],
annot=True, # 데이터값 표현
fmt="f", # 실수값으로 표현
linewidths=0.5, # 간격설정
cmap="RdPu",
)
plt.title("범죄 비율(정규화된 발생 건수로 정렬)")
plt.show()
drawGraph()

해석1-1 : 강남구는 살인을 제외하면, 전부 1등
해석1-2 : 서초구도 상위권에 속함
해석1-3 : 강남 3구의 범죄 발생 건수가 결코 낮지 않다
해석2-1 : 검거율은 낮은데, 범죄 발생 비율이 높다.
해석3-1 : 강남 송파 서초구가 과연 안전할까? 라는 의문을 계속 가질 수 있음
데이터 저장
crime_anal_norm.to_csv("../data/02. crime_in_Seoul_final.csv", sep=",", encoding="utf-8")
Folium을 사용한 지도 시각화
import folium
import pandas as pd
import json
crime_anal_norm = pd.read_csv(
"../data/02. crime_in_Seoul_final.csv", index_col=0, encoding="utf-8")
geo_path = "../data/02. skorea_municipalities_geo_simple.json"
geo_str = json.load(open(geo_path, encoding="utf-8"))
(1) 살인 사건
my_map = folium.Map(location=[37.5502, 126.982], zoom_start=10, tiles='CartoDB Voyager')
folium.Choropleth(
geo_data=geo_str,
data=crime_anal_norm["살인"],
columns=[crime_anal_norm.index, crime_anal_norm["살인"]],
fill_color="PuRd",
key_on="feature.id",
fill_opacity=0.7,
line_opacity=0.2,
legend_name="정규화된 살인 발생 건수",).add_to(my_map)

(2) 성범죄
my_map = folium.Map(location=[37.5502, 126.982], zoom_start=11)
folium.Choropleth(
geo_data=geo_str,
data=crime_anal_norm["강간"],
columns=[crime_anal_norm.index, crime_anal_norm["강간"]],
fill_color="PuRd",
key_on="feature.id",
fill_opacity=0.7,
line_opacity=0.2,
legend_name="정규화된 강간 발생 건수",).add_to(my_map)

(3) 5대 범죄
my_map = folium.Map(location=[37.5502, 126.982], zoom_start=11, tiles='CartoDB Voyager')
folium.Choropleth(
geo_data=geo_str,
data=crime_anal_norm["범죄"],
columns=[crime_anal_norm.index, crime_anal_norm["범죄"]],
fill_color="PuRd",
key_on="feature.id",
fill_opacity=0.7,
line_opacity=0.2,
legend_name="정규화된 범죄 발생 건수",).add_to(my_map)

(4) 인구수 대비 범죄 발생 건수
tmp_criminal = crime_anal_norm["범죄"] / crime_anal_norm["인구수"]
my_map = folium.Map(location=[37.5502, 126.982], zoom_start=11, tiles="CartoDB Voyager")
folium.Choropleth(
geo_data=geo_str,
data=tmp_criminal,
columns=[crime_anal_norm.index, tmp_criminal],
fill_color="PuRd",
key_on="feature.id",
fill_opacity=0.7,
line_opacity=0.2,
legend_name="정규화된 범죄 발생 건수",).add_to(my_map)

구별 범죄 현황과 경찰서별 검거에 대한 지도 시각화
경찰서별 정보를 가지고 범죄발생과 함께 정리
데이터 확인
import numpy as np
crime_anal_station = pd.read_csv(
"../data/02. crime_in_Seoul_1st.csv", index_col=0, encoding="utf-8"
)
col = ["살인검거", "강도검거", "강간검거", "절도검거", "폭력검거"]
tmp = crime_anal_station[col] / crime_anal_station[col].max()
crime_anal_station["검거"] = np.mean(tmp, axis=1)
crime_anal_station.head()

경찰서 위치 지도에 표시
my_map = folium.Map(location=[37.5502, 126.982], zoom_start=11)
for idx, rows in crime_anal_station.iterrows():
folium.Marker([rows["lat"], rows["lng"]]).add_to(my_map)
my_map

검거에 적절한 값은 곱해서 원의 넓이로 사용
my_map = folium.Map(location=[37.5502, 126.982], zoom_start=11)
for idx, rows in crime_anal_station.iterrows():
folium.CircleMarker(
[rows["lat"], rows["lng"]],
radius=rows["검거"] * 50,
popup=rows["구분"] + " : " + "%.2f" % rows["검거"],
color="#3186cc",
fill=True,
fill_color="#3186cc",
).add_to(my_map)
my_map

구별 범죄 현황과 경찰서별 검거율을 함께 표시
my_map = folium.Map(location=[37.5502, 126.982], zoom_start=11)
folium.Choropleth(
geo_data=geo_str,
data=crime_anal_norm["범죄"],
columns=[crime_anal_norm.index, crime_anal_norm["범죄"]],
fill_color="PuRd",
key_on="feature.id",
fill_opacity=0.7,
line_opacity=0.2,).add_to(my_map)
for idx, rows in crime_anal_station.iterrows():
folium.CircleMarker(
[rows["lat"], rows["lng"]],
radius=rows["검거"] * 50,
popup=rows["구분"] + " : " + "%.2f" % rows["검거"],
color="#3186cc",
fill=True,
fill_color="#3186cc",
).add_to(my_map)

발생 장소별 데이터 확인
crime_loc_raw = pd.read_csv(
"../data/02. crime_in_Seoul_location.csv", thousands=",", encoding="euc-kr")
crime_loc_raw.head()

중복되지 않은 범죄명 column 값 확인
crime_loc_raw["범죄명"].unique()

중복되지 않은 장소 column 값 확인
crime_loc_raw["장소"].unique()

장소와 범죄종류로 pivot table 생성
crime_loc = crime_loc_raw.pivot_table(
crime_loc_raw, index=["장소"], columns=["범죄명"], aggfunc=[np.sum]
)
crime_loc.columns = crime_loc.columns.droplevel([0, 1])
crime_loc.head()

정규화 값으로 변환
col = ["살인", "강도", "강간", "절도", "폭력"]
crime_loc_norm = crime_loc / crime_loc.max()
crime_loc_norm.head()

종합 column 생성 후 전체의 mean값 등록
crime_loc_norm["종합"] = np.mean(crime_loc_norm, axis=1)
crime_loc_norm.head()

Heatmap으로 시각화
crime_loc_norm_sort = crime_loc_norm.sort_values(by="종합", ascending=False)
def drawGraph():
plt.figure(figsize=(10, 10))
sns.heatmap(crime_loc_norm_sort, annot=True, fmt="f", linewidths=0.5, cmap="RdPu")
plt.title("범죄와 발생 장소")
plt.show()
drawGraph()
