Categories: 미분류

부동산 매매가 데이터 통계 정보 만들기 2편

부동산 매매가 데이터 통계 정보 만들기 2편 ㅣ 부동산 매매가 데이터를 가지고 추가적인 통계 자료와 시각적인 그래프를 추가적인 코드를 추가하겠습니다. 부동산 매매가로 충분한 데이터를 따로 만들 수 있지만 우리는 이 자료를 자동화 하는 것이 목표이기 때문이죠.

아직 1편을 못보신 분들은 1편을 보고 따라 가시면 쉬울 겁니다~

2024.09.21 – [부동산/자동화 프로젝트] – 부동산 매매가 데이터 통계 정보 만들기 1편

부동산 매매가 데이터 통계 정보 만들기 3편

1편에서 만든 코드를 다시 불러오겠습니다.

import streamlit as st
import pandas as pd
import PublicDataReader as pdr
from datetime import datetime
import json
import matplotlib.pyplot as plt
import matplotlib.font_manager as fm
import os

# Streamlit secrets에서 API 키 및 파일 경로 가져오기
service_key = st.secrets["general"]["SERVICE_KEY"]
json_file_path = "district.json"

# PublicDataReader API 서비스 키 사용
api = pdr.TransactionPrice(service_key)

# DistrictConverter 클래스 정의
class DistrictConverter:
    def __init__(self):
        self.districts = self.__read_district_file()

    def __read_district_file(self):
        with open(json_file_path, 'r') as f:
            return json.loads(f.read())

    def get_si_do_code(self, si_do_name):
        for district in self.districts:
            if si_do_name == district["si_do_name"]:
                return district["si_do_code"]

    def get_sigungu(self, si_do_code):
        for district in self.districts:
            if si_do_code == district["si_do_code"]:
                return district["sigungu"]

# 사용자 입력 받기
st.title("부동산 데이터 조회")
si_do_name = st.sidebar.text_input("시/도를 입력하세요 (예: 서울특별시) 또는 '전국' 입력", "전국")
start_year_month = st.sidebar.text_input("조회 시작 년월 (YYYYMM 형식, 예: 202301)", "")
end_year_month = st.sidebar.text_input("조회 종료 년월 (YYYYMM 형식, 예: 202312)", "")
data_query_button = st.sidebar.button("데이터 조회")

# 사용자 정의 폰트 설정
font_path = os.path.join(os.getcwd(), 'NanumGothicCoding.ttf')
fm.fontManager.addfont(font_path)
plt.rcParams['font.family'] = 'NanumGothicCoding'

# 현재 날짜를 기준으로 기간 설정
now = datetime.now()
if not start_year_month:
    start_year_month = f"{now.year}01"
if not end_year_month:
    end_year_month = now.strftime("%Y%m")

# 진행 상황 표시
progress_text = st.sidebar.empty()
status_text = st.sidebar.empty()

if data_query_button:
    if si_do_name and start_year_month and end_year_month:
        # DistrictConverter 인스턴스 생성
        converter = DistrictConverter()

        # 데이터 수집 및 처리
        all_data = pd.DataFrame()

        if si_do_name == "전국":
            total_count = sum(len(district["sigungu"]) for district in converter.districts)
            processed_count = 0

            for district in converter.districts:
                si_do_code = district["si_do_code"]
                sigungu_list = district["sigungu"]

                for sigungu in sigungu_list:
                    sigungu_code = sigungu["sigungu_code"]
                    sigungu_name = sigungu["sigungu_name"]

                    # 현재 진행 상황 업데이트
                    processed_count += 1
                    progress_text.text(f"진행율: {100 * processed_count / total_count:.2f}% ({processed_count}/{total_count})")
                    status_text.text(f"현재 처리 중: {sigungu_name} ({sigungu_code})")

                    df = api.get_data(
                        property_type="아파트",
                        trade_type="매매",
                        sigungu_code=sigungu_code,
                        start_year_month=start_year_month,
                        end_year_month=end_year_month
                    )

                    df["sigungu_name"] = sigungu_name
                    df["si_do_name"] = district["si_do_name"]

                    all_data = pd.concat([all_data, df], ignore_index=True)
        else:
            si_do_code = converter.get_si_do_code(si_do_name)
            sigungu_list = converter.get_sigungu(si_do_code)

            total_count = len(sigungu_list)
            processed_count = 0

            for sigungu in sigungu_list:
                sigungu_code = sigungu["sigungu_code"]
                sigungu_name = sigungu["sigungu_name"]

                # 현재 진행 상황 업데이트
                processed_count += 1
                progress_text.text(f"진행율: {100 * processed_count / total_count:.2f}% ({processed_count}/{total_count})")
                status_text.text(f"현재 처리 중: {sigungu_name} ({sigungu_code})")

                df = api.get_data(
                    property_type="아파트",
                    trade_type="매매",
                    sigungu_code=sigungu_code,
                    start_year_month=start_year_month,
                    end_year_month=end_year_month
                )

                df["sigungu_name"] = sigungu_name
                df["si_do_name"] = si_do_name

                all_data = pd.concat([all_data, df], ignore_index=True)

        # 컬럼 이름 변환
        columns_to_select = {
            "si_do_name": "시도",
            "sigungu_name": "시군구",
            "umdNm": "법정동",
            "roadNm": "도로명",
            "bonbun": "지번",
            "aptNm": "아파트",
            "buildYear": "건축년도",
            "excluUseAr": "전용면적",
            "floor": "층",
            "dealYear": "거래년도",
            "dealMonth": "거래월",
            "dealDay": "거래일",
            "dealAmount": "거래금액",
            "aptSeq": "일련번호",
            "dealingGbn": "거래유형",
            "estateAgentSggNm": "중개사소재지",
            "cdealType": "해제여부",
            "cdealDay": "해제사유발생일"
        }

        selected_data = all_data.rename(columns=columns_to_select)[list(columns_to_select.values())]

        # 데이터 표로 표시
        st.write("### 조회 결과")
        st.dataframe(selected_data)

        # 분석 자료
        st.write("### 분석 자료")
        total_transactions = selected_data.shape[0]
        st.write(f"총 거래량: {total_transactions}")

        # 매월 거래량
        monthly_transactions = selected_data.groupby(['거래년도', '거래월']).size().reset_index(name='거래량')
        monthly_transactions['합계'] = monthly_transactions['거래량'].sum()
        
        st.write("매월 거래량")
        plt.figure(figsize=(10, 6))
        plt.bar(monthly_transactions['거래년도'].astype(str) + '-' + monthly_transactions['거래월'].astype(str), monthly_transactions['거래량'], color='skyblue')
        plt.title('매월 거래량', fontsize=16)
        plt.xlabel('년도-월', fontsize=14)
        plt.ylabel('거래량', fontsize=14)
        plt.xticks(rotation=45)
        plt.tight_layout()
        st.pyplot(plt)

        st.dataframe(monthly_transactions)

        # 지역별 거래량 (월별)
        regional_monthly_transactions = selected_data.groupby(['거래년도', '거래월', '시군구']).size().reset_index(name='거래량')

        # 원형 그래프로 거래 비중 시각화
        plt.figure(figsize=(8, 8))
        regional_summary = selected_data['시군구'].value_counts()
        plt.pie(regional_summary, labels=regional_summary.index, autopct='%1.1f%%', startangle=140, colors=plt.cm.Paired.colors)
        plt.title('지역별 거래 비중', fontsize=16)
        plt.axis('equal')  # Equal aspect ratio ensures that pie is drawn as a circle.
        st.pyplot(plt)

        st.write("지역별 거래량 (월별)")
        regional_pivot = regional_monthly_transactions.pivot_table(index='시군구', columns='거래월', values='거래량', fill_value=0)
        regional_pivot['합계'] = regional_pivot.sum(axis=1)
        st.dataframe(regional_pivot)

    else:
        st.error("모든 필드를 채워주세요.")

부동산 매매가 데이터 통계 정보 만들기 2편

이제 이 코드를 바탕으로 아래와 같은 출력을 만들기 위해서, 코드를 수정해보겠습니다.

import streamlit as st
import pandas as pd
import PublicDataReader as pdr
from datetime import datetime
import json
import matplotlib.pyplot as plt
import matplotlib.font_manager as fm
import os

# Streamlit secrets에서 API 키 및 파일 경로 가져오기
service_key = st.secrets["general"]["SERVICE_KEY"]
json_file_path = "district.json"

# PublicDataReader API 서비스 키 사용
api = pdr.TransactionPrice(service_key)

# DistrictConverter 클래스 정의
class DistrictConverter:
    def __init__(self):
        self.districts = self.__read_district_file()

    def __read_district_file(self):
        with open(json_file_path, 'r') as f:
            return json.loads(f.read())

    def get_si_do_code(self, si_do_name):
        for district in self.districts:
            if si_do_name == district["si_do_name"]:
                return district["si_do_code"]

    def get_sigungu(self, si_do_code):
        for district in self.districts:
            if si_do_code == district["si_do_code"]:
                return district["sigungu"]

# 사용자 입력 받기
st.title("부동산 데이터 조회")
si_do_name = st.sidebar.text_input("시/도를 입력하세요 (예: 서울특별시) 또는 '전국' 입력", "전국")
start_year_month = st.sidebar.text_input("조회 시작 년월 (YYYYMM 형식, 예: 202301)", "")
end_year_month = st.sidebar.text_input("조회 종료 년월 (YYYYMM 형식, 예: 202312)", "")
data_query_button = st.sidebar.button("데이터 조회")

# 폰트 파일 경로 설정
current_dir = os.getcwd()
font_path = os.path.join(current_dir, 'NanumGothicCoding.ttf')
fm.fontManager.addfont(font_path)
plt.rcParams['font.family'] = 'NanumGothicCoding'  # 사용자 선택한 폰트 적용

# 현재 날짜를 기준으로 기간 설정
now = datetime.now()
if not start_year_month:
    start_year_month = f"{now.year}01"
if not end_year_month:
    end_year_month = now.strftime("%Y%m")

# 진행 상황 표시
progress_text = st.sidebar.empty()
status_text = st.sidebar.empty()

if data_query_button:
    if si_do_name and start_year_month and end_year_month:
        # DistrictConverter 인스턴스 생성
        converter = DistrictConverter()

        # 데이터 수집 및 처리
        all_data = pd.DataFrame()

        if si_do_name == "전국":
            total_count = sum(len(district["sigungu"]) for district in converter.districts)
            processed_count = 0

            for district in converter.districts:
                si_do_code = district["si_do_code"]
                sigungu_list = district["sigungu"]

                for sigungu in sigungu_list:
                    sigungu_code = sigungu["sigungu_code"]
                    sigungu_name = sigungu["sigungu_name"]

                    # 현재 진행 상황 업데이트
                    processed_count += 1
                    progress_text.text(f"진행율: {100 * processed_count / total_count:.2f}% ({processed_count}/{total_count})")
                    status_text.text(f"현재 처리 중: {sigungu_name} ({sigungu_code})")

                    df = api.get_data(
                        property_type="아파트",
                        trade_type="매매",
                        sigungu_code=sigungu_code,
                        start_year_month=start_year_month,
                        end_year_month=end_year_month
                    )

                    df["sigungu_name"] = sigungu_name
                    df["si_do_name"] = district["si_do_name"]

                    all_data = pd.concat([all_data, df], ignore_index=True)
        else:
            si_do_code = converter.get_si_do_code(si_do_name)
            sigungu_list = converter.get_sigungu(si_do_code)

            total_count = len(sigungu_list)
            processed_count = 0

            for sigungu in sigungu_list:
                sigungu_code = sigungu["sigungu_code"]
                sigungu_name = sigungu["sigungu_name"]

                # 현재 진행 상황 업데이트
                processed_count += 1
                progress_text.text(f"진행율: {100 * processed_count / total_count:.2f}% ({processed_count}/{total_count})")
                status_text.text(f"현재 처리 중: {sigungu_name} ({sigungu_code})")

                df = api.get_data(
                    property_type="아파트",
                    trade_type="매매",
                    sigungu_code=sigungu_code,
                    start_year_month=start_year_month,
                    end_year_month=end_year_month
                )

                df["sigungu_name"] = sigungu_name
                df["si_do_name"] = si_do_name

                all_data = pd.concat([all_data, df], ignore_index=True)

        # 컬럼 이름 변환
        columns_to_select = {
            "si_do_name": "시도",
            "sigungu_name": "시군구",
            "umdNm": "법정동",
            "roadNm": "도로명",
            "bonbun": "지번",
            "aptNm": "아파트",
            "buildYear": "건축년도",
            "excluUseAr": "전용면적",
            "floor": "층",
            "dealYear": "거래년도",
            "dealMonth": "거래월",
            "dealDay": "거래일",
            "dealAmount": "거래금액",
            "aptSeq": "일련번호",
            "dealingGbn": "거래유형",
            "estateAgentSggNm": "중개사소재지",
            "cdealType": "해제여부",
            "cdealDay": "해제사유발생일"
        }

        selected_data = all_data.rename(columns=columns_to_select)[list(columns_to_select.values())]

        # 데이터 표로 표시
        st.write("### 조회 결과")
        st.dataframe(selected_data)

        # 분석 자료
        st.write("### 분석 자료")
        total_transactions = selected_data.shape[0]
        st.write(f"총 거래량: {total_transactions}")

        # 전용면적 데이터 타입 변환 및 결측치 처리
        selected_data['전용면적'] = pd.to_numeric(selected_data['전용면적'], errors='coerce')
        selected_data.dropna(subset=['전용면적'], inplace=True)  # 결측치 삭제

        # 매월 거래량
        monthly_transactions = selected_data.groupby(['거래년도', '거래월']).size().reset_index(name='거래량')
        
        # 매월 거래량 시각화
        st.header("매월 거래량 📅")
        plt.figure(figsize=(10, 6))
        plt.bar(monthly_transactions['거래년도'].astype(str) + '-' + monthly_transactions['거래월'].astype(str), monthly_transactions['거래량'], color='skyblue')
        plt.xlabel('연도-월', fontsize=14)
        plt.ylabel('거래량', fontsize=14)
        plt.xticks(rotation=45)
        plt.tight_layout()
        st.pyplot(plt)
        
        # 매월 거래량 표
        st.dataframe(monthly_transactions)

        # 전용면적 범위별 거래량
        bins = [0, 80, 100, 120, 140, float('inf')]
        labels = ['0~80', '80~100', '100~120', '120~140', '140 이상']
        selected_data['면적 범위'] = pd.cut(selected_data['전용면적'], bins=bins, labels=labels, right=False)
        area_counts = selected_data['면적 범위'].value_counts().sort_index()

        # 전용면적 범위별 거래량 시각화
        st.header("전용면적 범위별 거래량 📏")
        plt.figure(figsize=(10, 6))
        plt.bar(area_counts.index, area_counts.values, color='lightgreen')
        plt.xlabel('면적 범위', fontsize=14)
        plt.ylabel('거래량', fontsize=14)
        plt.xticks(rotation=45)
        plt.tight_layout()
        st.pyplot(plt)
        
        # 전용면적 범위별 거래량 표
        st.dataframe(area_counts.reset_index().rename(columns={'index': '면적 범위', 0: '거래량'}))

        # 지역별 면적 대비 거래량
        regional_area_counts = selected_data.groupby(['시군구']).size()
        
        # 지역별 면적 대비 거래량 시각화
        st.header("지역별 면적 대비 거래량 🌍")
        plt.figure(figsize=(10, 6))
        plt.bar(regional_area_counts.index, regional_area_counts.values, color='salmon')
        plt.xlabel('시군구', fontsize=14)
        plt.ylabel('거래량', fontsize=14)
        plt.xticks(rotation=45)
        plt.tight_layout()
        st.pyplot(plt)
        
        # 지역별 면적 대비 거래량 표
        st.dataframe(regional_area_counts.reset_index().rename(columns={0: '거래량', '시군구': '시군구'}))

        # 거래유형 분석
        transaction_types = selected_data['거래유형'].value_counts()
        
        # 거래유형 분석 시각화
        st.header("거래유형 분석 🏠")
        plt.figure(figsize=(10, 6))
        plt.pie(transaction_types, labels=transaction_types.index, autopct='%1.1f%%', startangle=140, colors=['#ff9999','#66b3ff'])
        plt.axis('equal')  # Equal aspect ratio ensures that pie is drawn as a circle.
        st.pyplot(plt)
        
        # 거래유형 분석 표
        st.dataframe(transaction_types.reset_index().rename(columns={'index': '거래유형', 0: '거래량'}))

        # 거래량 합계
        total_volume = monthly_transactions['거래량'].sum()
        st.write(f"거래량 합계: {total_volume} 🏆")

이 코드를 실행하면, 간단한 통계 정보까지 포함합니다. 조금 더 다듬는 과정은 3편에서 진행하겠습니다.

감사합니다.

부동산 관련 크롤링 포스팅 모아서 정리해봅니다.

2024.09.15 – [부동산/자동화 프로젝트] – 부동산 매물 정보 수집하기 – 부동산 데이터 네이버 부동산 크롤링 및 가공 #1

2024.09.15 – [부동산/자동화 프로젝트] – 부동산 매물 정보 수집하기 – 부동산 데이터 네이버 부동산 크롤링 및 가공 #2

2024.09.15 – [부동산/자동화 프로젝트] – 부동산 매물 정보 수집하기 – 부동산 데이터 네이버 부동산 크롤링 및 가공 #3

2024.09.17 – [부동산/자동화 프로젝트] – [고급] 부동산 정보 필터 고도화 – 네이버 매물 정리하기

 

# 부동산매매, 부동산매매크롤링, 부동산정보, 매매가조회, 부동산매매조회, 부동산보고서, 부동산자동보고서, 부동산파이썬, 부동산파이썬크롤링

urjent

Share
Published by
urjent

Recent Posts

보완수사권 토론 취소, 이건태 하루 만에 철회하고 한동훈은 도망이라 비판

더불어민주당 이건태 의원이 한동훈 무소속 의원과 예정했던 검찰 보완수사권 폐지 공개토론을 수락한 지 하루 만인…

18시간 ago

홈플러스 2000억 자금 조달…메리츠·MBK 합의로 회생 가능성 열려

홈플러스는 2000억 원 규모의 긴급 운영자금 조달을 위해 메리츠금융그룹과 MBK파트너스 간에 잠정 합의를 도출했습니다. 서울회생법원은…

4일 ago

조회수에 눈 멀어 30층에서 떨어진 중국 남성, 사망 사고 경위 밝혀져

2026년 7월 14일 중국 쓰촨성 청두시에서 23세 남성 A씨가 30층 아파트 옥상에서 낙하산을 타고 뛰어내려…

4일 ago

신지 KT 시구 거절 논란, 8월 2일 홈경기 축하공연까지 전면 취소

신지가 프로야구 KT 위즈의 시구 제안을 거절한 뒤 소속사가 공식 사과하며 예정된 코요태의 축하공연까지 취소하기로…

4일 ago

초과이익 토론회, 7월 14일 정부·국회 차원에서 공동 개최

2026년 7월 14일, 정부와 국회 차원에서 초과이익 토론회가 다수 열린다. 국토교통부, 금융위원회, 재정경제부가 각각 부동산과…

5일 ago

국제유가 10% 폭등…호르무즈 해협 봉쇄 재개로 WTI 9.4%, 브렌트유 9.6% 급등

2026년 7월 13일(현지시간) 국제유가가 단 하루 만에 약 10% 폭등했습니다. 도널드 트럼프 미국 대통령이 호르무즈…

5일 ago