2017-11-06 10 views
1

文字ベースのn-gramをマッチしてマークしようとしています。文字列Python:リストからマッチした文字列を置換してください

txt = "how does this work" 

リスト<>

ngrams = ["ow ", "his", "s w"] 

とマークからnグラムと一致する - しかし、何の前の開いた引用符がない場合に限ります。私はこの文字列を求めている出力はh<ow >does t<his w>orkです(2つの-nd部分に二重一致がありますが、期待される引用符はわずか1組以内です)。

これは、しかし、全く希望の出力を生成しないために私が試したforループザ・:

switch = False 

for i in txt: 
    if i in "".join(ngrams) and switch == False: 
     txt = txt.replace(i, "<" + i) 
     switch = True 
    if i not in "".join(ngrams) and switch == True: 
     txt = txt.replace(i, ">" + i) 
     switch = False 

print(txt) 

任意の助けいただければ幸いです。

+0

代わりに何が生成されますか? –

+0

'ngrams = ['his'、 's wo'、 'wor']'があればどうなるでしょうか。 ' kさんはどうですか? ' –

+0

複数の一致を処理するにはどうすればよいですか?例えば。 'ngrams = ['ab']'、 'txt = 'abominable abs''です。あなたは ' ominable abs'、' ominable s'または 'abominable s'を期待していますか? –

答えて

2

をこのソリューションは、我々は簡単に試合を重ねる処理できるようindicesセットに各コピーのインデックスを保存し、txt文字列内のnグラムのすべてのコピーを見つけるためにstr.findメソッドを使用しています。

次に、txtのcharによる文字をresultリストにコピーし、必要な場合は角かっこを挿入します。この方法は、.replaceコールが複数の文字列全体を再構築する必要があるため、複数の.replaceコールを使用して山括弧を挿入するよりも効率的です。

私のコードがngramの複数のコピーを処理することを示すためにデータを少し拡張しました。

txt = "how does this work now chisolm" 
ngrams = ["ow ", "his", "s w"] 
print(txt) 
print(ngrams) 

# Search for all copies of each ngram in txt 
# saving the indices where the ngrams occur 
indices = set() 
for s in ngrams: 
    slen = len(s) 
    lo = 0 
    while True: 
     i = txt.find(s, lo) 
     if i == -1: 
      break 
     lo = i + slen 
     print(s, i) 
     indices.update(range(i, lo-1)) 

print(indices) 

# Copy the txt to result, inserting angle brackets 
# to show matches 
switch = True 
result = [] 
for i, u in enumerate(txt): 
    if switch: 
     if i in indices: 
      result.append('<') 
      switch = False 
     result.append(u) 
    else: 
     result.append(u) 
     if i not in indices: 
      result.append('>') 
      switch = True 

print(''.join(result)) 

出力

how does this work now chisolm 
['ow ', 'his', 's w'] 
ow 1 
ow 20 
his 10 
his 24 
s w 12 
{1, 2, 10, 11, 12, 13, 20, 21, 24, 25} 
h<ow >does t<his w>ork n<ow >c<his>olm 

あなたは隣接するグループをマージしたい場合は、我々は簡単にstr.replaceメソッドを使用していることを行うことができます。しかし、正しく動作させるためには、元のデータを前処理し、すべての空白を単一のスペースに変換する必要があります。これを行う簡単な方法は、データを分割して再結合することです。

txt = "how does this\nwork now chisolm hisow" 
ngrams = ["ow", "his", "work"] 

#Convert all whitespace to single spaces 
txt = ' '.join(txt.split()) 

print(txt) 
print(ngrams) 

# Search for all copies of each ngram in txt 
# saving the indices where the ngrams occur 
indices = set() 
for s in ngrams: 
    slen = len(s) 
    lo = 0 
    while True: 
     i = txt.find(s, lo) 
     if i == -1: 
      break 
     lo = i + slen 
     print(s, i) 
     indices.update(range(i, lo-1)) 

print(indices) 

# Copy the txt to result, inserting angle brackets 
# to show matches 
switch = True 
result = [] 
for i, u in enumerate(txt): 
    if switch: 
     if i in indices: 
      result.append('<') 
      switch = False 
     result.append(u) 
    else: 
     result.append(u) 
     if i not in indices: 
      result.append('>') 
      switch = True 

# Convert the list to a single string 
output = ''.join(result) 

# Merge adjacent groups 
output = output.replace('> <', ' ').replace('><', '') 
print(output) 

出力

how does this work now chisolm hisow 
['ow', 'his', 'work'] 
ow 1 
ow 20 
ow 34 
his 10 
his 24 
his 31 
work 14 
{32, 1, 34, 10, 11, 14, 15, 16, 20, 24, 25, 31} 
h<ow> does t<his work> n<ow> c<his>olm <hisow> 
+0

ありがとうございます。 'ngrams'にスペースがない場合、例えば、' ngrams = ["ow"、 "his"、 "work"] 'のように、' h を生成すると、現在t n c olm'となります。 'h は、 n c olm'のように、スペース(または改行)がそれらを区切っている場合、スペースを無視して囲まれた単語を組み合わせるように修正できますか? –

+1

@Россарх確かに、私の更新された答えをご覧ください。 –

+0

優秀なこの後処理アプローチは素晴らしい作品です。ありがとうございました! –

2

これは動作するはずです:

txt = "how does this work" 
ngrams = ["ow ", "his", "s w"] 

# first find where letters match ngrams 
L = len(txt) 
match = [False]*L 
for ng in ngrams: 
    l = len(ng) 
    for i in range(L-l): 
     if txt[i:i+l] == ng: 
      for j in range(l): 
       match[i+j] = True 

# then sandwich matches with quotes 
out = [] 
switch = False 
for i in range(L): 
    if not switch and match[i]: 
     out.append('<') 
     switch = True 
    if switch and not match[i]: 
     out.append('>') 
     switch = False 
    out.append(txt[i]) 
print "".join(out) 
+1

出力は 'H OES tの rk'で、OPは' H んがトン ork' –

+0

@SandeepLade OKそれ – Julien

+1

@Julein直って欲しい:今すぐその罰金を。私の投票を変更しました –

1

はここでループのための唯一の方法であります。私はそれを計り、それはこの質問に対する他の答えと同じくらい速いです。私はそれを書いたからかもしれないが、もう少し明確だと思う。

nグラムの最初の文字のインデックスを繰り返しています。一致する場合は、if-else句の束を使用して、<または>を追加するかどうかを確認します。私は元のtxtから文字列outputの最後に追加するので、実際には文字列の真中に挿入していません。

txt = "how does this work" 
ngrams = set(["ow ", "his", "s w"]) 
n = 3 
prev = -n 
output = '' 
shift = 0 
open = False 
for i in xrange(len(txt) - n + 1): 
    ngram = txt[i:i + n] 
    if ngram in ngrams: 
     if i - prev > n: 
      if open: 
       output += txt[prev:prev + n] + '>' + txt[prev + n:i] + '<' 
      elif not open: 
       if prev > 0: 
        output += txt[prev + n:i] + '<' 
       else: 
        output += txt[:i] + '<' 
       open = True 
     else: 
      output += txt[prev:i] 
     prev = i 
if open: 
    output += txt[prev:prev + n] + '>' + txt[prev + n:] 
print output 
関連する問題