import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
import seaborn as sns
from sklearn.manifold import MDS
from sklearn.metrics import pairwise_distances
from sklearn.preprocessing import StandardScaler, LabelEncoder
from sklearn.datasets import make_classification
from scipy.spatial.distance import pdist, squareform
import warnings
'ignore')
warnings.filterwarnings(
'font.family'] = 'Noto Sans KR' plt.rcParams[
다차원 척도법 (Multidimensional Scaling)
확률 통계
다차원 척도법(MDS)이란?
다차원 척도법(Multidimensional Scaling, MDS)은 고차원 데이터를 저차원 공간에 시각화하는 차원 축소 기법입니다. 객체들 간의 거리나 유사도를 보존하면서 2차원 또는 3차원 공간에 데이터를 투영합니다.
필요한 라이브러리 설치 및 임포트
샘플 데이터 생성
다양한 케이스를 위한 샘플 데이터를 생성합니다.
# 1. 연속변수만 포함하는 데이터
42)
np.random.seed(= make_classification(
continuous_data =100,
n_samples=5,
n_features=3,
n_classes=0,
n_redundant=5,
n_informative=42
random_state0]
)[
= pd.DataFrame(
continuous_df
continuous_data, =[f'feature_{i+1}' for i in range(5)]
columns
)
print("연속변수 데이터셋 shape:", continuous_df.shape)
print(continuous_df.head())
연속변수 데이터셋 shape: (100, 5)
feature_1 feature_2 feature_3 feature_4 feature_5
0 0.051936 -1.797511 -1.855638 -1.396449 -1.196204
1 0.403789 0.921306 3.200886 1.984403 0.106783
2 0.300321 -0.930015 0.162936 -0.576956 2.232421
3 -0.199444 -0.496488 -1.928236 0.929103 -1.480070
4 1.144153 -1.221289 -0.581620 -0.475414 1.675759
# 2. 명목변수를 포함하는 혼합 데이터
42)
np.random.seed(
# 연속변수
= np.random.normal(35, 10, 100)
age = np.random.normal(50000, 15000, 100)
income = np.random.normal(5, 3, 100)
experience
# 명목변수
= np.random.choice(['고등학교', '대학교', '대학원'], 100)
education = np.random.choice(['영업', '마케팅', '개발', 'HR'], 100)
department = np.random.choice(['서울', '부산', '대구', '광주'], 100)
location
= pd.DataFrame({
mixed_df 'age': age,
'income': income,
'experience': experience,
'education': education,
'department': department,
'location': location
})
print("\n혼합 데이터셋 shape:", mixed_df.shape)
print(mixed_df.head())
혼합 데이터셋 shape: (100, 6)
age income experience education department location
0 39.967142 28769.438869 6.073362 고등학교 개발 서울
1 33.617357 43690.320159 6.682354 대학교 마케팅 서울
2 41.476885 44859.282252 8.249154 고등학교 마케팅 광주
3 50.230299 37965.840962 8.161406 고등학교 개발 서울
4 32.658466 47580.714325 0.866992 대학원 개발 광주
# 3. 거리 행렬 데이터 (도시간 거리 예시)
= ['서울', '부산', '대구', '인천', '광주', '대전', '울산']
cities # 실제 도시간 거리 (km)
= np.array([
distance_matrix 0, 325, 237, 28, 267, 140, 340], # 서울
[325, 0, 88, 353, 158, 185, 45], # 부산
[237, 88, 0, 265, 215, 97, 85], # 대구
[28, 353, 265, 0, 295, 168, 368], # 인천
[267, 158, 215, 295, 0, 168, 200], # 광주
[140, 185, 97, 168, 168, 0, 230], # 대전
[340, 45, 85, 368, 200, 230, 0] # 울산
[
])
= pd.DataFrame(distance_matrix,
distance_df =cities,
index=cities)
columns
print("\n도시간 거리 행렬:")
print(distance_df)
도시간 거리 행렬:
서울 부산 대구 인천 광주 대전 울산
서울 0 325 237 28 267 140 340
부산 325 0 88 353 158 185 45
대구 237 88 0 265 215 97 85
인천 28 353 265 0 295 168 368
광주 267 158 215 295 0 168 200
대전 140 185 97 168 168 0 230
울산 340 45 85 368 200 230 0
1. 연속변수만 포함하는 기본 MDS 분석
연속변수로만 구성된 데이터에 MDS를 적용하는 예시입니다.
# 데이터 표준화
= StandardScaler()
scaler = scaler.fit_transform(continuous_df)
continuous_scaled
# 기본 MDS 적용 (2차원)
= MDS(n_components=2, random_state=42)
mds = mds.fit_transform(continuous_scaled)
mds_result
# 결과를 DataFrame으로 변환
= pd.DataFrame(mds_result, columns=['MDS1', 'MDS2'])
mds_df
print("MDS 결과:")
print(mds_df.head())
print(f"\nStress 값: {mds.stress_:.4f}")
MDS 결과:
MDS1 MDS2
0 0.329495 -1.755421
1 0.049463 3.238964
2 -0.946161 0.369966
3 0.357709 -0.007320
4 -0.769899 -0.435848
Stress 값: 3208.7256
# 시각화
=(10, 8))
plt.figure(figsize'MDS1'], mds_df['MDS2'], alpha=0.7, s=50)
plt.scatter(mds_df['MDS Dimension 1')
plt.xlabel('MDS Dimension 2')
plt.ylabel('MDS 결과 - 연속변수 데이터')
plt.title(True, alpha=0.3)
plt.grid(
# 각 점에 인덱스 번호 표시
for i, (x, y) in enumerate(zip(mds_df['MDS1'], mds_df['MDS2'])):
if i % 10 == 0: # 10개마다 번호 표시
str(i), (x, y), xytext=(5, 5),
plt.annotate(='offset points', fontsize=8)
textcoords
plt.tight_layout() plt.show()
# 클래스별로 색상을 다르게 하여 시각화 (원본 클래스 정보 사용)
= make_classification(
_, y_true =100,
n_samples=5,
n_features=3,
n_classes=0,
n_redundant=5,
n_informative=42
random_state
)
=(10, 8))
plt.figure(figsize= ['red', 'blue', 'green']
colors for i in range(3):
= y_true == i
mask 'MDS1'],
plt.scatter(mds_df.loc[mask, 'MDS2'],
mds_df.loc[mask, =colors[i], label=f'Class {i}', alpha=0.7, s=50)
c
'MDS Dimension 1')
plt.xlabel('MDS Dimension 2')
plt.ylabel('MDS 결과 - 클래스별 색상 구분')
plt.title(
plt.legend()True, alpha=0.3)
plt.grid(
plt.tight_layout() plt.show()
# 거리 보존 정도 확인
from sklearn.metrics import pairwise_distances
# 원본 데이터의 거리 행렬
= pairwise_distances(continuous_scaled)
original_distances # MDS 결과의 거리 행렬
= pairwise_distances(mds_result)
mds_distances
# 거리 상관계수 계산
= np.corrcoef(
distance_correlation
original_distances.flatten(),
mds_distances.flatten()0, 1]
)[
print(f"원본 거리와 MDS 거리의 상관계수: {distance_correlation:.4f}")
# Shepard diagram 그리기
=(8, 6))
plt.figure(figsize
plt.scatter(original_distances.flatten(),
mds_distances.flatten(), =0.3, s=1)
alpha'Original Distances')
plt.xlabel('MDS Distances')
plt.ylabel('Shepard Diagram - 거리 보존 정도')
plt.title(0, original_distances.max()],
plt.plot([0, original_distances.max()],
['r--', alpha=0.8)
True, alpha=0.3)
plt.grid(
plt.tight_layout() plt.show()
원본 거리와 MDS 거리의 상관계수: 0.8435
2. 명목변수를 포함하는 혼합 데이터 MDS 분석
명목변수와 연속변수가 혼합된 데이터에 MDS를 적용하는 예시입니다.
# 혼합 데이터 전처리
def preprocess_mixed_data(df):
"""혼합 데이터를 MDS에 적합하도록 전처리"""
= df.copy()
processed_df
# 연속변수 표준화
= ['age', 'income', 'experience']
continuous_cols = StandardScaler()
scaler = scaler.fit_transform(processed_df[continuous_cols])
processed_df[continuous_cols]
# 명목변수 원핫 인코딩
= ['education', 'department', 'location']
categorical_cols for col in categorical_cols:
= pd.get_dummies(processed_df[col], prefix=col)
dummies = pd.concat([processed_df, dummies], axis=1)
processed_df =1, inplace=True)
processed_df.drop(col, axis
return processed_df
# 데이터 전처리
= preprocess_mixed_data(mixed_df)
mixed_processed print("전처리된 혼합 데이터 shape:", mixed_processed.shape)
print("\n컬럼 목록:")
print(mixed_processed.columns.tolist())
전처리된 혼합 데이터 shape: (100, 14)
컬럼 목록:
['age', 'income', 'experience', 'education_고등학교', 'education_대학교', 'education_대학원', 'department_HR', 'department_개발', 'department_마케팅', 'department_영업', 'location_광주', 'location_대구', 'location_부산', 'location_서울']
# 혼합 데이터에 MDS 적용
= MDS(n_components=2, random_state=42)
mds_mixed = mds_mixed.fit_transform(mixed_processed)
mds_mixed_result
# 결과를 DataFrame으로 변환
= pd.DataFrame(mds_mixed_result, columns=['MDS1', 'MDS2'])
mds_mixed_df
print("혼합 데이터 MDS 결과:")
print(mds_mixed_df.head())
print(f"\nStress 값: {mds_mixed.stress_:.4f}")
혼합 데이터 MDS 결과:
MDS1 MDS2
0 1.112396 -1.853207
1 0.199845 -1.172801
2 -0.073573 -1.903613
3 -0.293398 -2.665801
4 1.287388 1.284487
Stress 값: 3843.1912
# 부서별로 색상을 다르게 하여 시각화
=(12, 8))
plt.figure(figsize= mixed_df['department'].unique()
departments = ['red', 'blue', 'green', 'orange']
colors
for i, dept in enumerate(departments):
= mixed_df['department'] == dept
mask 'MDS1'],
plt.scatter(mds_mixed_df.loc[mask, 'MDS2'],
mds_mixed_df.loc[mask, =colors[i], label=dept, alpha=0.7, s=60)
c
'MDS Dimension 1')
plt.xlabel('MDS Dimension 2')
plt.ylabel('MDS 결과 - 부서별 색상 구분 (혼합 데이터)')
plt.title(
plt.legend()True, alpha=0.3)
plt.grid(
plt.tight_layout() plt.show()
# 학력별로 마커를 다르게 하여 시각화
=(12, 8))
plt.figure(figsize= mixed_df['education'].unique()
educations = ['o', 's', '^']
markers
for i, edu in enumerate(educations):
= mixed_df['education'] == edu
mask 'MDS1'],
plt.scatter(mds_mixed_df.loc[mask, 'MDS2'],
mds_mixed_df.loc[mask, =markers[i], label=edu, alpha=0.7, s=60)
marker
'MDS Dimension 1')
plt.xlabel('MDS Dimension 2')
plt.ylabel('MDS 결과 - 학력별 마커 구분 (혼합 데이터)')
plt.title(
plt.legend()True, alpha=0.3)
plt.grid(
plt.tight_layout() plt.show()
# 부서와 학력을 함께 시각화
= plt.subplots(1, 2, figsize=(20, 8))
fig, (ax1, ax2)
# 부서별 시각화
= mixed_df['department'].unique()
departments = ['red', 'blue', 'green', 'orange']
colors
for i, dept in enumerate(departments):
= mixed_df['department'] == dept
mask 'MDS1'],
ax1.scatter(mds_mixed_df.loc[mask, 'MDS2'],
mds_mixed_df.loc[mask, =colors[i], label=dept, alpha=0.7, s=60)
c
'MDS Dimension 1')
ax1.set_xlabel('MDS Dimension 2')
ax1.set_ylabel('부서별 구분')
ax1.set_title(
ax1.legend()True, alpha=0.3)
ax1.grid(
# 학력별 시각화
= mixed_df['education'].unique()
educations = ['purple', 'brown', 'pink']
colors2
for i, edu in enumerate(educations):
= mixed_df['education'] == edu
mask 'MDS1'],
ax2.scatter(mds_mixed_df.loc[mask, 'MDS2'],
mds_mixed_df.loc[mask, =colors2[i], label=edu, alpha=0.7, s=60)
c
'MDS Dimension 1')
ax2.set_xlabel('MDS Dimension 2')
ax2.set_ylabel('학력별 구분')
ax2.set_title(
ax2.legend()True, alpha=0.3)
ax2.grid(
plt.tight_layout() plt.show()
# Gower 거리를 사용한 혼합 데이터 MDS
def gower_distance(X):
"""Gower 거리 계산 (연속변수와 명목변수 혼합용)"""
= X.shape
n_samples, n_features = np.zeros((n_samples, n_samples))
distances
# 각 피처가 연속변수인지 이진변수인지 판단
= []
is_continuous for j in range(n_features):
= np.unique(X[:, j])
unique_vals len(unique_vals) > 2)
is_continuous.append(
for i in range(n_samples):
for j in range(i+1, n_samples):
= 0
distance for k in range(n_features):
if is_continuous[k]:
# 연속변수: 절대차이를 범위로 나눔
= np.max(X[:, k]) - np.min(X[:, k])
range_k if range_k > 0:
+= abs(X[i, k] - X[j, k]) / range_k
distance else:
# 명목변수: 같으면 0, 다르면 1
+= 0 if X[i, k] == X[j, k] else 1
distance
= distances[j, i] = distance / n_features
distances[i, j]
return distances
# 원본 혼합 데이터로 Gower 거리 계산
= mixed_processed.values
mixed_array = gower_distance(mixed_array)
gower_dist
# Gower 거리 기반 MDS
= MDS(n_components=2, dissimilarity='precomputed', random_state=42)
mds_gower = mds_gower.fit_transform(gower_dist)
mds_gower_result
# 결과 시각화
=(10, 8))
plt.figure(figsize= mixed_df['department'].unique()
departments = ['red', 'blue', 'green', 'orange']
colors
for i, dept in enumerate(departments):
= mixed_df['department'] == dept
mask 0],
plt.scatter(mds_gower_result[mask, 1],
mds_gower_result[mask, =colors[i], label=dept, alpha=0.7, s=60)
c
'MDS Dimension 1')
plt.xlabel('MDS Dimension 2')
plt.ylabel('Gower 거리 기반 MDS 결과')
plt.title(
plt.legend()True, alpha=0.3)
plt.grid(
plt.tight_layout()
plt.show()
print(f"Gower 거리 기반 MDS Stress 값: {mds_gower.stress_:.4f}")
Gower 거리 기반 MDS Stress 값: 70.5540
3. 거리 행렬 기반 MDS 분석
미리 계산된 거리 행렬을 사용하여 MDS를 적용하는 예시입니다.
# 도시간 거리 행렬로 MDS 수행
= MDS(n_components=2, dissimilarity='precomputed', random_state=42)
mds_distance = mds_distance.fit_transform(distance_matrix)
city_mds_result
# 결과를 DataFrame으로 변환
= pd.DataFrame(city_mds_result,
city_mds_df =['MDS1', 'MDS2'],
columns=cities)
index
print("도시 MDS 결과:")
print(city_mds_df)
print(f"\nStress 값: {mds_distance.stress_:.4f}")
도시 MDS 결과:
MDS1 MDS2
서울 -85.433007 -154.346568
부산 59.327174 130.031955
대구 -27.112189 81.647524
인천 -99.393194 -179.287057
광주 145.126739 -10.586077
대전 -24.860674 -32.769460
울산 32.345151 165.309682
Stress 값: 2023.2616
# 도시 위치 시각화
=(12, 10))
plt.figure(figsize= ['red', 'blue', 'green', 'orange', 'purple', 'brown', 'pink']
colors
for i, city in enumerate(cities):
'MDS1'],
plt.scatter(city_mds_df.loc[city, 'MDS2'],
city_mds_df.loc[city, =colors[i], s=200, alpha=0.7,
c=city, edgecolors='black', linewidth=1)
label
# 도시 이름 표시
plt.annotate(city, 'MDS1'], city_mds_df.loc[city, 'MDS2']),
(city_mds_df.loc[city, =(10, 10), textcoords='offset points',
xytext=12, fontweight='bold')
fontsize
'MDS Dimension 1')
plt.xlabel('MDS Dimension 2')
plt.ylabel('도시간 거리 기반 MDS 결과')
plt.title(True, alpha=0.3)
plt.grid(=(1.05, 1), loc='upper left')
plt.legend(bbox_to_anchor
plt.tight_layout() plt.show()
# 실제 거리와 MDS 거리 비교
= pairwise_distances(city_mds_result)
mds_city_distances
# 거리 상관계수 계산
= np.corrcoef(
city_distance_correlation
distance_matrix.flatten(),
mds_city_distances.flatten()0, 1]
)[
print(f"실제 거리와 MDS 거리의 상관계수: {city_distance_correlation:.4f}")
# Shepard diagram for city distances
=(8, 6))
plt.figure(figsize
plt.scatter(distance_matrix.flatten(),
mds_city_distances.flatten(), =0.6, s=30)
alpha'Original Distances (km)')
plt.xlabel('MDS Distances')
plt.ylabel('Shepard Diagram - 도시간 거리 보존 정도')
plt.title(0, distance_matrix.max()],
plt.plot([0, distance_matrix.max()],
['r--', alpha=0.8)
True, alpha=0.3)
plt.grid(
plt.tight_layout() plt.show()
실제 거리와 MDS 거리의 상관계수: 0.9970
# 3차원 MDS 수행
= MDS(n_components=3, dissimilarity='precomputed', random_state=42)
mds_3d = mds_3d.fit_transform(distance_matrix)
city_mds_3d_result
# 3D 시각화
from mpl_toolkits.mplot3d import Axes3D
= plt.figure(figsize=(12, 10))
fig = fig.add_subplot(111, projection='3d')
ax
= ['red', 'blue', 'green', 'orange', 'purple', 'brown', 'pink']
colors
for i, city in enumerate(cities):
0],
ax.scatter(city_mds_3d_result[i, 1],
city_mds_3d_result[i, 2],
city_mds_3d_result[i, =colors[i], s=200, alpha=0.7,
c=city, edgecolors='black', linewidth=1)
label
# 도시 이름 표시
0],
ax.text(city_mds_3d_result[i, 1],
city_mds_3d_result[i, 2],
city_mds_3d_result[i, =10, fontweight='bold')
city, fontsize
'MDS Dimension 1')
ax.set_xlabel('MDS Dimension 2')
ax.set_ylabel('MDS Dimension 3')
ax.set_zlabel('3차원 MDS 결과 - 도시간 거리')
ax.set_title(=(1.1, 1), loc='upper left')
ax.legend(bbox_to_anchor
plt.tight_layout()
plt.show()
print(f"3차원 MDS Stress 값: {mds_3d.stress_:.4f}")
3차원 MDS Stress 값: 1974.2860
# 차원별 Stress 값 비교
= range(1, 6)
dimensions = []
stress_values
for dim in dimensions:
= MDS(n_components=dim, dissimilarity='precomputed', random_state=42)
mds_temp
mds_temp.fit(distance_matrix)
stress_values.append(mds_temp.stress_)
# Stress plot (Scree plot)
=(10, 6))
plt.figure(figsize'bo-', linewidth=2, markersize=8)
plt.plot(dimensions, stress_values, '차원 수')
plt.xlabel('Stress 값')
plt.ylabel('차원 수에 따른 Stress 값 변화')
plt.title(True, alpha=0.3)
plt.grid(
plt.xticks(dimensions)
# 각 점에 stress 값 표시
for i, stress in enumerate(stress_values):
f'{stress:.3f}',
plt.annotate(
(dimensions[i], stress), =(0, 10), textcoords='offset points',
xytext='center', fontsize=10)
ha
plt.tight_layout()
plt.show()
print("차원별 Stress 값:")
for dim, stress in zip(dimensions, stress_values):
print(f"{dim}차원: {stress:.4f}")
차원별 Stress 값:
1차원: 340937.8571
2차원: 2023.2616
3차원: 1974.2860
4차원: 1980.2935
5차원: 1982.6011
4. 다양한 MDS 변형 기법
다양한 MDS 알고리즘을 비교해보는 예시입니다.
# 다양한 MDS 알고리즘 비교
from sklearn.manifold import MDS
# 연속 데이터를 사용하여 다양한 MDS 알고리즘 비교
= {
algorithms 'Classical MDS': MDS(metric=True, random_state=42),
'Non-metric MDS': MDS(metric=False, random_state=42),
'MDS with different init': MDS(metric=True, n_init=10, random_state=42)
}
= plt.subplots(1, 3, figsize=(18, 6))
fig, axes
for idx, (name, mds_algo) in enumerate(algorithms.items()):
# MDS 수행
= mds_algo.fit_transform(continuous_scaled)
result
# 클래스별 색상으로 시각화
= ['red', 'blue', 'green']
colors for i in range(3):
= y_true == i
mask 0], result[mask, 1],
axes[idx].scatter(result[mask, =colors[i], label=f'Class {i}', alpha=0.7, s=30)
c
'MDS Dimension 1')
axes[idx].set_xlabel('MDS Dimension 2')
axes[idx].set_ylabel(f'{name}\nStress: {mds_algo.stress_:.4f}')
axes[idx].set_title(
axes[idx].legend()True, alpha=0.3)
axes[idx].grid(
plt.tight_layout() plt.show()
# 거리 메트릭 비교
from sklearn.metrics.pairwise import euclidean_distances, manhattan_distances, cosine_distances
# 다양한 거리 메트릭으로 MDS 수행
= {
distance_metrics 'Euclidean': euclidean_distances(continuous_scaled),
'Manhattan': manhattan_distances(continuous_scaled),
'Cosine': cosine_distances(continuous_scaled)
}
= plt.subplots(1, 3, figsize=(18, 6))
fig, axes
for idx, (metric_name, dist_matrix) in enumerate(distance_metrics.items()):
# 거리 행렬을 사용한 MDS
= MDS(n_components=2, dissimilarity='precomputed', random_state=42)
mds_metric = mds_metric.fit_transform(dist_matrix)
result
# 클래스별 색상으로 시각화
= ['red', 'blue', 'green']
colors for i in range(3):
= y_true == i
mask 0], result[mask, 1],
axes[idx].scatter(result[mask, =colors[i], label=f'Class {i}', alpha=0.7, s=30)
c
'MDS Dimension 1')
axes[idx].set_xlabel('MDS Dimension 2')
axes[idx].set_ylabel(f'{metric_name} Distance MDS\nStress: {mds_metric.stress_:.4f}')
axes[idx].set_title(
axes[idx].legend()True, alpha=0.3)
axes[idx].grid(
plt.tight_layout() plt.show()
5. MDS 결과 해석 및 평가
MDS 결과를 해석하고 평가하는 방법들을 소개합니다.
# Stress 값 해석 기준
def interpret_stress(stress_value):
"""Stress 값을 해석하는 함수"""
if stress_value < 0.05:
return "매우 좋음 (Excellent)"
elif stress_value < 0.1:
return "좋음 (Good)"
elif stress_value < 0.2:
return "보통 (Fair)"
else:
return "나쁨 (Poor)"
# 각 MDS 결과의 Stress 값 해석
print("=== MDS 결과 평가 ===")
print(f"연속변수 MDS Stress: {mds.stress_:.4f} - {interpret_stress(mds.stress_)}")
print(f"혼합데이터 MDS Stress: {mds_mixed.stress_:.4f} - {interpret_stress(mds_mixed.stress_)}")
print(f"Gower 거리 MDS Stress: {mds_gower.stress_:.4f} - {interpret_stress(mds_gower.stress_)}")
print(f"도시 거리 MDS Stress: {mds_distance.stress_:.4f} - {interpret_stress(mds_distance.stress_)}")
=== MDS 결과 평가 ===
연속변수 MDS Stress: 3208.7256 - 나쁨 (Poor)
혼합데이터 MDS Stress: 3843.1912 - 나쁨 (Poor)
Gower 거리 MDS Stress: 70.5540 - 나쁨 (Poor)
도시 거리 MDS Stress: 2023.2616 - 나쁨 (Poor)
# 차원 축소 효과 비교
def calculate_explained_variance_ratio(original_data, mds_result):
"""MDS로 설명되는 분산 비율 계산"""
= np.var(original_data, axis=0).sum()
original_var = np.var(mds_result, axis=0).sum()
mds_var return mds_var / original_var
# 연속변수 데이터의 분산 설명 비율
= calculate_explained_variance_ratio(continuous_scaled, mds_result)
explained_ratio print(f"\n연속변수 MDS 분산 설명 비율: {explained_ratio:.4f} ({explained_ratio*100:.2f}%)")
# 원본 데이터 차원과 MDS 결과 비교
print(f"원본 데이터 차원: {continuous_scaled.shape[1]}차원")
print(f"MDS 결과 차원: {mds_result.shape[1]}차원")
print(f"차원 축소율: {(1 - mds_result.shape[1]/continuous_scaled.shape[1])*100:.1f}%")
연속변수 MDS 분산 설명 비율: 0.9358 (93.58%)
원본 데이터 차원: 5차원
MDS 결과 차원: 2차원
차원 축소율: 60.0%
# MDS vs PCA 비교
from sklearn.decomposition import PCA
# PCA 수행
= PCA(n_components=2, random_state=42)
pca = pca.fit_transform(continuous_scaled)
pca_result
# 비교 시각화
= plt.subplots(1, 2, figsize=(16, 6))
fig, (ax1, ax2)
# MDS 결과
= ['red', 'blue', 'green']
colors for i in range(3):
= y_true == i
mask 0], mds_result[mask, 1],
ax1.scatter(mds_result[mask, =colors[i], label=f'Class {i}', alpha=0.7, s=30)
c
'MDS Dimension 1')
ax1.set_xlabel('MDS Dimension 2')
ax1.set_ylabel(f'MDS 결과\nStress: {mds.stress_:.4f}')
ax1.set_title(
ax1.legend()True, alpha=0.3)
ax1.grid(
# PCA 결과
for i in range(3):
= y_true == i
mask 0], pca_result[mask, 1],
ax2.scatter(pca_result[mask, =colors[i], label=f'Class {i}', alpha=0.7, s=30)
c
'PC1')
ax2.set_xlabel('PC2')
ax2.set_ylabel(f'PCA 결과\n설명분산비율: {pca.explained_variance_ratio_.sum():.4f}')
ax2.set_title(
ax2.legend()True, alpha=0.3)
ax2.grid(
plt.tight_layout()
plt.show()
print(f"PCA 설명 분산 비율: {pca.explained_variance_ratio_.sum():.4f} ({pca.explained_variance_ratio_.sum()*100:.2f}%)")
print(f"각 주성분별 설명 분산 비율: PC1={pca.explained_variance_ratio_[0]:.4f}, PC2={pca.explained_variance_ratio_[1]:.4f}")
PCA 설명 분산 비율: 0.5028 (50.28%)
각 주성분별 설명 분산 비율: PC1=0.2727, PC2=0.2301
결론
다차원 척도법(MDS)은 다양한 유형의 데이터에 적용할 수 있는 강력한 차원 축소 기법입니다:
주요 특징:
- 거리 보존: 원본 데이터의 거리 관계를 저차원에서 최대한 보존
- 유연성: 연속변수, 명목변수, 거리 행렬 등 다양한 데이터 타입 지원
- 직관적 해석: 2D/3D 시각화를 통한 직관적인 데이터 이해
적용 사례:
- 연속변수: 표준화 후 직접 적용
- 혼합 데이터: 원핫 인코딩 또는 Gower 거리 사용
- 거리 행렬: 미리 계산된 거리 정보 활용
평가 지표:
- Stress 값: 낮을수록 좋음 (< 0.1이 권장)
- 거리 상관계수: 원본과 MDS 거리의 상관관계
- Shepard diagram: 거리 보존 정도 시각화