初学者です。データフレームの3つ以上のカラムを用いて、多次元空間におけるクラスタリングを行いたいと考えています。
以下は2次元空間におけるxmeans適用の関数です。(出所)
これを3次元以上の多次元空間に応用すべく、修正したいと考えています。
import numpy as np from scipy import stats from sklearn.cluster import KMeans import matplotlib.pyplot as plt from IPython.display import display, HTML # Jupyter notebook用 import pandas as pd %matplotlib inline class XMeans: """ x-means法を行うクラス """ def __init__(self, k_init = 2, **k_means_args): """ k_init : The initial number of clusters applied to KMeans() """ self.k_init = k_init self.k_means_args = k_means_args def fit(self, X): """ x-means法を使ってデータXをクラスタリングする X : array-like or sparse matrix, shape=(n_samples, n_features) """ self.__clusters = [] clusters = self.Cluster.build(X, KMeans(self.k_init, **self.k_means_args).fit(X)) self.__recursively_split(clusters) self.labels_ = np.empty(X.shape[0], dtype = np.intp) for i, c in enumerate(self.__clusters): self.labels_[c.index] = i self.cluster_centers_ = np.array([c.center for c in self.__clusters]) self.cluster_log_likelihoods_ = np.array([c.log_likelihood() for c in self.__clusters]) self.cluster_sizes_ = np.array([c.size for c in self.__clusters]) return self def __recursively_split(self, clusters): """ 引数のclustersを再帰的に分割する clusters : list-like object, which contains instances of 'XMeans.Cluster' 'XMeans.Cluster'のインスタンスを含むリスト型オブジェクト """ for cluster in clusters: if cluster.size <= 3: self.__clusters.append(cluster) continue k_means = KMeans(2, **self.k_means_args).fit(cluster.data) c1, c2 = self.Cluster.build(cluster.data, k_means, cluster.index) beta = np.linalg.norm(c1.center - c2.center) / np.sqrt(np.linalg.det(c1.cov) + np.linalg.det(c2.cov)) alpha = 0.5 / stats.norm.cdf(beta) bic = -2 * (cluster.size * np.log(alpha) + c1.log_likelihood() + c2.log_likelihood()) + 2 * cluster.df * np.log(cluster.size) if bic < cluster.bic(): self.__recursively_split([c1, c2]) else: self.__clusters.append(cluster) class Cluster: """ k-means法によって生成されたクラスタに関する情報を持ち、尤度やBICの計算を行うクラス """ @classmethod def build(cls, X, k_means, index = None): if index is None: index = np.array(range(0, X.shape[0])) labels = range(0, k_means.get_params()["n_clusters"]) return tuple(cls(X, index, k_means, label) for label in labels) # index: Xの各行におけるサンプルが元データの何行目のものかを示すベクトル def __init__(self, X, index, k_means, label): self.data = X[k_means.labels_ == label] self.index = index[k_means.labels_ == label] self.size = self.data.shape[0] self.df = self.data.shape[1] * (self.data.shape[1] + 3) / 2 self.center = k_means.cluster_centers_[label] self.cov = np.cov(self.data.T) def log_likelihood(self): return sum(stats.multivariate_normal.logpdf(x, self.center, self.cov) for x in self.data) def bic(self): return -2 * self.log_likelihood() + self.df * np.log(self.size)
この関数に対して、あるデータフレームから取ってくる3×Nの行列を渡します。
data = pd.read_csv(r"file/to/path/data.csv") data = data.replace(np.inf,np.nan).dropna() if __name__ == "__main__": import matplotlib.pyplot as plt test_array = np.array([data["A"].tolist(),data["B"].tolist(),data["C"].tolist()], np.int32).T # クラスタリングの実行 x_means = XMeans(random_state = 1).fit(test_array)
しかし、以下のようなエラーとなります。逆行列を計算できないとあります。
--------------------------------------------------------------------------- LinAlgError Traceback (most recent call last) <ipython-input-21-450bc8bb6146> in <module>() 11 12 # クラスタリングの実行 ---> 13 x_means = XMeans(random_state = 1).fit(test_array) 14 print(x_means.labels_) 15 print(x_means.cluster_centers_) <ipython-input-5-4709a667a908> in fit(self, X) 19 20 clusters = self.Cluster.build(X, KMeans(self.k_init, **self.k_means_args).fit(X)) ---> 21 self.__recursively_split(clusters) 22 23 self.labels_ = np.empty(X.shape[0], dtype = np.intp) <ipython-input-5-4709a667a908> in __recursively_split(self, clusters) 50 51 if bic < cluster.bic(): ---> 52 self.__recursively_split([c1, c2]) 53 else: 54 self.__clusters.append(cluster) <ipython-input-5-4709a667a908> in __recursively_split(self, clusters) 50 51 if bic < cluster.bic(): ---> 52 self.__recursively_split([c1, c2]) 53 else: 54 self.__clusters.append(cluster) #中略 このまとまりと同じエラーが何度も繰り返されます <ipython-input-5-4709a667a908> in __recursively_split(self, clusters) 47 beta = np.linalg.norm(c1.center - c2.center) / np.sqrt(np.linalg.det(c1.cov) + np.linalg.det(c2.cov)) 48 alpha = 0.5 / stats.norm.cdf(beta) ---> 49 bic = -2 * (cluster.size * np.log(alpha) + c1.log_likelihood() + c2.log_likelihood()) + 2 * cluster.df * np.log(cluster.size) 50 51 if bic < cluster.bic(): <ipython-input-5-4709a667a908> in log_likelihood(self) 77 78 def log_likelihood(self): ---> 79 return sum(stats.multivariate_normal.logpdf(x, self.center, self.cov) for x in self.data) 80 81 def bic(self): <ipython-input-5-4709a667a908> in <genexpr>(.0) 77 78 def log_likelihood(self): ---> 79 return sum(stats.multivariate_normal.logpdf(x, self.center, self.cov) for x in self.data) 80 81 def bic(self): /usr/local/lib/python3.6/dist-packages/scipy/stats/_multivariate.py in logpdf(self, x, mean, cov, allow_singular) 478 dim, mean, cov = self._process_parameters(None, mean, cov) 479 x = self._process_quantiles(x, dim) --> 480 psd = _PSD(cov, allow_singular=allow_singular) 481 out = self._logpdf(x, mean, psd.U, psd.log_pdet, psd.rank) 482 return _squeeze_output(out) /usr/local/lib/python3.6/dist-packages/scipy/stats/_multivariate.py in __init__(self, M, cond, rcond, lower, check_finite, allow_singular) 155 d = s[s > eps] 156 if len(d) < len(s) and not allow_singular: --> 157 raise np.linalg.LinAlgError('singular matrix') 158 s_pinv = _pinv_1d(s, eps) 159 U = np.multiply(u, np.sqrt(s_pinv)) LinAlgError: singular matrix
クラス修正が必要と存じていますが、直し方がわかりません。教えて下さると幸いです。よろしくお願いいたします。
バッドをするには、ログインかつ
こちらの条件を満たす必要があります。