Сканирование каждого столбца во фрейме данных, а затем изменение значений в двоичный формат в R

#r #matrix

#r #матрица

Вопрос:

У меня есть матрица с несколькими отдельными элементами в строках и несколькими нуклеотидами (значениями) в столбцах. Это выглядит так:

      [,1][,2][,3][,4] ...
ind1   a   c   a   a
ind2   a   c   t   t
ind3   a   g   g   c
ind4   a   g   g   g
.
.
.
  

Теперь я хотел бы игнорировать все столбцы, в которых встречается только одно значение (как в примере над первым столбцом), и преобразовать каждый столбец с двумя, тремя и четырьмя (возможно не более 4!) различными нуклеотидами (значениями) в двоичный формат. В итоге это должно выглядеть так:

      [,1] [,2]  [,3] ...
ind1  10   100   1000
ind2  10   010   0100
ind3  01   001   0010
ind4  01   001   0001
.
.
.
  

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

 df <- apply(df, 2, function(x) length(unique(x)))
  

Кто-нибудь может мне помочь?

Комментарии:

1. Не ясно, как вы получаете ожидаемый результат ’01’ ’10’, где значения столбцов просто ‘a’ для первого столбца

2. Неясно, какой ожидаемый результат. Может быть library(pryr);apply(df[-1], 2, function(x) {n <- length(unique(x)); substr(pryr::bits(x), n, n n-1)})

Ответ №1:

Вот что еще нужно попробовать. Пользовательская функция обработает каждый столбец apply . Во-первых, вы можете создать вектор числовых значений, соответствующих уникальным символам в столбце ( unique используется, так как factor в противном случае порядок будет в алфавитном порядке). Будет создана строка нулей длиной максимального числа, и позиция символа, соответствующая каждому значению, будет заменена на «1».

 my_fun <- function(x) {
  vec <- as.numeric(factor(x, levels = unique(x)))
  vec_max <- max(na.omit(vec))
  lapply(vec, 
         function(y) ifelse(!is.na(y), 
                            sub(paste0("(.{", y - 1, "})."), 
                                "\11", 
                                paste0(rep("0", vec_max), collapse = "")), 
                            NA))
}

m[] <- matrix(unlist(apply(m, 2, my_fun)))
  

Вывод

      [,1] [,2] [,3]  [,4]  
ind1 "1"  "10" "100" "1000"
ind2 "1"  "10" "010" "0100"
ind3 "1"  "01" "001" "0010"
ind4 "1"  "01" "001" "0001"
  

Данные

 m <- structure(c("a", "a", "a", "a", "c", "c", "g", "g", "a", "t", 
"g", "g", "a", "t", "c", "g"), .Dim = c(4L, 4L), .Dimnames = list(
    c("ind1", "ind2", "ind3", "ind4"), NULL))
  

Комментарии:

1. большое спасибо за помощь, но я получаю эту ошибку: Error in rep("0", max(vec)) : invalid 'times' argument

2. Привет, я пробовал со своими собственными данными. Он слишком большой dput(head(df)) , в нем 543 строки и ~ 11000 столбцов, может быть, в этом причина?

3. Да, у меня есть, но я использовал [!is.na(x)] . Разве это не сработает? И при вставке в небольшую матрицу и выполнении вашей команды это работает.

4. Я бы хотел игнорировать NAs. Возможно ли это?

5. @LukasMe Смотрите Отредактированный ответ — дайте мне знать, если это поможет. Если значение отсутствует, оно будет использоваться NA в результате. Кроме того, длина двоичного результата также будет короче. Мне нужно запустить, но я проверю снова через несколько часов.

Ответ №2:

Вот моя попытка:

 r1 <- c("a","c","a","a")
r2 <- c("a","c","t","t")
r3 <- c("a","g","g","c")
r4 <- c("a","g","g","g")

n.mat <- rbind(r1,r2,r3,r4)

number_to_nucleotide_binary <- function(x,len) {
  out <- rep("0",len)
  out[x] <- "1"
  return(paste(out,collapse = ""))
}

nuc_to_binary <- function(x) {
  
  len <- length(unique(x))
  char <- sort(unique(x))
  out <- x
  
  if(len != 1) {
    pos <- match(x,char)
    out <- sapply(X = pos,FUN = function(x) {number_to_nucleotide_binary(x = x,len = len)})
  }
  
  return(out)
}

apply(X = n.mat,FUN = nuc_to_binary,MARGIN = 2)
  

Ввод:

    [,1] [,2] [,3] [,4]
r1 "a"  "c"  "a"  "a" 
r2 "a"  "c"  "t"  "t" 
r3 "a"  "g"  "g"  "c" 
r4 "a"  "g"  "g"  "g" 
  

Вывод:

      [,1] [,2] [,3]  [,4]  
[1,] "a"  "10" "100" "1000"
[2,] "a"  "10" "001" "0001"
[3,] "a"  "01" "010" "0100"
[4,] "a"  "01" "010" "0010"
  

Комментарии:

1. Привет, большое спасибо за помощь, но я хотел бы иметь только 1000 , 0100 , 0010 , 0001 , но нет 0011 , например, но, кроме того, ваша команда сработала довольно хорошо!

2. Я отредактировал код, чтобы упростить и устранить зависимость от другого ответа SO. Надеюсь, это то, что вам было нужно, хотя ответ Бена более масштабируемый / элегантный / лаконичный.