如何解决prcomp.default(): cannot rescale a constant/zero column to unit variance问题

25
我是一位有用的助手,可以为您翻译文本。

我有一个包含9个样本(行)和51608个变量(列)的数据集,但每当我尝试进行规模调整时,就会出现错误:

这个工作正常。

pca = prcomp(pca_data)

然而,
pca = prcomp(pca_data, scale = T)

提供

> Error in prcomp.default(pca_data, center = T, scale = T) : 
  cannot rescale a constant/zero column to unit variance

显然,很难提供一个可复制的示例。有任何想法是什么原因呢?
寻找常数列:
    sapply(1:ncol(pca_data), function(x){
               length = unique(pca_data[, x]) %>% length
             }) %>% table

输出:

    .
        2     3     4     5     6     7     8     9 
     3892  4189  2124  1783  1622  2078  5179 30741 

所以没有固定的列。NA值也是如此 -
    is.na(pca_data) %>% sum

    >[1] 0

这个可以正常工作:

    pca_data = scale(pca_data)

但之后两者仍然会给出完全相同的错误提示:
    pca = prcomp(pca_data)
    pca = prcomp(pca_data, center = F, scale = F)

那么为什么我不能对这些数据进行缩放的PCA呢?好吧,让我们确保它不是常数。

    pca_data = pca_data + rnorm(nrow(pca_data) * ncol(pca_data))

相同的错误。数字数据?
    sapply( 1:nrow(pca_data), function(row){
      sapply(1:ncol(pca_data), function(column){
         !is.numeric(pca_data[row, column])
       })
     } ) %>% sum

仍旧出现相同的错误,我已经没有任何想法。

编辑:需要更多的尝试和至少一种hack来解决它。

后来,我还是很难对这些数据进行聚类,例如:

    Error in hclust(d, method = "ward.D") : 
      NaN dissimilarity value in intermediate results. 

将小于某个截止值(例如<1)的值裁剪为零并没有任何效果。最终有效的方法是裁剪所有列中的零超过x个的列。对于#零<=6,它有效,但7个或更多会出错。不知道这是否意味着这是一个普遍问题,还是仅仅偶然捕捉到了一个有问题的列。如果有人有任何想法,仍然很乐意听到,因为只要没有变量全部为零(或以其他方式为常数),这就应该工作得非常好。


看看 sum(!is.finite(scale(pca_data))) 给你什么结果。 - Hong Ooi
你解决了吗? - Joe
3个回答

38

我认为您没有正确地寻找零方差列。让我们用一些虚拟数据来尝试。首先,是一个10x100的可接受矩阵:

mat <- matrix(rnorm(1000, 0), nrow = 10)

还有一个方差为零的列。我们称之为oopsmat

const <- rep(0.1,100)
oopsmat <- cbind(const, mat)
oopsmat 的前几个元素如下所示:
      const                                                                                               
 [1,]   0.1  0.75048899  0.5997527 -0.151815650  0.01002536  0.6736613 -0.225324647 -0.64374844 -0.7879052
 [2,]   0.1  0.09143491 -0.8732389 -1.844355560  0.23682805  0.4353462 -0.148243210  0.61859245  0.5691021
 [3,]   0.1 -0.80649512  1.3929716 -1.438738923 -0.09881381  0.2504555 -0.857300053 -0.98528008  0.9816383
 [4,]   0.1  0.49174471 -0.8110623 -0.941413109 -0.70916436  1.3332522  0.003040624  0.29067871 -0.3752594
 [5,]   0.1  1.20068447 -0.9811222  0.928731706 -1.97469637 -1.1374734  0.661594937  2.96029102  0.6040814

让我们尝试对 oopsmat 进行缩放和未缩放的主成分分析:

PCs <- prcomp(oopsmat) #works
PCs <- prcomp(oopsmat, scale. = T) #not forgetting the dot
#Error in prcomp.default(oopsmat, scale. = T) : 
   #cannot rescale a constant/zero column to unit variance

因为如果标准差是无穷大,就无法通过标准差来除以某个值。为了确定方差为零的列,我们可以使用以下方法使用which来获取变量名。

which(apply(oopsmat, 2, var)==0)
#const 
#1 

要从数据集中移除方差为零的列,你可以使用相同的apply表达式,将方差设置为不等于零。

oopsmat[ , which(apply(oopsmat, 2, var) != 0)]

希望这能更清晰地帮你理解!

我遇到了这个问题,我尝试了你的方法并找到了一些相关的列并将它们删除了。我还检查了一下是否有常量列。然而,我仍然收到相同的错误消息,并且当我到达一个不是常量的数字列(#111)时,它首先出现。为什么会这样呢? - GH28
你检查过所有列都是数字了吗?请参考 @orrymr 的回答。 - Joe

6
错误是因为其中一列具有常量值。
计算所有数值列的标准差以查找零方差变量。
如果标准差为零,则可以删除该变量并计算PCA。

6

除了Joe的答案以外,还要检查数据框中列的类别是否为数值型。

如果是整数,则会出现方差为0的情况,从而导致缩放失败。

因此,如果:

class(my_df$some_column)

如果is an integer64,那么请按照以下步骤操作。
my_df$some_column <- as.numeric(my_df$some_column)

希望这能帮助到某些人。

2
为什么如果存在整数,方差就是0? - gonidelis

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接