"BOKU"のITな日常

興味のむくまま気の向くままに調べたり・まとめたりしてます。

置換前と後の単語の組合せを辞書管理し、文章の複数単語を一括置換/pythonの.translate()とre.sub()

f:id:arakan_no_boku:20190223210405j:plain

今回は、文章中のある単語を別の単語に置き換える処理を、辞書を使って一括処理したい時の2つのやり方「str.translate()とre.sub()」を整理してみます。

目次

今回やりたいこと 

例えば、こんな文章があった時。

東京大学大阪大学では社会人向けの「AI講座」を開いている。
また経済産業省は「AIプログラム技術」や課題解決を企業や学生らが教え合う「AI学校」を9月にも立ち上げる。
実務経験を積んだ人材が互いに鍛え合い、専門人材を育成する。

「」に囲まれたAIがついた「AI講座」や「AI学校」・「AIプログラム技術」などを、「AI関連」みたいな一つの単語に置き換えるのなら、正規表現「re.sub()」で可能です。

intext = '''東京大学大阪大学では社会人向けの「AI講座」を開いている。
また経済産業省は「AIプログラム技術」や課題解決を企業や学生らが教え合う「AI学校」を9月にも立ち上げる。
実務経験を積んだ人材が互いに鍛え合い、専門人材を育成する。
'''
print(re.sub('「AI.+?」','「AI関連」',intext))

でも、「AI学校」は「AIスクール」、「AIプログラム技術」は「AIスキル」、「AI講座」は「AIクラス」など、それぞれ個別に変更したい場合は、どうしたらよいのか?が、今回のテーマです。 

str.translate()は使えるか? 

最初の選択肢は「str.translate()」です。

これは置換対象が1文字の場合だと、見事に機能します。

例えば、半角の「A」を全角の「A」、「i」を全角の「I」、漢字の「実」を「業」に置き換えるような用途なら、以下のようにすればできます。

def trans_single(inputtext):
    encode_dic = {'A':'A','i':'I','実':'業'}
    encode_table = str.maketrans(encode_dic)
    return inputtext.translate(encode_table)
    
intext = '''東京大学大阪大学では社会人向けの「AI講座」を開いている。
また経済産業省は「AIプログラム技術」や課題解決を企業や学生らが教え合う「AI学校」を9月にも立ち上げる。
実務経験を積んだ人材が互いに鍛え合い、専門人材を育成する。
'''
print(trans_single(intext))

この結果はこんな感じ。

東京大学大阪大学では社会人向けの「AI講座」を開いている。
また経済産業省は「AIプログラム技術」や課題解決を企業や学生らが教え合う「AI学校」を9月にも立ち上げる。
務経験を積んだ人材が互いに鍛え合い、専門人材を育成する。

 きれいに辞書通りに置き換わってます。

これで辞書を以下のように単語レベルに変更してみます。

replacements = {'AI講座':'AIクラス','AIプログラム技術':'AIスキル','AI学校':'AIスクール'}

すると、こんなエラーをはいて終了してしまいます。

string keys in translate table must be of length 1

 長さが1じゃなきゃダメ・・ということです。 

長さ1を超える単語の置き換えは「re.sub()」でないと無理

そこで「re.sub()」とlaambda式の組合せでやってみます。

import re

def trans_word2(inputtext):
    replacements = {'AI講座':'AIクラス','AIプログラム技術':'AIスキル','AI学校':'AIスクール'}
    print('({})'.format('|'.join(map(re.escape, replacements.keys()))))
    return re.sub('({})'.format('|'.join(map(re.escape, replacements.keys()))), lambda m: replacements[m.group()], inputtext)
   
intext = '''東京大学大阪大学では社会人向けの「AI講座」を開いている。
また経済産業省は「AIプログラム技術」や課題解決を企業や学生らが教え合う「AI学校」を9月にも立ち上げる。
実務経験を積んだ人材が互いに鍛え合い、専門人材を育成する。
'''
print(trans_word2(intext))

これを実行すると。 

東京大学大阪大学では社会人向けの「AIクラス」を開いている。
また経済産業省は「AIスキル」や課題解決を企業や学生らが教え合う「AIスクール」を9月にも立ち上げる。
実務経験を積んだ人材が互いに鍛え合い、専門人材を育成する。

うまく意図通りの置き換えができてます。 

re.sub()とlaambda式の組合せの補足説明 

上記はうまく行くのですが、以下のように複雑な式になるので、補足しときます。

 re.sub('({})'.format('|'.join(map(re.escape, replacements.keys()))), lambda m: replacements[m.group()], inputtext)

この式の中で、

'({})'.format('|'.join(map(re.escape, replacements.keys())))

 の部分からです。

ここで辞書(例だとreplacements)を使って、正規表現の条件式を生成しています。

map関数は、「map(関数, 配列)」なので、re.escape関数で辞書のキーを処理して、エスケープ処理をさせてるだけで、その結果をjoinで連結してます。

前段の例で実行すると。

(AI\講\座|AI\プ\ロ\グ\ラ\ム\技\術|AI\学\校)

 こんな感じの条件が生成されます。

条件が括弧()でくくられているので、結果はgroup()にはいっていくわけです。

それを。

lambda m: replacements[m.group()],

で一つずつ取り出して、その結果(置換前文字列がはいっている)をキーにして、 replacementsの値(置換後文字列)をとりだして、順番にre.sub()に置き換えさせている・・というわけです。 

 

 

ではでは。