[英]Check if one sample exists in the sample space (From PCA or other cluster analysis)
我有一個200 x 50的矩陣,這里200表示200個化合物(行),而50表示50個獨立變量(列),然后我使用200 * 50矩陣進行聚類分析(例如k均值等),我可以得到一個圖表以顯示這2000種化合物的分布。
我的問題是,當我有一個新化合物時,它具有與200 * 50矩陣相同的50個獨立變量,如何測試新化合物是否位於簇空間中?
謝謝。
編輯:請注意,我不需要在data.frame中找到該元素。 我認為第一步是對數據進行聚類(例如,使用pca和plot(pca1,pca2)),然后測試新記錄是位於圖中還是位於圖中。 就像這張圖片一樣 ,其中(2)屬於群集,而(1)不屬於群集空間。
因此,這是一種不同的(但概念上相似)的方法,同時還有一個警示故事。 由於您未提供任何數據,因此在示例中將使用內置的mtcars
數據集。
首先,我們設置數據,運行主成分分析,然后運行K-means聚類分析。
set.seed(5) # for reprpduceable example
df <- mtcars[,c(1,3,4,5,6,7)] # subset of mtcars dataset
trn <- sample(1:nrow(df),nrow(df)-3)
train <- mtcars[trn,] # training set: 29 obs.
test <- mtcars[-trn,] # test set: 3 obs.
pca <- prcomp(train, scale.=T, retx=T) # pca on training set
summary(pca)$importance[3,1:4] # 84% of variation in first 2 PC
# PC1 PC2 PC3 PC4
# 0.60268 0.83581 0.89643 0.92139
scores <- data.frame(pca$x)[1:2] # so use first two PC
km <- kmeans(scores,centers=3,nstart=25) # kmeans cluster analysis
pc.test <- predict(pca,test)[,1:2] # transform the test set
pc.test <- rbind(pc.test,c(-1.25,-1)) # add "special point"
rownames(pc.test) <- c(LETTERS[1:3],"X") # letters to make things simpler
現在,我們繪制PC,質心和測試集。
library(ggplot2)
# plot first two PC with cluster id
gg.train <- data.frame(cluster=factor(km$cluster), scores)
centroids <- aggregate(cbind(PC1,PC2)~cluster,data=gg.train,mean)
gg.train <- merge(gg.train,centroids,by="cluster",suffixes=c("",".centroid"))
gg.test <- data.frame(pc.test[,1:2])
# generate cluster plot...
cluster.plot <- ggplot(gg.train, aes(x=PC1, y=PC2, color=cluster)) +
geom_point(size=3) +
geom_point(data=centroids, size=4) +
geom_segment(aes(x=PC1.centroid, y=PC2.centroid, xend=PC1, yend=PC2))+
geom_point(data=gg.test,color="purple",size=8,shape=1)+
geom_text(data=gg.test,label=rownames(gg.test),color="purple")+
coord_fixed()
plot(cluster.plot)
根據目視檢查,我們可能會將B
和C
放在群集3(藍色群集)中, A
放在群集1(紅色)中。 X
是有問題的(故意的;這就是使它“特殊”的原因)。 但是請注意,如果我們基於與質心的接近度來分配聚類,則將A
放入聚類3中!
# "hard" prediction: assign to whichever cluster has closest centroid
predict.cluster <- function(z) {
closest <-function(z)which.min(apply(km$centers,1,function(x,z)sum((x-z)^2),z))
data.frame(pred.clust=apply(z,1,closest))
}
predict.cluster(pc.test)
# pred.clust
# A 3
# B 3
# C 3
# X 2
因此,一種不同的方法根據距質心的距離和散布的度量(聚類中的點分組緊密程度)來計算聚類成員的概率。 這種方法要求我們假設分布是危險的,尤其是點數少時。
最簡單的方法是假設給定群集中的點遵循多元正態分布。 在這個假設下,
即,如上形成的隨機變量以具有k個自由度的chi-sq分布(其中k是維數,此處為2)。 這里, x
是下考慮的一個點用於在群集成員,μ是群集的質心,Σ是對集群中的點的協方差矩陣,和χ2是卡平方統計量,其中k自由度在概率1- α。
對於給定的x
(在測試集中),我們可以通過應用此方程來計算隸屬概率,以計算α。 我們還可以通過計算點集x
來計算“集群邊界”,這些點對於給定的α滿足此條件。 后面的練習導致置信區間為1-α。 幸運的是,這已經在ellipse
包中使用ellispe(...)
在R(用於2維ellispe(...)
中實現了。
library(ellipse)
conf.rgn <- do.call(rbind,lapply(1:3,function(i)
cbind(cluster=i,ellipse(cov(scores[km$cluster==i,]),centre=km$centers[i,]))))
conf.rgn <- data.frame(conf.rgn)
conf.rgn$cluster <- factor(conf.rgn$cluster)
plot(cluster.plot + geom_path(data=conf.rgn, aes(x=PC1,y=PC2)))
基於此,我們將A
分配給群集1(紅色),即使它更接近群集3(藍色)。 這是因為群集3的分組更為緊密,因此成員資格的障礙更高。 請注意, X
在所有群集的“外部”。
下面的代碼針對給定的一組測試點計算每個群集中的成員資格概率。
# "soft" prediction: probability that point belongs in each cluster
pclust <- function(point,km,df){
get.p <- function(clust,x){
d <- as.numeric(x-km$centers[clust,])
sigma.inv <- solve(cov(df[km$cluster==clust,]))
X.sq <- d %*% sigma.inv %*% d
p <- pchisq(X.sq,length(d),lower.tail=FALSE)
}
sapply(1:max(km$cluster),get.p,x=point)
}
p <- apply(pc.test,1,pclust, km=km, df=scores)
print(p)
# A B C X
# [1,] 9.178631e-02 6.490108e-04 9.969140e-07 8.754585e-04
# [2,] 1.720396e-28 4.391488e-26 2.821694e-43 3.630565e-05
# [3,] 2.664676e-05 8.928103e-01 8.660860e-02 2.188450e-05
在這里,第i
行中的值是群集i
中隸屬的概率。 因此,我們可以看到A
屬於群集1的概率為9.2%,而其他群集中的成員身份的概率小於0.003%。 同樣, B
和C
顯然屬於群集3(分別為p = 89.2%和8.6%)。 最后,我們可以確定最可能的群集,如下所示:
data.frame(t(sapply(data.frame(p),function(x)
list(cluster=which.max(x),p.value=x[which.max(x)]))))
# cluster p.value
# A 1 0.09178631
# B 3 0.8928103
# C 3 0.0866086
# X 1 0.0008754585
通過指定p的截止值(例如0.05),我們可以斷言,如果最可能的群集具有p.value < cutoff
,則該點不屬於“群集空間”(使用您的術語)。
告誡性的故事是,盡管該分析值與PC1和PC2的均值相當接近,但在此分析中X
仍被排除在外。 這是因為,盡管X
位於數據集的中間,但它位於沒有群集的“空”區域中。 這是否意味着應該將其排除在外?
這是一個簡單的解決方案:
步驟1:設定資料
set.seed(1)
refData <- data.frame(matrix(runif(200*50),nrow=200))
newRec01 <- refData[11,] # A record that exists in data
newRec02 <- runif(50) # A record that does not exist in data
步驟2:測試:
TRUE %in% sapply(1:nrow(refData),function(i) all(newRec01 == refData[i,]))
TRUE %in% sapply(1:nrow(refData),function(i) all(newRec02 == refData[i,]))
如果需要,可以將其打包為一個函數:
checkNewRec <- function(refData, newRec) {
TRUE %in% sapply(1:nrow(refData),function(i) all(newRec == refData[i,]))
}
checkNewRec(refData, newRec01)
checkNewRec(refData, newRec02)
編輯:根據下面的新輸入,請嘗試以下操作:
准備:來自注釋的代碼:
ALL <- rbind(refData, newRec02)
pca <- prcomp(ALL)
pca1 <- pca$x[, 1]
pca2 <- pca$x[, 2]
pca1.in <- pca1[-length(pca1)]
pca2.in <- pca2[-length(pca2)]
現在,我們需要以某種方式定義集群。 為簡單起見,讓我們假設一個集群。
步驟1:找出refData的質心:
cent <- c(mean(pca1.in),mean(pca2.in))
第二步:找出所有數據點到refData中心的距離:
ssq <- (pca1 - mean(pca1.in))^2 + (pca2 - mean(pca2.in))^2
步驟3:現在我們需要選擇從中心的截止距離,超過該距離,新的傳入記錄將被視為集群的“外部”。 為簡單起見,我采取了dec
ision它是在95%分位數:
dec <- (quantile(head(ssq,-1), 0.95) > tail(ssq,1))
步驟4:既然已經對newRec
分類做出決定,我們可以將其繪制:
plot(pca1, pca2)
points(pca1[length(pca1)], pca2[length(pca2)],
col = ifelse(dec, "red", "green"),pch="X")
此外,為了驗證我們的dec
ision,讓劇情的錯誤,看看哪里的newRec
下降!
hist(ssq, main="Error Histogram",xlab="Square Error")
points(pca1[length(pca1)], pca2[length(pca2)],
col = ifelse(dec, "red", "green"),pch="X")
text(pca1[length(pca1)], pca2[length(pca2)],labels="New Rec",col="red",pos=3)
希望這可以幫助!!
這篇文章遵循jihoward的回答,將其擴展到python並顯示了在多元高斯條件下一些有趣的聚類分配條件。 從三個2d高斯分布中采樣生成的數據,並在源代碼中提供均值和協方差。 點W , X , Y , Z用於描述對群集的軟分配。 我們假設每個群集具有2個自由度的卡方分布。
在圖中,陰影區域表示與平均值的2個標准偏差。 請注意,按預期,X不屬於任何群集。 盡管Y更接近綠色質心,但鑒於其分布,Y並未分配給綠色簇。 藍色群集顯示了使用硬閾值進行群集分配的結果。 請注意,超出0.05截止值的點將如何分類為不屬於藍色簇。
假設卡方分布的概率。
Blue Red Green
W [ 1.50465863e-01 0.00000000e+00 0.00000000e+00]
X [ 2.44710474e-10 1.20447952e-05 0.00000000e+00]
Y [ 0.00000000e+00 0.00000000e+00 0.00000000e+00]
Z [ 0.00000000e+00 9.91055078e-01 0.00000000e+00]
知道數據是多元高斯的,因此可以使用scipy對Alan Genz多元正態CDF函數的實現。 使用此示例,無法從中獲得令人信服的結果。 有關scipy的實現的更多詳細信息,請檢查此鏈接。
import numpy as np
import matplotlib.pylab as plt
from matplotlib.patches import Ellipse
from sklearn.cluster import KMeans
from scipy import stats
chi2_cdf = stats.chi2.cdf
plt.ion()
def eigenDecomposition(cov_mat):
vals, vecs = np.linalg.eigh(cov_mat)
order = vals.argsort()[::-1]
return vals[order], vecs[:,order]
def plotEllipse(center, cov_mat, n_std, color):
vals, vecs = eigenDecomposition(cov_mat)
angle = np.degrees(np.arctan2(*vecs[:,0][::-1]))
width, height = 2 * n_std * np.sqrt(vals)
return Ellipse(xy=center, width=width, height=height, angle=angle, color=color, alpha=0.2)
def computeMembership(point, center, data):
# (x - mu).T cov.inv (x - mu)
cov_mat = np.cov(data.T)
dist = np.array([point - center]).T
X_sq = np.dot(dist.T, np.dot(np.linalg.inv(cov_mat), dist))
return 1 - chi2_cdf(X_sq, len(center))[0][0]
n_obs = 128
a = np.random.multivariate_normal((0, 0), [[1, 0], [0, 1]], n_obs)
b = np.random.multivariate_normal((10, 0), [[1, -0.9], [-0.9, 1]], n_obs)
c = np.random.multivariate_normal((10, 10), [[1, 0.05], [1, 0.05]], n_obs)
d = np.array([[0,2], [5, 5], [10, 9.5], [10, 0]])
markers = [r"$ {} $".format(lbl) for lbl in ('W', 'X', 'Y', 'Z')]
clustering = KMeans(n_clusters=3).fit(np.vstack((a, b, c)))
_, idx = np.unique(clustering.labels_, return_index=True)
ids = clustering.labels_[np.sort(idx)]
colors = 'rgb'
fig = plt.figure()
ax = fig.add_subplot(111)
ax.scatter(a[:,0], a[:,1], color=colors[ids[0]])
ax.scatter(b[:,0], b[:,1], color=colors[ids[1]])
ax.scatter(c[:,0], c[:,1], color=colors[ids[2]])
for i in xrange(len(d)):
ax.scatter(d[i,0], d[i,1], color='k', s=128, marker=markers[i])
ax.scatter(clustering.cluster_centers_[:,0], clustering.cluster_centers_[:,1], color='k', marker='D')
# plot ellipses with 2 std
n_std = 2
probs = []
for i, data in enumerate((a, b, c)):
ax.add_artist(plotEllipse(clustering.cluster_centers_[ids[i]],
np.cov(data.T),
n_std,
color=colors[ids[i]]))
probs.append([computeMembership(x, clustering.cluster_centers_[ids[i]], data) for x in d])
print np.array(probs).T
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.