2017-06-08 10 views
-3

私は巨大なファイルを解析しようとしていますが、以下のコードを解析するのに時間がかかりすぎます。ファイルのサイズは2GBです。私はそれをスピードアップするのに役立ついくつかを期待しています。次のpython3コードを高速化するには?

import os, shlex 

def extractLinkField(inDir, outDir): 
    fileList = os.listdir(inDir) 
    links = set() 

    for File in fileList: 
     print(File) 
     with open(os.path.join(inDir, File), encoding="utf8") as inFile: 
      for line in inFile: 
       try: 
        links.add(shlex.split(line)[2]) 
       except Exception: 
        continue 

     outFile = open(os.path.join(outDir, 'extractedLinks.txt'), 'a+', encoding="utf8") 
     for link in list(links): 
      outFile.write(link + '\n') 
     outFile.close() 

Path = os.path.join(os.getcwd(), 'logs') 
extractLinkField(Path, os.getcwd()) 

次のようにファイルの形式は次のとおりです。

90 "m z pd gk y xr vo" "n l v ogtc dj wzb" "d zi pfgyo b tmhek" "df qu venr ls hzw j" 
82 "p wgd lv f kt eb uq" " ij cw v a r y qp" " pf qdlcgm jz os y" "f xm n cr ublzig" 
89 "c pgib a ost whk" "ria m h fvcb es z" "qzoy g xbr  makc" "ms lqc v ektb w " 
66 "zxm pe hb vi dj " "rg ebfwp y zv oakm" "b nut ko je m crsh" " imsxtzfw g ka j l " 
2 "uyhnpt l dj qak " "o hned j pqub t a " "v hlyc afwi sgr p" "h wtvi g o nc sujqx" 
17 "apo ufliz qctbd xh " "k lxgbrcwzf mnhtq p" "z gk m rsbu l" " ds m au w cior " 
9 " h t ac jpn ok mz" "aty rs w box vk zefp" "nm fbc x egt zruap " "xg oi j z wyf v dqp" 
82 "xs q ve  k oi c " " z lfa dwiprxb ku g" "kua p f b oqz jrt " " t wlvy d po qrx e" 
51 "cx iq wuvhb gkmo y" " u p yx bv mjz r" "oatc wuxd yfgjs ri " "vbg w  h ife myl" 
91 "cdqkp rn u ow h f" "ko rt y c eis d q jl" " lv fe r zpju yw " " wz vtxa jn lg s" 
83 "bts dl kjycre ozv " " k i q m r ypsu lh " "pr exw sznqa yvu i " " uq tzk nomrx e " 

(それはまだに包まれている引用符で包まれたファイル内の文字列を分割すべきではないと全体として解析されなければならないことに注意してください引用符)

Sample output and directory structure:

+0

私の質問に投票するつもりなら、なぜそれをやっているのか説明してください。ありがとう。 – KingMak

+2

あなたは、あなたが素早いパンダのパーサーを書こうとすることができるように、あなたが解析しようとしているファイルの10行を含めますか? – Matt

+0

今、私は今すぐそれをやろうよ@Matt – KingMak

答えて

2

犯人は、明らかに、shlex.split()です。これは非常に高価な操作です(分割ごとに多くの定型文を使用して完全に新しいオブジェクトを作成します)。したがって、データが表示された形式に従う場合は、手動でデータを解析できます。

だから、ここshlex.split()としてあなたのサンプルデータで同じように実行するメソッドがあります:shlex.split()に比べ

def manual_split(data): 
    data = data.strip() # clear artifacts 
    tokens = [] 
    head = 0 
    open_quote = False 
    while True: 
     if open_quote: 
      quote = data.find('"', head) 
      if quote == -1: # this should not happen, no matching quotes 
       break 
      tokens.append(data[head:quote]) 
      head = quote + 1 
      open_quote = False 
     else: 
      space = data.find(' ', head) 
      quote = data.find('"', head) 
      if space == -1 and quote == -1: # nothing more to split 
       break 
      if space < quote: 
       if not tokens or space - head > 1: 
        tokens.append(data[head:space]) 
       head = space + 1 
       open_quote = False 
      else: 
       open_quote = True 
       head = quote + 1 
    if head < len(data): # add leftovers, if any, as the last token 
     tokens.append(data[head:]) 
    return tokens 

、ここでは(ループを含む)同じサンプルデータの上にあなたのためにいくつかの時間をされ実行されています。

shlex.split: 10,000 loops: 11.51 s, per loop: 1.151 ms 
manual_split: 10,000 loops: 0.951 s, per loop: 95.11 µs 

したがって、12倍以上速くなります。しかし、私たちはもっとうまくいく可能性があります...このアプローチの問題点は、遅い数字の文字列を使って文字列を遅くすることですが、遅い文字列を使ってPython側をシャッフルすることです。その魔法をやるチャンス。これをCで実装して、モジュールとしてロードすると非常に高速ですが、悲しいかなか速いでしょう...

だから私はregexが大部分であなたがそれをより複雑なルールにしても、純粋なPythonの文字列検索&を十分に大きなデータに渡って処理することができるはずです。だから、次の候補:

import re 

FIELDS_PATTERN = re.compile(r"(?:\"(.*?)\"|(\S+))") 

def regex_split(data): 
    return [x[0] or x[1] for x in FIELDS_PATTERN.findall(data)] 

そして今、我々は、最終的なベンチマークがあります

shlex.split: 10,000 loops: 11.51 s, per loop: 1.151 ms 
manual_split: 10,000 loops: 0.951 s, per loop: 95.11 µs 
regex_split: 10,000 loops: 0.482 s, per loop: 48.16 µs 

をはい、正規表現1はshlex.split()よりも、ほぼ24倍高速です!そして、それらはすべてあなたのテストデータに対して同じ分割結果を生成します。

しかしあなたがジャンプしshlexをキックする前に、あなたは徹底的に彼らがあなたのデータに合うことを確認するには、これらの両方をテストする必要があります - たとえば、彼らはそうエスケープ引用符や引用符で囲まれた文字列の特殊なPOSIXのほつれを認識しません。あなたのデータにそのようなケースがある場合は、そのことを考慮する必要があります。あなたはスピードアップのほんの少しを追加したい場合は

また、関係のない、あなたの出力に書き込むのではなく、ただちに後でそれを介してだけループするように設定して保存する:下の

with open(os.path.join(out_dir, 'extractedLinks.txt'), 'a+', encoding="utf8") as out_file: 
    links = set() # temp store to ensure uniqueness... 
    for current_file in file_list: 
     with open(os.path.join(in_dir, current_file), encoding="utf8") as in_file: 
      for line in in_file: 
       try: 
        link = shlex.split(line)[2] # or whatever other function 
        if link not in links: 
         links.add(link) 
         out_file.write(link + "\n") 
       except Exception: 
        continue 

あなたの巨大な入力ファイルに数百万行を集めることを前提にして、これは2番目または2番目のものを削り取る可能性があります...

+0

素敵な答えは、私はパンダのバージョンでドロップするつもりだったが、あなたのベンチマークは、必要性を否定するようだ – Matt

+0

うわー:詳細をありがとうございます。私はあなたの答えから多くを学んだ。 – KingMak

関連する問題