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()
Name을 인덱스로 두고 재정렬
df.pivot_table(index="Name")
index를 여러개 지정할 수 있음
df.pivot_table(index=["Name","Rep","Manager"])
values를 지정할 수 있다.
df.pivot_table(index=["Manager","Rep"], values="Price")
values에 함수를 적용할 수 있다.
디폴트는 평균
합산 등의 다른 함수를 적용할 때는 aggfunc 옵션을 지정
df.pivot_table(index=["Manager","Rep"], values="Price", aggfunc=np.sum)
갯수도 적용(len)
df.pivot_table(index=["Manager","Rep"], values="Price", aggfunc=[np.sum,len])
columns 지정
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개 이상 index, values 설정
df.pivot_table(index=["Manager","Rep","Product"],values=["Price","Quantity"], aggfunc=np.sum, fill_value=0)
aggfunc 2개 이상 설정
df.pivot_table(index=["Manager","Rep","Product"],
values=["Price","Quantity"],
aggfunc=[np.sum,np.mean],
fill_value=0,
margins=True) # 총계(ALL)추가
pivot_table을 적용하면 column이나 index가 다중으로 잡힌다.
multi index에 대한 접근
다중 컬럼에서 특정 컬럼 제거
crime_station.columns = crime_station.columns.droplevel([0,1])
crime_station.columns
터미널에 pip install googlemaps 패키지 설치
설치한 후
import googlemaps
gmaps_key = "발급받은 GEOCODING API KEY값 입력"
gmaps = googlemaps.Client(key=gmaps_key)
count = 0
# crime_station에서 index(idx)와 나머지(rows)를 받아서 반복문 수행
for idx, rows in crime_station.iterrows():
# station_name: 구글 검색을 용이하게 하기 위해 검색어를 가급적 상세하게 잡아줌
station_name = "서울" + str(idx) + "경찰서"
tmp = gmaps.geocode(station_name, language="ko")
# 앞서 수행한 formatted_address에서 구 이름을 잡는 과정은 그대로
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"]
# loc 옵션을 사용
crime_station.loc[idx, "lat"] = lat
crime_station.loc[idx, "lng"] = lng
crime_station.loc[idx, "구별"] = tmp_gu.split()[2]
# 행(idx)과 열(lat,lng,구별)을 지정해서 구글 검색에서 얻은 정보를 기록
print(count)
count = count + 1
경찰서별 데이터로 정리되어 있다
서울은 한 구에 경찰서가 두 곳인 구가 있다
그러므로 구의 이름으로 다시 정렬해야 한다
pivot_table을 이용해서 구 별로 정리하자
pivot_table의 func을 sum으로 잡자
필요없는 컬럼은 제거(del)
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()
# 검거율 생성
# 하나의 컬럼을 다른 컬럼으로 나누기
crime_anal_gu["강도검거"] / crime_anal_gu["강도발생"]
# 다수의 컬럼을 다른 컬럼으로 나누기
crime_anal_gu[["강도검거","살인검거"]].div(crime_anal_gu["강도발생"], axis=0).head(3)
# 다수의 컬럼을 다수의 컬럼으로 각각 나누기
num = ["강간검거","강도검거","살인검거","절도검거","폭력검거"]
den = ["강간발생","강도발생","살인발생","절도발생","폭력발생"]
crime_anal_gu[num].div(crime_anal_gu[den].values).head()
필요없는 컬럼들 제거 후
작년에서 올해로 넘어오면서 검거했을수도 있어서 검거율이 100이 넘을 수 있다.
하지만 나중에 문제가 될 수 있어서 100 이상의 수치는 100으로 만든다.
# 100보다 큰 숫자 찾아서 바꾸기
crime_anal_gu[crime_anal_gu[target] > 100] = 100
crime_anal_gu.head()
검거율까지의 데이터는 잘 정리되었다.
범죄의 경중에 따라 발생 건수의 차이가 크다.
살인은 한 자리 수 발생일 때, 절도는 네 자리수 발생이다.
정규화
본래의 DataFrame은 두고, 정규화된 데이터를 따로 만들기
최고값을 1로 두고, 최소값을 0으로
crime_anal_norm[["인구수","CCTV"]] = result_CCTV[["인구수","소계"]]
crime_anal_norm.head()
# 검거율의 평균을 구해서 검거 컬럼의 대표값으로 사용
col = ["강간검거율","강도검거율","살인검거율","절도검거율","폭력검거율"]
crime_anal_norm["검거"] = np.mean(crime_anal_norm[col], axis=1)
crime_anal_norm.head()
# !conda install -y seaborn
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
import seaborn as sns
from matplotlib import rc
plt.rcParams["axes.unicode_minus"] = False
rc("font", family="Arial Unicode MS") # Windows: Malgun Gothic
# %matplotlib inline
get_ipython().run_line_magic("matplotlib", "inline")
# boxplot hue, palette option
# hue: 카테고리 데이터 표현
plt.figure(figsize=(8, 6))
sns.boxplot(x="day", y="total_bill", data=tips, hue="smoker", palette="Set1") # Set 1 ~ 3
plt.show()
# colormap
plt.figure(figsize=(10, 8))
sns.heatmap(flights, annot=True, fmt="d", cmap="YlGnBu")
plt.show()
다수의 컬럼을 비교하는 pairplot
hue 옵션 가능
원하는 컬럼만 pairpoint
sns.pairplot(iris,
x_vars=["sepal_width", "sepal_length"],
y_vars=["petal_width", "petal_length"])
plt.show()
import matplotlib.pyplot as plt
import seaborn as sns
from matplotlib import rc
plt.rcParams["axes.unicode_minus"] = False
get_ipython().run_line_magic("matplotlib", "inline")
rc("font", family="Arial Unicode MS") # Windows: Malgun Gothic
# "인구수", "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
# "검거" 컬럼을 기준으로 정렬
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()
현재까지의 결론
- 강남 3구의 범죄 발생 건수가 결코 낮지 않다.
- 강남 3구의 범죄 검거율 조차 높지 않다.
그러나 인구대비 현황 등을 고려해야 할 것이다.
pip install folium
import folium
import pandas as pd
import json
튜플과 리스트의 형태로. 디폴트값은 없다.
m = folium.Map(location=[37.544564958079896, 127.05582307754338], zoom_start=14) # 0 ~ 18
m
m.save("./folium.html")
### tiles option
```
- "OpenStreetMap"
- "Mapbox Bright" (Limited levels of zoom for free tiles)
- "Mapbox Control Room" (Limited levels of zoom for free tiles)
- "Stamen" (Terrain, Toner, and Watercolor)
- "Cloudmade" (Must pass API key)
- "Mapbox" (Must pass API key)
- "CartoDB" (positron and dark_matter)
```
m = folium.Map(
location=[37.544564958079896, 127.05582307754338],
zoom_start=14,
tiles="OpenStreetMap"
) # 0 ~ 18
m
m = folium.Map(
location=[37.544564958079896, 127.05582307754338], # 성수역
zoom_start=14,
tiles="OpenStreetMap"
) # 0 ~ 18
# 뚝섬역
folium.Marker((37.54712311308356, 127.04721916917774)).add_to(m)
# 성수역
folium.Marker(
location=[37.544564958079896, 127.05582307754338],
popup="<b>Subway</b>"
).add_to(m)
# tooltip
folium.Marker(
location=[37.544564958079896, 127.05582307754338],
popup="<b>Subway</b>",
tooltip="<i>성수역</i>"
).add_to(m)
# html
folium.Marker(
location=[37.54558642069953, 127.05729705810472],
popup="<a href='https://zero-base.co.kr/' target=_'blink'>제로베이스</a>",
tooltip="<i>Zerobase</i>"
).add_to(m)
m
m = folium.Map(
location=[37.544564958079896, 127.05582307754338], # 성수역
zoom_start=14,
tiles="OpenStreetMap"
) # 0 ~ 18
# icon basic
folium.Marker(
(37.54712311308356, 127.04721916917774),
icon=folium.Icon(color="black", icon='info-sign')
).add_to(m)
# icon icon_color
folium.Marker(
location=[37.544564958079896, 127.05582307754338],
popup="<b>Subway</b>",
tooltip="icon color",
icon=folium.Icon(
color="red",
icon_color="blue",
icon="cloud")
).add_to(m)
# Icon custom
folium.Marker(
location=[37.54035903907497, 127.06913328776446], # 건대입구역
popup="건대입구역",
tooltip="Icon custom",
icon=folium.Icon(
color="purple",
icon_color="white",
icon="glyphicon glyphicon-cloud",
angle=50,
prefix="glyphicon") # glyphicon
).add_to(m)
m
지도 위에 클릭했을 때 마커 생성
m = folium.Map(
location=[37.544564958079896, 127.05582307754338], # 성수역
zoom_start=14,
tiles="OpenStreetMap"
) # 0 ~ 18
m.add_child(folium.ClickForMarker(popup="ClickForMarker"))
지도를 마우스로 클릭했을 때 위도 경도 정보를 반환해줍니다
m = folium.Map(
location=[37.544564958079896, 127.05582307754338], # 성수역
zoom_start=14,
tiles="OpenStreetMap"
) # 0 ~ 18
m.add_child(folium.LatLngPopup())
circle 마커 지원
m = folium.Map(
location=[37.55068861733562, 127.04420997492151],
zoom_start=14,
tiles="OpenStreetMap"
) # 0 ~ 18
# Circle
folium.Circle(
location=[37.555243442409406, 127.04370422643919], # 한양대학교
radius=100,
fill=True,
color="#eb9e34",
fill_color="red",
popup="Circle Popup",
tooltip="Circle Tooltip"
).add_to(m)
# CircleMarker
folium.CircleMarker(
location=[37.54347089498245, 127.04439204503049], # 한양대학교
radius=100,
fill=True,
color="#34ebc6",
fill_color="#c634eb",
popup="CircleMarker Popup",
tooltip="CircleMarker Tooltip"
).add_to(m)
m
m = folium.Map([43, -102], zoom_start=3)
folium.Choropleth(
geo_data="../data/02. us-states.json", # 경계선 좌표값이 담긴 데이터
data=state_data, # Series or DataFrame
columns=["State", "Unemployment"], # DataFrame columns
key_on="feature.id",
fill_color="BuPu",
fill_opacity=0.5, # 0~1
line_opacity=0.2, # 0~1
legend_name="Unemployment rate (%)"
).add_to(m)
m
# folium
m = folium.Map(location=[37.50589466533131, 126.93450729567374], zoom_start=13)
for idx, rows in df.iterrows():
# location
lat, lng = rows.위도, rows.경도
# Marker
folium.Marker(
location=[lat, lng],
popup=rows.주소,
tooltip=rows.분류,
icon=folium.Icon(
icon="home",
color="lightred" if rows.세대수 >= 199 else "lightblue",
icon_color="darkred" if rows.세대수 >= 199 else "darkblue",
)
).add_to(m)
# CircleMarker
folium.Circle(
location=[lat, lng],
radius=rows.세대수 * 0.5,
fill=True,
color="pink" if rows.세대수 >= 518 else "green",
fill_color="pink" if rows.세대수 >= 518 else "green",
).add_to(m)
m
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"))
# 2016년 서울시에서 어느정도 살인사건이 있는가?
# 영등포구에서 살인사건이 제일 많이 일어났다
my_map = folium.Map(location=[37.5502, 126.982], zoom_start=11, tiles="Stamen Toner")
my_map.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="정규화된 살인 발생 건수",
)
# 강남3구 중 2개 구가 포함되어 있다
my_map = folium.Map(location=[37.5502, 126.982], zoom_start=11, tiles="Stamen Toner")
my_map.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="정규화된 강간 발생 건수",
)
# 강남구가 역시 포함되어 있다
my_map = folium.Map(location=[37.5502, 126.982], zoom_start=11, tiles="Stamen Toner")
my_map.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="정규화된 범죄 발생 건수",
)
tmp_criminal = crime_anal_norm["범죄"] / crime_anal_norm["인구수"] # 인구수 대비 범죄발생 비율
my_map = folium.Map(location=[37.5502, 126.982], zoom_start=11, tiles="Stamen Toner")
my_map.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="정규화된 범죄 발생 건수",
)
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 = 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 = folium.Map(location=[37.5502, 126.982], zoom_start=11)
my_map.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,
)
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()
# 시각화
crime_loc_norm_sort = crime_loc_norm.sort_values(by="종합", ascending=False)
def drawGraph():
plt.figure(figsize=(5, 5))
sns.heatmap(crime_loc_norm_sort, annot=True, fmt=".4f", linewidths=0.5, cmap="RdPu")
plt.title("범죄와 발생 장소")
plt.show()