簡體   English   中英

檢查樣本空間中是否存在一個樣本(來自PCA或其他聚類分析)

[英]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)

根據目視檢查,我們可能會將BC放在群集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%。 同樣, BC顯然屬於群集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高斯分布中采樣生成的數據,並在源代碼中提供均值和協方差。 WXYZ用於描述對群集的軟分配。 聚類在手動創建的點和使用三個多元高斯生成的合成數據上。 我們假設每個群集具有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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM