[EDA] (2) 서울시 범죄 현황 데이터 분석

greta·2023년 5월 30일
0

1. Pandas Pivot table

Pandas pivot table

  • index, columns, values, aggfunc
# Name 컬럼을 인덱스로 설정
# pd.pivot_table(df, index="Name")
df.pivot_table(index="Name")

# 멀티 인덱스 설정 
df.pivot_table(index=["Name", "Rep", "Manager"])

# value 설정
df.pivot_table(index=["Manager", "Rep"], values="Price")

# Price 컬럼 sum 연산 적용 (aggfunc)
df.pivot_table(index=["Manager", "Rep"], values="Price", aggfunc=np.sum)

# Product를 컬럼으로 지정
df.pivot_table(index=["Manager", "Rep"], values="Price", columns="Product", aggfunc=np.sum)

# Nan 값 설정 : fill_value 
df.pivot_table(index=["Manager", "Rep"], values="Price", columns="Product", aggfunc=np.sum, fill_value=0)

2. 서울시 범죄 현황 데이터 정리

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

crime_station.columns = crime_station.columns.droplevel([0, 1]) # 다중 컬럼에서 특정 컬럼 제거 
crime_station.columns

3. Google Maps를 이용한 데이터 정리

  • 경찰서 이름에서 소속된 구이름 얻기
  • 구이름과 위도 경도 정보를 저장할 준비
  • 반복문을 이용해서 위 표의 NaN을 모두 채워줍니다
  • 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 

  • values 제목 정리
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)))
]
crime_station.columns = tmp 

4. 구별 데이터로 정리

  • 25개구 30개 경찰서 > 구별 데이터로 정리(주소 이용)
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()

  • 검거율 생성
target = ["강간검거율", "강도검거율", "살인검거율", "절도검거율", "폭력검거율"]

num = ["강간검거", "강도검거", "살인검거", "절도검거", "폭력검거"]
den = ["강간발생", "강도발생", "살인발생", "절도발생", "폭력발생"]

crime_anal_gu[target] = crime_anal_gu[num].div(crime_anal_gu[den].values) * 100 
crime_anal_gu.head()

# 필요 없는 컬럼 제거 

del crime_anal_gu["강간검거"]
del crime_anal_gu["강도검거"]
crime_anal_gu.drop(["살인검거", "절도검거", "폭력검거"], axis=1, inplace=True)

crime_anal_gu.head()

# 100보다 큰 숫자 찾아서 바꾸기 

crime_anal_gu[crime_anal_gu[target] > 100] = 100 
crime_anal_gu.head()

5. 범죄 데이터 정렬을 위한 데이터 정리

  • 정규화
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()

  • 대표값: 정규화된 범죄발생 건수, 검거율 전체의 평균
# 정규화된 범죄발생 건수 전체의 평균을 구해서 범죄 컬럼 대표값으로 사용 

col = ["강간", "강도", "살인", "절도", "폭력"]
crime_anal_norm["범죄"] = np.mean(crime_anal_norm[col], axis=1)
crime_anal_norm.head()

# 검거율의 평균을 구해서 검거 컬럼의 대표값으로 사용 

col = ["강간검거율", "강도검거율", "살인검거율", "절도검거율", "폭력검거율"]
crime_anal_norm["검거"] = np.mean(crime_anal_norm[col], axis=1) # axis=1 행을 따라서 연산하는 옵션 
crime_anal_norm.head()

6. 서울시 범죄 데이터 시각화

  • pairplot
import matplotlib.pyplot as plt 
import seaborn as sns 

# pairplot 강도, 살인, 폭력에 대한 상관관계 확인 
# 해석1. 폭력 사건이 살인으로 이어지는 경우가 강도 사건이 살인으로 이어지는 것 보다 더 많다. 
# 해석2. 강도와 폭력은 상관관계가 매우 높다.
sns.pairplot(data=crime_anal_norm, vars=["살인", "강도", "폭력"], kind="reg", height=3);

# "인구수", "CCTV"와 "살인", "강도"의 상관관계 확인 
# 해석1-1. 인구수가 증가하는 것에 비해 강도가 많이 증가한다고 볼 수는 없다. (아웃라이어도 존재하며, 이를 제외하면 증가폭은 더욱 줄어들 것이다)
# 해석1-2. 강남3구가 인구수가 많은 곳이라면, 강도 발생 비율이 낮으니까 안전하다고 느낄 수 있지 않을까? 
# 해석2-1. 인구수가 증가함에 따라 살인은 증가하는 경향을 보인다. 
# 해석2-2. CCTV가 많이 설치되어있을 수록 강도 사건이 많이 일어난다? 이는 해석의 오류. 그렇다면 CCTV가 많아서 강도사건이 많이 발생하니까, CCTV를 줄여야한다 라고 연결될 수 있다. 
# 해석2-2. 강도 사건이 많이 발생하는 곳에 CCTV를 많이 설치한 것일 수도 있다. 
# 해석2-2. 아웃라이어를 제외하면, 회귀선이 조금 더 내려가서 해석을 달리 할 수 있는 여지가 있다. 

def drawGraph():
    sns.pairplot(
        data=crime_anal_norm, 
        x_vars=["인구수", "CCTV"],
        y_vars=["살인", "강도"],
        kind="reg",
        height=4
    )
    plt.show()
drawGraph()

# "인구수", "CCTV"와 "살인검거율", "폭력검거율"의 상관관계 확인
# 해석1-1. 인구수가 증가할 수록 폭력검거율이 떨어진다. 
# 해석2-1. 인구수와 살인검거율은 조금 높아지는 것 같은 느낌?
# 해석3-1. CCTV와 살인검거율은 해석하기 애매(100에 모여있는 이유는, 검거율은 100으로 제한했기 때문) 
# 해석4-1. CCTV가 증가할수록 폭력검거율이 약간 하향세를 보인다. 

def drawGraph():
    sns.pairplot(
        data=crime_anal_norm, 
        x_vars=["인구수", "CCTV"],
        y_vars=["살인검거율", "폭력검거율"],
        kind="reg",
        height=4
    )
    plt.show()
drawGraph()

# "인구수", "CCTV"와 "절도검거율", "강도검거율"의 상관관계 확인
# 해석1-1. CCTV가 증가할수록 절도검거율이 감소하고 있다. 
# 해석2-1. CCTV가 증가할수록 강도검거율은 증가하고 있다. 

def drawGraph():
    sns.pairplot(
        data=crime_anal_norm, 
        x_vars=["인구수", "CCTV"],
        y_vars=["절도검거율", "강도검거율"],
        kind="reg",
        height=4
    )
    plt.show()
drawGraph()

  • heatmap
# 검거율 heatmap 
# "검거" 컬럼을 기준으로 정렬 

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()

# 범죄발생 건수 heatmap 
# "범죄" 컬럼을 기준으로 정렬 

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()

# 강남구는 살인을 제외하면, 전부 1등 
# 서초구도 상위권에 속함
# 검거율은 낮은데, 범죄 발생 비율이 높다. 
# 강남 송파 서초구가 과연 안전할까? 라는 의문을 계속 가질 수 있음 

drawGraph()

7. 서울시 범죄 현황에 대한 지도 시각화

  • folium
#5대 범죄 발생 건수 지도 시각화

my_map = folium.Map(
    location=[37.5502, 126.982],
    zoom_start=11,
    tiles='Stamen toner')

folium.Choropleth(
    geo_data=geo_str,
    data=crime_anal_norm['범죄'],
    columns=[crime_anal_norm.index, crime_anal_norm['범죄']],
    key_on='feature.id',
    fill_color='PuRd',
    fill_opacity=0.7,
    line_opacity=0.2,
    legend_name='정규화된 5대 범죄 발생 건수'
).add_to(my_map)

my_map

#인구 대비 범죄 발생 건수 지도 시각화

tmp_criminal = crime_anal_norm['범죄'] / crime_anal_norm['인구수']

my_map = folium.Map(
    location=[37.5502, 126.982],
    zoom_start=11,
    tiles='Stamen toner')

folium.Choropleth(
    geo_data=geo_str,
    data=tmp_criminal,
    columns=[crime_anal_norm.index, tmp_criminal],
    key_on='feature.id',
    fill_color='PuRd',
    fill_opacity=0.7,
    line_opacity=0.2,
    legend_name='인구 대비 범죄 발생 건수'
).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.Marker(
        location=[rows['lat'],rows['lng']]
    ).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['범죄']],
    key_on='feature.id',
    fill_color='PuRd',
    fill_opacity=0.7,
    line_opacity=0.2,
).add_to(my_map)

for idx, rows in crime_anal_station.iterrows():
    folium.CircleMarker(
        location=[rows['lat'],rows['lng']],
        radius=rows['검거']*50,
        popup=rows['구분']+' : '+ '%.2f'%rows['검거'],
        color='#3186cc',
        fill=True,
        fill_color='#3186cc'
    ).add_to(my_map)
    
my_map

8. 서울시 범죄 현황 발생 장소 분석

col = ['살인','강도','강간','절도','폭력']
crime_loc_norm = crime_loc / crime_loc.max()
crime_loc.head()

crime_loc_norm['종합'] = np.mean(crime_loc_norm, axis=1)
crime_loc_norm_sort = crime_loc_norm.sort_values('종합',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()

🎅오늘의 한마디

조금 더 복잡한 데이터를 정리해주는 마법의 pivot_table(). 이를 잘 사용하려면 내가 뭘 보고 있는지, 뭘 하려는지 알아야 한다는 것이다. 데이터 정리를 잘 해야 뭐가 보인다.

0개의 댓글