230515_파이썬_EDA_웹크롤링

김지태·2023년 5월 22일
0
post-thumbnail

02. Analysis Seoul Crime

int(5)

1. 프로젝트 개요

2. 데이터 개요

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

  • info(): 데이터의 개요 확인하기
  • RangeIndex가 65534인데, 310개이다
    crime_raw_data["죄종"].unique()
  • 특정 컬럼에서 unique 조사
  • nan 값이 들어가 있다
    crime_raw_data[crime_raw_data["죄종"].isnull()].head()
    crime_raw_data = crime_raw_data[crime_raw_data["죄종"].notnull()]
    crime_raw_data.info()
    crime_raw_data.head()
    crime_raw_data.tail()

Pandas pivot table

  • index, columns, values, aggfunc
    df = pd.read_excel("../data/02. sales-funnel.xlsx")
    df.head()

index 설정

Name 컬럼을 인덱스로 설정

pd.pivot_table(df, index="Name")

df.pivot_table(index="Name")

멀티 인덱스 설정

df.pivot_table(index=["Name", "Rep", "Manager"])

멀티 인덱스 설정

df.pivot_table(index=["Manager", "Rep"])

values 설정

df.head()
df.pivot_table(index=["Manager", "Rep"], values="Price")

Price 컬럼 sum 연산 적용

df.pivot_table(index=["Manager", "Rep"], values="Price", aggfunc=np.sum)
df.pivot_table(index=["Manager", "Rep"], values="Price", aggfunc=[np.sum, len])

columns 설정

df.head()

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개 이상 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) 추가

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

crime_raw_data.head()
crime_station = crime_raw_data.pivot_table(
crime_raw_data,
index="구분",
columns=["죄종", "발생검거"],
aggfunc=[np.sum])
crime_station.head()
crime_station.columns # Multiindex
crime_station["sum", "건수", "강도", "검거"][:5]
crime_station.columns = crime_station.columns.droplevel([0, 1]) # 다중 컬럼에서 특정 컬럼 제거
crime_station.columns
crime_station.head()
crime_station.index

  • 현재 index는 경찰서 이름으로 되어 있습니다
  • 경찰서 이름으로 구 이름을 알아내야 한다

4. Python 모듈 설치

pip 명령

  • python의 공식 모듈 관리자
  • pip list
  • pip install module_name
  • pip uninstall module_name
  • mac(M1)

!pip list

get_ipython().system("pip list")

conda 명령

  • conda list
  • conda install module_name
  • conda uninstall module_name
  • conda install -c channel_name module_name
    • 지정된 배포 채널에서 모듈 설치
  • Windows, mac(intel)

5. Google Maps API 설치

구글 계정

AIzaSyDmJzezN3W6GDp3ud_w8mRF_CcGRjg1Vns

Windows, mac(intel)

  • conda install -c conda-forge googlemaps

mac(M1)

  • pip install googlemaps
    import googlemaps
    gmaps_key = "AIzaSyC0rNHG0J4CCtvHUWhbw8pq4bu7v1UJBdo"
    gmaps = googlemaps.Client(key=gmaps_key)
    gmaps.geocode("서울영등포경찰서", language="ko")

Python 반복문

간단한 for문 예제

for n in [1, 2, 3, 4]:
print("Number is", n)

조금 복잡한 for문 예제

for n in range(0, 10):
print(n ** 2)

위 코드를 한 줄로 : list comprehension

[n ** 2 for n in range(0, 10)]

Pandas에 잘 맞춰진 반복문용 명령 iterrows()

  • Pandas 데이터 프레임은 대부분 2차원
  • 이럴 때 for문을 사용하면, n번째라는 지정을 반복해서 가독률이 떨어짐
  • Pandas 데이터 프레임으로 반복문을 만들때 itterows() 옵션을 사용하면 편함
  • 받을 때, 인덱스와 내용으로 나누어 받는 것만 주의

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

import googlemaps
gmaps_key = "AIzaSyC0rNHG0J4CCtvHUWhbw8pq4bu7v1UJBdo"
gmaps = googlemaps.Client(key=gmaps_key)
gmaps.geocode("서울영등포경찰서", language="ko") # 단순 테스트 코드
tmp = gmaps.geocode("서울영등포경찰서", language="ko")
len(tmp)
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]
crime_station.head()

  • 구별, lat, lng 컬럼
    crime_station["구별"] = np.nan
    crime_station["lat"] = np.nan
    crime_station["lng"] = np.nan
    crime_station.head()
  • 경찰서 이름에서 소속된 구이름 얻기
  • 구이름과 위도 경도 정보를 저장할 준비
  • 반복문을 이용해서 위 표의 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 

crime_station.head()
crime_station.columns.get_level_values(0)[2] + crime_station.columns.get_level_values(1)[2]
len(crime_station.columns.get_level_values(0))
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
tmp, len(tmp), len(crime_station.columns.get_level_values(0))
crime_station.columns = tmp
crime_station.head()

데이터 저장

crime_station.to_csv("../data/02. crime_in_Seuol_raw.csv", sep=",", encoding="utf-8")
pd.read_csv("../data/02. crime_in_Seoul_raw.csv").head(2)

7. 구별 데이터로 정리

crime_anal_station = pd.read_csv(
"../data/02. crime_in_Seoul_raw.csv", index_col=0, encoding="utf-8") # index_col "구분"을 인덱스 컬럼으로 설정
crime_anal_station.head()
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()
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()

컬럼 이름 변경

crime_anal_gu.rename(columns={"강간발생": "강간", "강도발생": "강도", "살인발생": "살인", "절도발생": "절도", "폭력발생": "폭력"},
inplace=True)
crime_anal_gu.head()

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

살인은 다른 범죄와 비교했을 때 건수는 적지만, 매우 중대한 범죄

시각화 했을 때, 눈에 띄지 않을 수 있다

crime_anal_gu.head()

정규화 : 최고값은 1, 최소값은 0

crime_anal_gu["강도"] / crime_anal_gu["강도"].max()
col = ["살인", "강도", "강간", "절도", "폭력"]
crime_anal_norm = crime_anal_gu[col] / crime_anal_gu[col].max()
crime_anal_norm.head()
crime_anal_gu.head(1)

검거율 추가

col2 = ["강간검거율", "강도검거율", "살인검거율", "절도검거율", "폭력검거율"]
crime_anal_norm[col2] = crime_anal_gu[col2]
crime_anal_norm.head()

구별 CCTV 자료에서 인구수와 CCTV수 추가

result_CCTV = pd.read_csv("../data/01. CCTV_result.csv", index_col="구별", encoding="utf-8")
result_CCTV.head()
crime_anal_norm[["인구수", "CCTV"]] = result_CCTV[["인구수", "소계"]]
crime_anal_norm.head()

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

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

crime_anal_norm.head()

np.mean()

import numpy as np
np.array([0.357143, 1.000000, 1.000000, 0.977118, 0.733773])
np.mean(np.array([0.357143, 1.000000, 1.000000, 0.977118, 0.733773]))
np.array(
[[0.357143, 1.000000, 1.000000, 0.977118, 0.733773],
[0.285714, 0.358974, 0.310078, 0.477799, 0.463880]]
)
np.mean(np.array(
[[0.357143, 1.000000, 1.000000, 0.977118, 0.733773],
[0.285714, 0.358974, 0.310078, 0.477799, 0.463880]]
), axis=1) # axis=1 행, axis=0 열
np.mean(np.array(
[[0.357143, 1.000000, 1.000000, 0.977118, 0.733773],
[0.285714, 0.358974, 0.310078, 0.477799, 0.463880]]

), axis=0) # axis=1 행, axis=0 열

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

col = ["강간검거율", "강도검거율", "살인검거율", "절도검거율", "폭력검거율"]
crime_anal_norm["검거"] = np.mean(crime_anal_norm[col], axis=1) # axis=1 행을 따라서 연산하는 옵션
crime_anal_norm.head()
crime_anal_norm
crime_anal_norm = pd.read_csv("../data/02. crime_in_Seoul_final.csv", index_col=0, encoding="utf-8")

crime_anal_norm

Seaborn

!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="Malgun Gothic") # Windows: Malgun Gothic / Mac: Arial Unicode MS

%matplotlib inline

get_ipython().run_line_magic("matplotlib", "inline")

예제1: seaborn 기초

np.linspace(0, 14, 100)
x = np.linspace(0, 14, 100)
y1 = np.sin(x)
y2 = 2 np.sin(x + 0.5)
y3 = 3
np.sin(x + 1.0)
y4 = 4 * np.sin(x + 1.5)
plt.figure(figsize=(10, 6))
plt.plot(x, y1, x, y2, x, y3, x, y4)
plt.show()

sns.set_style()

"white", "whitegrid", "dark", "darkgrid"

sns.set_style("white")
plt.figure(figsize=(10, 6))
plt.plot(x, y1, x, y2, x, y3, x, y4)
plt.show()

sns.set_style()

sns.set_style("dark")
plt.figure(figsize=(10, 6))
plt.plot(x, y1, x, y2, x, y3, x, y4)
plt.show()

sns.set_style()

sns.set_style("whitegrid")
plt.figure(figsize=(10, 6))
plt.plot(x, y1, x, y2, x, y3, x, y4)
plt.show()

sns.set_style()

sns.set_style("darkgrid")
plt.figure(figsize=(10, 6))
plt.plot(x, y1, x, y2, x, y3, x, y4)
plt.show()

예제2: seaborn tips data

  • boxplot
  • swarmplot
  • lmplot
    tips = sns.load_dataset("tips")
    tips
    tips.info()

boxplot

plt.figure(figsize=(8, 6))
sns.boxplot(x=tips["total_bill"])
plt.show()
tips["day"].unique()

boxplot

plt.figure(figsize=(8, 6))
sns.boxplot(x="day", y="total_bill", data=tips)
plt.show()
tips.head(2)

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

swarmplot

color: 0~1 사이 검은색부터 흰색 사이 값을 조절

plt.figure(figsize=(8, 6))
sns.swarmplot(x="day", y="total_bill", data=tips, color="0.5")
plt.show()

boxplot with swarmplot

plt.figure(figsize=(8, 6))
sns.boxplot(x="day", y="total_bill", data=tips)
sns.swarmplot(x="day", y="total_bill", data=tips, color="0.25")
plt.show()
tips

lmplot: total_bil과 tip 사이 관계 파악

sns.set_style("darkgrid")
sns.lmplot(x="total_bill", y="tip", data=tips, height=7) # size => height
plt.show()
tips

hue option

sns.set_style("darkgrid")
sns.lmplot(x="total_bill", y="tip", data=tips, height=7, hue="smoker")
plt.show()

예제3: flights data

  • heatmap
    flights = sns.load_dataset("flights")
    flights.head()
    flights.info()

pivot

index, columns, values

flights = flights.pivot(index="month", columns="year", values="passengers")
flights.head()

heatmap

plt.figure(figsize=(10, 8))
sns.heatmap(data=flights, annot=True, fmt="d") # annot=True 데이터 값 표시, fmt="d" 정수형 표현
plt.show()

colormap

plt.figure(figsize=(10, 8))
sns.heatmap(flights, annot=True, fmt="d", cmap="YlGnBu")
plt.show()

예제4: iris data

  • pairplot
    iris = sns.load_dataset("iris")
    iris.tail()

pairplot

sns.set_style("ticks")
sns.pairplot(iris)
plt.show()
iris.head(2)
iris["species"].unique()

hue option

sns.pairplot(iris, hue="species")
plt.show()

원하는 컬럼만 pairplot

sns.pairplot(iris,
x_vars=["sepal_width", "sepal_length"],
y_vars=["petal_width", "petal_length"])
plt.show()

예제5: anscombe data

  • lmplot
    anscombe = sns.load_dataset("anscombe")
    anscombe.tail()
    anscombe["dataset"].unique()
    sns.set_style("darkgrid")
    sns.lmplot(x="x", y="y", data=anscombe.query("dataset == 'I'"), ci=None, height=7) # ci 신뢰구간 선택, None 옵션은 신뢰구간 영역 보이는 옵션을 끄는 것
    plt.show()
    sns.set_style("darkgrid")
    sns.lmplot(x="x", y="y", data=anscombe.query("dataset == 'I'"), ci=None, height=7, scatter_kws={"s": 80}) # 마커사이즈 변경

order option

sns.set_style("darkgrid")
sns.lmplot(
x="x",
y="y",
data=anscombe.query("dataset == 'II'"), # 2차식
order=1, # 차수에 따라 옵션 변경
ci=None,
height=7,
scatter_kws={"s": 80}) # ci 신뢰구간 선택
plt.show()

order option

sns.set_style("darkgrid")
sns.lmplot(
x="x",
y="y",
data=anscombe.query("dataset == 'II'"),
order=2, # 차수에 따라 옵션 변경
ci=None,
height=7,
scatter_kws={"s": 80}) # ci 신뢰구간 선택
plt.show()

outlier

sns.set_style("darkgrid")
sns.lmplot(
x="x",
y="y",
data=anscombe.query("dataset == 'III'"), # 아웃라이어 있는 데이터
ci=None,
height=7,
scatter_kws={"s": 80}) # ci 신뢰구간 선택
plt.show()

outlier

sns.set_style("darkgrid")
sns.lmplot(
x="x",
y="y",
data=anscombe.query("dataset == 'III'"),
robust=True, # 원본에서 많이 떨어진 데이터는 없는 셈 친다
ci=None,
height=7,
scatter_kws={"s": 80}) # ci 신뢰구간 선택

plt.show()

9. 서울시 범죄현황 데이터 시각화

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="Malgun Gothic") # Windows: Malgun Gothic Mac: Arial Unicode MS
crime_anal_norm.head()

pairplot 강도, 살인, 폭력에 대한 상관관계 확인

해석1. 폭력 사건이 살인으로 이어지는 경우가 강도 사건이 살인으로 이어지는 것 보다 더 많다.

해석2. 강도와 폭력은 상관관계가 매우 높다.

sns.pairplot(data=crime_anal_norm, vars=["살인", "강도", "폭력"], kind="reg", height=3);
crime_anal_norm.head(1)

"인구수", "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()
crime_anal_norm.head(3)

검거율 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()

검거를 기준으로 정렬

검거율이 높은 곳을 보면, 강남3구가 없다.

drawGraph()
crime_anal_norm.head(1)

범죄발생 건수 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()

데이터 저장

crime_anal_norm.to_csv("../data/02. crime_in_Seoul_final.csv", sep=",", encoding="utf-8")


folium

Windows, mac(intel, m1)

!pip install folium

Windows

!pip install charset

!pip install charset-normalizer

import folium
import pandas as pd
import json

folium.Map()

location: tuple or list, default None
    Latitude and Longitude of Map (Northing, Easting).

m = folium.Map(location=[37.544564958079896, 127.05582307754338], zoom_start=14) # 0 ~ 18
m

save("path")

m.save("./folium.html")
!ls

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

folium.Marker()

  • 지도에 마커 생성
    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="Subway"
).add_to(m)

tooltip

folium.Marker(
location=[37.544564958079896, 127.05582307754338],
popup="Subway",
tooltip="성수역"
).add_to(m)

html

folium.Marker(
location=[37.54558642069953, 127.05729705810472],
popup="<a href='https://zero-base.co.kr/' target=_'blink'>제로베이스",
tooltip="Zerobase"
).add_to(m)

m

folium.Icon()

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="Subway",
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

folium.ClickForMarker()

  • 지도위에 마우스로 클릭했을 때 마커를 생성해줍니다
    m = folium.Map(
    location=[37.544564958079896, 127.05582307754338], # 성수역
    zoom_start=14,
    tiles="OpenStreetMap"
    ) # 0 ~ 18

m.add_child(folium.ClickForMarker(popup="ClickForMarker"))

folium.LatLngPopup()

  • 지도를 마우스로 클릭했을 때 위도 경도 정보를 반환해줍니다
    m = folium.Map(
    location=[37.544564958079896, 127.05582307754338], # 성수역
    zoom_start=14,
    tiles="OpenStreetMap"
    ) # 0 ~ 18

m.add_child(folium.LatLngPopup())

folium.Circle(), folium.CircleMarker()

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

folium.Choropleth

import json
state_data = pd.read_csv("../data/02. US_Unemployment_Oct2012.csv")
state_data.tail(2)
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

아파트 유형 지도 시각화

df.tail(2)
df.info()

NaN 데이터 제거

df = df.dropna()
df.info()
df = df.reset_index(drop=True)
df.tail(2)

del df["연번"] # "연번" 에 공백 하나가 붙어서 이를 표기해야 삭제됨.

df.columns

df["연번 "]

df = df.rename(columns={"연번 ": "연번", "분류 ": "분류"})
df.연번[:10]
del df["연번"]
df.tail(2)
df.위도[0]
df.describe()

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

reference


10. 지도시각화

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) 살인 사건

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="정규화된 살인 발생 건수",
)
my_map

(2) 성범죄

강남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

(3) 5대 범죄

강남구가 역시 포함되어 있다

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

(4) 인구 대비 범죄 발생 건수

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="정규화된 범죄 발생 건수",
)
my_map

경찰서별 검거현황과 구별 범죄발생 현황을 표현하기

  • 경찰서별 정보를 가지고 범죄발생과 함께 정리
    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)

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)
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_raw["범죄명"].unique()
    crime_loc_raw["장소"].unique()
    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()
    crime_loc_norm["종합"] = np.mean(crime_loc_norm, axis=1)
    crime_loc_norm.head()
    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()

profile
데이터 분석가

0개의 댓글