전국 아파트 분양가격 시각화 분석

lecture/practice
visualization
Author

agsu

Published

April 19, 2022

🚩전국 신규 민간 아파트 분양가격 동향

분양가란 처음 분양을 시작할 때의 가격을 말합니다. 이번 데이터 분석에서는 2013년부터 2019년도까지의 전국 신규 민간 아파트 분양가격 데이터를 사용하여 아파트 분양가를 연도, 지역 별로 분석해보면서 부동산 가격 변동 추세를 알아봅니다.

* 인프런, 전공 수업시간에 진행한 내용을 토대로 가설 설정과 해석, 시각화 디자인을 재구성한 글입니다.

0. 라이브러리

import sys
print('python', sys.version)

import numpy as np
print('numpy', np.__version__)

import pandas as pd
print('pandas', pd.__version__)

import matplotlib as mpl
print('matplotlib', mpl.__version__)

import matplotlib.pyplot as plt

import seaborn as sns
print('pandas', sns.__version__)

import matplotlib.pyplot as plt
plt.rc("font", family="Malgun Gothic", size=15) 

# 결과 확인을 용이하게 하기 위한 코드
from IPython.core.interactiveshell import InteractiveShell
InteractiveShell.ast_node_interactivity = 'all'

# 경고 메시지 무시.
import warnings
warnings.filterwarnings("ignore")
python 3.7.3 (default, Mar 27 2019, 17:13:21) [MSC v.1915 64 bit (AMD64)]
numpy 1.21.6
pandas 1.2.0
matplotlib 3.3.2
pandas 0.11.1



1. 데이터 로드

데이터: [공공데이터 포털] 주택도시보증공사_전국 평균 분양가격

# 1) 2015.10 ~ 2019.12 
df_last = pd.read_csv("3w/data/전국평균 분양가격 (2015년10월~2019년12월).csv", encoding="cp949") # 한글이 깨지지 않게 하려면 인코딩을 해줘야 한다.

# 2) 2013.12 ~ 2015.8 
df_first = pd.read_csv("3w/data/전국평균 분양가격 (2013년12월~2015년8월).csv", encoding="cp949") # 한글이 깨지지 않게 하려면 인코딩을 해줘야 한다.
print("== shape ==")
print("df_last", df_last.shape)
print("df_first", df_first.shape)

print()
print("== head ==")
df_last.head() 
df_first.head()
== shape ==
df_last (4335, 5)
df_first (17, 22)

== head ==
지역명 규모구분 연도 분양가격(㎡)
0 서울 전체 2015 10 5841
1 서울 전용면적 60㎡이하 2015 10 5652
2 서울 전용면적 60㎡초과 85㎡이하 2015 10 5882
3 서울 전용면적 85㎡초과 102㎡이하 2015 10 5721
4 서울 전용면적 102㎡초과 2015 10 5879
지역 2013년12월 2014년1월 2014년2월 2014년3월 2014년4월 2014년5월 2014년6월 2014년7월 2014년8월 ... 2014년11월 2014년12월 2015년1월 2015년2월 2015년3월 2015년4월 2015년5월 2015년6월 2015년7월 2015년8월
0 서울 18189 17925 17925 18016 18098 19446 18867 18742 19274 ... 20242 20269 20670 20670 19415 18842 18367 18374 18152 18443
1 부산 8111 8111 9078 8965 9402 9501 9453 9457 9411 ... 9208 9208 9204 9235 9279 9327 9345 9515 9559 9581
2 대구 8080 8080 8077 8101 8267 8274 8360 8360 8370 ... 8439 8253 8327 8416 8441 8446 8568 8542 8542 8795
3 인천 10204 10204 10408 10408 10000 9844 10058 9974 9973 ... 10020 10020 10017 9876 9876 9938 10551 10443 10443 10449
4 광주 6098 7326 7611 7346 7346 7523 7659 7612 7622 ... 7752 7748 7752 7756 7861 7914 7877 7881 8089 8231

5 rows × 22 columns

2013 ~ 2015년 데이터와 2015 ~ 2019년 데이터가 서로 다른 형태로 되어있는 것을 볼 수 있습니다. 먼저, 같은 형태로 전처리 한 후에 데이터셋을 병합하여 분석을 진행하도록 하겠습니다.



2. 데이터 전처리

2.1 규모구분 => 전용면적 컬럼 변경

규모구분 컬럼에 전용면적이라는 문구가 공통적으로 들어가고, 규모구분 보다 전용 면적이 더 직관적이므로 새로운 ‘전용면적’ 이라는 컬럼을 생성합니다. 또, 기존 규모구분의 데이터에서 전용면적, 초과, 이하 와 같은 문구를 빼고 간결하게 만듭니다.

df_last["전용면적"] = df_last["규모구분"].str.replace("전용면적", "")
df_last["전용면적"] = df_last["전용면적"].str.replace("초과", "~")
df_last["전용면적"] = df_last["전용면적"].str.replace("이하", "")
df_last["전용면적"] = df_last["전용면적"].str.replace(" ", "")
df_last.head()
지역명 규모구분 연도 분양가격(㎡) 분양가격 평당분양가격 전용면적
0 서울 전체 2015 10 5841 5841.0 19275.3 전체
1 서울 전용면적 60㎡이하 2015 10 5652 5652.0 18651.6 60㎡
2 서울 전용면적 60㎡초과 85㎡이하 2015 10 5882 5882.0 19410.6 60㎡~85㎡
3 서울 전용면적 85㎡초과 102㎡이하 2015 10 5721 5721.0 18879.3 85㎡~102㎡
4 서울 전용면적 102㎡초과 2015 10 5879 5879.0 19400.7 102㎡~


2.2 데이터 병합

2.2.1 분양가격 데이터 타입 변경

df_last 데이터에서 분양가격이 object 데이터 타입으로 되어있습니다. 계산을 위해서는 float형으로 바꿔주어야 합니다.

def to_float(x): 
    if x!=x: # 결측값 검사
        return np.nan 
    
    x = x.replace(",", "")
    if x.isdigit(): 
        return float(x) 
    
    return np.nan 

df_last["분양가격"] = df_last['분양가격(㎡)'].map(to_float)
df_last["분양가격"].dtypes
dtype('float64')


2.2.2 df_last: 평당 분양 가격 컬럼 생성

먼저, df_first 데이터는 평당 분양가격 기준으로 이루어져있는데, df_last 데이터는 ㎡당 분양가격으로 들어가있기 때문에 분양 가격을 평당 기준으로 보기 위해 3.3을 곱해서 df_last 데이터에 평당분양가격 컬럼을 생성하도록 합니다.

df_last['평당분양가격'] = df_last['분양가격'] * 3.3 
df_last.head()
지역명 규모구분 연도 분양가격(㎡) 분양가격 평당분양가격 전용면적
0 서울 전체 2015 10 5841 5841.0 19275.3 전체
1 서울 전용면적 60㎡이하 2015 10 5652 5652.0 18651.6 60㎡
2 서울 전용면적 60㎡초과 85㎡이하 2015 10 5882 5882.0 19410.6 60㎡~85㎡
3 서울 전용면적 85㎡초과 102㎡이하 2015 10 5721 5721.0 18879.3 85㎡~102㎡
4 서울 전용면적 102㎡초과 2015 10 5879 5879.0 19400.7 102㎡~


2.2.3 df_first: 형태 변경 (melt)

df_last와 같은 형태가 되도록 melt를 사용하여 변경해줍니다. 컬럼의 이름도 같게 만들어주도록 합니다.

df_first_melt = df_first.melt(id_vars='지역', var_name="날짜", value_name='평당분양가격').rename(columns={"지역": "지역명"})
df_first_melt.head()
지역명 날짜 평당분양가격
0 서울 2013년12월 18189
1 부산 2013년12월 8111
2 대구 2013년12월 8080
3 인천 2013년12월 10204
4 광주 2013년12월 6098


2.2.4 df_first_melt: 날짜 컬럼 => 연도, 월 컬럼으로 분리하기

def parse_year(x):
    year = x.split("년")[0]
    return int(year) 

def parse_month(x):
    year = x.split("년")[1].replace("월", "")
    return int(year) 

df_first_melt['연도'] = df_first_melt['날짜'].map(parse_year)
df_first_melt['월'] = df_first_melt['날짜'].map(parse_month)
df_first_melt.head()
지역명 날짜 평당분양가격 연도
0 서울 2013년12월 18189 2013 12
1 부산 2013년12월 8111 2013 12
2 대구 2013년12월 8080 2013 12
3 인천 2013년12월 10204 2013 12
4 광주 2013년12월 6098 2013 12


2.2.5 df_last + df_first_melt 병합 (concat)

df_last와 df_fist_melt 에 공통적으로 있는 column 추출하기

cols = df_last.columns.intersection(df_first_melt.columns) 
cols
Index(['지역명', '연도', '월', '평당분양가격'], dtype='object')

공통적으로 있는 컬럼만 뽑아 df_first_prepare, df_last_prepare 생성

df_first_prepare = df_first_melt[cols]
df_first.head() 

df_last_prepare = df_last.loc[df_last['전용면적']=='전체', cols].copy() 
df_last_prepare.head() 
지역 2013년12월 2014년1월 2014년2월 2014년3월 2014년4월 2014년5월 2014년6월 2014년7월 2014년8월 ... 2014년11월 2014년12월 2015년1월 2015년2월 2015년3월 2015년4월 2015년5월 2015년6월 2015년7월 2015년8월
0 서울 18189 17925 17925 18016 18098 19446 18867 18742 19274 ... 20242 20269 20670 20670 19415 18842 18367 18374 18152 18443
1 부산 8111 8111 9078 8965 9402 9501 9453 9457 9411 ... 9208 9208 9204 9235 9279 9327 9345 9515 9559 9581
2 대구 8080 8080 8077 8101 8267 8274 8360 8360 8370 ... 8439 8253 8327 8416 8441 8446 8568 8542 8542 8795
3 인천 10204 10204 10408 10408 10000 9844 10058 9974 9973 ... 10020 10020 10017 9876 9876 9938 10551 10443 10443 10449
4 광주 6098 7326 7611 7346 7346 7523 7659 7612 7622 ... 7752 7748 7752 7756 7861 7914 7877 7881 8089 8231

5 rows × 22 columns

지역명 연도 평당분양가격
0 서울 2015 10 19275.3
5 인천 2015 10 10437.9
10 경기 2015 10 10355.4
15 부산 2015 10 10269.6
20 대구 2015 10 8850.6

df_first_prepare + df_last_prepare 합치기

df = pd.concat([df_first_prepare, df_last_prepare])
df.head()
지역명 연도 평당분양가격
0 서울 2013 12 18189.0
1 부산 2013 12 8111.0
2 대구 2013 12 8080.0
3 인천 2013 12 10204.0
4 광주 2013 12 6098.0

2013년도부터 2019년도까지의 데이터를 같은 형태로 모두 합쳤습니다. 이제 본격적으로 전국의 아파트 분양가 시각화 분석을 진행해보도록 하겠습니다.



3. 병합된 데이터 확인

print("df.shape: ", df.shape)
print()
print("df.info(): ", df.info())
print()
print("== 결측값 ==")
print(df.isnull().sum())
df.shape:  (1224, 4)

<class 'pandas.core.frame.DataFrame'>
Int64Index: 1224 entries, 0 to 4330
Data columns (total 4 columns):
 #   Column  Non-Null Count  Dtype  
---  ------  --------------  -----  
 0   지역명     1224 non-null   object 
 1   연도      1224 non-null   int64  
 2   월       1224 non-null   int64  
 3   평당분양가격  1215 non-null   float64
dtypes: float64(1), int64(2), object(1)
memory usage: 47.8+ KB
df.info():  None

== 결측값 ==
지역명       0
연도        0
월         0
평당분양가격    9
dtype: int64

평당분양가격에 결측값이 9개 있는 것을 확인하였습니다. 시각화 정보전달의 목적이므로 결측값 처리는 진행하지 않고, 어떤 데이터에 결측값이 있는지 알아보겠습니다.

df[df['평당분양가격'].isnull()]
지역명 연도 평당분양가격
3265 울산 2018 12 NaN
3350 울산 2019 1 NaN
3435 울산 2019 2 NaN
3520 울산 2019 3 NaN
3605 울산 2019 4 NaN
3690 울산 2019 5 NaN
3775 울산 2019 6 NaN
3860 울산 2019 7 NaN
3945 울산 2019 8 NaN

울산 지역에서 2018년도부터 데이터가 존재하지 않는 것으로 보입니다. 이 데이터셋(2013~2019)에서 울산 지역의 최신 데이터는 없는 것을 감안하고 분석을 진행하도록 합니다.



4. 가설 설정

1 ) 연도별 분양가격 동향 - 지속적으로 분양가격이 증가 할 것이다.
2 ) 지역별 분양가격 - 수도권 근방의 분양가격이 높을 것이다.
3 ) 지역별 연도별 분양가격 - 특정 지역의 연도별 분양가격이 증가할 것이다.

먼저 연도별로 분양가격을 분석해보고 분양가격의 추이를 살펴봅니다. 그 다음 지역 별로 분양가격을 분석하여 각 지역의 평균 분양가격을 파악하고, 어느 지역의 분양가격이 높고 낮은지 확인합니다. 마지막으로 지역별 연도별로 시각화를 진행하고, 특정 지역의 어느 연도에서 증가 추세를 띄는지, 동향이 어떻게 되는지 분석합니다.



5. 데이터 시각화 분석

5.1 연도별 분양가격 분석

5.1.1 연도별 평당분양가격

연도 별 평당분양가격의 평균을 그려봅니다.

  • barplot + pointplot
_=plt.figure(figsize=(8, 5))

_=plt.title("연도별 평당 분양가격 평균 (단위: 천원)", fontsize=20, pad=10)
bars=sns.barplot(data=df, x='연도', y='평당분양가격', ci=None, palette=sns.color_palette("Pastel1"))
_=sns.pointplot(data=df, x="연도", y="평당분양가격", ci=None, color='#bfaeae')

for i, b in enumerate(bars.patches):
    _=plt.text(b.get_x()+b.get_width()*(1/2),b.get_height()+500, \
            round(b.get_height()),ha='center',fontsize=15, color='grey')

_=plt.ylim(0, 13500)

2013년도부터 2019년도까지 평당 분양가격의 평균을 시각화해보았더니 지속적으로 증가하는 추세를 보였습니다.

  • boxplot
_=plt.figure(figsize=(8, 5))

_=plt.title("연도별 평당 분양가격 (단위: 천원)", fontsize=20, pad=10)
_=sns.boxplot(data=df, x='연도', y='평당분양가격', palette=sns.color_palette("Pastel1"))

boxplot으로는 전체 값의 분포와 이상치를 확인할 수 있습니다. 연도별로 IQR 방식의 이상치로 판단되는 값이 조금씩 있는 것으로 보여집니다. 그리고 중앙값이나 사분위수 등 모두 연도별로 증가하는 것을 볼 수 있습니다.

  • violinplot
_=plt.figure(figsize=(8, 5))

_=plt.title("연도별 평당 분양가격 (단위: 천원)", fontsize=20, pad=10)
_=sns.violinplot(data=df, x='연도', y='평당분양가격', palette=sns.color_palette("Pastel1"))

violinplot을 사용하여 연도별 분양가격의 빈도를 파악할 수 있고, 마찬가지로 연도별로 증가하는 추세를 보이고 있음을 확인할 수 있습니다.


5.1.2 연도별 월별 평당분양가격 평균

이번엔 연도별 월별 평당분양가격의 평균을 시각화하여 달마다 어떤 추세가 있는지 확인해보겠습니다.

_=plt.figure(figsize=(15, 7))

_=plt.title("연도별 월별 평당 분양가격 평균", fontsize=20, pad=10)
_=sns.barplot(data=df, x='연도', y='평당분양가격', hue='월', ci=None, palette=sns.color_palette("Accent"))

_=plt.legend(loc="upper left")

_=plt.ylim(0, 13000)

연도별 월별로 평당 분양가격의 평균 시각화를 그려보았을 때, 대체적으로 연초보다는 연말에 분양가격이 높은 것으로 보입니다.


5.2 지역별 분양가격 분석

5.2.1 지역별 평당 분양가격

  • baplot
_=plt.figure(figsize=(17, 6))

_=plt.title("지역별 평당 분양가격 평균 (단위: 천원)", fontsize=20, pad=10)
bars=sns.barplot(data=df, x='지역명', y='평당분양가격', ci=None, palette=sns.color_palette("turbo"))

for i, b in enumerate(bars.patches):
    _=plt.text(b.get_x()+b.get_width()*(1/2),b.get_height()+500, \
            round(b.get_height()),ha='center',fontsize=15, color='#706363')

_=plt.ylim(0, 25000)

서울의 분양가가 압도적으로 높은 것을 확인할 수 있습니다. 더 직관적으로 지역별 집값을 확인해보기 위해 내림차순으로 sorting 하여 그려보도록 하겠습니다.

_=plt.figure(figsize=(17, 6))

# order에 사용 할 시리즈 생성 
sr1_order = df.groupby("지역명")["평당분양가격"].mean().sort_values(ascending=False)

_=plt.title("지역별 평당 분양가격 평균 (단위: 천원)", fontsize=20, pad=10)
bars=sns.barplot(data=df, x='지역명', y='평당분양가격', ci=None, palette=sns.color_palette("turbo"), order=sr1_order.index)

for i, b in enumerate(bars.patches):
    _=plt.text(b.get_x()+b.get_width()*(1/2),b.get_height()+500, \
            round(b.get_height()),ha='center',fontsize=15, color='#706363')

_=plt.ylim(0, 25000)

서울-경기-부산-인천 등의 순으로 분양가가 높고, 전남-전북-충북 지역의 분양가가 가장 낮은 것으로 나타납니다.

  • boxplot & violinplot
fig = plt.figure(figsize=(17, 15))
ax1, ax2 = fig.subplots(2, 1)

_=plt.suptitle("지역별 평당 분양가격 (단위: 천원)", fontsize=25)

_=sns.boxplot(data=df, x='지역명', y='평당분양가격', palette=sns.color_palette("turbo"), ax=ax1)
_=sns.violinplot(data=df, x='지역명', y='평당분양가격', palette=sns.color_palette("turbo"), ax=ax2)

_=ax1.set_title("boxplot", pad=10, fontsize=20)
_=ax2.set_title("violinplot", pad=10, fontsize=20)

fig.tight_layout()

지역별로 시각화를 해보니, 이상치는 얼마 나타나지 않았습니다. 연도별 시각화에서 나온 이상치는 서울의 분양가였던 것으로 해석할 수 있습니다. 뒤에서 연도별 지역별로 그려보고 직접 확인해보도록 하겠습니다.


5.3 지역별 연도별 분양가격 분석

5.3.1 지역별 연도별 평당 분양가격 평균

  • heatmap
# 지역별 연도별 pivot table 생성 
df_region_year = df.pivot_table(index="연도", columns='지역명', values='평당분양가격').round().astype(int)

fig = plt.figure(figsize=(20, 17), dpi=100)
ax1, ax2 = fig.subplots(2, 1)

_=sns.heatmap(df_region_year, annot=True, fmt=".0f", cmap="Purples", ax=ax1)
_=sns.heatmap(df_region_year.T, annot=True, fmt=".0f", cmap="Purples", ax=ax2)

_=ax1.set_title("지역별 연도별 평당 분양가격 평균", fontsize=20, pad=10)
_=ax2.set_title("연도별 지역별 평당 분양가격 평균", fontsize=20, pad=10)

fig.tight_layout()

위에서 연도 별 boxplot을 그려봤을 때 이상치로 나온 값들이 서울 분양가인 것으로 보입니다. 서울이 대체적으로 다른 지역의 두배 이상의 값인 것을 확인할 수 있습니다.

  • lineplot
_=plt.figure(figsize=(13, 8))

_=sns.lineplot(data=df, x="연도", y="평당분양가격", hue="지역명", ci=None, marker='o')
_=plt.legend(bbox_to_anchor=(1.02, 1), loc=2)

_=plt.title("연도별 지역별 평당 분양가격 (단위: 천원)", pad=10, fontsize=20)

lineplot으로 확인해 봐도 서울의 분양가격이 월등히 높은 것을 볼 수 있습니다. 또, 증가 폭도 높은 것으로 확인됩니다. 따라서 다음으로는 서울 지역만 따로 뽑아 시각화 해보도록 하겠습니다.


5.3.2 서울 분양가격 분석

df_seoul = df[df['지역명']=='서울'].copy()
df_seoul.shape
(72, 4)

서울 지역만 따로 뽑아 df_seoul 변수에 넣어주었습니다.

  • barplot
_=plt.figure(figsize=(10, 5))

_=plt.title("연도별 평당분양가격 평균 (서울)", fontsize=20, pad=10)

bars=sns.barplot(data=df_seoul, x="연도", y="평당분양가격", ci=None, palette=sns.color_palette("magma_r"))

for i, b in enumerate(bars.patches):
    _=plt.text(b.get_x()+b.get_width()*(1/2),b.get_height()+500, \
            round(b.get_height()),ha='center',fontsize=15, color='#706363')

_=plt.ylim(0, 29900)

연도 별로 증가하는 추세를 보입니다.

  • boxplot
_=plt.figure(figsize=(10, 5))

_=plt.title("연도별 평당분양가격 (서울)", fontsize=20, pad=10)

bars=sns.boxplot(data=df_seoul, x="연도", y="평당분양가격", palette=sns.color_palette("magma_r"))

서울지역의 연도별 boxplot을 그려보았을 때, 이상치는 존재하지 않습니다. 또, 값을 살펴보면, 위에서 전체 지역의 연도별 평당분양가격의 boxplot에서 이상치로 나왔던 값들이 서울 지역의 값들인 것을 직접 확인할 수 있습니다.



6. 분석결과 요약

4번에서 설정했던 가설을 토대로 시각화 분석 결과를 요약합니다.

1 ) 연도별 분양가격 동향 - 지속적으로 분양가격이 증가한다.
2 ) 지역별 분양가격 - 수도권 근방, (서울, 경기도, ..) 광역시 (대구, ..)의 분양가격이 높다. 특히 서울은 다른 지역의 2배 정도의 분양가임을 확인하였다.
3 ) 지역별 연도별 분양가격 - 대체적으로 모든 지역에서 연도마다 지속적으로 분양가가 증가해왔다.


전국의 분양가격 데이터로 시각화 분석을 진행하였습니다. 분양가격의 동향을 연도별, 지역별로 확인하였고, 어느 지역의 분양가가 높은지 정보를 얻을 수 있었습니다. 시각화 분석 과정에서 전체 지역에서는 이상치로 나왔던 값들이 서울 지역의 보편적인 값이라는 점을 확인하였고, 이를 통해 이상치를 섣불리 삭제해서는 안 되고, 값을 분석해 본 후에 처리해야 한다는 것을 배웠습니다.