비지도 학습 템플릿

데이터 분석
공개

2025년 10월 5일

군집 분석

Distance-based methods

  • Partitioning methods
k-means
from sklearn.cluster import KMeans
from sklearn.preprocessing import StandardScaler

scaler = StandardScaler() # or RobustScaler
df_scaled = scaler.fit_transform(df)

# Elbow method
I = []
for i in range(1, 11):
    kmeans = KMeans(n_clusters=i) # sklearn은 기본적으로 k-means++
    kmeans.fit(df_scaled)
    I.append(kmeans.inertia_)
plt.plot(range(1, 11), I, marker='o')

# k 선택 후 군집화
kmeans = KMeans(n_clusters=3)
kmeans.fit(df_scaled)
df['cluster'] = kmeans.labels_

# 군집 중심값 정보
centers = scaler.inverse_transform(kmeans.cluster_centers_)
centers_df = pd.DataFrame(centers, columns=df.columns[:-1], index=[f'cluster_{i}' for i in range(centers.shape[0])])
display(centers_df)
  1. Kmeans
    • polinominal 시간 안에 해결 가능
    • noise, outlier에 민감함
    • 수치형만 처리 가능
  2. k-modes: 범주형 데이터 처리 가능. 빈도수로 유사도 처리함
  3. k-prototype: 범주형, 수치형 섞인거 처리 가능
  4. k-medoids: 중심에 위치한 데이터 포인트를 사용해서 outlier 잘 처리함
    • PAM: Partitioning Around Medoids
      • scalability 문제 있음
    • CLARA: sampling을 통해서 PAM의 scalability 문제를 해결
      • 샘플링 과정에서 biased될 수 있음
    • CLARANS: medoid 후보를 랜덤하게 선택함

k-modes, k-prototype, k-medoids는 ADP 환경에서 제공 안함 ADP 환경에서 제공하는 모듈로는 범주형, 수치형 섞인거 처리하는 군집 방법이 없음 (일단 내가 생각하기로는 그렇다)

  • Hierarchical methods
  1. top-down: divisive, dia
  2. bottom-up: agglomerative
agglomerative
from scipy.cluster.hierarchy import dendrogram, linkage. cut_tree

z = linkage(df_scaled, method='ward') # 'single', 'complete', 'average', 'ward' 등등

result = cut_tree(z, n_clusters=3).flatten()

d = dendogram(z, labels=list(df.index))
plt.show()
합쳐지는 거리
thr = pd.DataFrame(d['dcoord'])
thr # (0, 3) = 합쳐지기 전 왼쪽, 오른쪽 높이, (1, 2) = 합쳐진 후 높이
thr[thr[2] > 70].sort_values(2)[[2]] # 70 이상의 높이에서 합쳐지는 군집들의 높이
  • 거리기반 군집의 단점:
    • 군집의 모양이 구형이 아닐 경우 찾기 어려움
    • 군집의 갯수 결정하기 어려움
    • 군집의 밀도가 높아야함

Density-based methods

  • 다양한 모양의 군집을 찾을 수 있음
  • noise, outlier에 강함
  1. DBSCAN: 잡음 포인트는 군집에서 제외
    1. core point를 찾음(eps 이내에 minPts 이상 있는 점)
    2. core point를 중심으로 군집을 확장
      • core point가 아닌 경우 확장 종료
    • 고정된 파라미터를 사용하기 때문에 군집간 밀도가 다를 경우 잘 못찾음
    • 군집간 계층관계를 인식하기 어렵다
    • 대신 빠르고, DBSCAN만으로도 충분해서 많이 사용되는 듯
DBSCAN eps 결정
from sklearn.neighbors import NearestNeighbors

MIN_SAMPLES = 5
df_scaled = scaler.fit_transform(df)

neigh = NearestNeighbors(n_neighbors=MIN_SAMPLES)
neigh.fit(df_scaled)
distances, indices = neigh.kneighbors(df_scaled)

k_distances = np.sort(distances[:, MIN_SAMPLES-1])[::-1]

# k-dist plot 그리기
plt.figure(figsize=(12, 6))
plt.plot(k_distances)
plt.xlabel('Data Points sorted by distance')
plt.ylabel(f'{MIN_SAMPLES-1}-th Nearest Neighbor Distance') # 자기 자신을 제외한 거리
plt.title('k-distance Graph')
plt.grid(True)
plt.show()
  • min samples 기준
    • 2 * 차원, log(샘플 수), 4~5개 등등의 기준이 있다.
    • 이론적으로 증명이 되거나 한건 아니니까 적절히 선택하거나, for문 돌려가면서 최적값 찾기
  • eps 기준
    • k-dist plot에서 급격히 꺾이는 지점
DBSCAN
from sklearn.cluster import DBSCAN

# k-dist plot에서 찾은 값으로 DBSCAN 적용
eps_value = 18  # k-dist plot에서 결정한 값

db = DBSCAN(eps=eps_value, min_samples=MIN_SAMPLES).fit(df_scaled)
labels = db.labels_
  1. OPTICS: DBSCAN의 단점을 보완
    • 군집의 밀도가 다를 때도 잘 처리함
    • 군집의 계층 구조를 인식할 수 있음
    • minPts 파라미터가 필요함
    • 얘로도 이상치 탐지 가능
OPTICS
from sklearn.cluster import OPTICS

optics = OPTICS(min_samples=MIN_SAMPLES).fit(df_scaled)
labels = optics.labels_

평가

  • silhuette score: \(\frac{\sum_{i=1}^{n} s(i)}{n}\)
    • s(i): \(\frac{b(i) - a(i)}{max((a(i), b(i)))}\)
      • a(i): 군집 내 노드간의 평균 거리
      • b(i): 가장 가까운 군집과의 노드 간 평균 거리
    • 1에 가까울 수록 좋음
  • 그 외 sklearn metrics 참고

차원 축소

  • PCA, LSA, t-SNE, UMAP, ICA, MDS, NMF 등등
    • 정말 많은 방법들이 있다.
  • 요인분석에서 확인적 요인분석은 python에서 제공하는 library가 없는걸로 알고 있음

연관 분석

from mlxtend.preprocessing import TransactionEncoder
from mlxtend.frequent_patterns import apriori, association_rules

transactions = {}
te = TransactionEncoder()

# 전처리
data = df['target'].str.split(', ').values
te_ary = te.fit_transform(data)
transactions[name] = pd.DataFrame(te_ary, columns=te.columns_)

# 빈발패턴 생성
fset = apriori(t, min_support=0.6, use_colnames=True, verbose=False)
if fset.shape[0] == 0:
    print("빈발패턴이 존재하지 않습니다.")
else:
    # 연관규칙 생성
    rule = association_rules(fset, metric="confidence", min_threshold=0.7)
    rule['len_ant'] = rule['antecedents'].apply(lambda x: len(x))
    rule['len_con'] = rule['consequents'].apply(lambda x: len(x))
    display(rule[(rule['len_con'] == 1) & (rule['lift'] >= 1.2)].reset_index(drop=True))
  • 지지도: 전체 거래에서 특정 항목 집합이 나타나는 비율
  • 신뢰도: 특정 항목 집합이 주어졌을 때 다른 항목
  • 향상도: 두 항목 집합이 독립적인 경우에 비해 함께 나타날 가능성
맨 위로