chen7qi的个人博客分享 http://blog.sciencenet.cn/u/chen7qi

博文

一句话加速grep近30倍

已有 1648 次阅读 2020-8-10 16:00 |系统分类:科研笔记

生物信息学习的正确姿势

NGS系列文章包括NGS基础、转录组分析 (Nature重磅综述|关于RNA-seq你想知道的全在这)、ChIP-seq分析 (ChIP-seq基本分析流程)、单细胞测序分析 (重磅综述:三万字长文读懂单细胞RNA测序分析的最佳实践教程 (原理、代码和评述))、DNA甲基化分析、重测序分析、GEO数据挖掘(典型医学设计实验GEO数据分析 (step-by-step) - Limma差异分析、火山图、功能富集)等内容。

最近做一个项目,需要从表达矩阵中提取单个特定基因的表达值。最开始时文件比较小,使用awk单个读取处理也很快,但后来数据多了,从一个1.2 G的文件中提取单个基因的表达需要30 s,用grep来写需要25 S,这在平时写程序是可以接受的,但在网站上是接受不了的。所以就想着如何优化一下。

探索下来优化也很简单,把grep换为LC_ALL=C grep再加其它参数速度就快了近30倍,把时间控制在1 s左右。

下面是整个探索过程 (写这篇总结文章是在早晨,服务器不繁忙,所以下面的示例中只能看出来快了5倍左右。这也表明不加LC_All=Cgrep受服务器负载影响较大,加了之后则几乎不受影响。)

获取单基因表达量

查看下文件大小

ls -sh 334d41a7-e34a-4bab-841c-eb07bd84513f.txt# 1.2G 334d41a7-e34a-4bab-841c-eb07bd84513f.txt

查看下文件内容

head 334d41a7-e34a-4bab-841c-eb07bd84513f.txt | cut -f 1,2# Rnu2-1    -0.52# Tmsb4Xp6    11.81# S100A14    1.99# Krt17    1.26# Aldh1A1    6.92# Fxyd3    0.56# Rnu2-2P    0.35# Rarres1    6.03# Rnvu1-7    9.53# Lcn2    3.44

假设基因名字大小写一致时使用awk提取其表达信息,用时14 s

time awk '{if($1=="Tmsb4Xp6") print $2;}' 334d41a7-e34a-4bab-841c-eb07bd84513f.txt >1real    0m14.569suser    0m12.943ssys    0m0.626s

实际上大小写可能不一致而需要转换,耗时17 s

time awk '{if(tolower($1)=="tmsb4xp6") print $2;}' 334d41a7-e34a-4bab-841c-eb07bd84513f.txt >2real    0m17.638suser    0m17.031ssys    0m0.595s

采用grep命令提取 (-i忽略大小写),用时5 s

time cat 334d41a7-e34a-4bab-841c-eb07bd84513f.txt | grep -i 'Tmsb4Xp6' >4real    0m5.454suser    0m5.134ssys    0m1.272s

上面的grep是全句匹配,想着加上^匹配行首是否会减少匹配量,速度能快一些,效果不明显,用时4 s

time cat 334d41a7-e34a-4bab-841c-eb07bd84513f.txt | grep -iP '^Tmsb4Xp6' >5real    0m4.262suser    0m3.984ssys    0m1.233s

grep是处理匹配关系,获得的是包含关键词但不一定全等于关键词,加一个-w参数,匹配更精确些,耗时6.7 s

time cat 334d41a7-e34a-4bab-841c-eb07bd84513f.txt | grep -iPw '^Tmsb4Xp6' >6real    0m6.723suser    0m6.390ssys    0m1.348s

从上面来看,采用正则限定并不能提速,还是采用固定字符串方式提取,速度也差不多,耗时5 s。(fgrep等同于grep -F)

time cat 334d41a7-e34a-4bab-841c-eb07bd84513f.txt | fgrep -i 'Tmsb4Xp6' >7real    0m5.496suser    0m5.128ssys    0m1.366s

主角出场,加上LC_ALL=C后,速度明显提升了,只需要1 s时间。

time LC_ALL=C fgrep -i '^Tmsb4Xp6' 334d41a7-e34a-4bab-841c-eb07bd84513f.txt >8real    0m1.027suser    0m0.671ssys    0m0.355s

多次测试下来,发现添加LC_ALL=Cgrep命令快了很多,而且多次测试速度都很稳定 (不论服务器是繁忙还是空闲)。这里面的原理是涉及字符搜索空间的问题,我们操作的文件只包含字母、字符、数字,没有中文或其它复杂符号时都是适用的,具体原理和更多评估可查看文末的两篇参考链接,了解更多信息。

为了简化应用,我们可以alias grep='LC_ALL=C grep' (把这句话放到~/.bashrc~/.bahs_profile里面(具体用法见:PATH和path,傻傻分不清)),后续再使用grep时就可以直接得到速度提升了。

time grep -F -i '^Tmsb4Xp6' 334d41a7-e34a-4bab-841c-eb07bd84513f.txtreal    0m1.013suser    0m0.679ssys    0m0.334s

那如果获取多个基因怎么操作呢?

一个方式是使用正则表达式,多个基因一起传递过去,分别匹配,耗时4.6 s

time cat 334d41a7-e34a-4bab-841c-eb07bd84513f.txt | LC_ALL=C grep -iP 'Tmsb4Xp6|Sox1|Sox2|Sox3'real    0m4.654suser    0m4.366ssys    0m1.227s

或者还是使用固定字符串查找模式,把所有基因每行一个写入文件a,然后再去匹配,耗时2.5 s,且测试发现在基因数目少于10时(这是通常的应用场景),基因多少影响不大 (这也说明能用固定字符串查找时最好显示指定)。

time cat 334d41a7-e34a-4bab-841c-eb07bd84513f.txt | LC_ALL=C fgrep -i -f a >11real    0m2.539suser    0m2.191ssys    0m1.249s

这里还比较了另外2个号称比grep快的命令agrg在这个应用场景没体现出性能优势。

time cat 334d41a7-e34a-4bab-841c-eb07bd84513f.txt | LC_ALL=C ag -i '^Tmsb4Xp6|Sox1|Sox2|Sox3' >10real    0m11.281suser    0m9.713ssys    0m5.326stime cat 334d41a7-e34a-4bab-841c-eb07bd84513f.txt | rg -iF -f a >12real    0m4.337suser    0m3.444ssys    0m2.787s
  • https://www.inmotionhosting.com/support/website/speed-up-grep-searches-with-lc-all/

  • https://stackoverflow.com/questions/42239179/fastest-way-to-find-lines-of-a-file-from-another-larger-file-in-bash#





https://blog.sciencenet.cn/blog-118204-1245779.html

上一篇:高通量数据中批次效应的鉴定和处理(六)- 直接校正表达矩阵
下一篇:一文掌握Conda软件安装:虚拟环境、软件通道、加速solving、跨服务器迁移
收藏 IP: 124.64.18.*| 热度|

0

该博文允许注册用户评论 请点击登录 评论 (0 个评论)

数据加载中...

Archiver|手机版|科学网 ( 京ICP备07017567号-12 )

GMT+8, 2024-4-29 00:11

Powered by ScienceNet.cn

Copyright © 2007- 中国科学报社

返回顶部