yuns

Mahalanobis 거리 구하기(직접 구현/scipy모듈 활용) 본문

머신러닝

Mahalanobis 거리 구하기(직접 구현/scipy모듈 활용)

yuuuun 2025. 4. 6. 16:20
반응형

Mahalanobis 거리 직접 계산 vs. scipy로 계산하기

데이터 간의 거리를 측정할 때 유클리디안 거리(Euclidean distance)가 가장 많이 쓰이지만, 데이터의 분포를 고려해야 할 땐 Mahalanobis 거리(Mahalanobis distance)가 훨씬 더 적합합니다.

이번 글에서는 두 가지 방식으로 Mahalanobis 거리를 계산해 보겠습니다:

  1. np.matmul을 이용한 수식 구현
  2. scipy.spatial.distance.cdist를 이용한 간단한 계산

📌 Mahalanobis 거리란?

Mahalanobis 거리는 다음과 같은 수식으로 정의됩니다:

$$
D_M(x, \mu) = \sqrt{(x - \mu)^T S^{-1} (x - \mu)}
$$

  • $x$: 샘플 벡터
  • $\mu$: 평균 벡터 (또는 기준 벡터)
  • $S$: 공분산 행렬
  • $S^{-1}$: 공분산 행렬의 역행렬

1️⃣ np.matmul을 사용한 Mahalanobis 거리 계산

import numpy as np

# 예제 데이터
x = np.array([2, 3])
mu = np.array([0, 0])
X = np.array([
    [1, 2],
    [2, 1],
    [3, 5],
])

# 공분산 행렬 계산 및 역행렬
S = np.cov(X.T)
S_inv = np.linalg.inv(S)

# Mahalanobis 거리 수식 직접 계산
diff = x - mu
d = np.sqrt(np.matmul(np.matmul(diff.T, S_inv), diff))
print(f"Mahalanobis Distance (manual): {d}")

✅ np.matmul(A, B)은 $A\cdot B$와 동일한 행렬 곱입니다. 중첩 사용해서 수식 그대로 구현 가능합니다.


2️⃣ scipy.spatial.distance.cdist를 활용한 계산

더 간편하게 여러 벡터 간의 Mahalanobis 거리를 계산하고 싶다면, cdist 함수가 제격입니다

from scipy.spatial import distance

# X: 여러 샘플, y: 하나의 기준 벡터
X = np.array([
    [1, 2],
    [2, 1],
    [3, 5],
])
y = np.array([[2, 3]])

# 공분산 행렬의 역행렬
VI = np.linalg.inv(np.cov(X.T))

# cdist를 이용해 Mahalanobis 거리 계산
dists = distance.cdist(X, y, metric='mahalanobis', VI=VI)
print(f"Mahalanobis Distance (cdist): \n{dists}")

 

## 코드로 비교

import numpy as np
import pandas as pd

# 데이터 생성
df1 = np.random.rand(100, 20)
df2 = np.random.rand(5, 20)

def mahalanobis_distance_matrix(df1, df2, cov):
    """
    df1: (n, d) numpy 배열
    df2: (m, d) numpy 배열
    cov: (d, d) 공분산 행렬
    반환값: (n, m) 거리 행렬
    """
    cov_inv = np.linalg.inv(cov)
    
    # 두 배열의 점들 간 차이 벡터 생성
    diff = df1[:, np.newaxis, :] - df2[np.newaxis, :, :]  # (n, m, d)
    
    # (x - μ) @ Σ^-1
    left = np.matmul(diff, cov_inv)  # (n, m, d)
    
    # (x - μ) @ Σ^-1 @ (x - μ)^T = element-wise 곱 후 축 계산
    dist_sq = np.sum(left * diff, axis=2)  # (n, m)
    
    return np.sqrt(dist_sq)

# 직접 구현
cov = np.cov(np.vstack([df1, df2]).T)
print("직접 구현\t", mahalanobis_distance_matrix(df1, df2, cov))

# scipy 모듈 활용
from scipy.spatial.distance import cdist
print("scipy 모듈 활용\t", cdist(df1, df2, metric='mahalanobis'))
반응형
Comments