📝

【一応自己解決済】ファイル内の行に対して重い処理をする時になるべく高速化したい!

2023/08/29に公開

https://qiita.com/items/492e10e3a534eae8fca1


お詫び

Qiitaの元記事にて、区切り線を「---」で書いている場所があり、これがZennの記法に干渉して一部うまく表示できない記事がある事を認識しています。
全ての記事を精査しきれていないため、お手数ですがお見かけの際は教えていただけると大変喜びます。


元々某質問コミュニティに投げようと思った内容でしたが、とりあえず自己解決できたのでこっちに持ってきます。

要件

大量の行(およそ100万行)があるファイルの内容を精査してTrue,Falseの結果を返す、という事をやりたいのですが、100行の中に非常に多くの重複があり、実は1万項目だけ処理すればよい場合があります。

要件は以下の通りです。

  1. Trueになった行の総数(重複含む)が知りたい
  2. 一度精査した内容が次の行以降で見つかった場合、処理はせず当該箇所と同じ結果を返したい

サンプル

簡単ですが、以下のようなテキストファイルを用意します。

aaa
aa1
a1a
aaa
1aa
1aa
aa1

2,aaa,[1,4]
2,aa1,[2,7]
1,a1a,[3]
2,1aa,[5,6]

私の考え

単純に書けば

count = 0
for fileline in file.readlines():
    count += 1
    if fileline.find('1') > -1:
        if not (ここで重複チェック):
            (なんかすっごい複雑で重たい処理)
            print '%d %s' % (count, fileline)

で、とりあえず要件は満たせそうなのですが、重複チェックをどうやってやるべきか悩んでいます。
馬鹿正直にやるなら

def checker(fileline, checklist):
    for list_row in checklist:
        if line == list_row:
            return True

    checklist.append(fileline)
    return False

みたいな関数なりを作って比較させれば良いのでしょうが、結局ファイル内の全件チェックが前提であることと、forの中でforを回すというのが個人的にはイケてない感じがすごくあります。

私の実装

が、他に方法も思い当たらないのでとりあえず実装したのが以下。

f = open('sample.txt', 'r')

def checker(fileline, filerow, checklist):
    list_cnt = 0
    for list_row in checklist:
        if fileline == list_row[1]:
            checklist[list_cnt][0] += 1
            checklist[list_cnt][2].append(filerow)
            return True
        list_cnt += 1

    checklist.append([1, fileline,list()])
    checklist[list_cnt][2].append(filerow)
    return False

count = 0
checklist = list()

for fileline in f.readlines():
    count += 1
    if not checker(fileline, count, checklist):
        checklist.append((なんかすっごい複雑で重たい処理)の結果)
        print '%d %s' % (count, fileline.strip())

print checklist
#for row in checklist:
#    print row[]  #細かいものが欲しい場合

上記のような出力は確かに得られるんだけど……う~ん。
絶対他にいい方法があるはず。

GitHubで編集を提案

Discussion