web-dev-qa-db-ja.com

prcomp.default()の解決方法:定数/ゼロ列を単位分散に再スケーリングできません

51608の変数(列)を持つ9つのサンプル(行)のデータセットがあり、スケーリングしようとするとエラーが発生し続けます。

これはうまくいきます

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を取得できないのでしょうか?わかりました、それが一定でないことを100%確かめてみましょう。

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

同じエラー。 Numiercデータ?

    sapply( 1:nrow(pca_data), function(row){
      sapply(1:ncol(pca_data), function(column){
         !is.numeric(pca_data[row, column])
       })
     } ) %>% sum

それでも同じエラー。私はアイデアがありません。

編集:少なくともそれを解決するためのハック。

後で、このデータをクラスタリングするのに苦労します:

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

特定のカットオフ、たとえば1未満から0未満の値をトリミングしても効果はありませんでした。最終的に機能したのは、列にx個以上のゼロがあるすべての列をトリミングすることでした。 #zeros <= 6で動作しましたが、7 +でエラーが発生しました。これが一般的な問題であることを意味するのか、それともたまたま問題のある列をキャッチしたのかがわかりません。変数がすべてゼロ(または他の方法で定数)でない限り、これがうまく機能するため、誰かが何かアイデアを持っているかどうかを聞いて喜んでいるでしょう。

13
Brian Jackson

ゼロ分散列を正しく探しているとは思わない。ダミーデータをいくつか試してみましょう。まず、許容可能なマトリックス: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でスケーリングされたPCAとスケーリングされていないPCAを試してみましょう:

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[ , apply(oopsmat, 2, var) != 0]

それが物事をより明確にするのに役立つことを願っています!

22
Joe

ジョーの答えに加えて、データフレームの列のクラスが数値であることを確認してください。

整数がある場合、分散が0になり、スケーリングが失敗します。

だから、

class(my_df$some_column)

たとえば、integer64の場合、次のようにします

my_df$some_column <- as.numeric(my_df$some_column)

これが誰かを助けることを願っています。

2
orrymr