自然言語処理100本ノック
準備運動の続きです。

解説動画はこちら


自然言語処理100本ノックはこちら
自然言語処理100本ノック


前回はこちら
準備運動1

さて
続きですが

06. 集合

"paraparaparadise"と"paragraph"に含まれる
文字bi-gramの集合を, それぞれ, XとYとして求め,
XとYの和集合,積集合,差集合を求めよ.

さらに,'se'というbi-gramが
XおよびYに含まれるかどうかを調べよ.

文字列の集合を考える問題です。

bi-gramというのはふた文字の組み合わせです。

前回作成したn-gramを作る関数を
用いてbi-gramを作っていきます。
def n_gram(st, n):
    return [st[i:i+n] for i in range(len(st)-n+1)]
pythonでは集合を表現するデータ型があります。
set型は重複を排除したデータ構造です。

リスト型からset関数で作成できるので
bi-gramの結果をそのままsetにします。

X = set(n_gram("paraparaparadise",2))
Y = set(n_gram("paragraph",2))
print(X)
print(Y)
{'ap', 'ad', 'di', 'ra', 'is', 'ar', 'pa', 'se'}
{'ap', 'ph', 'ra', 'ar', 'pa', 'ag', 'gr'}

先頭から二文字ずつ切り取った文字を
集合にしているので重複した部分は
取り除かれます。

Pythonの集合の型同士で
そのまま計算ができます。

和集合、積集合、差集合
文字を含むかどうかの判定は
以下のようなコードになります。

# 和集合 | union
print(X|Y)

# 積集合 & intersection
print(X&Y)

# 差集合 - difference
print(X-Y)

# 含まれるかどうか in
print('se' in X)
print('se' in Y)
{'ap', 'ad', 'di', 'ra', 'ph', 'is', 'ar', 'pa', 'ag', 'gr', 'se'}
{'pa', 'ap', 'ar', 'ra'}
{'is', 'ad', 'se', 'di'}
True
False


07. テンプレートによる文生成

引数x, y, zを受け取り「x時のyはz」という
文字列を返す関数を実装せよ.

さらに,x=12, y="気温", z=22.4として,
実行結果を確認せよ.

恐らくですが
気温を英語に直すと
temperature

なので
template
とかけているんじゃ無いかと!!!!

素敵すぎる問題です。
嫌いじゃ無い、こういうの

ということで
Pythonでは文字列のformatで
文字の差込ができます。

def temperature(x,y,z):
    return '{0}時の{1}は{2}'.format(x,y,z)

temperature(x=12, y="気温", z=22.4)
'12時の気温は22.4'

メールの定型文など
文字の差し込みをする際に非常に便利で
業務でも多用します。

formatは覚えておくと
非常に重宝しますね。

08. 暗号文

与えられた文字列の各文字を,
以下の仕様で変換する関数cipherを実装せよ.
・英小文字ならば(219 - 文字コード)の文字に置換
・その他の文字はそのまま出力
この関数を用い,英語のメッセージを暗号化・復号化せよ.

まずは英小文字かどうかを
判定するという部分です。

これは非常に厄介で
Pythonでは文字列型にそれっぽい関数があるのですが
うまく判定されないという致命的な欠陥があります。

なので判定関数を先に作ります。

reライブラリを用いて
正規表現で英小文字を判定します。

import re

def isalpha(st):
    return re.compile(r'^[a-z]+$').match(st) is not None

この関数を用いると英小文字以外はFalseになります。

この判定結果を用いて
暗号文を作成する関数を作ります。

英小文字の際は
条件として
(219 - 文字コード)の文字に置換 
というのがあります。

文字というのは
全て文字番号が存在し
一文字ずつ番号が降られています。

Pythonでは
ord(文字列)
で文字番号を取得できます。

chr(番号)
で文字を取得できます。

組み合わせると文字を
変換できるというわけです。

最終的にできた関数はこちら。

def cipher(st):
    return ''.join([chr(219-ord(s)) if isalpha(s) else s for s in st])

message = '私の名前は乙pyです'

#暗号化
cipher_text = cipher(message)
print(cipher_text)

#復号化
print(cipher(cipher_text))
私の名前は乙kbです
私の名前は乙pyです

関数の結果は文字が暗号化され
その結果をもう一度関数にかけると
元の文字に復号されます。

09. Typoglycemia

スペースで区切られた単語列に対して,
各単語の先頭と末尾の文字は残し,
それ以外の文字の順序を
ランダムに並び替えるプログラムを作成せよ.
ただし,長さが4以下の単語は並び替えないこととする.

適当な英語の文(例えば
"I couldn't believe that I could actually understand
what I was reading :
the phenomenal power of the human mind ." )
を与え,その実行結果を確認せよ.


これは非常に厄介な問題ですね

考え方はいくつもあり
答えは一つにならないと思いますが
自分のコードはこうなりました。

import random

w = "I couldn't believe that I could actually understand what I was reading : the phenomenal power of the human mind ."

def typoglycemia(w):
    ws = w.split(' ')
    start,end=ws[0],ws[-1]
    base = ['' if len(a)>4 else a for a in ws[1:-1]]
    tmp = [a  for a in ws[1:-1] if len(a)>4]
    random.shuffle(tmp)
    result,c = [start],0
    for b in base:
        if b=='':
            result.append(tmp[c])
            c+=1
        else:
            result.append(b)
    return result + [end]

print(typoglycemia(w))
['I', 'power', 'actually', 'that', 'I', "couldn't", 'reading', 'believe', 'what', 'I', 'was', 'human', ':', 'the', 'understand', 'phenomenal', 'of', 'the', 'could', 'mind', '.']

実行毎に文字の順番は入れ替わります。

この場合の考え方としては
元の文章の四文字以下はそのままに
五文字以上は空白にしたデータを用意します。

別途五文字以上のものをリストに確保します。

元の文章の空白部分に
ランダムに並び替えしたリストを
順番に差し込んでいます。

もう少しスマートに書けるかなと思います
リファクタリングは大事ですね。

さて
これで準備運動は終わりです。

これで
準備運動ですからねー

なかなかパズルのようで
面白いですよね。

続きはまた
それでは。