2015-11-18 73 views
2

所以,這個一直給我一個很難!
我正在與巨大的文本文件,並由巨大的我的意思是100Gb +。具體來說,他們在fastq format。這種格式用於DNA測序數據,以及由四條線,像這樣記錄:Python - 檢查兩個巨大的文本文件之間的一致性

@REC1 
GATTTGGGGTTCAAAGCAGTATCGATCAAATAGTAAATCCATTTGTTCAACTCACAGTTT 
+ 
!''*((((***+))%%%++)(%%%%).1***-+*''))*55CCF>>>>>>CCCCCCC65 
@REC2 
GATTTGGGGTTCAAAGCAGTATCGATCAAATAGTAAATCCATTTGTTCAACTCACAGTTT 
+ 
!''*((((***+))%%%++)(%%%%).1***-+*''))**55CCF>>>>>>CCCCCCC65 
. 
. 
. 

對於這個問題的緣故,只專注於標題行,開始用「@」。

因此,爲了QA的目的,我需要比較兩個這樣的文件。這些文件應該有匹配的標題,所以另一個文件中的第一個記錄也應該有'@ REC1'標題,下一個應該是'@ REC2',依此類推。在進行重大的下游分析之前,我想確保是這種情況。
由於文件太大,天真的迭代一個字符串comparisson會花費很長時間,但是這個QA步驟將運行很多次,我不能等待那麼久。所以我認爲更好的方法是從文件中的幾個點採樣記錄,例如每10%的記錄。如果記錄的順序搞砸了,我很可能會發現它。
到目前爲止,我已經能夠通過估計文件大小來處理這些文件,而不是使用python的file.seek()來訪問文件中間的記錄。例如,大約訪問線在中間,我會做:

file_size = os.stat(fastq_file).st_size 
start_point = int(file_size/2) 
with open(fastq_file) as f: 
    f.seek(start_point) 
    # look for the next beginning of record, never mind how 

但是現在的問題是比較複雜的,因爲我不知道如何將兩個文件之間的協調,因爲字節位置不是文件中行索引的指示符。換句話說,我怎樣才能訪問這兩個文件中的第10,567,311行,以確保它們是相同的,而不必查看整個文件?

希望任何想法\提示。也許迭代平行?但究竟如何?
謝謝!

+1

我縮進您的文件樣本,以防止因此從格式化粗體/斜體等 - 我希望結果是正確的。請檢查我是否搞砸了一些東西。 –

+0

請求澄清:如果在兩個文件中相同的行號處出現相應的「@ REC123」行,則會考慮兩個文件一致。沒有其他標準? –

+0

@TimPietzcker - 感謝編輯,是的,這是唯一的標準。很簡單... – soungalo

回答

3

抽樣是一種方法,但你依靠運氣。另外,Python是這項工作的錯誤工具。使用標準的Unix命令行工具,您可以以不同的方式做出不同的事情並計算出確切的答案:

  1. 線性化您的FASTQ記錄:用製表符替換前三行中的換行符。
  2. 在一對線性化文件上運行diff。如果有差異,diff會報告它。

爲了線性,您可以通過awk運行FASTQ文件:

$ awk '\ 
    BEGIN { \ 
     n = 0; \ 
    } \ 
    { \ 
     a[n % 4] = $0; \ 
     if ((n+1) % 4 == 0) { \ 
     print a[0]"\t"a[1]"\t"a[2]"\t"a[3]; \ 
     } \ 
     n++; \ 
    }' example.fq > example.fq.linear 

要比較一對文件:

$ diff example_1.fq.linear example_2.fq.linear 

如果有任何區別,diff會發現它,並告訴你哪個FASTQ記錄是不同的。

您可以直接在兩個文件上直接運行diff,而無需執行額外的線性化工作,但是如果首次線性化,則更容易看出哪些讀取有問題。

所以這些都是大文件。編寫新文件在時間和磁盤空間上都很昂貴。有一種方法可以改進,使用streams

如果你把awk腳本到一個文件中(如linearize_fq.awk),你可以像這樣運行:

$ awk -f linearize_fq.awk example.fq > example.fq.linear 

這可能是您的100+ GB的文件非常有用,因爲你可以現在通過bashprocess substitutions設置了兩個Unix文件流,直接運行在這些流diff

$ diff <(awk -f linearize_fq.awk example_1.fq) <(awk -f linearize_fq.awk example_2.fq) 

或者你可以使用named pipes

$ mkfifo example_1.fq.linear 
$ mkfifo example_2.fq.linear 
$ awk -f linearize_fq.awk example_1.fq > example_1.fq.linear & 
$ awk -f linearize_fq.awk example_2.fq > example_2.fq.linear & 
$ diff example_1.fq.linear example_2.fq.linear 
$ rm example_1.fq.linear example_2.fq.linear 

命名管道和工藝替代避免產生額外的(普通)文件,這可能是你的類型的輸入問題的步驟。將100多個Gb文件的線性化副本寫入磁盤可能需要一段時間,而且這些副本還可能使用您可能沒有太多空間的磁盤空間。

使用流可以解決這兩個問題,這使得它們對於以有效的方式處理生物信息數據集非常有用。

你可以用Python重現這些方法,但它幾乎肯定會運行得慢得多,因爲Python在這類I/O繁重的任務中速度很慢。

+0

謝謝!在我作爲答案標記之前,我會嘗試並與其他方法進行比較。 – soungalo

2

並行迭代可能是在Python中執行此操作的最佳方法。我不知道有多快將運行(一個快速的SSD很可能會加快這最好的方式),但因爲你無論如何都要算在這兩個文件的新行,我看不出解決的辦法:

with open(file1) as f1, open(file2) as f2: 
    for l1, l2 in zip(f1,f2): 
     if l1.startswith("@REC"): 
      if l1 != l2: 
       print("Difference at record", l1) 
       break 
    else: 
     print("No differences") 

這是爲Python 3編寫的,其中zip返回一個迭代器;在Python 2中,您需要使用itertools.izip()

+0

謝謝,但這是我試圖避免的那種天真的解決方案。我會嘗試在這裏建議的其他方法,看看哪個效果最好。 – soungalo

+0

當然,我明白這一點。我只是懷疑這是沒有辦法的。無論如何你必須讀取每一個文件中的每一行(否則沒有辦法找出當前行號),並且因爲你不能將它們讀入內存,所以你別無選擇,只能並行迭代......除非我錯過了一些東西。 –

+0

@soungalo同意TimPietzcker,所有三個答案都建議你逐行閱讀一個文件。 – Vovanrock2002

0
import sys 
import re 

""" To find of the difference record in two HUGE files. This is expected to 
use of minimal memory. """ 

def get_rec_num(fd): 
    """ Look for the record number. If not found return -1""" 
    while True: 
     line = fd.readline() 
     if len(line) == 0: break 
     match = re.search('^@REC(\d+)', line) 
     if match: 
      num = int(match.group(1)) 
      return(num) 
    return(-1) 

f1 = open('hugefile1', 'r') 
f2 = open('hugefile2', 'r') 

hf1 = dict() 
hf2 = dict() 
while f1 or f2: 
    if f1: 
     r = get_rec_num(f1) 
     if r < 0: 
      f1.close() 
      f1 = None 
     else: 
      # if r is found in f2 hash, no need to store in f1 hash 
      if not r in hf2: 
       hf1[r] = 1 
      else: 
       del(hf2[r]) 
     pass 
    pass 
    if f2: 
     r = get_rec_num(f2) 
     if r < 0: 
      f2.close() 
      f2 = None 
     else: 
      # if r is found in f1 hash, no need to store in f2 hash 
      if not r in hf1: 
       hf2[r] = 1 
      else: 
       del(hf1[r]) 
     pass 
    pass 

print('Records found only in f1:') 
for r in hf1: 
    print('{}, '.format(r)); 
print('Records found only in f2:') 
for r in hf2: 
    print('{}, '.format(r)); 
+0

謝謝!在我作爲答案標記之前,我會嘗試並與其他方法進行比較。 – soungalo

0

從@AlexReynolds和@TimPietzcker這兩個答案都是從我的觀點很好,但我希望把我的兩分錢中,您可能還需要加快你的硬件。

  • Raplace HDD與SSD
  • 採取n SSD的和創建RAID 0。在完美的世界,你會得到n時間加快你的磁盤IO。
  • 調整從SSD/HDD讀取的塊的大小。例如,我期望一個16 MB的讀取比16個1 MB的讀取更快地執行。 (這適用於單個SSD,對於RAID 0優化,必須查看RAID控制器選項和功能)。

的最後一個選項是NOR SSD的尤爲重要。不要追求最小的內存利用率,而是儘可能多地讀取數據以保持磁盤讀取速度。例如,從平行的兩個文件單行大概可以速度降下來閱讀的讀取 - 想象的HDD,其中兩個文件的兩行總是在同一個磁盤(S)的同一側。

1

有你看着使用rdiff命令。
rdiff進行的有利的一面是:

  • 用相同的4.5GB的文件,使用rdiff只吃了約66MB的RAM和比例非常好。它從未墜毀到目前爲止。
  • 它比diff差不多快。
  • 使用rdiff本身結合了差異和補丁功能,讓你可以創建三角洲和使用相同的程序

rdiff進行的缺點應用它們是:

  • 它不是標準的Linux/UNIX的一部分分發 - 你必須 安裝librsync軟件包。
  • delta文件rdiff生成的格式與diff的格式略有不同。
  • 增量文件略大(但不足以小心)。
  • 當使用rdiff生成增量時,會使用稍微不同的方法,這個方法既好又壞 - 需要2個步驟。 第一個產生一個特殊的簽名文件。在第二步中,使用另一個rdiff調用創建了一個 delta(全部如下所示)。雖然 這兩步過程可能看起來很煩人,但它具有 的優勢,比使用diff時提供更快的增量。

參見:http://beerpla.net/2008/05/12/a-better-diff-or-what-to-do-when-gnu-diff-runs-out-of-memory-diff-memory-exhausted/

+0

這看起來很有趣,感謝發佈這個答案。 –