Project 2 - 서울시 범죄 현황 데이터 분석 (4)
서울시 범죄현황 데이터 시각화
crime_anal_norm.head()
| 살인 | 강도 | 강간 | 절도 | 폭력 | 강간검거율 | 강도검거율 | 살인검거율 | 절도검거율 | 폭력검거율 | 인구수 | CCTV | 범죄 | 검거 | |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 구별 | ||||||||||||||
| 강남구 | 0.357143 | 1.000000 | 1.000000 | 0.977118 | 0.733773 | 80.038760 | 100.000000 | 100.000000 | 53.470867 | 88.130935 | 561052 | 3238 | 0.813607 | 84.328112 |
| 강동구 | 0.285714 | 0.358974 | 0.310078 | 0.477799 | 0.463880 | 95.000000 | 92.857143 | 100.000000 | 51.425314 | 86.996047 | 440359 | 1010 | 0.379289 | 85.255701 |
| 강북구 | 0.500000 | 0.128205 | 0.420543 | 0.332879 | 0.509351 | 73.271889 | 80.000000 | 85.714286 | 54.991817 | 89.344852 | 328002 | 831 | 0.378196 | 76.664569 |
| 관악구 | 0.428571 | 0.307692 | 0.624031 | 0.572868 | 0.593143 | 81.987578 | 83.333333 | 100.000000 | 44.555397 | 83.678516 | 520929 | 2109 | 0.505261 | 78.710965 |
| 광진구 | 0.285714 | 0.282051 | 0.540698 | 0.718060 | 0.438577 | 83.870968 | 54.545455 | 100.000000 | 40.098634 | 84.071906 | 372298 | 878 | 0.453020 | 72.517393 |
pairplot 강도, 살인, 폭력에 대한 상관관계 확인
sns.pairplot(data=crime_anal_norm, vars=["살인", "강도", "폭력"], kind="reg", height=3);

“인구수”, “CCTV”와 “살인”, “강도”의 상관관계 확인
def drawGraph():
sns.pairplot(data=crime_anal_norm,
x_vars=["인구수", "CCTV"],
y_vars=["살인", "강도"],
kind="reg",
height=4)
plt.show()
drawGraph()

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

“인구수”, “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()
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", # 실수값으로 표현
linewidth=0.5, # 간격설정
cmap="RdPu"
)
plt.title("범죄 비율(정규화된 발생 건수로 정렬)")
plt.show()
drawGraph()

데이터 저장
crime_anal_norm.to_csv("../data/02. crime_in_Seoul_final.csv", sep=",", encoding="utf-8")
folium
folium.Map()
- location: tuple or list, default None Latitude and Longitude of Map (Northing, Easting).
m = folium.Map(location=[37.5665512, 126.97805437], zoom_start=18) # 0 ~ 18 m
save(“path”)
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) ```py m = folium.Map( location=[37.5665512, 126.97805437], zoom_start=14, # 0 ~ 18 tiles=”openstreetmap” ) folium.Marker((37.56583779,126.97512197)).add_to(m) folium.Marker( location=[37.5665512, 126.97805437], popup=”서울”, tooltip=”서울” ).add_to(m) m
folium.Marker( location=[37.54878936,126.973356], popup=”<a href=’https://zero-base.co.kr/’ target=_‘blink’>제로베이스</a>”, tooltip=”Zerobase” ).add_to(m) m

---
### folium.Marker()
- 지도에 마커 생성
```py
m = folium.Map(
location=[37.5665512, 126.97805437],
zoom_start=14, # 0 ~ 18
tiles="openstreetmap"
)
folium.Marker((37.56583779,126.97512197)).add_to(m)
folium.Marker(
location=[37.5665512, 126.97805437],
popup="<b>서울</b>",
tooltip="<i>서울</i>"
).add_to(m)
m
folium.Marker(
location=[37.54878936,126.973356],
popup="<a href='https://zero-base.co.kr/' target=_'blink'>제로베이스</a>",
tooltip="<i>Zerobase</i>"
).add_to(m)
m

folium.Icon()
- https://www.w3schools.com/bootstrap/bootstrap_ref_comp_glyphs.asp
- https://fontawesome.com/icons
m = folium.Map(
location=[37.5665512, 126.97805437],
zoom_start=13, # 0 ~ 18
tiles="openstreetmap"
)
# icon basic
folium.Marker(
(37.54878936,126.97336),
icon=folium.Icon(color="black", icon='info-sign')
).add_to(m)
# icon _color
folium.Marker(
(37.54712, 127.047219),
popup="<b>Subway</b>",
tooltip="icon_color",
icon=folium.Icon(
color="red",
icon_color="pink",
icon="cloud"
)
).add_to(m)
# Icon custom
folium.Marker(
location=[37.540372,127.069276],
popup="건대입구역",
tooltip="Icon custiom",
icon=folium.Icon(
color="purple",
icon_color="white",
icon="glyphicon-cloud",
angle=50,
prefix="glyphicon") # glyphicon
).add_to(m)
# tooltip
folium.Marker(
location=[37.544569,127.055974],
popup="<b>Subway</b>",
tooltip="<i>성수역</i>"
).add_to(m)
# html
folium.Marker(
location=[37.5030426,127.041588],
popup="<a href='https://zero-base.co.kr/' target=_'blink'>제로베이스</a>",
tooltip="<i>Zerobase</i>"
).add_to(m)
m

folium.ClickForMarker()
- 지도위에 마우스로 클릭했을 때 마커 생성 ```py m = folium.Map( location=[37.5445, 127.0558], zoom_start=14, tile=”OpenStreetMap” )
m.add_child(folium.ClickForMarker(popup=”ClickForMarker”))

---
### folium.LatLngPopup()
- 지도를 마우스로 클릭했을 때 위도 경도 정보 반환
```py
m = folium.Map(
location=[37.5445, 127.0558],
zoom_start=14,
tile="OpenStreetMap"
)
m.add_child(folium.LatLngPopup())

folium.Circle(), folium.CircleMarker()
m = folium.Map(
location=[37.5445, 127.0558],
zoom_start=14,
tile="OpenStreetMap"
)
# Circle
folium.Circle(
location=[37.5574, 127.04370],
radius=100,
fill=True,
color="#eb9e34",
fill_color="red",
popup="Circle Popup",
tooltip="Circle Tooltip"
).add_to(m)
# CircleMarker
folium.Circle(
location=[37.5434, 127.04470],
radius=100,
fill=True,
color="#34ebc6",
fill_color="#c636eb",
popup="CircleMarker Popup",
tooltip="CircleMarker Tooltip"
).add_to(m)
m

folium.Choropleth
state_data = pd.read_csv("../data/02. US_Unemployment_Oct2012.csv")
state_data.tail(2)
| State | Unemployment | |
|---|---|---|
| 48 | WI | 6.8 |
| 49 | WY | 5.1 |
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=1, # 0~1
line_opacity=1, # 0~1
legend_name="unemployment rate (%)"
).add_to(m)
m

아파트 유형 지도 시각화
- 공공데이터포털, https://data.go.kr/data/15066101/fileData.do
df = pd.read_csv("../data/02. 서울특별시 동작구_주택유형별 위치 정보 및 세대수 현황_20210825.csv", encoding="cp949")
df.tail(2)
| 연번 | 분류 | 건물명 | 행정동 | 주소 | 세대수 | 위도 | 경도 | |
|---|---|---|---|---|---|---|---|---|
| 165 | 166 | 연립주택 | 능내연립 | 사당5동 | 서울특별시 동작구 사당로8길 39 | 22 | 37.483599 | 126.968672 |
| 166 | 167 | 연립주택 | 천록 | 대방동 | 서울특별시 동작구 등용로 43 | 29 | 37.505475 | 126.933434 |
NaN 데이터 제거
df = df.dropna()
df.info()
=>
<class 'pandas.core.frame.DataFrame'>
Int64Index: 163 entries, 0 to 166
Data columns (total 8 columns):
# Column Non-Null Count Dtype
--- ------ -------------- -----
0 연번 163 non-null int64
1 분류 163 non-null object
2 건물명 163 non-null object
3 행정동 163 non-null object
4 주소 163 non-null object
5 세대수 163 non-null int64
6 위도 163 non-null float64
7 경도 163 non-null float64
dtypes: float64(2), int64(2), object(4)
memory usage: 11.5+ KB
-------------------------------------------
df = df.reset_index(drop=True)
df.tail(2)
| 연번 | 분류 | 건물명 | 행정동 | 주소 | 세대수 | 위도 | 경도 | |
|---|---|---|---|---|---|---|---|---|
| 161 | 166 | 연립주택 | 능내연립 | 사당5동 | 서울특별시 동작구 사당로8길 39 | 22 | 37.483599 | 126.968672 |
| 162 | 167 | 연립주택 | 천록 | 대방동 | 서울특별시 동작구 등용로 43 | 29 | 37.505475 | 126.933434 |
df.columns
=>
Index(['연번 ', '분류 ', '건물명', '행정동', '주소', '세대수', '위도', '경도'], dtype='object')
--------------------------------------------------------------------------
df["연번 "]
=>
0 1
1 2
2 3
3 4
4 5
...
158 163
159 164
160 165
161 166
162 167
Name: 연번 , Length: 163, dtype: int64
-----------------------------------------------------------
df = df.rename(columns={"연번 ": "연번", "분류 ": "분류"})
df.연번[:10]
=>
0 1
1 2
2 3
3 4
4 5
5 6
6 7
7 8
8 9
9 10
Name: 연번, dtype: int64
------------------------
del df["연번"]
df.tail(2)
| 분류 | 건물명 | 행정동 | 주소 | 세대수 | 위도 | 경도 | |
|---|---|---|---|---|---|---|---|
| 161 | 연립주택 | 능내연립 | 사당5동 | 서울특별시 동작구 사당로8길 39 | 22 | 37.483599 | 126.968672 |
| 162 | 연립주택 | 천록 | 대방동 | 서울특별시 동작구 등용로 43 | 29 | 37.505475 | 126.933434 |
df.describe()
| 세대수 | 위도 | 경도 | |
|---|---|---|---|
| count | 163.000000 | 163.000000 | 163.000000 |
| mean | 371.920245 | 37.497442 | 126.949817 |
| std | 413.115354 | 0.009532 | 0.019861 |
| min | 21.000000 | 37.477376 | 126.906940 |
| 25% | 86.000000 | 37.490626 | 126.933284 |
| 50% | 199.000000 | 37.496940 | 126.949902 |
| 75% | 518.500000 | 37.505321 | 126.967196 |
| max | 2621.000000 | 37.514280 | 126.981966 |
folium
# folium
m = folium.Map(
location=[37.497112,126.94437795],
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="darked" 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
