1

私は、100万文字の長さで、句読点がなく、文字の集まりだけの8つのファイルを読み取ることを目的としたプログラムを持っています。

8 つのファイルは、見つかった 4 つの DNA サンプルを表しており、プログラムが行うことは、サンプル内の 1 つのファイルから文字を取得し、同じサンプルの別のファイル内の文字と結合することです。たとえば、file1 が次のように読み取られた場合:

abcdefg

と file2 読み取り:

hijklmn

組み合わせは次のようになります。

ah, bi, cj, dk, el, fm, gn

いずれにせよ、プログラムは次に、各ペアの組み合わせがいくつ存在するかをカウントし、たとえば次のような辞書を出力します。

{'mm': 52, 'CC': 66, 'SS': 24, 'cc': 19, 'MM': 26, 'ss': 58, 'TT': 43, 'tt': 32}

問題は、プログラムは小さなファイルでは正常に動作しますが、100 万文字の長さ (はい、これはリテラル数であり、誇張ではありません) のファイルでは、プログラムがハングし、タスクを完了できないように見えることです。(私は一度一晩実行したままにしましたが、何も起こりませんでした。)

オーバーフロー エラーですか、それとも私が使用しているメソッドは大きなファイルに対して小さすぎますか? これを処理するより良い方法はありますか?

私のコード:

import re
from collections import Counter

def ListStore(fileName):
    '''Purpose, stores the contents of file into a single string'''           

    #old code left in for now
    '''
    with open(fileName, "r") as fin:
        fileContents = fin.read().rstrip()
        fileContents = re.sub(r'\W', '', fin.read())
    '''
    #opens up the file given to the function
    fin = open(fileName,'r')

    #reads the file into a string, strips out the newlines as well
    fileContents = fin.read().rstrip()


    #closes up the file
    fin.close()

    #splits up the fileContents into a list of characters
    fileContentsList = list(fileContents)   

    #returns the string
    return fileContentsList


def ListCombo(list1, list2):
    '''Purpose: combines the two DNA lists into one'''


    #creates an empty dictionary for list3
    list3 = []

    #combines the codes from one hlaf with their matching from the other
    list3 = [''.join(pair) for pair in zip(list1, list2)]

    return list3


def printResult(list):
    '''stores the result of the combination in a dictionary'''




    #stores the result into a dictionary
    result = dict((i,list.count(i)) for i in list)

    print (result)
    return result


def main():

    '''Purpose: Reads the contents of 8 files, and finds out how many
    combinations exist'''


    #first sample files

    file_name = "a.txt"
    file_name2 = "b.txt"

    #second sample files
    file_name3 = "c.txt"
    file_name4 = "d.txt"

    #third sample files
    file_name5 = "e.txt"
    file_name6 = "f.txt"

    #fourth sample files
    file_name7 = "g.txt"
    file_name8 = "h.txt"


    #Get the first sample ready

    #store both sides into a list of characters

    contentList = ListStore(file_name)

    contentList2 = ListStore(file_name2)

    #combine the two lists together
    combo_list = ListCombo(contentList, contentList2)

    #store the first sample results into a dictionary
    SampleA = printResult(combo_list)

    print (SampleA)

    # ****Get the second sample ready****

    #store both sides into a list of characters
    contentList3 = ListStore(file_name3)
    contentList4 = ListStore(file_name4)

    #combine the two lists together
    combo_list2 = ListCombo(contentList3, contentList4)

    #store the first sample results into a dictionary
    SampleB = printResult(combo_list2)

    print (SampleB)

    # ****Get the third sample ready****

    #store both sides into a list of characters
    contentList5 = ListStore(file_name5)
    contentList6 = ListStore(file_name6)

    #combine the two lists together
    combo_list3 = ListCombo(contentList5, contentList6)

    #store the third sample results into a dictionary
    SampleC = printResult(combo_list3)

    print (SampleC)

    # ****Get the second sample ready****

    #store both sides into a list of characters
    contentList7 = ListStore(file_name7)
    contentList8 = ListStore(file_name8)

    #combine the two lists together
    combo_list4 = ListCombo(contentList7, contentList8)

    #store the fourth sample results into a dictionary
    SampleD = printResult(combo_list4)

    print (SampleD)



if __name__ == '__main__':
    main()
4

3 に答える 3

2

内容全体をメモリに読み込まないでください。必要はありません。さらに、zip()すでに文字列を文字に分割しているため、自分でこれを行う必要はありません。

ここでの秘訣は、2 つのファイルをチャンクで読み取りながら文字をペアにするジェネレーターを作成することです。これは、ファイルを読み取る最も効率的な方法です。

最後に、collections.Counter()カウントを保持するために使用します。

from functools import partial
from collections import Counter

with open(filename1, 'r') as file1, open(filename2, 'r') as file2:
    chunked1 = iter(partial(file1.read, 1024), '')
    chunked2 = iter(partial(file2.read, 1024), '')
    counts = Counter(''.join(pair) for chunks in zip(chunked1, chunked2) for pair in zip(*chunks))

ここでは、コードは 1024 バイトのチャンクで読み取られます。最高のパフォーマンスを得るために必要に応じて調整します。一度にメモリに保持されるのは、ファイルから 2048 バイトまでです。ペアは、カウントされるとオンザフライで生成されます。

于 2013-07-30T13:40:59.437 に答える
1

このprintResultメソッドでは、 の各要素iを調べて、辞書のキーにlist値を割り当てます。list.count(i)iresult

どのように機能するかは完全にはわかりませんcount(i)が、リストの大部分を検索し、実行するたびに要素の数をカウントする必要があるとi思います。あなたのコードでは、 のように重複がある場合、リスト内にある['aa','bb','aa']要素の数を 2 回カウントし、'aa'そのたびにリスト全体を調べます。これは、長いリストでは非常に時間がかかります。

各タイプの要素の数を数えるには、リストを 1 回確認するだけで済みます。これには a を使用することをお勧めします。これは、デフォルト値でdefaultdict新しい開始をそれぞれ作成できるためです。key0

    from collections import defaultdict
    result = defaultdict(int)
    for i in list:
        result[i] = result[i] + 1
    print result

defaultdictwith を作成するとint、それぞれの newkeyを value で開始できます0。次に、リストを 1 回トラバースし、1各ペアが見つかるたびに値を追加します。これにより、リストを複数回調べる必要がなくなります。

于 2013-07-30T13:48:15.630 に答える
1

書かれているように、私は個人的にあなたのプログラムが I/O バウンドだとは思いません。たとえそうであったとしても、それを多くの呼び出しに分割することは、バッファリングされたとしても、すべてをメモリに読み込むほど速くはありません。やっていました。とはいえ、あなたのプログラムが巨大なファイルを処理するのに時間がかかる理由は正確にはわかりません.文字列とリストは両方ともシーケンスであるため、多くの不要な操作が行われている可能性があります。 .

これは、冗長および/または不必要なもののほとんどが削除された、プログラムの最適化されたバージョンです。コードにインポートされたクラスを実際に使用しcollections.Counterますが、使用されることはありません。ファイルの内容をメモリに読み取っても、それらの各ペアを処理するのに必要な最小限の時間だけ保持します。

from collections import Counter
import os

DATA_FOLDER = 'datafiles' # folder path to data files ('' for current dir)

def ListStore(fileName):
    '''return contents of file as a single string with any newlines removed'''
    with open(os.path.join(DATA_FOLDER, fileName), 'r') as fin:
        return fin.read().replace('\n', '')

def ListCombo(seq1, seq2):
    '''combine the two DNA sequences into one'''
    # combines the codes from one half with their matching from the other
    return [''.join(pair) for pair in zip(seq1, seq2)]

def CountPairs(seq):
    '''counts occurences of pairs in the list of the combinations and stores
    them in a Counter dict instance keyed by letter-pairs'''
    return Counter(seq)

def PrintPairs(counter):
    #print the results in the counter dictionary (in sorted order)
    print('{' + ', '.join(('{}: {}'.format(pair, count)
        for pair, count in sorted(counter.items()))) + '}')

def ProcessSamples(file_name1, file_name2):
    # store both sides into a list of characters
    contentList1 = ListStore(file_name1)
    contentList2 = ListStore(file_name2)

    # combine the two lists together
    combo_list = ListCombo(contentList1, contentList2)

    # count the sample results and store into a dictionary
    counter = CountPairs(combo_list)

    #print the results
    PrintPairs(counter)

def main():
    '''reads the contents of N pairs of files, and finds out how many
    combinations exist in each'''
    file_names = ('a.txt', 'b.txt',
                  'c.txt', 'd.txt',
                  'e.txt', 'f.txt',
                  'g.txt', 'h.txt',)

    for (file_name1, file_name2) in zip(*([iter(file_names)]*2)):
        ProcessSamples(file_name1, file_name2)

if __name__ == '__main__':
    main()
于 2013-07-30T16:04:41.400 に答える