왕초보 괴발자
(공공데이터) 파이썬 서울특별시 평균 월세 법정동 별 folium 시각화 본문
1. 이걸 할거에요
2. 순서에요
1) 공공데이터 "서울특별시_전월세가_2023.csv"파일을 준비해요.
2) 법정동별로 월세 평균을 구해요.
3) EPSG5179 에서 EPSG4326 으로 좌표변환을 해요.
4) folium으로 시각화해요.
3. 이렇게 해요
1) 공공데이터를 받아요. https://data.seoul.go.kr/dataList/OA-21276/S/1/datasetView.do
df_23 = pd.read_csv('서울특별시_전월세가_2023.csv', encoding='utf-8')
df_23.head()
접수년도 자치구코드 자치구명 법정동코드 법정동명 지번구분코드 지번구분 본번 부번 층 ... 보증금(만원) 임대료(만원) 건물명 건축년도 건물용도 계약기간 신규계약구분 갱신청구권사용 종전보증금 종전임대료
0 2023 11440 마포구 11000 노고산동 1.0 대지 1.0 1.0 8.0 ... 10000 40 \t(1-1)\t 1998.0 오피스텔 23.01~24.01 신규 NaN 0.0 0.0
1 2023 11440 마포구 11000 노고산동 1.0 대지 1.0 1.0 5.0 ... 10000 55 \t(1-1)\t 1998.0 오피스텔 23.02~24.02 갱신 NaN 10000.0 55.0
2 2023 11440 마포구 11000 노고산동 1.0 대지 1.0 1.0 9.0 ... 21000 0 \t(1-1)\t 1998.0 오피스텔 23.02~24.02 신규 NaN 0.0 NaN
3 2023 11170 용산구 11300 원효로2가 1.0 대지 1.0 0.0 9.0 ... 23900 0 \t(1)\t 1990.0 오피스텔 23.09~25.09 신규 NaN 0.0 NaN
4 2023 11440 마포구 11000 노고산동 1.0 대지 1.0 1.0 9.0 ... 22000 0 \t(1-1)\t 1998.0 오피스텔 NaN NaN NaN NaN NaN
2) 데이터가 어떤지 조금 살펴볼게요.
len(df_23['자치구명'].unique())
25
- 서울의 행정구 25개에 대한 정보가 전부 있어요.
df_23['자치구명'].value_counts(ascending=False).head(10)
송파구 46398
강남구 36943
강서구 36397
관악구 30862
강동구 30636
마포구 28013
서초구 27365
영등포구 25995
동작구 24368
광진구 23416
Name: 자치구명, dtype: int64
- 행정구 단위로 본다면 강남송파의 거래량이 가장 많네요.
df_23['법정동명'].value_counts(ascending=False)
봉천동 15757
신림동 13886
화곡동 12016
상계동 9642
신정동 8379
...
산림동 1
재동 1
궁정동 1
관훈동 1
북창동 1
Name: 법정동명, Length: 402, dtype: int64
- 법정동 단위로는 관악구에 있는 봉천동과 신림동의 거래량이 가장 많아요. 왜 그럴까요?
- 서울의 법정동은 467개인데, 데이터가 있는 법정동이 402개네요. 조사가 안되었거나, 거래가 없다는 거겠죠.
df_bong = df_23[df_23['자치구명'] == '관악구']
df_bong['건물용도'].value_counts(ascending=False)
단독다가구 17944
아파트 5889
연립다세대 4708
오피스텔 2321
Name: 건물용도, dtype: int64
- 관악구에는 저렴한 월세가 많아서 높은 거래량을 보이는 것으로 추측할 수 있겠네요.
df_bong = df_23[df_23['자치구명'] == '송파구']
df_bong['건물용도'].value_counts(ascending=False)
아파트 20968
연립다세대 15200
오피스텔 5602
단독다가구 4628
Name: 건물용도, dtype: int64
- 반면 송파구에는 아파트 대단지들이 많이 있기 때문에 아파트의 거래량이 많은 것을 볼 수 있어요.
3) 필요한 데이터만 남겨요.
- '건물용도'가 아파트면서, '전월세구분'이 월세인 경우도 있지만, 매우 비싸고 '월세방'과는 거리가 있기 때문에 제외해요.
df = df_23[['법정동명', '전월세구분', '임대료(만원)', '건물용도']]
df = df[df['건물용도'] != '아파트']
df = df[df['전월세구분'] == '월세']
df
법정동명 전월세구분 임대료(만원) 건물용도
0 노고산동 월세 40 오피스텔
1 노고산동 월세 55 오피스텔
5 방배동 월세 55 오피스텔
6 사당동 월세 22 오피스텔
7 노고산동 월세 30 오피스텔
... ... ... ... ...
545801 삼청동 월세 50 단독다가구
545802 대조동 월세 85 단독다가구
545807 자곡동 월세 50 단독다가구
545808 상도동 월세 100 단독다가구
545809 대치동 월세 150 단독다가구
df_avg = df.groupby('법정동명')['임대료(만원)'].mean().sort_values(ascending=False)
df_avg
법정동명
한남동 182.172691
신천동 173.900000
신문로2가 151.714286
압구정동 150.000000
의주로1가 149.461538
...
양평동1가 36.435294
토정동 35.000000
하중동 32.000000
을지로6가 27.666667
문래동1가 21.545455
Name: 임대료(만원), Length: 386, dtype: float64
- 402개였던 행이 386개로 줄었어요. '전세' 또는 '아파트'만 거래했던 법정동들도 있었다는 거에요.
- df_avg 데이터프레임은 잠시 놔두도록 해요.
4) 법정동 경계 데이터를 불러와요.
- 읍면동, 23년 7월 데이터를 다운받을게요. http://www.gisdeveloper.co.kr/?p=2332
df_geo = gpd.read_file('emd.shp', encoding='CP949')
df_geo = df_geo[df_geo['EMD_CD'].str.startswith('11')]
df_geo = df_geo.rename(columns={'EMD_KOR_NM': '법정동명'})
df_geo
- 다운 받은 파일은 dbf, shp, shx 라는 확장자인데, 변환하는 것은 shp파일이지만 셋 다 같은 폴더 경로에 있어야 해요.
- 11로 시작하는 법정동코드만 남겨서, 서울특별시의 법정동만 남게 해요.
EMD_CD EMD_ENG_NM 법정동명 geometry
0 11110101 Cheongun-dong 청운동 POLYGON ((953700.022 1954605.065, 953693.871 1...
1 11110102 Singyo-dong 신교동 POLYGON ((953233.465 1953996.984, 953235.183 1...
2 11110103 Gungjeong-dong 궁정동 POLYGON ((953560.228 1954257.466, 953561.190 1...
3 11110104 Hyoja-dong 효자동 POLYGON ((953519.843 1953890.785, 953518.489 1...
4 11110105 Changseong-dong 창성동 POLYGON ((953516.123 1953734.362, 953516.526 1...
... ... ... ... ...
462 11740106 Dunchon-dong 둔촌동 POLYGON ((969669.593 1948748.489, 969656.716 1...
463 11740107 Amsa-dong 암사동 POLYGON ((968514.203 1950677.234, 968505.336 1...
464 11740108 Seongnae-dong 성내동 POLYGON ((967686.073 1948534.011, 967685.029 1...
465 11740109 Cheonho-dong 천호동 POLYGON ((968336.280 1950222.697, 968337.437 1...
466 11740110 Gangil-dong 강일동 POLYGON ((970882.440 1951500.730, 970882.447 1...
- 서울특별시 법정동 개수 467개가 잘 남았네요.
df_mg = pd.merge(df_geo, df_avg, on='법정동명', how='outer')
df_mg = df_mg[['법정동명', '임대료(만원)', 'geometry']]
df_mg.sample(10)
- 아까 만들었던 df_avg와 병합을 하고, 필요한 컬럼만 남겨요. 데이터의 몇가지를 살펴볼게요.
법정동명 임대료(만원) geometry
15 도렴동 NaN POLYGON ((953680.961 1952885.050, 953683.242 1...
14 사직동 64.321429 POLYGON ((952745.849 1953334.909, 952758.791 1...
432 역삼동 77.668003 POLYGON ((960137.165 1945122.850, 960156.153 1...
0 청운동 147.823529 POLYGON ((953700.022 1954605.065, 953693.871 1...
315 봉원동 55.083333 POLYGON ((951341.687 1953070.323, 951347.456 1...
241 동소문동2가 57.375000 POLYGON ((956688.961 1954613.224, 956694.806 1...
391 당산동6가 54.898148 POLYGON ((947049.315 1948853.170, 947057.122 1...
13 내자동 NaN POLYGON ((953411.883 1953152.129, 953414.609 1...
411 상도동 47.055121 MULTIPOLYGON (((951797.594 1945502.652, 951798...
345 성산동 58.003104 POLYGON ((946907.590 1953016.814, 946918.338 1...
- 임대료가 집계되지 않은 법정동도 있고, 좌표가 '멀티폴리곤'인 데이터도 있어요. 멀티폴리곤의 예시는 다음과 같아요.
- 멀티폴리곤은 법정동명은 하나지만, 폴리곤 개수가 2개 이상이에요.
5) pyproj 라이브러리를 이용해서 EPSG5179에서 EPSG4326으로 좌표변환해요.
- 흔히 보던 우리나라의 좌표는 12X, 3X 인데, 지금 우리가 가진 데이터프레임은 90만, 190만이에요.
- 이는 우리나라에서 좌표를 측정할 때 EPSG5179 (UTM-K, GRS80)를 사용하지만, 세계지구좌표계는 EPSG4326 (WGS84)를 사용하기 때문이에요.
import pyproj
from shapely.ops import transform
epsg5179 = pyproj.Proj(init='epsg:5179')
epsg4326 = pyproj.Proj(init='epsg:4326')
def epsg5179_to_epsg4326(geometry):
return transform(lambda x, y: pyproj.transform(epsg5179, epsg4326, x, y), geometry)
df_mg['geometry'] = df_mg['geometry'].apply(epsg5179_to_epsg4326)
df_mg
법정동명 임대료(만원) geometry
0 청운동 147.823529 POLYGON ((126.97556 37.58968, 126.97549 37.589...
1 신교동 60.342105 POLYGON ((126.97031 37.58418, 126.97033 37.584...
2 궁정동 47.000000 POLYGON ((126.97400 37.58654, 126.97401 37.586...
3 효자동 85.500000 POLYGON ((126.97356 37.58323, 126.97355 37.582...
4 창성동 62.200000 POLYGON ((126.97353 37.58182, 126.97354 37.581...
... ... ... ...
462 둔촌동 48.271144 POLYGON ((127.15669 37.53756, 127.15654 37.537...
463 암사동 45.594828 POLYGON ((127.14353 37.55490, 127.14343 37.554...
464 성내동 47.324013 POLYGON ((127.13424 37.53556, 127.13423 37.535...
465 천호동 51.026956 POLYGON ((127.14153 37.55080, 127.14154 37.550...
466 강일동 49.440860 POLYGON ((127.17030 37.56240, 127.17030 37.562...
df_mg['임대료(만원)'] = df_mg['임대료(만원)'].round(1)
df_mg['임대료(만원)'] = df_mg['임대료(만원)'].fillna(0).astype(int)
df_mg['임대료(만원)'] = df_mg['임대료(만원)'].astype(int)
df_mg
- 추가로 임대료를 1의자리 숫자로 반올림하고, 시각화를 위해 NaN값을 0으로 바꾸고, 타입을 소수에서 정수로 바꿔줘요.
법정동명 임대료(만원) geometry
0 청운동 147 POLYGON ((126.97556 37.58968, 126.97549 37.589...
1 신교동 60 POLYGON ((126.97031 37.58418, 126.97033 37.584...
2 궁정동 47 POLYGON ((126.97400 37.58654, 126.97401 37.586...
3 효자동 85 POLYGON ((126.97356 37.58323, 126.97355 37.582...
4 창성동 62 POLYGON ((126.97353 37.58182, 126.97354 37.581...
... ... ... ...
462 둔촌동 48 POLYGON ((127.15669 37.53756, 127.15654 37.537...
463 암사동 45 POLYGON ((127.14353 37.55490, 127.14343 37.554...
464 성내동 47 POLYGON ((127.13424 37.53556, 127.13423 37.535...
465 천호동 51 POLYGON ((127.14153 37.55080, 127.14154 37.550...
466 강일동 49 POLYGON ((127.17030 37.56240, 127.17030 37.562...
6) folium 라이브러리를 이용해 시각화해요.
import folium
import branca # folium에 쓰일 꾸밈용 라이브러리에요.
m = folium.Map(location=[37.5665, 126.9780], zoom_start=12) # 서울을 중심으로 맵 생성
df_mg.crs = "EPSG:4326" # ValueError: Cannot transform naive geometries. Please set a crs on the object first. 오류 발생 시
# Define a linear colormap
colormap = branca.colormap.LinearColormap(
vmin=df_mg["임대료(만원)"].quantile(0.0),
vmax=df_mg["임대료(만원)"].quantile(1),
colors=["white","red"],
caption="임대료(만원)",
)
# Define a tooltip
tooltip = folium.GeoJsonTooltip(
fields=["법정동명", "임대료(만원)"],
aliases=["법정동명:", "임대료(만원):"],
localize=True,
sticky=False,
labels=True,
style="""
background-color: #F0EFEF;
border: 1px solid black;
border-radius: 3px;
box-shadow: 3px;
""",
max_width=800,
)
# Create a GeoJson layer
g = folium.GeoJson(
df_mg,
style_function=lambda x: {
"fillColor": colormap(x["properties"]["임대료(만원)"])
if x["properties"]["임대료(만원)"] is not None
else "transparent",
"color": "black",
"weight": 1, # 경계 두께
"fillOpacity": 0.6,
},
tooltip=tooltip,
).add_to(m)
# Add the colormap to the map
colormap.add_to(m)
m
- 이제 마우스를 갖다대면 법정동명과 임대료를 볼 수 있을거에요. :)
- 컬러맵 및 툴팁코드는 여기를 참조했어요. https://python-visualization.github.io/folium/latest/user_guide/geojson/geojson_popup_and_tooltip.html
'행정구역' 카테고리의 다른 글
파이썬 직방 원룸 크롤링, 서울 내 대학교 근처 원룸 folium 시각화 (0) | 2024.03.28 |
---|