2017-12-15 5 views
3

私は単語リストと私は複製する必要があるいくつかの文章があります。単語リストに基づいて単語を置き換えることによって、文章を複製する最速の方法は何ですか?

wordlist_dict = { 
    'class1': ['word_a', 'word_b', 'word_c'], 
    'class2': ['word_d', 'word_e'], 
    'class3': ['word_f', 'word_g', 'word_h', 'word_i', 'word_a'] 
} 

sent_list = [ 
    "I have a sentence with word_g", 
    "And another sentence with word_d", 
    "Don't forget word_b", 
    "no keyword here", 
    "Last sentence with word_c and word_e" 
] 

私の予想される結果は次のとおりです。

I have a sentence with word_f 
I have a sentence with word_h 
I have a sentence with word_i 
I have a sentence with word_a 
And another sentence with word_e 
Don't forget word_a 
Don't forget word_c 
Last sentence with word_a and word_d 
Last sentence with word_a and word_e 
Last sentence with word_b and word_d 
Last sentence with word_b and word_e 
Last sentence with word_c and word_d 

ここでは私の方法である:

import re 

pattern_list = [] 
pattern_all = '' 
wordlist = sorted(wordlist_dict.values()) 
for v in wordlist: 
    pattern_list.append('({})+'.format('|'.join(v))) 
    pattern_all += '|' + '|'.join(v) 
pattern_all = '({})+'.format(pattern_all[1:]) 
print(pattern_list) 
# ['(word_a|word_b|word_c)+', '(word_d|word_e)+', '(word_f|word_g|word_h|word_i)+'] 
print(pattern_all) 
# (word_a|word_b|word_c|word_d|word_e|word_f|word_g|word_h|word_i)+ 

new_sent_list = [] 
for sent in sent_list: 
    match_list = re.findall(pattern_all, sent) 
    print(match_list) 
    if match_list: 
     for match in match_list: 
      for i in range(len(pattern_list)): 
       if re.search(pattern_list[i], sent): 
        if match in wordlist[i]: 
         match_wordlist = wordlist[i] 
         match_wordlist.remove(match) 
         for word in match_wordlist: 
          new_sent_list.append(sent.replace(match, word)) 
        else: 
         continue 

そして、私はかしら単語リストと文リストが例よりはるかに大きいので、これを行うには効率的です。前もって感謝します。

更新:複数のクラスに属する単語と2つ以上のキーワードを持つ文があることを認識したので、私のコードは今は動作しません。

+2

コードが正常に機能している場合は、[codereview.se]に問い合わせてください。前もって感謝します。 – usr2564301

答えて

1

まず、あなたは自分のクラスにマッピングする辞書の単語にwordlist_dictを "反転" でした。ここでは、各単語が1つのクラスに属すると仮定します。そうでない場合は、もう少し複雑になります。

wordclass_dict = {w: c for c in wordlist_dict for w in wordlist_dict[c]} 

次に、あなたは、(a)はすべての単語クラスを取得し、(b)は、文を再フォーマットするためのテンプレートを作成するpatternを使用して、以下のいずれかのワードの出現をすべて見つけることができます。パターンをワード境界\bにラップするので、ワード断片に一致しないことに注意してください。

pattern = r"\b(" + "|".join(wordclass_dict) + r")\b" 
classes = [wordclass_dict[c] for c in re.findall(pattern, sentence)] 
template = re.sub(pattern, "{}", sentence) 

さて、あなたはすべての可能な置換のproductを繰り返すことができ、それらを置き換えます。この道を

for prod in itertools.product(*(wordlist_dict[c] for c in classes)): 
    print(template.format(*prod)) 

を、文"And another sentence with word_a and word_d"のための結果は次のとおりです。

And another sentence with word_a and word_d 
And another sentence with word_a and word_e 
And another sentence with word_b and word_d 
And another sentence with word_b and word_e 
And another sentence with word_c and word_d 
And another sentence with word_c and word_e 

これがあるべきpatternの2回しか検索しないため、あなたのアプローチよりもかなり高速です(時間はかかりませんでした)。別々のパターン。また、これは複数のプレースホルダー単語を持つセンテンスに対しても機能します。


複数のクラスにあることができる言葉が、あなたはこれを使用することができた場合:

wordclass_dict = collections.defaultdict(list) 
for c in wordlist_dict: 
    for w in wordlist_dict[c]: 
     wordclass_dict[w].append(c) 

# pattern, classes, template as above 

for prod in itertools.product(*([w for c in cls for w in wordlist_dict[c]] 
           for cls in classes)): 
    print(template.format(*prod)) 

あなた可能性もextendすべての単語のthenselvesとwordclass_dictエントリを、代わりにそのクラス名をproductの方がはるかに単純ですが、単語クラスのサイズと "重複"に応じて、潜在的に非常に高いスペース要件が発生します。

0

ここに、次のアイデアを実装する別のバージョンがあります。クイックルックアップのための逆辞書 "word - > class"を持っています。これは、マッピングが可逆であることを前提としています。その後、replace()をキックオフして、単語クラス内の他のすべての単語に置き換えて印刷します。

def replace(s, v, c): 
    for w in filter(lambda w: w != v, wordlist_dict[c]): 
     print(s.replace(v, w)) 

def invert(d): 
    inv = {} 
    for (c,ws) in d.items(): 
     for w in ws: 
      inv[w] = c 
    return inv 

inv_dict = invert(wordlist_dict) 

for s in sent_list: 
    for w in s.split(): 
     if w in inv_dict: 
      replace(s, w, inv_dict[w]) 
      break 
+0

私は、複数のクラスに属する単語と、複数のキーワードを持つ文があることを認識しましたが、助けてくれてありがとう。 – user7065687

0

あなたはこれを試すことができます。

import re 
wordlist_dict = { 
'class1': ['word_a', 'word_b', 'word_c'], 
'class2': ['word_d', 'word_e'], 
'class3': ['word_f', 'word_g', 'word_h', 'word_i'] 
} 

sent_list = [ 
    "I have a sentence with word_g", 
    "And another sentence with word_d", 
    "Don't forget word_b", 
    "no key word here" 
] 
final_data = [filter(lambda x:x!=''.join(re.findall('(?<=\s)[a-zA-Z]+_[a-zA-Z]+$', i)), [c for a, c in wordlist_dict.items() if any(h.endswith(''.join(re.findall('(?<=\s)[a-zA-Z]+_[a-zA-Z]+$', i))) for h in c)][0]) for i in sent_list] 
new_final_data = [a for i, a in enumerate(final_data) if not any(c in d for d in final_data[:i] for c in a)] 
second_final_data = reduce(lambda x, y:x+y, [[a[:-6]+b for b in c] for a, c in zip(sent_list, new_final_data)]) 

出力:

['I have a sentence with word_f', 'I have a sentence with word_h', 'I have a sentence with word_i', 'And another sentence with word_e', "Don't forget word_a", "Don't forget word_c"] 
+1

申し訳ありませんが、あなたのコードを読むのが難しいです。少し説明できますか?また、私は、複数のクラスに属する単語と、複数のキーワードを持つ文があることを認識しました。 – user7065687

+1

@ T.Starkもしあなたが快適になったら、そのコードを読むのにも問題があります。 :-P Ajax、おそらく、あなたはその "1つのライナー"を多くの行に壊すか、それを(同じ?) 'join'部分を' lambda'に抽出できますか? –

関連する問題