🗄️ 데이터센터
home
주식거래 데이터
home
🔍

부동산 매물이 많은 동네 상위10위 찾기

간단한 프로젝트를 시작해보겠습니다. 정말 쉽습니다. 아시죠? 파이썬 기초 지식이 없어도 괜찮아요. 실제로 사용할 수 있는 함수를 만들어볼 건데, 이 과정에서 흥미를 느끼시면 자연스럽게 지식을 넓혀갈 수 있습니다. 필요한 모든 정보는 제가 연결해드리고, 최대한 쉽게 설명해드릴 거예요. 혹시 어려운 부분이 있다면 제 설명이 부족했던 거니까, 말씀해 주시면 바로 정말 눈에 쏙 들어가게 다시 쉽게 설명합니다.
처음 시작하는 분들은 파이썬과 편집기 설치부터 진행하겠습니다.

데이터 준비

부동산 데이터가 필요합니다. 데이터 센터에서 제공하는 부동산 매물 정보를 사용할 예정입니다. 지금 다운로드해 주세요.

데이터 폴더

데이터는 한곳에 모아주고 해당위치주피터노트북을 위치시켜 주겠습니다.

코드작성

완성된 전체 코드를 한 번에 실행하는 것은 공부에 도움이 되지 않습니다. 코드를 한 줄씩 실행하면서 각각의 결과를 확인해보세요.

라이브러리 임포트

pandasglob 라이브러리를 사용할 예정입니다. 라이브러리에 대한 자세한 설명은 import 페이지와 라이브러리 페이지를 참고해주세요. 코드에서 "#" 기호로 시작하는 줄은 주석으로, 코드로 실행되지 않는 설명문입니다. 이 주석들은 관련 페이지로 이동하는 링크를 포함하고 있습니다.
# import 페이지 참고 import pandas as pd # pandas 참고 import glob # glob 참고
Python
복사

파일 찾기

파일 검색을 시작하겠습니다. 도시 이름을 'cityName' 변수에 담아두고, 우리는 예시로 '인천시'를 사용합니다. glob.glob() 함수로 'cityName'이 들어간 모든 파일을 찾을 수 있습니다. 이 파일들의 경로는 'filePaths'에 저장되는데요, 출력해보면 인천시 관련 파일들이 전부 나타납니다.
import pandas as pd import glob cityName = '인천시' # 변수 참고 filePaths = glob.glob(f'{cityName}*') # glob페이지 참고 filePaths
Python
복사

파일을 데이터프레임으로 읽기

파일들을 데이터프레임으로 변환하는 과정을 진행하겠습니다. 우리가 찾은 파일들은 CSV 형식으로 되어 있는데, 이 파일들을 판다스pd.read_csv() 함수를 사용해서 데이터프레임이라는 형태로 바꿀 것입니다. 이때 리스트 컴프리헨션 방식을 활용해서 filePaths에 있는 모든 파일을 한 번에 처리할 수 있습니다. 파일을 읽을 때는 low_memory=False 옵션을 설정해서 대용량 파일 처리 시 발생할 수 있는 메모리 경고를 방지합니다. 이렇게 변환된 데이터프레임들은 모두 dfs라는 리스트에 저장되어, 인천시의 모든 데이터를 효율적으로 관리할 수 있게 됩니다.
import pandas as pd import glob cityName = '인천시' filePaths = glob.glob(f'{cityName}*') # 결과는 리스트로 반환됩니다. 리스트 페이지 참고 dfs = [pd.read_csv(file, low_memory=False) for file in filePaths] #리스트페이지의 컴프리헨션 참고 dfs
Python
복사

데이터프레임 병합

이제 우리가 가진 여러 개의 데이터프레임을 하나로 합쳐보겠습니다. 판다스concat() 함수를 사용하면 여러 데이터프레임을 손쉽게 결합할 수 있습니다. 이때 ignore_index=True 옵션을 설정하면 새로운 인덱스가 자동으로 생성되어 데이터가 순차적으로 정리됩니다. 이렇게 만들어진 하나의 큰 데이터프레임은 df 변수에 저장됩니다.
import pandas as pd import glob cityName = '인천시' filePaths = glob.glob(f'{cityName}*') dfs = [pd.read_csv(file, low_memory=False) for file in filePaths] ################################################################### df = pd.concat(dfs, ignore_index=True) # concat페이지 참고 df
Python
복사

아파트만 선택

이제 전체 데이터에서 아파트 정보만 선택해보겠습니다. dType 변수에 '아파트'라는 문자열을 저장하고, pandasloc 함수와 contains() 메서드를 사용해 필터링을 수행합니다. 여기서 na=False 옵션은 결측값(NA)을 제외하라는 의미입니다. 필터링된 결과는 filteredDf라는 새로운 데이터프레임에 저장됩니다.
import pandas as pd import glob cityName = '인천시' filePaths = glob.glob(f'{cityName}*') dfs = [pd.read_csv(file, low_memory=False) for file in filePaths] df = pd.concat(dfs, ignore_index=True) ################################################################### dType = '아파트' filteredDf = df.loc[df['rletTpNm'].str.contains(dType, na=False)] # loc참고 filteredDf
Python
복사

동별 매물 개수파악

이제 동별 매물 개수를 파악해보겠습니다. groupby() 함수를 사용하여 cortarNo(법정동코드)를 기준으로 데이터를 그룹화하고, size() 함수로 각 동별 매물의 개수를 계산합니다. 이렇게 계산된 결과는 result 변수에 저장되어 각 동네별 아파트 매물의 수를 보여줍니다.
import pandas as pd import glob cityName = '인천시' filePaths = glob.glob(f'{cityName}*') dfs = [pd.read_csv(file, low_memory=False) for file in filePaths] df = pd.concat(dfs, ignore_index=True) dType = '아파트' filteredDf = df.loc[df['rletTpNm'].str.contains(dType, na=False)] ################################################################### result = filteredDf.groupby('cortarNo').size() # groupby 페이지 참고 result
Python
복사

인덱스 정리

이제 데이터를 보기 좋게 정리해보겠습니다. resultDf 변수를 만들어서 result의 인덱스를 초기화하고, 개수를 나타내는 열의 이름을 'count'로 지정합니다. reset_index() 함수를 사용하면 기존의 인덱스가 일반 열로 변환되고, name 매개변수로 새로운 열 이름을 지정할 수 있습니다. 이렇게 하면 데이터가 더 보기 좋은 형태의 데이터프레임으로 변환됩니다.
import pandas as pd import glob cityName = '인천시' filePaths = glob.glob(f'{cityName}*') dfs = [pd.read_csv(file, low_memory=False) for file in filePaths] df = pd.concat(dfs, ignore_index=True) dType = '아파트' filteredDf = df.loc[df['rletTpNm'].str.contains(dType, na=False)] result = filteredDf.groupby('cortarNo').size() ################################################################### resultDf = result.reset_index(name='count') # 인덱스가 컬럼형으로 변환 됩니다. 인덱스는 자동채번됩니다. resultDf
Python
복사

데이터프레임 병합

이제 우리가 만든 resultDf에 추가 정보를 병합해보겠습니다. merge() 함수를 사용하여 기존 데이터프레임에 도시이름(cityName), 구분이름(divisionName), 섹터이름(sectorName)을 추가합니다. 이때 drop_duplicates() 함수로 중복된 행을 제거하고, cortarNo를 기준(on)으로 left join 방식으로 데이터를 결합합니다.
import pandas as pd import glob cityName = '인천시' filePaths = glob.glob(f'{cityName}*') dfs = [pd.read_csv(file, low_memory=False) for file in filePaths] df = pd.concat(dfs, ignore_index=True) dType = '아파트' filteredDf = df.loc[df['rletTpNm'].str.contains(dType, na=False)] result = filteredDf.groupby('cortarNo').size() resultDf = result.reset_index(name='count') ################################################################### resultDf = resultDf.merge( filteredDf[['cortarNo', 'cityName', 'divisionName', 'sectorName']].drop_duplicates(), on='cortarNo', how='left' ) resultDf
Python
복사

열순서 변경

데이터프레임의 열 순서를 보기 좋게 재배열하겠습니다. 대괄호 표기법을 사용하여 원하는 열을 순서대로 선택하면, 도시이름, 구분이름, 섹터이름, 법정동코드, 매물개수 순으로 깔끔하게 정리된 데이터프레임이 완성됩니다.
import pandas as pd import glob cityName = '인천시' filePaths = glob.glob(f'{cityName}*') dfs = [pd.read_csv(file, low_memory=False) for file in filePaths] df = pd.concat(dfs, ignore_index=True) dType = '아파트' filteredDf = df.loc[df['rletTpNm'].str.contains(dType, na=False)] result = filteredDf.groupby('cortarNo').size() resultDf = result.reset_index(name='count') resultDf = resultDf.merge( filteredDf[['cortarNo', 'cityName', 'divisionName', 'sectorName']].drop_duplicates(), on='cortarNo', how='left' ) ################################################################### resultDf = resultDf[['cityName', 'divisionName', 'sectorName', 'cortarNo', 'count']] resultDf
Python
복사

정렬하기

마지막으로 매물 개수를 기준으로 데이터를 정렬해보겠습니다. sort_values() 함수를 사용하여 'count' 열을 기준으로 정렬하는데, ascending=False 옵션을 설정하여 내림차순으로 정렬합니다. 이렇게 하면 매물이 가장 많은 동네부터 순서대로 확인할 수 있습니다.
import pandas as pd import glob cityName = '인천시' filePaths = glob.glob(f'{cityName}*') dfs = [pd.read_csv(file, low_memory=False) for file in filePaths] df = pd.concat(dfs, ignore_index=True) dType = '아파트' filteredDf = df.loc[df['rletTpNm'].str.contains(dType, na=False)] result = filteredDf.groupby('cortarNo').size() resultDf = result.reset_index(name='count') resultDf = resultDf.merge( filteredDf[['cortarNo', 'cityName', 'divisionName', 'sectorName']].drop_duplicates(), on='cortarNo', how='left' ) resultDf = resultDf[['cityName', 'divisionName', 'sectorName', 'cortarNo', 'count']] ################################################################### resultDf = resultDf.sort_values(by='count', ascending=False) resultDf
Python
복사

함수로 완성

최종함수

지금까지 작성한 코드를 함수로 만들어보겠습니다. filterData라는 함수를 정의하고, 이 함수는 cityNamedType을 매개변수로 받습니다. 여기서 dType의 기본값은 '아파트'로 설정됩니다.
함수는 다음과 같은 순서로 동작합니다
먼저 glob을 사용하여 지정된 도시 이름으로 시작하는 모든 파일을 찾습니다.
찾은 파일들을 pandas를 사용하여 읽어들이고 하나의 데이터프레임으로 결합합니다.
str.contains()를 사용하여 지정된 건물 유형에 맞는 데이터만 필터링합니다.
groupby()를 사용하여 법정동코드별로 매물 개수를 집계합니다.
필요한 정보(도시, 구, 동 이름)를 병합하고 열 순서를 정리합니다.
마지막으로 매물 개수를 기준으로 내림차순 정렬하여 결과를 반환합니다.
이렇게 만든 함수는 cityName만 입력하면 해당 지역의 아파트 매물 현황을 바로 확인할 수 있으며, 필요한 경우 dType을 변경하여 다른 유형의 부동산 정보도 확인할 수 있습니다.
import pandas as pd import glob # 함수페이지 참고 def filterData(cityName,dType='아파트'): ''' 현재 dType은 아파트와 상가 매물만 있습니다. ''' filePaths = glob.glob(f'{cityName}*') dfs = [pd.read_csv(file, low_memory=False) for file in filePaths] df = pd.concat(dfs, ignore_index=True) filteredDf = df.loc[df['rletTpNm'].str.contains(dType, na=False)] result = filteredDf.groupby('cortarNo').size() resultDf = result.reset_index(name='count') resultDf = resultDf.merge( filteredDf[['cortarNo', 'cityName', 'divisionName', 'sectorName']].drop_duplicates(), on='cortarNo', how='left' ) resultDf = resultDf[['cityName', 'divisionName', 'sectorName', 'cortarNo', 'count']] resultDf = resultDf.sort_values(by='count', ascending=False) return resultDf filterData('인천시')
Python
복사

거래유형별 분석 함수

이제 이전 함수를 조금 더 발전시켜 보겠습니다. 매물 유형별로 상세한 분석이 가능한 새로운 함수를 만들어보겠습니다.
새로 만든 filterAndCountByTradTpNm 함수는 다음과 같은 특징이 있습니다
기존 함수와 동일하게 cityNamedType을 매개변수로 받습니다.
tradTpNm(거래유형)별로 매물을 구분하여 집계합니다.
각 지역의 전체 매물 수(totalCount)도 함께 계산됩니다.
결과는 전체 매물 수를 기준으로 내림차순 정렬되며, 같은 지역 내에서는 거래유형별로 정렬됩니다.
이 함수를 사용하면 지역별로 매매, 전세, 월세 등 거래유형별 매물 현황을 한눈에 파악할 수 있습니다.
import pandas as pd import glob def filterData(cityName, dType='아파트'): filePaths = glob.glob(f'{cityName}*') dfs = [pd.read_csv(file, low_memory=False) for file in filePaths] df = pd.concat(dfs, ignore_index=True) filteredDf = df.loc[df['rletTpNm'].str.contains(dType, na=False)] ##################################################################### # tradTpNm에는 매매, 전세, 월세, 단기임대 데이터를 가지고 있습니다. result = filteredDf.groupby(['cortarNo', 'tradTpNm']).size() ##################################################################### resultDf = result.reset_index(name='count') resultDf = resultDf.merge( filteredDf[['cortarNo', 'cityName', 'divisionName', 'sectorName']].drop_duplicates(), on='cortarNo', how='left' ) ##################################################################### # 매매, 전세, 월세를 카운팅 했기때문에 법정동기준의 카운팅이 없으므로 추가합니다. totalCount = resultDf.groupby('cortarNo')['count'].sum().reset_index(name='totalCount') resultDf = resultDf.merge(totalCount, on='cortarNo', how='left') ##################################################################### resultDf = resultDf.sort_values(by=['totalCount', 'cortarNo', 'tradTpNm', 'count'], ascending=[False, True, True, False]) resultDf = resultDf[['cityName', 'divisionName', 'sectorName', 'cortarNo', 'totalCount', 'tradTpNm', 'count']] return resultDf df = filterData('경기도') df
Python
복사

평수별 매물 분석 함수

이번에는 매물의 평수를 상세하게 분석하는 고급 함수를 만들어보겠습니다.주요 기능과 특징은 다음과 같습니다
면적 단위 변환
기존 데이터의 면적(spc1)을 한국인이 익숙한 평수로 자동 변환
소수점 첫째 자리까지 정확하게 계산된 areaPyeong 컬럼 생성
체계적인 평수 구간 분류
~10평: 소형 원룸, 오피스텔
11~20평: 소형 아파트
21~30평: 중소형 아파트
31~40평: 중형 아파트
41~50평: 중대형 아파트
50평 이상: 대형 아파트
거래 유형 최적화
거래유형(tradTpNm) 데이터를 카테고리형으로 변환하여 처리 속도 향상
매매 → 전세 → 월세 → 단기임대 순으로 자동 정렬되어 직관적인 분석 가능
다차원 데이터 분석
법정동코드(cortarNo) 기반의 지역별 분석
거래유형(tradTpNm) 별 시장 동향 파악
평수구간(areaRange) 기준 수요 분포 확인
코드가 좀 더 복잡해졌기 때문에 각 부분에 주석을 추가했습니다. 설명이 부족한 부분은 관련 페이지를 새로 만들어 링크로 연결해드리겠습니다.
import pandas as pd import glob def filterData(cityName, dType='아파트'): # 도시 이름으로 시작하는 모든 CSV 파일을 찾아서 리스트로 저장 filePaths = glob.glob(f'{cityName}*') # 모든 CSV 파일을 하나의 DataFrame으로 통합 df = pd.concat([pd.read_csv(file, low_memory=False) for file in filePaths], ignore_index=True) # 지정된 부동산 유형(예: 아파트)만 필터링하고 복사본 생성 filteredDf = df[df['rletTpNm'].str.contains(dType, na=False)].copy() ##################################################################### # 제곱미터 단위를 평수로 변환 (1평 = 3.3058㎡) # spc1 컬럼의 값을 3.3058로 나누어 평수로 변환하고 소수점 첫째자리까지 반올림 filteredDf['areaPyeong'] = (filteredDf['spc1'] / 3.3058).round(1) ##################################################################### ##################################################################### # 평수 구간 설정 및 카테고리화 # 평수 구간을 리스트로 정의 rangeList = ['~10평', '11~20평', '21~30평', '31~40평', '41~50평', '50평 이상'] # pd.cut을 사용하여 평수를 구간으로 나누고, Categorical 데이터로 변환 filteredDf['areaRange'] = pd.Categorical( pd.cut( filteredDf['areaPyeong'], # 변환할 데이터 bins=[0, 10, 20, 30, 40, 50, 1000], # 구간 경계값 labels=rangeList # 각 구간에 부여할 레이블 ), categories=rangeList, ordered=True # 구간 순서 유지 ) # 거래유형을 카테고리형 데이터로 변환하여 정렬 순서 지정 filteredDf['tradTpNm'] = pd.Categorical( filteredDf['tradTpNm'], categories=['매매', '전세', '월세', '단기임대'], ordered=True ) ##################################################################### # 법정동코드, 거래유형, 평수구간별로 매물 수 집계 result = filteredDf.groupby(['cortarNo', 'tradTpNm', 'areaRange'], observed=False).size().reset_index(name='count') # 지역 정보(시/군/구) 추가 result = result.merge( filteredDf[['cortarNo', 'cityName', 'divisionName', 'sectorName']].drop_duplicates(), on='cortarNo', how='left' ) # 각 법정동의 전체 매물 수 계산 result['totalCount'] = result.groupby('cortarNo')['count'].transform('sum') # 데이터 정렬: 전체매물수 → 법정동코드 → 거래유형 → 평수구간 → 매물수 result = result.sort_values(by=['totalCount', 'cortarNo', 'tradTpNm', 'areaRange', 'count'], ascending=[False, True, True, True, False]) # 필요한 컬럼만 선택하여 반환 return result[['cityName', 'divisionName', 'sectorName', 'cortarNo', 'totalCount', 'tradTpNm', 'areaRange', 'count']] # 인천시 데이터로 함수 테스트 df = filterData('인천시') df.head(30) # 상위 30개 결과 출력
Python
복사

시각화

아직 시각화 관련 학습페이지를 작성하지 않았으나, 맛보기로 한번 해보시길 바랍니다. 저는 데이터분석의 꽃은 시각화에 있다 생각하는 입장입니다. 데이터 자체를 해석하는 것에도 의미가 있지만 시각화를 통해 표로 분석하지 못하는 영역을 볼 수 있습니다.
Plotly는 다른 시각화 도구들과 차별화되는 특별한 장점들이 있습니다. 먼저 실시간 상호작용이 가능해서 마우스 호버 시 상세 정보를 확인할 수 있고, 확대/축소와 데이터 선택이 자유롭습니다. 또한 웹 기반으로 작동하여 결과물을 HTML로 저장하고 공유할 수 있으며, 애니메이션 기능으로 시간에 따른 데이터 변화를 동적으로 표현할 수 있습니다. 3D 시각화도 지원하여 복잡한 데이터도 입체적으로 표현이 가능하죠. 이런 강력한 기능들로 인해 저는 Plotly를 적극 추천드립니다!
Plotly를 사용하여 막대 그래프를 생성하는 함수입니다. createStackedBarChart 함수는 데이터프레임과 거래유형을 입력받아 지역별 평수 구간의 매물 분포를 시각화합니다.
먼저 입력받은 거래유형에 따라 데이터를 필터링하고, 도시명을 추출합니다. 그런 다음 동네(sectorName)평수 구간(areaRange)별로 매물 수를 집계합니다.
그래프의 x축은 동네명을, y축은 매물 수를 나타내며, 각 막대는 평수 구간별로 색상이 구분됩니다. 그래프의 크기는 가로 1000픽셀, 세로 600픽셀로 설정되어 있습니다.
마지막으로 그래프의 스타일을 조정하여 배경을 투명하게 만들고, 글꼴 크기를 조정하며, x축 레이블을 90도 회전시켜 가독성을 높였습니다.
# Plotly Express 라이브러리 임포트 import plotly.express as px def createChart(df, tradType='매매'): # 선택한 거래유형으로 데이터 필터링 df = df[df['tradTpNm'] == tradType] # 도시 이름 추출 (첫 번째 고유값) cityName = df['cityName'].unique()[0] # 동네와 평수 구간별로 매물 수 집계 groupedDf = df.groupby(['sectorName', 'areaRange'], observed=False)['count'].sum().reset_index() # Plotly Express를 사용하여 막대 그래프 생성 fig = px.bar( groupedDf, x='sectorName', # x축: 동네 이름 y='count', # y축: 매물 수 color='areaRange', # 색상 구분: 평수 구간 title=f'[{cityName} {tradType}] 평수별 매물정보', # 그래프 제목 labels={ # 레이블 한글화 'sectorName': '동', 'count': '매물 수', 'tradTpNm': '거래유형', 'areaRange': '평수 구간' }, category_orders={ # 평수 구간 순서 지정 'areaRange': ['~10평','11~20평','21~30평','31~40평','41~50평','50평 이상'] }, width=1000, # 그래프 너비 height=600 # 그래프 높이 ) # 그래프 레이아웃 설정 fig.update_layout( barmode='stack', # 누적 막대 그래프 설정 plot_bgcolor='rgba(0,0,0,0)', # 플롯 영역 배경 투명 paper_bgcolor='rgba(0,0,0,0)', # 전체 배경 투명 font=dict(size=12), # 기본 폰트 크기 xaxis_title=None, # x축 제목 제거 yaxis_title=None, # y축 제목 제거 xaxis_tickangle=-90, # x축 레이블 90도 회전 xaxis=dict( tickmode='linear', # x축 눈금 모드 tickfont=dict(size=8) # x축 레이블 폰트 크기 ) ) # 그래프 표시 fig.show() # 매매 데이터로 그래프 생성 createChart(df, tradType='매매')
Python
복사