寫在前面的廢話
這一周我為自己的馬虎付出了嚴(yán)重的代價(jià)Q锸妗!!
每天十幾個(gè)小時(shí)一直在debug……以后寫代碼之前一定要拜拜雍正宝恶,專治八阿哥(bug)
雖然debug讓我無心其他事情,但是咱說好的周更不能斷(其實(shí)是我找到了bug乘综,想寫篇文章壓壓驚)
太長不看系列
- 最粗魯?shù)霓k法:grep -f (慢憎账,內(nèi)存需求太大)
- 比較省事的辦法:R的merge函數(shù) (較快,但是內(nèi)存需求賊大)
- 比較機(jī)智的辦法:字典/哈希(很快卡辰,Python里面叫作字典鼠哥,Perl里面似乎叫作哈希熟菲,對于不會(huì)這兩門語言的同學(xué)友好性太差)
-
穩(wěn)如老狗的辦法:awk命令 (也是依賴字典方法,速度快朴恳,操作簡單抄罕,深得吾心)
廢話超多系列
我相信你們和我一樣,有時(shí)需要知道文件A中有哪些行出現(xiàn)在文件B中于颖。說起來有點(diǎn)繞呆贿,直接舉個(gè)例子可能會(huì)更通俗易懂一些:
> cat A.txt
Hello
Thank you
Thank you very much
> cat B.txt
Hello
Welcome to
Beijing
文件A中有哪些行在文件B中也出現(xiàn)過,這個(gè)例子比較簡單森渐,我們直接就能看出A.txt
中的Hello在B.txt
文件中出現(xiàn)做入。
但是當(dāng)文件較大時(shí)怎么辦呢?一百行的話同衣,你也許還可以試試 肉眼去觀察竟块,一旦行數(shù)上萬,就必須借助計(jì)算機(jī)幫助我們完成這些事情了
不瞞你說耐齐,我之前查找的兩個(gè)文件浪秘,一個(gè)幾百萬行,另一個(gè)上千萬行埠况,為了找一個(gè)合適的方法可是苦惱了很久
最粗魯?shù)霓k法:grep
grep
方法比較粗魯耸携,但針對比較小的文件,我還是很喜歡用它的辕翰。因?yàn)樗恍枰昧鶄€(gè)字符夺衍,真的節(jié)省體力。
cat -f A.txt B.txt
: 可以把B文件中存在于A文件中的行輸出喜命,具體操作如下
如果你使用了-v參數(shù)沟沙,則可以進(jìn)行另一個(gè)騷操作,輸出只存在與B文件而不存在與A文件中的行壁榕。但是這個(gè)方法有一些缺點(diǎn)尝胆,比如:
- 不能分字段查找(比如A文件的第二列信息,是否在B文件中的第三列信息中出現(xiàn))
-
針對大文件护桦,耗時(shí)長且內(nèi)存消耗大(時(shí)間我沒有專門統(tǒng)計(jì)過含衔,但是在運(yùn)行這個(gè)命令的時(shí)候內(nèi)存倒是溢出過,如下圖)
比較省事的辦法:R的merge函數(shù)
做生信的同學(xué)二庵,你可以不會(huì)Python贪染,你可以不會(huì)Perl,但是R你總要會(huì)一點(diǎn)吧……
當(dāng)我們在R中合并文件時(shí)催享,我們經(jīng)常會(huì)用到merge()
函數(shù)杭隙,它可以根據(jù)兩文件中指定的列進(jìn)行合并,對文件取交集因妙。換個(gè)角度想想痰憎,這個(gè)取交集的操作不就是找把B文件中存在于A文件中的行么票髓。
這里,因?yàn)槠鶈栴}铣耘,我就不介紹merge函數(shù)是如何使用的了洽沟。這個(gè)函數(shù)的原理我自己也不是很懂,只是知道是它用空間換時(shí)間蜗细,速度很快裆操,但是內(nèi)存消耗是真的大。
下面是我對兩個(gè)幾百兆的文件merge時(shí)的報(bào)錯(cuò)信息炉媒,可以看到內(nèi)存溢出的那是相當(dāng)嚴(yán)重
比較機(jī)智的辦法:字典/哈希
如果你學(xué)過Python踪区,一定知道dict()
字典。這個(gè)方法是真的快吊骤,就和查字典一樣缎岗,不存在遍歷的問題。這里推薦閱讀廖雪峰老師關(guān)于字典的介紹白粉,我自己的語言功底很難三兩句話把這個(gè)名詞解釋清楚传泊。
Python里面叫作字典,Perl里面似乎叫作哈希
但是該方法對于不會(huì)這兩門語言的同學(xué)友好型太差蜗元,我只是想比對個(gè)文件或渤,你卻讓我從頭開始學(xué)一門編程語言系冗?奕扣??(內(nèi)心:你這是想要我死掌敬?惯豆??奔害?)
穩(wěn)如老狗的辦法:awk命令
鋪墊了這么多楷兽,終于可以扯到重點(diǎn)上了。awk/sed/grep可以說是shell中處理數(shù)據(jù)的三劍客华临,我們大部分的數(shù)據(jù)清洗問題芯杀,都可以使用這三個(gè)方法解決。
聽起來這么牛雅潭,但是我們大部分人只會(huì)其基本操作揭厚,稍微復(fù)雜一點(diǎn)就會(huì)觸碰到我們的知識盲區(qū)……
沒關(guān)系,飯要一口一口吃扶供,畢竟肥肉不是一天長成的筛圆,頭發(fā)也不是一天就掉光的……
這里說個(gè)實(shí)話,希望不會(huì)挨打
這里簡單介紹一下awk的一些參數(shù)椿浓,只介紹稍后用到的參數(shù)太援,想了解更多闽晦,可以自行搜索學(xué)習(xí)
-
FNR
:各文件分別計(jì)數(shù)的行號。當(dāng)awk命令后面跟了不止一個(gè)文件時(shí)提岔,每讀入一個(gè)新文件仙蛉,行號就要從頭開始計(jì)算。 -
NR
:已經(jīng)讀出的記錄數(shù)唧垦,就是行號捅儒,從1開始,不斷累加振亮,讀入新文件也不會(huì)從頭開始計(jì)算比如 awk命令之后跟了兩個(gè)文件巧还,分別有3行和5行,那么FNR的值依次時(shí)1坊秸,2麸祷,3,1褒搔,2阶牍,3,4星瘾,5走孽;而NR的值則是1,2琳状,3磕瓷,4,5念逞,6困食,7,8
- $0表示讀取的文件某一行的所有內(nèi)容翎承,$1表示某一行的第一列硕盹,$2表示某一行的第二列,以此類推
- '[]':數(shù)組叨咖,比如a[$0]
介紹了這么多瘩例,我應(yīng)該如何使用這些參數(shù)呢?
awk 'NR==FNR {a[$0]} NR>FNR&&!($0 in a){print $0}' A.txt B.txt
這里的NR==FNR
甸各,表示當(dāng)前讀取的是第一個(gè)文件A.txt的內(nèi)容垛贤,這個(gè)時(shí)候創(chuàng)建一個(gè)數(shù)組,將第一個(gè)文件的每一行內(nèi)容都作為一個(gè)key值輸入痴晦,如果不存在這個(gè)a[$0]變量南吮,則創(chuàng)建一個(gè)。通過這個(gè)方法誊酌,我們可以把文件A.txt的所有內(nèi)容逐行放入數(shù)組變量a中部凑。
接著NR>FNR
表示露乏,當(dāng)前讀取的不是第一個(gè)文件(即開始讀取B.txt文件)。!($0 in a)
表示當(dāng)前讀入的行涂邀,不存在之前的a數(shù)組中(即:B.txt文件中不存在于A.txt文件中的行)瘟仿。中間的&&
表示前后兩個(gè)條件都要滿足,若滿足則執(zhí)行print $0
命令比勉。
最終輸出的結(jié)果將是只存在于B文件中劳较,而不存在于A文件中的行。除此之外浩聋,我們可以指定比較兩個(gè)文件中的特定列观蜗,比如:
awk 'NR==FNR {a[$3]} NR>FNR&&!($2 in a){print $0}' A.txt B.txt
以上命令表示,我想找出只存在于B文件中第二列衣洁,而不存在于A文件中第三列的字段墓捻,若存在這樣的字段,則將B文件中該字段所在的行打印輸出坊夫。
這個(gè)方法砖第,速度快,容易上手环凿,基本上你會(huì)用Linux就可以梧兼。既然提到了awk命令,那就再說一個(gè)騷操作:文件去重V翘S鸾堋!
好吧瞭稼,我知道一旦說去重忽洛,你首先想到的一定是以下兩條命令:
sort A.txt | uniq
sort -u A.txt
這個(gè)方法固然是好腻惠,但是sort命令排序十分耗時(shí)环肘。處理小文件時(shí)我們可能體會(huì)不到,但是一旦遇到上百萬行的文件集灌,光是等待時(shí)間就夠我們喝一壺茶了悔雹。這個(gè)時(shí)候awk命令就體現(xiàn)出其重要性了。命令如下:
awk '!a[$0]++' A.txt
這里不講太多欣喧,簡單說一下這個(gè)命令執(zhí)行的步驟:
- 首先執(zhí)行
a[$0]
腌零,將輸入文件的一整行當(dāng)作數(shù)組a的key值 - 接著執(zhí)行
!a[$0]
,對a[$0]
的值取反唆阿,如果a[0]`為1,執(zhí)行awk的默認(rèn)操作驯鳖,打印輸出這一行 - 最后是
!a[$0]++
闲询,對a[$0]
的值加1久免,因此當(dāng)之后再遇到相同行時(shí),!a[$0]
會(huì)被看作是0扭弧,不hi行默認(rèn)的打印操作
也許你覺得我講的還是不夠清楚阎姥,那么這里給你推薦一個(gè)問答,里面的第一個(gè)回答(最高贊)將這個(gè)去重的邏輯講的十分清楚鸽捻。
一點(diǎn)題外話
大部分做生信的人呼巴,對于算法都是一知半解,只求解決問題不求代碼優(yōu)美御蒲。但寫代碼衣赶,我們畢竟不是專業(yè)的。我們的終極目的是解決生物學(xué)問題厚满,而不是過多的糾結(jié)代碼的優(yōu)美性屑埋。
代碼寫的再好看,解決不了問題痰滋,依然發(fā)不了sci
另外代碼不要晚上寫摘能,一個(gè)原因是晚上寫代碼容易出bug(這是一個(gè)涉及玄學(xué)的話題),另外一個(gè)原因就是熬夜令人頭禿敲街。
最近頭有點(diǎn)涼团搞,也不知道是天氣冷了,還是頭發(fā)禿了