Вызов функции с переменным объектом - PullRequest
0 голосов
/ 01 ноября 2018

У меня есть кадр данных из нескольких строк, содержащий только один столбец, причем столбец содержит строки переменной длины в диапазоне от 30000 до 200000 символов (последовательность ДНК). [Ниже приведен образец из 150 символов]

TTCCCCAAACAGCAACTTTAAGGAGCAGCTTCCTTTATGATCCCTGATTGCCTCCCCTTTGTTCCCATAACAAGTAGTTTAAATTTTCTGTTAAAGTCCAAACCACATATTTACAATACCTCGCACC

Вот полный набор данных: https://drive.google.com/open?id=1f9prtKW5NnS-BLI5lqsl4FEi4PvRfxGR

У меня есть код в R, который делит каждую строку на 20 бинов в зависимости от ее длины, подсчитывает вхождения G и C для каждого бина и возвращает мне матрицу из 20 столбцов. Вот код:

library(data.table)
data <- fread("string.fa", header = F)

loopchar <- function(data){ bins <- sapply(seq(1, nchar(data), nchar(data)/20), function(x) substr(data, x, x + nchar(data)/20 - 1))output <- (str_count(bins, c("G"))/nchar(bins) + str_count(bins, c("C"))/nchar(bins))*100}

result <- data.frame(t(apply(data,1,loopchar)))

Однако теперь я хочу сделать что-то другое. Вместо nchar(data)/20 я хочу, чтобы сегменты подстроки (20) отличались от моего списка. Итак, теперь для моего фрейма данных, первая строка должна быть разделена на 22 бина / сегмента, и код будет nchar(data)/22.

Вторая строка должна быть разделена на 21 ячейку, код будет nchar(data)/21 и так далее. Я хочу, чтобы функция продолжала изменять количество ячеек для данных. И мой массив данных со строками и векторный список чисел с ячейками имеют одинаковую длину.

Каков наилучший способ сделать это?

1 Ответ

0 голосов
/ 05 ноября 2018

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

Данные

Ваш файл слишком большой, поэтому я создал текстовый файл (в памяти), который содержит случайную ДНК для каждой строки:

# set seed to create reproducible example

set.seed(53101614)

# create an example text file in memory

temp <- tempfile()
writeLines(
  sapply(1:100, function(i){
    paste(sample(c("A", "T", "C", "G"), sample(100:6000), 
                 replace = T), collapse = "")
  }),
  con = temp
)

# read lines from tmp file

dna <- readLines(temp)

# unlink file

unlink(temp)

Предварительная обработка данных

Создание Biostrings::DNAStringSet объекта

Используя функцию Biostrings::DNAStringSet(), мы можем прочитать character vector для создания DNAStringSet объекта. Обратите внимание, что я предполагаю, что все записи в стандартном алфавите ДНК , т.е. каждая строка содержит только A, T, C, G символов. Если это не относится к вашему делу, обратитесь к Biostrings документации.

dna <- DNAStringSet(dna, use.names = F)

# inspect the output

dna

A DNAStringSet instance of length 100
      width seq
  [1]  2235 GGGCTTCCGGTGGTTGTAGGCCCATAAGGTGGGAAATATACA...GAAACGTCGACAAGATACAAACGAGTGGTCAACAGGCCAGCC
  [2]  1507 ATGCGGTCTATCTACTTGTTCGGCCGAACCTTGAGGGCAGCC...AACGCTTTGTACCTGTCCCAGAGTCAGAAGTAACAGTTTAGC
  [3]  1462 CATTGGAGTACATAGGGTATTCCCTCTCGTTGTATAACTCCA...TCCTACTTGCGAAGGCAGTCGCACACAAGGGTCTATTTCGTC
  [4]  1440 ATGCTACGTTGGTAGGGTAACGCAGACTAGAACCACACGGGA...ATAAAGCCGTCACAAGGAATGTTAGCACTCAATGGCTCGCTA
  [5]  3976 AAGCGGAAGTACACGTACCCGCGTAGATTACGTATAGTCGCC...TTACGCGTTGCTCAAATCGTTCGGTGCAGTTTTATAGTGATG
  ...   ... ...
 [96]  4924 AGTAAGCAGATCCAGAGTACTGTGAAAGACGTCAGATCCCGA...TATAATGGGTTGCGTGTTTGATTCTGCCATGAATCCTATGTT
 [97]  5702 CCTGAAGAGGACGTTTCCCCCTACATCCAGTAGTATTGGTGT...TCTGCTTTGCGCGGCGGGGCCGGACTGTCCATGGCTCACTTG
 [98]  5603 GCGGCTGATTATTGCCCGTCTGCCTGCATGATCGAGCAGAAC...CTCTTTACATGCTCATAGGAATCGGCAACGAAGGAGAGAGTC
 [99]  3775 GGCAAGACGGTCAGATGTTTTGATGTCCGGGCGGATATCCTT...CGCTGCCCGTGACAATAGTTATCATAAGGAGACCTGGATGGT
[100]   407 TGTCGCAACCTCTCTTGCACGTCCAATTCCCCGACGGTTCTA...GCGACATTCCGGAGTCTGCGCAGCCTATGTATACCCTACAGA

Создать вектор случайных N чисел бинов

set.seed(53101614)

k <- sample(100, 100, replace = T)

# inspect the output

head(k)

[1] 37 32 63 76 19 41

Создать Views объект, каждая последовательность ДНК которого представлена ​​N = k[i] кусками

Намного легче решить вашу проблему, используя IRanges::Views контейнер. Эта штука невероятно быстрая и красивая.

Прежде всего, мы делим каждую ДНК, секвенированную на k[i] диапазоны:

seqviews <- lapply(seq_along(dna), function(i){

  seq = dna[[i]]
  seq_length = length(seq)

  starts = seq(1, seq_length - seq_length %% k[i], seq_length %/% k[i])

  Views(seq, start = starts, end = c(starts[-1] - 1, seq_length))
  }
)

# inspect the output for k[2] and seqviews[2]

k[2]
seqviews[2]

32

Views on a 1507-letter DNAString subject subject: ATGCGGTCTATCTACTTG...GTCAGAAGTAACAGTTTAG
    views:
         start  end width
     [1]     1   47    47 [ATGCGGTCTATCTACTTGTTCGGCCGAACCTTGAGGGCAGCCAGCTA]
     [2]    48   94    47 [ACCGCCGGAGACCTGAGTCCACCACACCCATTCGATCTCCATGGTTG]
     [3]    95  141    47 [GCGCTCTCCGAGGTGCCACGTCAAGTTGTACTACTCTCTCAGACCTC]
     [4]   142  188    47 [TTGTTAGAAGTCCCGAGGTATATGCGCAATACCTCAACCGAAGCGCC]
     [5]   189  235    47 [TGATGAGCAAACGTTTCTTATAGTCGCGACCTTGTCCCGAGGACTTG]
     ...   ...  ...   ... ...
    [28]  1270 1316    47 [AGGCGAGGGCAGGGCACATGTTTCTACAGTGAGGCGTGATCCGCTCC]
    [29]  1317 1363    47 [GAGGCAAGCTCGTGAACTGTCGTGGCAAGTTACTTATGAGGATGTCA]
    [30]  1364 1410    47 [TGGGCAGATGCAACAGACTGCTATTGGCGGGAGAGAGGCATCGACAT]
    [31]  1411 1457    47 [ACCGTCTCAAGTACCACAGCTGAGAGGCTCTCGTGGAGATGCGCACA]
    [32]  1458 1507    50 [TGAGTCGTAACGCTTTGTACCTGTCCCAGAGTCAGAAGTAACAGTTTAGC]

После этого мы проверяем, все ли последовательности были разделены на желаемое количество фрагментов:

all(sapply(seq_along(k), function(i) k[i] == length(seqviews[[i]])))

[1] TRUE

Важное замечание

Прежде чем мы продолжим, есть одно важное замечание относительно вашей функции.

Ваша функция выдает N чанков с переменной длиной (поскольку производимые ею индексы равны с плавающей запятой , но не целых , поэтому substr() при вызове округляет предоставленные индексы до ближайшего целое число.

Например, извлечение 1-й записи из набора dna и разбиение этой последовательности на 37 бинов с использованием вашего кода даст следующие результаты:

dna_1 <- as.character(dna[[1]])

sprintf("DNA#1: %d bp long, 37 chunks", nchar(dna_1))
[1] "DNA#1: 2235 bp long, 37 chunks"

bins <- sapply(seq(1, nchar(dna_1), nchar(dna_1)/37), 
               function(x){
                 substr(dna_1, x, x + nchar(dna_1)/37 - 1)
                 }
               )

bins_length <- sapply(bins, nchar)

barplot(table(bins_length), 
        xlab = "Bin's length", 
        ylab = "Count", 
        main = "Bin's length variability"
)

bin's length variability 1

Подход Я использую в своем коде, в то время как length(dna[[i]]) %% k[i] != 0 (напоминание) создает k[i] - 1 корзин равной длины и только последний бин имеет длину, равную length(dna[i]) %/% k[i] + length(dna[[i]] %% k[i]:

bins_length <- sapply(seqviews, length)

barplot(table(bins_length), 
        xlab = "Bin's length", 
        ylab = "Count", 
        main = "Bin's length variability"
)

variability of bins's length 2

Расчет содержания GC

Как уже упоминалось выше, Biostrings::letterFrequency() применительно к IRanges::Views позволяет легко рассчитать содержание GC: * ​​1084 *

Найти частоту GC для каждого бина в каждой последовательности ДНК

GC <- lapply(seqviews, letterFrequency, letters = "GC", as.prob = TRUE)
Конвертировать в проценты
GC <- lapply(GC, "*", 100)
Проверьте выход
head(GC[[1]])
          G|C
[1,] 53.33333
[2,] 46.66667
[3,] 50.00000
[4,] 55.00000
[5,] 60.00000
[6,] 45.00000

График содержания GC для ДНК 1:9

par(mfrow = c(3, 3))

invisible(
  lapply(1:9, function(i){
    plot(GC[[i]], 
         type = "l", 
         main = sprintf("DNA #%d, %d bp, %d bins", i, length(dna[[i]]), k[i]),
         xlab = "N bins",
         ylab = "GC content, %",
         ylim = c(0, 100)
    )
    abline(h = 50, lty = 2, col = "red")
  }
  )
)

enter image description here

...