Как решить prcomp.default(): невозможно перемасштабировать константу/нулевой столбец для дисперсии единицы

У меня есть набор данных из 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)

Итак, почему я не могу получить масштабированную информацию об этих данных? Хорошо, давайте сделаем 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 до нуля не влияло. Что в конечном итоге работало, было обрезка всех столбцов, в столбце которых было больше нуля. Работал для # нулей <= 6, но 7+ дал ошибки. Не знаю, означает ли это, что это проблема вообще или если это только что произошло, чтобы поймать проблемный столбец. Тем не менее, было бы приятно услышать, есть ли у кого-нибудь идеи, потому что это должно работать нормально, если никакая переменная не является всеми нулями (или постоянными по-другому).

Ответ 1

Я не думаю, что вы правильно искали нулевые столбцы. Попробуйте использовать некоторые фиктивные данные. Во-первых, приемлемая матрица: 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

Попробуйте масштабированные и немасштабированные PCA на 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[ , apply(oopsmat, 2, var) != 0]

Надеюсь, что это поможет сделать вещи более ясными!

Ответ 2

В дополнение к ответу Джо, просто убедитесь, что классы столбцов в вашем фрейме данных являются числовыми.

Если есть целые числа, то вы получите дисперсию 0, что приведет к сбою масштабирования.

Так что если,

class(my_df$some_column)

является целым числом 64, например, затем выполните следующее

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

Надеюсь, это кому-нибудь поможет.