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)