데이터 파편화 문제 - 거리 기반 병합이 필요한 경우

2021-07-18

.

Data_Preprocessing_TIL(20210718)

[학습자료]

패스트캠퍼스 온라인 강의 “파이썬을 활용한 데이터 전처리 Level UP 올인원 패키지 Online.” 를 공부하고 정리한 내용입니다.

URL : https://fastcampus.co.kr/data_online_preprocess

[학습내용]

  • 문제정의

아파트 가격예측 등 지역이 포함되는 문제에서 주소나 위치변수 등을 기준으로 거리가 가까운 레코드 및 관련 통계치를 통합해야 하는 경우가 종종 있음

지리나 위치데이터를 다루는데 가끔 발생하는 경우이다.

  • 해결방안

step 1) 데이터별로 레코드 간 거리를 나타내는 거리행렬을 생성

step 2) 거리행렬의 행 혹은 열 기준 최소 값을 가지는 인덱스를 바탕으로 이웃을 탐색

step 3) 이웃을 기존 데이터에 merge

1

  • 해결을 위해서 자주쓰이는 함수 : scipy.spatial.distance.cdist

1) 두개의 행렬을 바탕으로 거리행렬을 반환하는 함수

2) 주요입력

두개의 데이터 프레임을 XA와 XB를 입력받는다. 아래에 distance에서 cityblock은 멘하탄거리이다. XA와 XB의 컬럼개수는 같아야 한다. 물론 시계열 데이터나 텍스트 데이터인 경우에는 컬럼개수가 다를 수 있다.

2

  • 해결을 위해서 자주쓰이는 함수 : ndarray.argsort

1) 작은값부터 순서대로 데이터 위치를 변환하는 함수로, 이웃을 찾는데 주로 활용하는 함수다. 인덱스를 sort하는 함수로 위의 경우에는 최소값을 갖는 인덱스를 찾는데 활용된다.

2) 주요입력

3

  • 실습

아파트별로 가장가까운 지하철역 정보를 만드는 실습

# data 불러오기
import os
import pandas as pd

os.chdir(r"C:/Users/user/Desktop/aa/part-4.-머신러닝을-위한-필수-전처리/Part 4. 머신러닝을 위한 필수 전처리/데이터/")

# 아파트 관련 데이터 불러오기
df1 = pd.read_csv("2019년_서울_아파트매매_실거래가.csv", encoding = "cp949")
df2 = pd.read_csv("2019년_서울시_아파트주소.csv", encoding = "cp949")

df1.head()
거래일자 법정동 도로명 아파트 전용면적 건축년도 거래금액
0 2019-10-05 면목동 용마산로 현대 2 84.81 1994 52000
1 2019-10-07 면목동 중랑천로 신성 5 59.91 1998 37000
2 2019-10-09 면목동 용마산로 현대 19 84.09 1994 57500
3 2019-10-10 면목동 사가정로41길 동원베네스트 5 84.86 2003 56000
4 2019-10-14 면목동 용마산로 현대 9 72.63 1994 50500
df2.head()
시군구명 읍면동명 도로명 건물명 경도 위도
0 종로구 신교동 필운대로 월드빌 126.966744 37.586644
1 종로구 숭인동 숭인동1길 삼성타운 127.017780 37.579576
2 종로구 숭인동 숭인동1나길 삼성타운 127.018026 37.579582
3 종로구 평창동 평창6길 월드빌 126.973484 37.620055
4 중구 장충동1가 장충단로6가길 삼우빌라 127.006113 37.563739

두 데이터를 일단 이어붙인다.

df = pd.merge(df1, df2,left_on = ['법정동', '도로명', '아파트'], right_on = ['읍면동명', '도로명', '건물명'])
df.head()
거래일자 법정동 도로명 아파트 전용면적 건축년도 거래금액 시군구명 읍면동명 건물명 경도 위도
0 2019-10-23 면목동 겸재로 동서그랜드맨션 4 131.34 1993 43000 중랑구 면목동 동서그랜드맨션 127.075856 37.588817
1 2019-10-25 면목동 면목로 킴스아파트 4 38.91 2018 28900 중랑구 면목동 킴스아파트 127.084734 37.592972
2 2019-10-29 면목동 용마산로86길 면목동2차미소지움아파트 12 78.56 2005 45400 중랑구 면목동 면목동2차미소지움아파트 127.094170 37.589641
3 2019-12-04 면목동 용마산로86길 면목동2차미소지움아파트 4 81.96 2005 45500 중랑구 면목동 면목동2차미소지움아파트 127.094170 37.589641
4 2019-12-26 면목동 용마산로86길 면목동2차미소지움아파트 3 82.28 2005 45500 중랑구 면목동 면목동2차미소지움아파트 127.094170 37.589641
df3 = pd.read_excel("지하철역_위경도.xlsx")
df3.head()
역명 위도 경도
0 가양역[9호선] 37.561560 126.854261
1 완정역[2호선] 37.592867 126.673027
2 증미역[9호선] 37.558144 126.860617
3 마전역[2호선] 37.597649 126.667201
4 등촌역[9호선] 37.550694 126.865540

거리 행렬 생성을 위한 컬럼만 추출

df_location = df[['경도', '위도']]
df3_location = df3[['경도', '위도']]

df_location.head()
경도 위도
0 127.075856 37.588817
1 127.084734 37.592972
2 127.094170 37.589641
3 127.094170 37.589641
4 127.094170 37.589641
df3_location.head()
경도 위도
0 126.854261 37.561560
1 126.673027 37.592867
2 126.860617 37.558144
3 126.667201 37.597649
4 126.865540 37.550694
# !pip install haversine # 위경도 거리 계산을 위한 모듈 설치
# 거리 행렬 생성
from scipy.spatial.distance import cdist
from haversine import haversine

# 위경도의 거리계산은 harversine 척도가 적합하다. 유클리디안 거리를 사용해도 된다.
dist_mat = cdist(df_location, df3_location, metric = haversine) # 'euclidean', 'cityblock', 'jaccard'
dist_mat
array([[24.70758112, 44.79342922, 24.02119047, ..., 88.14854137,
        94.9533221 , 86.43696871],
       [25.71344931, 45.77984903, 25.02930797, ..., 88.09491531,
        94.78756009, 86.40922445],
       [26.74278032, 46.8295911 , 26.05525129, ..., 87.55632968,
        94.14934866, 85.89451817],
       ...,
       [11.19169406, 31.45145226, 10.46634004, ..., 90.80270294,
        98.97645174, 88.77523134],
       [11.19169406, 31.45145226, 10.46634004, ..., 90.80270294,
        98.97645174, 88.77523134],
       [11.19169406, 31.45145226, 10.46634004, ..., 90.80270294,
        98.97645174, 88.77523134]])
# 기본적으로 argsort()에 아무것도 넣지 않으면 axis가 0으로 설정된다.
# 즉 열별로 가장 가까운 것을 가져오게 된다.
# 각 아파트별로 가장 가까운 지하철역이 나오게 될 것이다.
close_subway_index = dist_mat.argsort()[:, 0]
close_subway_index
array([488,  74, 683, ..., 118, 118, 118], dtype=int64)
df['가까운역'] = df3.iloc[close_subway_index]['역명'].values 
# 새로운 시리즈를 만들 때는 list, ndarray를 사용하는 것이 바람직하다!
df.head()
거래일자 법정동 도로명 아파트 전용면적 건축년도 거래금액 시군구명 읍면동명 건물명 경도 위도 가까운역
0 2019-10-23 면목동 겸재로 동서그랜드맨션 4 131.34 1993 43000 중랑구 면목동 동서그랜드맨션 127.075856 37.588817 중랑역[경춘선]
1 2019-10-25 면목동 면목로 킴스아파트 4 38.91 2018 28900 중랑구 면목동 킴스아파트 127.084734 37.592972 상봉역[7호선]
2 2019-10-29 면목동 용마산로86길 면목동2차미소지움아파트 12 78.56 2005 45400 중랑구 면목동 면목동2차미소지움아파트 127.094170 37.589641 망우역[경의중앙선]
3 2019-12-04 면목동 용마산로86길 면목동2차미소지움아파트 4 81.96 2005 45500 중랑구 면목동 면목동2차미소지움아파트 127.094170 37.589641 망우역[경의중앙선]
4 2019-12-26 면목동 용마산로86길 면목동2차미소지움아파트 3 82.28 2005 45500 중랑구 면목동 면목동2차미소지움아파트 127.094170 37.589641 망우역[경의중앙선]
df['가까운역까지_거리'] = dist_mat[close_subway_index][:, 0]
df.head()
거래일자 법정동 도로명 아파트 전용면적 건축년도 거래금액 시군구명 읍면동명 건물명 경도 위도 가까운역 가까운역까지_거리
0 2019-10-23 면목동 겸재로 동서그랜드맨션 4 131.34 1993 43000 중랑구 면목동 동서그랜드맨션 127.075856 37.588817 중랑역[경춘선] 22.308503
1 2019-10-25 면목동 면목로 킴스아파트 4 38.91 2018 28900 중랑구 면목동 킴스아파트 127.084734 37.592972 상봉역[7호선] 25.366110
2 2019-10-29 면목동 용마산로86길 면목동2차미소지움아파트 12 78.56 2005 45400 중랑구 면목동 면목동2차미소지움아파트 127.094170 37.589641 망우역[경의중앙선] 25.379852
3 2019-12-04 면목동 용마산로86길 면목동2차미소지움아파트 4 81.96 2005 45500 중랑구 면목동 면목동2차미소지움아파트 127.094170 37.589641 망우역[경의중앙선] 25.379852
4 2019-12-26 면목동 용마산로86길 면목동2차미소지움아파트 3 82.28 2005 45500 중랑구 면목동 면목동2차미소지움아파트 127.094170 37.589641 망우역[경의중앙선] 25.379852