데이터 전처리 과정에서 오류 발생. 이를 고쳐보려한다.
# 가장 가까운 지하철역과 거리 찾기(이진탐색)
def find_nearest_subway(row, subway_sorted):
min_distance = float('inf')
nearest_subway_code = None
left, right = 0, len(subway_sorted) - 1
while left <= right:
mid = (left + right) // 2
subway_lat = subway_sorted.iloc[mid]['latitude']
subway_lon = subway_sorted.iloc[mid]['longitude']
# 이부분 나중에 Haversine 방식으로 변경!!
distance = geodesic((row['latitude'], row['longitude']), (subway_lat, subway_lon)).meters
if distance < min_distance:
min_distance = distance
nearest_subway_code = subway_sorted.iloc[mid]['subway_code']
if (row['latitude'], row['longitude']) < (subway_lat, subway_lon):
right = mid - 1
else:
left = mid + 1
return nearest_subway_code, min_distance
temp_df['nearest_subway_code'], temp_df['nearest_subway_distance'] = zip(*temp_df.apply(find_nearest_subway, subway_sorted=subway_sorted, axis=1))
temp_df.head()
def find_subways_within_1km(row, subway_sorted):
subways_within_1km = []
for _, subway in subway_sorted.iterrows():
distance = geodesic((row['latitude'], row['longitude']), (subway['latitude'], subway['longitude'])).meters
if distance <= 1000:
subways_within_1km.append(subway['subway_code'])
elif distance > 1000 and len(subways_within_1km) > 0:
break
return len(subways_within_1km), subways_within_1km
temp_df['num_subway_within_1km'], temp_df['list_subway_within_1km'] = zip(*temp_df.apply(find_subways_within_1km, subway_sorted=subway_sorted, axis=1))
temp_df.head()
import numpy as np
import pandas as pd
from sklearn.neighbors import BallTree
# 지구의 평균 반경 (킬로미터 단위)
EARTH_RADIUS_KM = 6371.0
# 아파트의 위도와 경도를 라디안으로 변환
temp_df_rad = np.radians(temp_df[['latitude', 'longitude']].values)
# 지하철 역의 위도와 경도를 라디안으로 변환
subway_sorted_rad = np.radians(subway_sorted[['latitude', 'longitude']].values)
# BallTree 생성 (Haversine 거리 메트릭 사용)
tree = BallTree(subway_sorted_rad, metric='haversine')
# 반경 1km을 라디안으로 변환
radius = 1 / EARTH_RADIUS_KM # 약 0.000157 라디안
# 가장 가까운 지하철역과 거리 찾기
distances, indices = tree.query(temp_df_rad, k=1)
temp_df['nearest_subway_distance'] = distances.flatten() * EARTH_RADIUS_KM * 1000 # meters
temp_df['nearest_subway_idx'] = subway_sorted['subway_idx'].iloc[indices.flatten()].values
# 반경 1km 내의 지하철역 인덱스 찾기
indices_within_1km = tree.query_radius(temp_df_rad, r=radius)
# 반경 1km 내의 지하철역 개수
temp_df['num_subway_within_1km'] = [len(ind) for ind in indices_within_1km]
# 반경 1km 내의 지하철역 subway_idx 리스트
temp_df['list_subway_idx_within_1km'] = [subway_sorted['subway_idx'].iloc[ind].tolist() for ind in indices_within_1km]
# 반경 1km 내의 Interchange_station이 2 이상인 지하철역 존재 여부
interchange_counts = [subway_sorted['Interchange_station'].iloc[ind].ge(2).sum() for ind in indices_within_1km]
temp_df['has_Interchange_2_or_more'] = [count >= 1 for count in interchange_counts]
# 결과 확인
print(temp_df[['nearest_subway_idx', 'nearest_subway_distance', 'num_subway_within_1km',
'list_subway_idx_within_1km', 'has_Interchange_2_or_more']].head())
이 방법은 EARTH_RADIUS_KM = 6371.0 으로 두고 시작한다.
Haversine 공식은 지구를 완전한 구로 간주하는 반면, Geodesic 방식은 지구의 타원체 형태를 반영하기 때문이다.
Haversine 공식은 삼각함수를 기반으로 한 근사 계산을 사용하므로 약간(약 0.5%이하, 10km당 50m이하)의 오차가 발생할 수 있지만 계산이 월등히 빠르다.
아파트와 지하철역의 위도 경도를 np.radians를 통해 라디안으로 변환한 뒤 ballTree를 생성해서 거리를 측정한다.
이전 방법으로는 30분이 넘게 걸려도 완성되지 못한 코드를 10초 이내로 빠르게 해결할 수 있게 되었다. 오차도 그렇게 큰 편이 아니었다.