2017-06-15 36 views
0

私は文字列のリストを持っています/私は比較する必要があり、各文字列の間の距離の測定値を取得する。私が書いた現在のコードは動作しますが、大きなリストの場合は2 forループを使用してから時間がかかります。私は文字列間の距離を測定するためにlevenshtien距離を使用しました。Python/Pandas - 文字列の比較

文字列/説明のリストは、データフレームに格納されます。

def edit_distance(s1, s2): 
    m=len(s1)+1 
    n=len(s2)+1 

    tbl = {} 
    for i in range(m): tbl[i,0]=i 
    for j in range(n): tbl[0,j]=j 
    for i in range(1, m): 
     for j in range(1, n): 
      cost = 0 if s1[i-1] == s2[j-1] else 1 
      tbl[i,j] = min(tbl[i, j-1]+1, tbl[i-1, j]+1, tbl[i-1, j-1]+cost) 
    return tbl[i,j] 

def narrative_feature_extraction(df): 
    startTime = time.time() 
    leven_matrix = np.zeros((len(df['Narrative']),len(df['Narrative']))) 
    for i in range(len(df['Narrative'])): 
     for j in range(len(df['Narrative'])): 
      leven_matrix[i][j] = edit_distance(df['Narrative'].iloc[i],df['Narrative'].iloc[j]) 
    endTime = time.time() 
    total = (endTime - startTime) 
    print "Feature Extraction (Leven) Runtime:" + str(total) 
    return leven_matrix 


X = narrative_feature_extraction(df) 

リストがn個の物語を持っている場合、得られたXが行が物語と列であるN×Nの行列であり、その物語が比較されるものです。例えば、距離(i、j)については、それは物語iとjとの間のレベンション距離である。

このコードを最適化してforループをあまり必要としない方法がありますか?または、これを計算する無限の方法がありますか?

+0

codereviewがこれに適している可能性があります – depperm

答えて

1

データ/例なしで正確なコードを与えるのは難しいが、いくつかの提案:パンダのバージョンによって

  • ...よりもはるかに速く

    • 使用リスト内包、...範囲内、 "df [i] [j]"索引付けは遅くてもかまいません。代わりに.ilocまたは.locを使用してください(.iloc [df.index.get_loc( "itemname")、df.columns。この問題がある場合、locをilocに正しく変換するにはget_loc( "itemname")]を使用してください(データフレームスライスに書き込むための警告フラグを取得していて、Pythonのどのバージョン/持っているが、広範囲にテストしていない)
    • すべての計算を実行し、ユースケースに応じて1つのデータフレームにスローする
    • forループのpythonの読み方が好きなら、少なくとも範囲内での使用は避け、代わりに "for j例えばX [:、0]である。

      def test1(): #list comprehension 
          X=np.random.normal(size=(100,2)) 
          results=[[x*y for x in X[:,0]] for y in X[:,1]] 
          df=pd.DataFrame(data=np.array(results)) 
      
      if __name__ == '__main__': 
          import timeit 
          print("test1: "+str(timeit.timeit("test1()", setup="from __main__ import test1",number=10))) 
      
      def test2(): #enumerate, df at end 
          X=np.random.normal(size=(100,2)) 
          results=np.zeros((100,100)) 
          for ind,i in enumerate(X[:,0]): 
           for col,j in enumerate(X[:,1]): 
            results[ind,col]=i*j 
          df=pd.DataFrame(data=results) 
      
      if __name__ == '__main__': 
          import timeit 
          print("test2: "+str(timeit.timeit("test2()", setup="from __main__ import test2",number=10))) 
      
      def test3(): #in range, but df at end 
          X=np.random.normal(size=(100,2)) 
          results=np.zeros((100,100)) 
          for i in range(len(X)): 
           for j in range(len(X)): 
            results[i,j]=X[i,0]*X[j,1] 
          df=pd.DataFrame(data=results) 
      
      if __name__ == '__main__': 
          import timeit 
          print("test3: "+str(timeit.timeit("test3()", setup="from __main__ import test3",number=10))) 
      
      def test4(): #current method 
          X=np.random.normal(size=(100,2)) 
          df=pd.DataFrame(data=np.zeros((100,100))) 
          for i in range(len(X)): 
           for j in range(len(X)): 
            df[i][j]=(X[i,0]*X[j,1]) 
      
      if __name__ == '__main__': 
          import timeit 
          print("test4: "+str(timeit.timeit("test4()", setup="from __main__ import test4",number=10))) 
      

      出力:

      test1: 0.0492231889643 
      test2: 0.0587620022106 
      test3: 0.123777403419 
      test4: 12.6396287782 
      

    例/タイミング(以下の例)私は、これはほとんどの場合、より高速であることがわかり、そしてあなたは、インデックスの値を保持するために列挙して使用することができますリストの理解は〜250倍速く、列挙は "for x in range"の2倍の速さです。実際の減速はデータフレームの個々のインデックス作成です(たとえ.locや.ilocを使用しても、これはあなたのボトルネックになりますので、可能であれば、df以外の配列で作業することをお勧めします)

    これは役に立ちます。あなたのケースに適用されます。 http://book.pythontips.com/en/latest/map_filter.html

    残念ながら私はあなたのユースケースにはあまりよく慣れていませんが、私はドンには触れていませんが、私は地図、フィルター、リダクション、機能を列挙することをお勧めします。このタイプのコードチューニングには適用できない、または互換性がない理由はありません。