基因组最邻近区域距离计算,去噪音多线程优化版

  上周的推文[计算基因组范围内邻近DSB的间距]介绍了邻近DSB间距的计算方法。虽然结果没有问题,但总觉得哪里有点怪怪的。思来想去,是不是应该去除掉不关心的噪音,即区域间的gap。例如,下图中DSB1DSB2互为最邻近,DSB3DSB4的最邻近分别为DSB2DSB5DSB5DSB6互为最邻近。探索关心的肯定是最邻近DSB之间的距离,而类似DSB3DSB4之间的距离即为gap,这样的gap并不需要关心,留下来就属于噪音,为了更加精准的探索距离情况应该想办法去除掉。

  这个问题整体诉求并不难,为了解决该问题第一时间还是想到了借助distanceToNearest函数。先看一下这个函数的用法:

suppressPackageStartupMessages(library(GenomicRanges))

query <- GRanges(c("chr1", "chr2"), IRanges(c(1, 5), width=1))
subject <- GRanges("chr1", IRanges(c(6, 5, 13), c(10, 10, 15)))
d <- distanceToNearest(query, subject)
d
Hits object with 1 hit and 1 metadata column:
      queryHits subjectHits |  distance
      <integer>   <integer> | <integer>
  [1]         1           2 |         3
  -------
  queryLength: 2 / subjectLength: 3

  可以看到很好用,只要输入查询和搜索集即可,这样查询集里面的每一个区域都会在搜索集找到一个最近的区域。函数返回的结果是一个Hits对象,里面记录了哪两个区域最靠近以及距离。有了这个函数,便可以很方便地计算DSB与其最近DSB之间的距离。接下来,问题就变成了如何去计算每一个DSB最邻近的距离。是不是每个DSB循环一遍就可以了呢?问题看似简单,但有些细节还是需要留心。毕竟,不是程序跑起来有结果就是对的。

  肯定是要利用循环,每次取出一个作为查询区域,数据集剩余的区域作为搜索集,但与此同时会产生一个重复计算的问题。如第一次以DSB1作为查询时,最邻近区域为DSB2,第二次以DSB2查询时,最邻近区域为DSB1,如此一来这两者的距离就计算两次了。这样肯定是不合理的,计算的时候需要避免。

  这里,为避免重复计算,本人采用的策略是构造一个新的id,这样互为最邻近的区域就会形成一个id组合,最后根据这个组合来去除重复。下面来看看具体的实现代码,为方便使用定义成一个函数:

calcdist <- function(gr, sname){
    chr <- table(gr@seqnames) > 1
    gr <- gr[gr@seqnames %in% names(chr[chr!=F])]
    num <- length(gr)
    gr$id <- 1:num
    out <- data.frame()
    
    for(idx in 1:num){
        sgr <- gr[-idx]
        hit <- distanceToNearest(gr[idx], sgr)
        idv <- paste0(sort(c(gr[idx]$id, sgr[subjectHits(hit)]$id)), collapse='_')
        dis <- mcols(hit)$distance
        tmp <- data.frame(chr=as.vector(gr[idx]@seqnames), dist=dis, sample=sname, id=idv, stringsAsFactors=F)
        out <- rbind(out, tmp)
    }
    
    out <- subset(out[!duplicated(out$id),], select=-id)
    return(out)
}

  函数的魅力在于便于重复使用,以后再有这样的需求调用一下函数就可以了。下面用样本测试一下:

gr <- ChIPpeakAnno::toGRanges('sample_peaks.bed')
gr
GRanges object with 8187 ranges and 1 metadata column:
                   seqnames               ranges strand |     score
                      <Rle>            <IRanges>  <Rle> | <numeric>
     MACS_peak_1 GL456210.1     [ 21815,  21815]      * |       424
     MACS_peak_2 GL456211.1     [ 97142,  97142]      * |       330
     MACS_peak_3 GL456211.1     [174111, 174111]      * |       146
     MACS_peak_4 GL456212.1     [ 85136,  85136]      * |       827
     MACS_peak_7 GL456221.1     [128963, 128963]      * |       491
             ...        ...                  ...    ... .       ...
  MACS_peak_8183       chrY [ 1404762,  1404762]      * |       457
  MACS_peak_8184       chrY [ 4036698,  4036698]      * |      8868
  MACS_peak_8185       chrY [11799470, 11799470]      * |       623
  MACS_peak_8186       chrY [16484365, 16484365]      * |       611
  MACS_peak_8187       chrY [90763211, 90763211]      * |       891

system.time(out <- calcdist(gr, 'ctrl'))
   user  system elapsed 
432.552   0.135 433.711

head(out)
         chr    dist sample
1       chr1 1484377   ctrl
2       chr1  201371   ctrl
3       chr1   50155   ctrl
4       chr1   66122   ctrl
5       chr1   59074   ctrl

  虽然函数可以正常运行结果也没有问题,可是这个运行时间着实没有料到,8千多行循环一遍居然用了七八分钟,只能说还是尽量不要用R写for循环的好,不然数据多了这耗时真的很严重。不过,话说回来,如果不着急的话这样也完全可以接受。

  时间和效率有时候还是很重要的,如果想要节约时间,很快拿到结果,就得想想办法加速了,最简单快捷的方法自然是多线程并发。可以知道计算过程是染色体独立的,这样可以利用多线程同时计算多个染色体,如此便可以大大提高计算速度。下面用多线程来计算一遍:

suppressPackageStartupMessages(library(foreach))
suppressPackageStartupMessages(library(doParallel))

chr <- table(gr@seqnames) > 1
chr <- names(chr[chr!=F])

cl <- makeCluster(10)
registerDoParallel(cl)

system.time(
    out2 <- foreach(i = chr, .combine = rbind) %dopar% {
        suppressPackageStartupMessages(library(GenomicRanges))
        calcdist(gr[gr@seqnames == i], 'ctrl')}
)
 user  system elapsed 
0.073   0.004  44.498 

stopCluster(cl)

head(out2)
         chr    dist sample
1       chr1 1484377   ctrl
2       chr1  201371   ctrl
3       chr1   50155   ctrl
4       chr1   66122   ctrl
5       chr1   59074   ctrl

  可以看到相同的数据,用10个线程同时计算,全部时间消耗也只有半分钟左右,这速度可以说是所见即所得了。

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 191,715评论 5 459
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 80,725评论 2 368
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 139,084评论 0 314
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 51,513评论 1 262
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 60,422评论 4 352
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 45,614评论 1 268
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 36,171评论 3 379
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 34,850评论 0 252
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 39,049评论 1 284
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 34,237评论 2 304
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 36,002评论 1 320
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 31,895评论 3 309
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 37,265评论 3 296
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 28,663评论 0 17
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 29,926评论 1 249
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 41,102评论 2 337
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 40,317评论 2 335

推荐阅读更多精彩内容