서울 452개 법정동의 교육·상업 인프라 밀도를 K-Means 클러스터링으로 분류하고, PCA로 시각화하며, Decision Tree로 판별 규칙을 도출합니다.
- 사용 알고리즘
- 1단계 — 문제 정의
- 2단계 — 데이터 수집 및 설명
- 3단계 — 데이터 전처리
- 4단계 — 모델 설계 및 적용
- 5단계 — 성능 평가
- 6단계 — 결과 분석 및 해석
- 실행 방법
- 프로젝트 구조
수업에서 다룬 알고리즘 중 이 프로젝트에서 실제로 적용한 항목입니다.
| 분류 | 알고리즘 | 적용 목적 |
|---|---|---|
| 군집화 (Clustering) | K-Means Clustering | 서울 법정동을 인프라 특성 기준으로 4개 그룹으로 분류 |
| 차원 축소 | Principal Component Analysis (PCA) | 3차원 데이터를 2차원으로 압축해 군집 분포 시각화 |
| 분류 (Classification) | Decision Tree | K-Means 군집 레이블을 학습해 동네 유형 판별 규칙 도출 및 군집 유효성 검증 |
이 외에도 StandardScaler(정규화), Silhouette Score(군집 품질 평가), Elbow Method(최적 K 탐색), 5-Fold Cross Validation(트리 깊이 최적화), Ridge Regression(집값 예측 보조 분석)이 활용되었습니다.
"서울 동네들은 어떤 기준으로 나눌 수 있을까? 그리고 그 기준을 명확한 규칙으로 표현할 수 있을까?"
우리는 흔히 "교육 특구는 집값이 높다", "상업지는 주거 선호도가 낮다"고 말합니다. 하지만 이를 데이터로 객관적으로 검증한 사례는 많지 않습니다.
이 프로젝트는 다음 세 가지 질문에 답합니다.
- 서울 동네들은 인프라 특성(소상공인·학교·학원 밀도)에 따라 어떻게 나뉘는가?
- 각 동네 유형을 구분하는 인프라 밀도 기준값은 얼마인가?
- 인프라 유형과 실거래가 사이에는 어떤 관계가 있는가?
- 대상: 서울시 전체 452개 법정동
- 입력 변수: 동별 소상공인 수 / 면적(km²), 학교 수 / 면적, 학원 수 / 면적 (밀도 기준)
- 비교 지표: 아파트 실거래가 (2025.04 ~ 2026.03)
모든 데이터는 공공데이터포털에서 수집하였습니다.
| 파일명 | 설명 | 활용 |
|---|---|---|
소상공인.csv |
서울시 전체 소상공인 사업장 목록 (주소 포함) | 클러스터링 변수 |
학교.csv |
서울시 초·중·고·대학교 목록 | 클러스터링 변수 |
학원.csv |
서울시 등록 학원 중 개원 중인 곳 | 클러스터링 변수 |
토지 면적.csv |
법정동별 면적 (km²) | 밀도 계산 기준 |
인구밀도.csv |
서울시 읍면동별 인구 현황 | 유효 법정동 452개 선정 기준 |
서울시_실거래가.csv |
아파트 실거래가 (2025.04 ~ 2026.03) | 집값 비교 분석 |
| 변수 | 평균 | 표준편차 | 최솟값 | 최댓값 | 비고 |
|---|---|---|---|---|---|
| 소상공인 밀도 (개/km²) | 63.9 | 91.0 | 0.0 | 814.5 | 명동(814) 극단값 |
| 학교 밀도 (개/km²) | 5.5 | 17.8 | 0.0 | 288.8 | right-skewed |
| 학원 밀도 (개/km²) | 32.3 | 51.6 | 0.0 | 430.5 | 대치동(430) 극단값 |
변수 선택 근거: 인구밀도·지하철역 추가 시 Silhouette Score가 0.49 → 0.39로 하락(noise 증가)해 제외하고, 생활 인프라 핵심 3축만 사용합니다.
원본 데이터 (소상공인 / 학교 / 학원 / 면적)
│
▼
법정동별 개수 집계 → 면적(km²)으로 나누어 밀도 계산
│
▼
Log1p 변환 → right-skewed 분포 완화
(예: 대치동 학원밀도 430 → log(431)=6.07 / 전체 평균 log(33)=3.53)
│
▼
StandardScaler → 평균 0, 표준편차 1로 정규화
│
▼
Z-score 기반 이상치 제거 (|Z| > 3인 동네 3개 제거)
단, 분석상 중요한 대치동·신사동은 강제 보존
│
▼
최종 입력 데이터: 449개 법정동 × 3개 변수
밀도 데이터는 극단적으로 치우쳐 있습니다. 명동의 소상공인 밀도(814/km²)는 평균(64/km²)의 12배입니다. 이대로 클러스터링하면 알고리즘이 극단값에 끌려가 의미 있는 군집이 만들어지지 않습니다. Log1p 변환으로 극단값을 완화하면서도 동네 간 상대적 차이는 유지합니다.
| 처리 방법 | 대상 | 결과 |
|---|---|---|
| Z-score > 3 제거 | 종로구 숭인동, 중구 흥인동, 마포구 아현동 | 3개 동 제거 |
| 화이트리스트 보존 | 강남구 대치동, 강남구 신사동 | 분석 중요 동네 강제 포함 |
K-Means는 군집 수(K)를 미리 정해야 합니다. Elbow Method(관성값 감소 추이)와 Silhouette Score(군집 응집도)를 함께 확인해 K=4를 선택했습니다.
km = KMeans(n_clusters=4, random_state=42, n_init=10)
labels = km.fit_predict(X)- 입력: 449개 법정동 × 3개 변수 (소상공인·학교·학원 정규화 밀도)
- 출력: 각 법정동에 클러스터 번호(0~3) 부여
K 탐색 결과 (data/processed/k_selection.png):
| K | Silhouette Score | 해석 |
|---|---|---|
| 2 | 0.476 | 분리 과도 |
| 3 | 0.420 | |
| 4 | 0.446 | 최적 (Elbow + Silhouette 동시 고려) |
| 5 | 0.381 | 과분할 |
3차원 데이터를 그래프로 나타내기 어렵기 때문에 PCA로 2차원으로 압축해 군집 분포를 시각화했습니다.
pca = PCA(n_components=2, random_state=42)
coords = pca.fit_transform(X)- PC1: 교육 인프라 축 (학원·학교 밀도 반영, 설명 분산 ~70%)
- PC2: 상권 집중 축 (소상공인 밀도 반영, 설명 분산 ~20%)
- 누적 설명 분산 ~90% → 3개 변수의 정보를 거의 손실 없이 보존
K-Means가 만든 클러스터 레이블(0~3)을 target으로 놓고, 인프라 밀도로 분류하는 Decision Tree를 학습합니다.
입력(X): 소상공인_밀도, 학교_밀도, 학원_밀도
출력(y): K-Means 클러스터 번호 (0, 1, 2, 3)
두 가지 목적으로 활용합니다.
- 규칙 도출: "학원 밀도 > X이면 교육 특화, 소상공인 밀도 > Y이면 상업 특화"와 같은 명확한 기준값 추출
- 군집 유효성 검증: 정확도가 높을수록 K-Means 군집이 인프라로 잘 설명되는 실제 패턴임을 의미
최적 트리 깊이는 5-Fold Cross Validation으로 탐색합니다.
dt = DecisionTreeClassifier(max_depth=best_depth, random_state=42)
dt.fit(X_train, y_train)트리 깊이별 정확도 변화:
| 지표 | 값 | 해석 |
|---|---|---|
| Silhouette Score | 0.4458 | 군집 간 분리가 뚜렷하며 신뢰할 수 있는 수준 |
일반적으로 Silhouette Score 0.4 이상이면 의미 있는 군집 구조가 존재한다고 봅니다.
| 클러스터 | 이름 | 법정동 수 |
|---|---|---|
| C0 | 상업·학원 복합 | 119개 |
| C1 | 교육·주거 밀집 | 128개 |
| C2 | 저밀도 주거 | 71개 |
| C3 | 상업 특화 | 131개 |
| 주성분 | 설명 분산 | 해석 |
|---|---|---|
| PC1 | ~70% | 교육 인프라 축 (학원·학교 밀도 반영) |
| PC2 | ~20% | 상권 집중 축 (소상공인 밀도 반영) |
| 합계 | ~90% | 3개 변수의 정보를 90% 이상 보존 |
| 지표 | 값 | 해석 |
|---|---|---|
| Test 정확도 | 98.9% | 449개 법정동 중 단 1개만 오분류 |
| 5-Fold CV 정확도 | 95.8% | 과적합 없이 안정적으로 높은 성능 |
| 최적 트리 깊이 | 5 | Cross Validation으로 탐색 |
4개 클러스터가 소상공인·학교·학원 밀도에서 어떻게 다른지 한눈에 비교합니다.
| 클러스터 | 이름 | 주요 특징 | 대표 동네 |
|---|---|---|---|
| C0 | 상업·학원 복합 | 소상공인과 학원 밀도가 모두 높음 | 신사동, 역삼동, 서교동, 인사동 |
| C1 | 교육·주거 밀집 | 학원·학교 밀도가 압도적으로 높음 | 대치동, 중계동, 반포동, 잠실동 |
| C2 | 저밀도 주거 | 모든 지표가 낮음 (조용한 주거지) | 이촌동, 세곡동, 율현동, 자곡동 |
| C3 | 상업 특화 | 소상공인 밀도만 극단적으로 높음 | 명동, 소공동, 충무로, 을지로 |
3개 변수를 2개의 주성분으로 압축해 서울 449개 법정동의 분포를 시각화했습니다. 같은 색상의 점들이 비슷한 인프라 특성을 가진 동네들입니다.
PC1과 PC2가 각각 어떤 변수를 얼마나 반영하는지 분석합니다.
| 주성분 | 해석 | 의미 |
|---|---|---|
| PC1 (교육 인프라 축) | 학원·학교 변수에 높은 양의 가중치 | PC1이 높을수록 교육 특화 지역 |
| PC2 (상권 집중 축) | 소상공인에 양, 학교에 음의 가중치 | PC2가 높을수록 상업 특화 지역 |
인프라 밀도 기준값으로 어떻게 4개 유형이 나뉘는지 트리로 표현합니다.
449개 법정동 중 단 1개만 오분류. 4개 클러스터 모두 높은 정밀도·재현율을 보입니다.
K-Means 레이더 차트와 Decision Tree Feature Importance가 같은 변수를 중요하게 본다면, 분석 결론이 두 모델에서 일관되게 지지되는 것을 의미합니다.
인프라 유형이 실제 집값에 어떤 영향을 미치는지 확인합니다.
| 클러스터 | 이름 | 거래건수 | 평균가 | 중앙값 |
|---|---|---|---|---|
| C0 | 상업·학원 복합 | 17,443건 | 12.22억 | 11.00억 |
| C1 | 교육·주거 밀집 | 44,721건 | 11.68억 | 9.28억 |
| C2 | 저밀도 주거 | 2,025건 | 13.43억 | 10.55억 |
| C3 | 상업 특화 | 6,932건 | 12.48억 | 10.75억 |
핵심 인사이트:
- 교육·주거 밀집(C1) 클러스터가 거래량 44,721건으로 전체의 63%를 차지합니다. 학원·학교 밀도가 높은 지역에 서울 아파트 거래가 집중됩니다.
- 단, 평균가(11.68억)와 중앙값(9.28억) 모두 4개 클러스터 중 가장 낮습니다. 대치동·반포동 같은 고가 지역과 노원·강북·은평 등 중저가 교육 지역이 같은 군집에 혼재하기 때문입니다.
- 저밀도 주거(C2) 클러스터는 거래량(2,025건)이 가장 적지만 평균가(13.43억)는 가장 높습니다. 이촌동처럼 조용하고 희소한 고급 주거지의 프리미엄이 반영됩니다.
- 상업 특화(C3) 클러스터(명동·충무로 등)의 평균가는 12.48억으로, 상권 활성화가 반드시 주거 비선호를 의미하지 않음을 보여줍니다.
각 클러스터의 핵심 지표와 대표 동네를 한눈에 정리합니다.
"서울에서 교육 인프라가 집중된 동네가 거래량은 압도적 1위지만, 가격이 가장 높은 곳은 오히려 조용한 저밀도 주거지입니다."
- 교육 인프라 = 최고의 주거 수요: C1의 거래량이 44,721건으로 전체 거래의 63%를 차지합니다. 학원·학교 밀도가 높은 동네에 서울 아파트 거래가 집중됩니다.
- 교육 접근성은 모든 가격대에 걸쳐 있다: C1은 평균가 11.68억으로 4개 클러스터 중 가장 낮습니다. 대치동(고가)과 중계동·노원·강북(중저가)이 같은 '교육 특화' 군집에 함께 속해 평균을 끌어내립니다.
- 조용한 저밀도 주거지가 실제 가격은 가장 높다: C2(저밀도 주거)는 거래량(2,025건)이 가장 적지만 평균 13.43억으로 집값 1위입니다. 이촌동처럼 밀도가 낮고 희소한 주거지에 프리미엄이 붙습니다.
- 같은 강남도 다르다: 신사동(C0, 상업·학원 복합)과 대치동(C1, 교육 특화)은 같은 강남구지만 완전히 다른 클러스터에 속합니다. 인프라 구성이 다르면 동네 성격도 다릅니다.
- PCA로 본 서울의 두 축: 서울 동네의 특성은 크게 '교육 인프라 축'과 '상권 집중 축' 두 가지로 설명할 수 있습니다.
- Decision Tree로 검증: 정확도 98.9%로, K-Means 군집이 임의로 나뉜 게 아니라 인프라 밀도로 설명되는 실제 패턴임을 확인했습니다.
git clone <repo-url>
cd DataMining-Project
pip install -r requirements.txt1. notebooks/02_preprocessing/kmeans_prep.ipynb
→ 소상공인·학교·학원 밀도 계산, Log1p 변환, StandardScaler 정규화
→ 출력: data/processed/df_scaled.csv, df_density.csv
2. notebooks/03_modeling/kmeans.ipynb
→ Elbow Method + Silhouette Score로 최적 K 탐색 (K=4 선택)
→ K-Means 클러스터링 수행
→ 출력: data/processed/kmeans_result.csv
→ 시각화: notebooks/result/kmeans/k_selection.png, cluster_radar_k5.png, notebooks/result/pca/cluster_pca_k5.png
3. notebooks/03_modeling/price_cluster.ipynb
→ 아파트 실거래가 데이터와 클러스터 결과 병합 및 통계 분석
→ 출력: notebooks/result/kmeans/cluster_price.png, cluster_price_box.png
4. notebooks/03_modeling/cluster_decision_tree.ipynb
→ K-Means 군집 레이블로 Decision Tree 학습
→ 군집 판별 규칙 도출 및 유효성 검증
→ 출력: notebooks/result/decision_tree/dt_*.png
5. notebooks/04_visualization/viz_presentation.ipynb
→ 레이더 차트, 집값 비교, PCA 시각화, 요약 카드 생성
→ 출력: notebooks/result/kmeans/ (pres_radar, pres_price, pres_summary)
notebooks/result/pca/ (pres_pca, pres_pca_explained, pres_pca_table)
notebooks/01_eda/노트북(store, school, academy, people)은 변수 탐색 참고용으로, 위 순서와 무관하게 실행 가능합니다.
notebooks/03_modeling/predict.ipynb은 인프라 밀도 → 집값 Ridge 회귀 보조 분석입니다.
모든 노트북은 .git 폴더를 기준으로 프로젝트 루트를 자동 탐색합니다.
별도의 경로 수정 없이 어느 위치에서 Jupyter를 실행해도 동작합니다.
DataMining-Project/
├── data/
│ ├── raw/ # 원본 CSV 데이터 (수정 금지)
│ │ ├── 소상공인.csv
│ │ ├── 학교.csv
│ │ ├── 학원.csv
│ │ ├── 토지 면적.csv
│ │ ├── 인구밀도.csv
│ │ └── 서울시_실거래가.csv
│ └── processed/ # 전처리 결과물 (CSV만 유지)
│ ├── df_scaled.csv # 정규화된 피처 데이터
│ ├── df_density.csv # 법정동별 밀도 데이터
│ └── kmeans_result.csv # 클러스터링 결과
├── notebooks/
│ ├── 01_eda/ # 변수별 탐색적 분석 (참고용)
│ │ ├── store.ipynb
│ │ ├── school.ipynb
│ │ ├── academy.ipynb
│ │ └── people.ipynb
│ ├── 02_preprocessing/ # 전처리 및 스케일링
│ │ └── kmeans_prep.ipynb
│ ├── 03_modeling/ # 모델링
│ │ ├── kmeans.ipynb # K-Means 클러스터링
│ │ ├── price_cluster.ipynb # 집값-군집 비교
│ │ ├── cluster_decision_tree.ipynb # Decision Tree 분류
│ │ └── predict.ipynb # Ridge 회귀 (집값 예측 보조)
│ ├── 04_visualization/ # 발표용 시각화
│ │ └── viz_presentation.ipynb
│ ├── common/ # 공통 유틸리티
│ └── result/ # 알고리즘별 시각화 결과
│ ├── kmeans/ # K-Means 관련 이미지
│ │ ├── k_selection.png # Elbow + Silhouette 그래프
│ │ ├── cluster_radar_k5.png # 클러스터별 레이더 차트 (중간)
│ │ ├── cluster_price.png # 클러스터별 평균·중앙값 집값
│ │ ├── cluster_price_box.png # 클러스터별 집값 박스플롯
│ │ ├── pres_radar.png # 발표용 레이더 차트
│ │ ├── pres_price.png # 발표용 집값 비교
│ │ └── pres_summary.png # 클러스터 요약 카드
│ ├── pca/ # PCA 관련 이미지
│ │ ├── cluster_pca_k5.png # K-Means PCA 산점도 (중간)
│ │ ├── pres_pca.png # 발표용 PCA 산점도
│ │ ├── pres_pca_explained.png # PCA 해석 슬라이드
│ │ └── pres_pca_table.png # PCA 로딩 테이블
│ └── decision_tree/ # Decision Tree 관련 이미지
│ ├── dt_depth.png # 트리 깊이 탐색
│ ├── dt_tree.png # Decision Tree 시각화
│ ├── dt_confusion.png # 혼동 행렬
│ └── dt_importance.png # Feature Importance
├── requirements.txt
└── README.md
| 라이브러리 | 용도 |
|---|---|
pandas |
데이터 로드 및 처리 |
numpy |
수치 연산, Log1p 변환 |
scikit-learn |
KMeans, PCA, DecisionTreeClassifier, StandardScaler, Ridge, silhouette_score |
matplotlib |
레이더 차트, 산점도, 트리 시각화 등 |









