0

私は Python の初心者で、どこにも答えが見つからないという問題に遭遇しました。

別のファイルに基づいて一連のファイルをフィルター処理するコードを作成しようとしています。ファイルは、複数の行と列を持つ配列です。私が望むのは、特定の列のフィルター ファイル内の行に一致する行をデータ ファイルから削除することです。

コードは次のとおりです。

paths = ('filepaths.txt')#file that has filepaths to open
filter_file = ('filter.txt')#file of items to filter
filtered = open('filtered.txt','w') #output file

filtering = open(filter_file, 'r').readlines()
for f in filtering:
    filt = f.rstrip().split('\t')

files = open(paths).read().splitlines()
for file in files:
    try:
        lines = open(file,'r').readlines()
        for l in lines:
            data = l.rstrip().split('\t')

        a = [data[0], data[5], data[6], data[10], data[11]] #data columns to match
        b= [filt[0], filt[1], filt[2], filt[3], filt[4]] #filter columns to match

        for i,j in zip(a,b): #loop through two lists to filter
            if i != j:
                matches = '\t'.join(data)
                print (matches)
                filtered.write(matches + '\n')
filtered.close()

コードは実行されますが、思い通りに動作しません。返されるのは、各ファイルの最後の行で、5 回繰り返されます。

明らかに、私は何かが欠けています。zip が使用するのに適切な機能であるかどうか、または他の機能が優れているかどうかはわかりません。アドバイスをいただければ幸いです。

編集:

フィルタの入力例:

HSPG2   22161380    22161380    G   A
PPTC7   110974744   110974744   G   C
OR1S2   57971546    57971546    A   C

フィルタリングするファイルの入力例 (余分な列は省略):

TKTL1   8277    broad.mit.edu   37  X   153558089   153558089   +   3'UTR   SNP G   C   C
MPP1    4354    broad.mit.edu   37  X   154014502   154014502   +   Silent  SNP G   A   A
BRCC3   79184   broad.mit.edu   37  X   154306908   154306908   +   Silent  SNP A   T   T

出力例 (余分な列は省略):

BRCC3   79184   broad.mit.edu   37  X   154306908   154306908   +   Silent  SNP A   T   T
BRCC3   79184   broad.mit.edu   37  X   154306908   154306908   +   Silent  SNP A   T   T
BRCC3   79184   broad.mit.edu   37  X   154306908   154306908   +   Silent  SNP A   T   T
BRCC3   79184   broad.mit.edu   37  X   154306908   154306908   +   Silent  SNP A   T   T
BRCC3   79184   broad.mit.edu   37  X   154306908   154306908   +   Silent  SNP A   T   T
4

2 に答える 2

2

csv簡単な変更から始めて、Python のライブラリやany関数などの組み込みツールを使用してコードを簡素化する方法を示します。

これは、物事を少し整理し、正しいロジックを使用するバージョンですが、新しい言語機能をあまり導入していません。それが使用する主な新しいものは、withステートメント(終了時にファイルを自動的に閉じる)と、使用するのではなく、ファイルを直接反復処理することですreadlines

paths = ('filepaths.txt')#file that has filepaths to open
filter_file = ('filter.txt')#file of items to filter
with open(filter_file, 'r') as filter_source:
    filters = []
    for line in filter_source:
        filters.append(line.rstrip().split('\t'))
with open(paths, 'r') as filename_source:
    filenames = []
    for line in filename_source:
        filenames.append(line.rstrip())
with open('filtered.txt','w') as filtered:
    for filename in filenames:
        with open(filename,'r') as datafile:
            for line in datafile:
                data = l.rstrip().split('\t')
                a = [data[0], data[5], data[6], data[10], data[11]] #data columns to match
                for filter in filters:
                    matched = True
                    for i,j in zip(a,filter):
                        if i != j:
                            matched = False
                            break
                    if matched:
                         # the data row matched a filter, stop checking for others
                         break
                if not matched:
                    filtered.write(line)

ここで何度か行うことの 1 つは、for ループを使用してリストを作成することです。リスト内包表記と呼ばれる同じことを行う、より簡潔な式があります。したがって、それを使用すると、次のようになります。

with open(filter_file, 'r') as filter_source:
    filters = [line.rstrip().split('\t') for line in filter_source]
with open(paths, 'r') as filename_source:
    filenames = [line.rstrip() for line in filename_source]

しかし、Python にはcsv、タブ区切り形式の読み取りを処理できる便利なライブラリもあります。

import csv

with open(filter_file, 'rb') as filter_source:
    filter_reader = csv.reader(filter_source, delimiter='\t')
    filters = list(filter_reader)

それを繰り返すと、区切り文字で区切られたフィールドのリストが返されます。bモードで開いたことに注意してください。それが違いを生むかどうかはプラットフォームによって異なりますが、そうする場合は csv ドキュメントにそれが必要であることが記載されています。

データ ファイルにも同様に使用できます。オプションで、writerクラスを使用してフィルター処理された出力を書き込むこともできます。

最後に、anyビルトインallは iterable を取りTrue、 iterable の内容の一部または全部が に評価された場合に戻りTrueます。これらを使用して、ジェネレーター式を使用して、ネストされた for ループを削除できます。これは、遅延評価されることを除いて、リスト内包表記に似た構造anyですall。というわけでこんな書き方です。

def match(dataline, filter):
    return all(i==j for (i, j) in zip(dataline, filter))

zipこの特定のケースでは、タプルの実際のリストを作成するために使用しているため、ショートサーキットからあまり得られません。しかし、そのような短いリストの場合は問題なく、既にメモリ内にあるリスト (遅延評価バージョン) よりzipも優れています。itertools.zip

any次に、行をすべてのフィルターと簡潔に比較し、一致するとすぐに短絡するために使用できます。

a = [data[0], data[5], data[6], data[10], data[11]]
if not any(match(a, filter) for filter in filters):
    filtered.write(line)

これがまだやり過ぎであることを除いて。このmatch関数は、2 つの入力のすべての要素が等しくなければならないことを強制していますが、2 つのリストが等しいかどうかをテストする場合、これは Python が自動的に行うことの一部です。私が書いたmatch関数は、長いリストの開始要素がすべて短いリストと一致する限り、長さが等しくないリストを一致させることができますが、Python リストの等価性はそうではありませんが、それはここでは問題ではありません。したがって、これも機能します。

a = [data[0], data[5], data[6], data[10], data[11]]
if not any (a==filter for filter in filters):
    filtered.write(line)

または、通常よりも長いフィルタが許容できる場合:

if not any (a==filter[:5] for filter in filters):

非スライシング バージョンは、直接リスト メンバーシップ テストを使用して記述することもできます。

if a not in filters:
    filtered.write(line)

また、Blckknght が指摘しているように、Python には、線のようなものが多くのパターンのいずれかに一致するかどうかをすばやくテストするためのより優れた方法がありsetます。これは、一定時間のルックアップを使用するデータ型です。csvライブラリや によって返されるようなリストsplitは、セットのメンバーになることはできませんが、タプルのメンバー自体がハッシュ可能である限り、タプルはセットのメンバーになることができます。したがって、フィルターとデータ行のサブセットをタプルに変換すると、リストではなくセットを維持して、さらに高速にチェックできます。これを行うには、各フィルターをタプルに変換する必要があります。

filters = set(tuple(filter) for filter in filter_reader)

次に、aタプルとして定義します。

a = (data[0], data[5], data[6], data[10], data[11])
if a not in filters:
    filtered.write(line)

インスタンスを使用して出力を書き込んでいる場合は、メソッドとジェネレータ式csv.writerを使用してさらに統合することもできます。writerows

filtered_writer.writerows(data for data in data_reader if (data[0], data[5], data[6], data[10], data[11]) not in filters)

すべてをまとめると、次のようにします。

import csv

paths = ('filepaths.txt') #file that has filepaths to open
filter_file = ('filter.txt') #file of items to filter
with open(filter_file, 'rb') as filter_source:
    filters = set(tuple(filter) for filter in csv.reader(filter_source, delimiter='\t'))
with open(paths, 'r') as filename_source:
    filenames = [line.rstrip() for line in filename_source]
with open('filtered.txt','wb') as filtered:
    filtered_writer = csv.writer(filtered, delimiter='\t')
    for filename in filenames:
        with open(filename,'rb') as datafile:
            data_reader = csv.reader(datafile, delimiter='\t')
            filtered_writer.writerows(data for data in data_reader if (data[0], data[5], data[6], data[10], data[11]) not in filters)
于 2013-11-07T01:04:05.020 に答える
0

を作成するfiltと、1 つの文字列変数が作成され、複数回上書きされます。交換してみる

for f in filtering:
    filt = f.rstrip().split('\t')

filt = [f.rstrip().split('\t') for f in filtering]

filtこれはリストのリストで、各要素が 1 つの行を表しています。たとえば、filt[0]は 1 行目をfilt[2][3]表示し、 は 3 行目の 4 列目を表示します。これを正しく処理するには、プログラムの残りの部分を変更する必要がある場合があります。

于 2013-11-07T00:20:02.867 に答える