Project/On campus

[Project] 한국 부동산 가격 예측

Doyun+ 2021. 6. 23. 00:13

Subject : Predicting real estate prices in Korea with various variables

Language : R

Data : ‘직방’ 데이터

  • train.csv : Apartment transaction data (1.6 million)
  • school.csv : Elementary, middle and high school information (1,100)
  • subways.csv : Subway information (400)

1. Data preprocessing

colnames(train) <- c("번호", "키", "아파트id", "도시", "거래년월", "거래일", "입주년도", "실평수",
                     "층", "위도", "경도", "법적주소", "주차용량", "세대수", "건축물수", "가장높은층",
                     "가장낮은층", "난방방법", "난방연료", "방id", "평수", "같은방id가구수", "방수", 
                     "화장실수", "입구구조", "실거래가", "실거래가로그", "평수앞자리",
                     "실평수1", "실평수대")
head(train)

colnames(subway) <- c("역id", "위도", "경도", "호선", "법적주소")
head(subway)

colnames(school) <- c("학교코드", "위도", "경도", "학교등급", "운영타입", "학교타입", "성", "설립일", "법적주소")
head(school)

# ------------------------------------------------------------------
# 변수 추가
# 실거래가 로그값 추가
train$실거래가로그 <- log(train$실거래가)

# 평수 앞자리 추가
area <- c()
for (a in train$평수) {
  if(nchar(as.integer(a)) == 2) {
    area <- c(area, str_sub(a, 1, 1))}
  if(nchar(as.integer(a)) == 3) {
    area <- c(area, str_sub(a, 1, 2))}
}

train$평수앞자리 <- area

# 실평수를 3.3 으로 나눈 값 추가
train$실평수1 <- train$실평수/3.3

# 실평수대 추가
area1 <- c()
for (a in train$실평수1) {
  if(nchar(as.integer(a)) == 1) {
    area1 <- c(area1, 1)}
  if(nchar(as.integer(a)) == 2) {
    area1 <- c(area1, paste0(str_sub(a, 1, 1), 0))}
}
train$실평수2 <- area1

# ------------------------------------------------------------------
# 파일 저장
write.csv(train, "직방.csv")

# ------------------------------------------------------------------
# 서울 구명 추가
s <- subset(train, train$도시 == 1)
gu <- c()
for (code in s$법적주소) {
  if(str_sub(code, 1, 5) == 11110) {
    gu <- c(gu, "종로구")}
  if(str_sub(code, 1, 5) == 11140) {
    gu <- c(gu, "중구")}
  if(str_sub(code, 1, 5) == 11170) {
    gu <- c(gu, "용산구")}
  if(str_sub(code, 1, 5) == 11200) {
    gu <- c(gu, "성동구")}
  if(str_sub(code, 1, 5) == 11215) {
    gu <- c(gu, "광진구")}
  if(str_sub(code, 1, 5) == 11230) {
    gu <- c(gu, "동대문구")}
  if(str_sub(code, 1, 5) == 11260) {
    gu <- c(gu, "중랑구")}
  if(str_sub(code, 1, 5) == 11290) {
    gu <- c(gu, "성북구")}
  if(str_sub(code, 1, 5) == 11305) {
    gu <- c(gu, "강북구")}
  if(str_sub(code, 1, 5) == 11320) {
    gu <- c(gu, "도봉구")}
  if(str_sub(code, 1, 5) == 11350) {
    gu <- c(gu, "노원구")}
  if(str_sub(code, 1, 5) == 11380) {
    gu <- c(gu, "은평구")}
  if(str_sub(code, 1, 5) == 11410) {
    gu <- c(gu, "서대문구")}
  if(str_sub(code, 1, 5) == 11440) {
    gu <- c(gu, "마포구")}
  if(str_sub(code, 1, 5) == 11470) {
    gu <- c(gu, "양천구")}
  if(str_sub(code, 1, 5) == 11500) {
    gu <- c(gu, "강서구")}
  if(str_sub(code, 1, 5) == 11530) {
    gu <- c(gu, "구로구")}
  if(str_sub(code, 1, 5) == 11545) {
    gu <- c(gu, "금천구")}
  if(str_sub(code, 1, 5) == 11560) {
    gu <- c(gu, "영등포구")}
  if(str_sub(code, 1, 5) == 11590) {
    gu <- c(gu, "동작구")}
  if(str_sub(code, 1, 5) == 11620) {
    gu <- c(gu, "관악구")}
  if(str_sub(code, 1, 5) == 11650) {
    gu <- c(gu, "서초구")}
  if(str_sub(code, 1, 5) == 11680) {
    gu <- c(gu, "강남구")}
  if(str_sub(code, 1, 5) == 11710) {
    gu <- c(gu, "송파구")}
  if(str_sub(code, 1, 5) == 11740) {
    gu <- c(gu, "강구")}
}
s$구명 <- gu
write.csv(s, "서울.csv")

# ------------------------------------------------------------------
# 용적률 추가
home$용적률 <- as.integer((home$실평수/home$평수)*100)

# ------------------------------------------------------------------
# 세대당 주차용량 추가
home$세대당주차 <- round(home$주차용량/home$세대수, digits = 2L)

# ------------------------------------------------------------------
# 서울 부산 분리
busan <- subset(train, train$도시 == 0)

# ------------------------------------------------------------------
# 결측치 확인
a <- list()
for (i in 1:length(train)){
  a[[i]] <- sum(is.na(train[i]))
}

# 주차용량 : 91813 / 가장높은층 : 9 / 가장낮은층 : 9 / 방수 : 691 / 화장실수 : 691 /
# 입구구조 : 13892 / 난방방법 : 2017 / 난방연료 : 9667 /
# 결측치 제거
home <- na.omit(train)
b <- list()
for (i in 1:length(home)){
  b[[i]] <- sum(is.na(home[i]))
}

# ------------------------------------------------------------------
  • NA값은 na.omit 함수를 이용하여 제거

> Added variable

  • 실거래가 자연로그 값
  • 실평수
  • 실평수대 (ex. 10평대, 20평대 …)
  • 지역구명
  • 용적률 (전용면적 / (배타적 사용 영역 + 공용 공간 영역))
  • 세대당 주차용량 (주차용량 / 세대수)

2. Data basic analysis and visualization

### 위의 시각화 부분 중 빠진 코드가 존재할 수 있습니다.
# 평균거래가
# 도시별 평균거래가
p <- home %>%
  group_by(도시) %>%
  summarise(평균거래가 = mean(실거래가))
  
ggplot(p, aes(x = 도시, y = 평균거래가, fill = 도시)) +
  geom_bar(stat = "identity")
  
# ------------------------------------------------------------------
# 난방방법별 평균거래가
p1 <- home %>%
  group_by(난방방법) %>%
  summarise(평균거래가 = mean(실거래가)) %>%
  arrange(desc(평균거래가))
  
ggplot(p1, aes(x = 난방방법, y = 평균거래가, fill = 난방방법)) +
  geom_bar(stat = "identity")
# district (지역난방) 방식이 가장 비싸다.

# ------------------------------------------------------------------
# 난방연료별 평균거래가
p2 <- home %>%
  group_by(난방연료) %>%
  summarise(평균거래가 = mean(실거래가)) %>%
  arrange(desc(평균거래가))
  
ggplot(p2, aes(x = 난방연료, y = 평균거래가, fill = 난방연료)) +
  geom_bar(stat = "identity")
# cogeneration 연료가 가장 비싸다.

# ------------------------------------------------------------------
# 입구구조별 평균거래가
p3 <- home %>%
  group_by(입구구조) %>%
  summarise(평균거래가 = mean(실거래가)) %>%
  arrange(desc(평균거래가))
  
ggplot(p3, aes(x = 입구구조, y = 평균거래가, fill = 입구구조)) +
  geom_bar(stat = "identity")
# stairway 구조가 가장 비싸다,

# ------------------------------------------------------------------
# 구별 평균거래가
p4 <- seoul %>%
  group_by(구명) %>%
  summarise(평균거래가 = mean(실거래가)) %>%
  top_n(5, wt = 평균거래가) %>%
  arrange(desc(평균거래가))
  
p5 <- seoul %>%
  group_by(구명) %>%
  summarise(평균거래가 = mean(실거래가)) %>%
  top_n(-5, wt = 평균거래가) %>%
  arrange(desc(-평균거래가))
  
install.packages("gridExtra")
library(gridExtra)

p4_1 <- ggplot(p4, aes(reorder(구명, -평균거래가), y = 평균거래가, fill = 구명)) + 
  geom_bar(stat = "identity") +
  xlab("구명")
p5_1 <- ggplot(p5, aes(reorder(구명, -평균거래가), y = 평균거래가, fill = 구명)) + 
  geom_bar(stat = "identity") +
  xlab("구명")
grid.arrange(p4_1, p5_1)
# 강남구, 서초구, 용산구 순서로 비싸다.
# 도봉구, 노원구, 금천구 순서로 싸다.

# ------------------------------------------------------------------
# 실거래가 히스토그램
ggplot(home, aes(x = 실거래가, fill = ..count..)) +
  geom_histogram(binwidth = 10000000)
  
ggplot(home, aes(x = 실거래가로그, fill = ..count..)) +
  geom_histogram(binwidth = 0.5)
  
ggplot(home, aes(x = 난방방법, y = 실거래가, fill = 난방방법)) + 
  geom_boxplot(alpha=0.3)
  
# ------------------------------------------------------------------
#아파트 분양가 상승률
apartment <- seoul[,c(4,6,27)]
a <- apartment %>%
  group_by(아파트id, 거래년월) %>%
  summarise(평균거래가 = mean(실거래가))
  
# 최초 거래년월과 최근 거래년월 추출
b <- a %>%
  group_by(아파트id) %>%
  filter(거래년월 == max(거래년월) | 거래년월 == min(거래년월))
  
# 최초 거래년월과 최근 거래년월 둘 중에 하나만 있는 행 탐색
b %>%
  group_by(아파트id) %>%
  summarise(n = n()) %>%
  arrange(desc(-n))
b <- as.data.frame(b)
which(b$아파트id == 316)
which(b$아파트id == 22241)
which(b$아파트id == 36912)
which(b$아파트id == 37467)
which(b$아파트id == 38419)

# 하나만 있는 행 삭제
b <- b[-c(259, 4882, 5215, 5238, 5243), ]

library(reshape2)
first <- b[!duplicated(b$아파트id),]
recent <- b[duplicated(b$아파트id),]
colnames(first) <- c("아파트id", "최초거래", "최초거래가")

first$최근거래 <- recent$거래년월
first$최근거래가 <- recent$평균거래가

fir_rec <- first
fir_rec$변동 <- (fir_rec$최근거래가 - fir_rec$최초거래가)
head(fir_rec)

fir_rec %>% arrange(desc(-변동))
ggplot(fir_rec, aes(x = 변동, fill = ..count..)) +
  geom_histogram(binwidth = 10000000)
# 대부분 집값이 0원 ~ 1억 사이의 상승률을 보인다.

# ------------------------------------------------------------------
# 세대당 주차 히스토그램
ggplot(home, aes(x = 세대당주차, fill = ..count..)) +
  geom_histogram(binwidth = 0.3)
  
ggplot(home, aes(x = 세대당주차, y = 실거래가)) +
  geom_bar(stat = "identity")

# 세대당주차는 1 ~ 1.3 대가 많이 분포한다

# ------------------------------------------------------------------
# 용적률 히스토그램
ggplot(home, aes(x = 용적률, fill = ..count..)) +
  geom_histogram(binwidth = 1)
  
ggplot(home, aes(x = 용적률, y = 실거래가)) +
  geom_bar(stat = "identity")
  
# 용적률은 75~80% 대가 많이 분포한다

# ------------------------------------------------------------------
# 실평수 히스토그램
ggplot(home, aes(x = 실평수1, fill = ..count..)) +
  geom_histogram(binwidth = 5)
# 25평대가 제일 많이 분포한다

#거래일자 Date 형태로 변환하기
#년월(월초, 월중, 월말)기준
category <- str_split_fixed(string = seoul$거래일,pattern="~",2)
seoul$거래년월일 <- paste0(seoul$거래년월,data.frame(category)[,1])
seoul$거래년월일 <- as.Date(seoul$거래년월일, format= "%Y%m%d")
buydate <- seoul %>%
  group_by(거래년월일, 구명) %>%
  summarise(카운트=n()) %>%
  arrange(desc(카운트))
  
ggplot(buydate, aes(x = 거래년월일, y = 카운트, group = 구명, colour = 구명)) +
  geom_line() +
  facet_wrap(~ 구명) +
  coord_cartesian(ylim=c(0, 1500))
  
# ------------------------------------------------------------------
#년월 기준
category <- str_split_fixed(string = seoul$거래일,pattern="~",2)
seoul$거래년월일 <- paste0(seoul$거래년월,1)
seoul$거래년월일 <- as.Date(seoul$거래년월일, format= "%Y%m%d")

buydate <- seoul %>%
  group_by(거래년월일, 구명) %>%
  summarise(카운트=n())
  
ggplot(buydate, aes(x = 거래년월일, y = 카운트, group = 구명, colour = 구명)) +
  geom_line() +
  facet_wrap(~ 구명) +
  coord_cartesian(ylim=c(0, 1500))
  
#-------------------------------------------------------------------
#구별 평균거래액의 시대별 추이
평균거래가 <- seoul %>%
  group_by(거래년월일, 구명) %>%
  summarise(평균액 = mean(실거래가))
  
ggplot(평균거래가, aes(x = 거래년월일, y = 평균액, group = 구명, colour = 구명)) +
  geom_line() +
  facet_wrap(~ 구명)
###어느 지역이나 평수가 클수록 비싸고, 작을수록  싸다. 따라서 이걸 공통변수로 보고평균거래액 추이를 그림.

3. Correlation analysis

# 상관분석
cor.test(train$실거래가, train$실평수)
cor.test(train$실거래가, train$층)
cor.test(train$실거래가, train$세대수)
cor.test(train$실거래가, train$건축물수)
cor.test(train$실거래가, train$평수)

# ------------------------------------------------------------------
num <- c(home[,4], home[,5], home[,8], home[,9], home[,13], home[,14], home[,15], home[,16],
         home[,17], home[,21], home[,22], home[,23], home[,24], home[,26], home[,31], home[,32])
num <- as.data.frame(num)

x <- cor(num)

library(corrplot)
corrplot(x)

# 학교수 vs 거래가
km <- read.csv("면적.csv")
sch_gu <- seoul_school %>%
  group_by(구명) %>%
  summarise(학교수 = n())
  
seo_gu <- seoul %>%
  group_by(구명) %>%
  summarise(실거래가평균 = mean(실거래가))
  
sch_gu$면적당학교수 <- sch_gu$학교수/km$면적
sch_gu %>%
  arrange(-sch_gu$면적당학교수)
cor.test(sch_gu$면적당학교수, seo_gu$실거래가평균)
# 학교 수와 거래가는 음의 관계

# ------------------------------------------------------------------
# 지하철수 vs 거래가
sub_gu <- seoul_subway %>%
  group_by(구명) %>%
  summarise(지하철수 = n()) %>%
  arrange((구명))
  
sub_gu$면적당지하철수 <- sub_gu$지하철수/km$면적
sub_gu %>%
  arrange(-sub_gu$면적당지하철수)
cor.test(sub_gu$면적당지하철수, seo_gu$실거래가평균)

# 지하철 수와 거래가는 양의 관계

4. Modeling

 

attach(home)
ad <- lm(실거래가 ~ 실평수 + 층 + 주차용량 + 세대수 + 건축물수 +
               가장높은층 + 가장낮은층 + 평수 + 같은방id가구수 +
               방수 + 화장실수 + 난방방법 + 입구구조 + 도시 +
               용적률 + 세대당주차)
               
summary(ad)

require(MASS)
anova(ad)

par(mfrow = c(2, 2))
plot(ad)

4. Conclusion

  • 서울의 아파트들은 보통 학교와 가까이 있는 모습을 보인다
  • 거래는 3월과 10월에 활발하고, 1월과 12월에 저조하다
  • 보통 최초 거래일에 비해 집 값이 상승하는 모습을 보인다
  • 강동구, 마포구 등은 안정적으로 집 값이 오른다
  • 구별 거래가는 지하철 수에 영향을 받는다고 할 수 있다.