周回遅れでIT業界デビューしたエンジニアのブログ

就職氷河期にモロにぶち当たり、人生で混迷を極めた末にIT業界に安寧を見出そうとしているアラフォーのお勉強日記です。

「言語処理100ノック2015」をPythonで練習(No.0~9)

f:id:sionff:20180119162019j:plain

www.cl.ecei.tohoku.ac.jp

自然言語処理の練習をあまりやってこなかったので、空いた時間を見つけてはちょこちょこコードを書いています。機械学習なりお手伝いAIなりを実装していくのに使いますしね!

今回は足慣らし。第1章: 準備運動(No.0~9)です。
以下を出来るだけ意識して書いてみました。

  • 内包表記を使う
  • 関数化する
  • 一般化する

00. 文字列の逆順

文字列"stressed"の文字を逆に(末尾から先頭に向かって)並べた文字列を得よ.

word = "stressed"
ans = word[::-1]
print(ans)

# desserts

01. 「パタトクカシーー」

「パタトクカシーー」という文字列の1,3,5,7文字目を取り出して連結した文字列を得よ.

word = "パタトクカシーー"
ans = word[0] + word[2] + word[4] + word[6]
print(ans)

# パトカー

02. 「パトカー」+「タクシー」=「パタトクカシーー」

「パトカー」+「タクシー」の文字を先頭から交互に連結して文字列「パタトクカシーー」を得よ.

pat = "パトカー"
tax = "タクシー"
lis = []
for i in range(len(pat)):
    lis.append(pat[i])
    lis.append(tax[i])
ans = "".join(lis)
print(ans)

# パタトクカシーー

03. 円周率

"Now I need a drink, alcoholic of course, after the heavy lectures involving quantum mechanics."という文を単語に分解し,各単語の(アルファベットの)文字数を先頭から出現順に並べたリストを作成せよ.

sentence = "Now I need a drink, alcoholic of course, after the heavy lectures involving quantum mechanics."
words = sentence.replace(',', '').replace('.', '').split(' ')

#words_num = []
#for word in words:
#    words_num.append(len(word))
#print(words_num)

words_num = [len(word) for word in words]
print(words_num)

# [3, 1, 4, 1, 5, 9, 2, 6, 5, 3, 5, 8, 9, 7, 9]

04. 元素記号

"Hi He Lied Because Boron Could Not Oxidize Fluorine. New Nations Might Also Sign Peace Security Clause. Arthur King Can."という文を単語に分解し,1, 5, 6, 7, 8, 9, 15, 16, 19番目の単語は先頭の1文字,それ以外の単語は先頭に2文字を取り出し,取り出した文字列から単語の位置(先頭から何番目の単語か)への連想配列(辞書型もしくはマップ型)を作成せよ.

sentence = "Hi He Lied Because Boron Could Not Oxidize Fluorine. New Nations Might Also Sign Peace Security Clause. Arthur King Can."
words = sentence.replace('.', '').split(' ')
place = [1,0,0,0,5,6,7,8,9,0,0,0,0,0,15,16,0,0,19,0]

#element = []
#for i in range(len(words)):
#    if place[i] is i+1:
#        element.append(words[i][:1])
#    else:
#        element.append(words[i][:2])
        
element = [ words[i][:1] if place[i] is i+1 else words[i][:2] for i in range(len(words)) ]

element_dict = {}
for i in range(len(element)):
    elem = element[i]
    element_dict[elem] = i + 1

print(element_dict)

# {'H': 1, 'He': 2, 'Li': 3, 'Be': 4, 'B': 5, 'C': 6, 'N': 7, 'O': 8, 'F': 9, 'Ne': 10, 'Na': 11, 'Mi': 12, 'Al': 13, 'Si': 14, 'P': 15, 'S': 16, 'Cl': 17, 'Ar': 18, 'K': 19, 'Ca': 20}

05. n-gram

与えられたシーケンス(文字列やリストなど)からn-gramを作る関数を作成せよ.この関数を用い,"I am an NLPer"という文から単語bi-gram,文字bi-gramを得よ.

def NGram(seq, n=2):
    word_result = {}
    for i in range(len(seq)-n+1):
        key = str(seq[i:i+n])
        if key in word_result:
            word_result[key] += 1
        else:
            word_result[key] = 1
    return word_result

sentence = "I am an NLPer"
ans1 = NGram(sentence.split(' '))
ans2 = NGram(sentence)

print("単語bi-gram", ans1)
print("文字bi-gram", ans2)

# 単語bi-gram {"['I', 'am']": 1, "['am', 'an']": 1, "['an', 'NLPer']": 1}
# 文字bi-gram {'I ': 1, ' a': 2, 'am': 1, 'm ': 1, 'an': 1, 'n ': 1, ' N': 1, 'NL': 1, 'LP': 1, 'Pe': 1, 'er': 1}

06. 集合

"paraparaparadise"と"paragraph"に含まれる文字bi-gramの集合を,それぞれ, XとYとして求め,XとYの和集合,積集合,差集合を求めよ.さらに,'se'というbi-gramがXおよびYに含まれるかどうかを調べよ.

words = ["paraparaparadise", "paragraph"]

X = set(NGram(words[0]))
Y = set(NGram(words[1]))

print("和集合", X|Y)
print("積集合", X&Y)
print("差集合", X-Y)

print("seがXかYに含まれる?", str('se' in X|Y))

# 和集合 {'ar', 'ad', 'gr', 'di', 'ph', 'ap', 'ag', 'se', 'pa', 'ra', 'is'}
# 積集合 {'ra', 'ar', 'ap', 'pa'}
# 差集合 {'di', 'se', 'ad', 'is'}
# seがXかYに含まれる? True

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

引数x, y, zを受け取り「x時のyはz」という文字列を返す関数を実装せよ.さらに,x=12, y="気温", z=22.4として,実行結果を確認せよ.

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

print(create_sentence(12, "気温", 22.4))

# 12時の気温は22.4

08. 暗号文

与えられた文字列の各文字を,以下の仕様で変換する関数cipherを実装せよ.

  • 英小文字ならば(219 - 文字コード)の文字に置換
  • その他の文字はそのまま出力

この関数を用い,英語のメッセージを暗号化・復号化せよ.

def cipher(sentence):
    return "".join([ chr(219 - ord(b)) if b.islower() else b for b in sentence ])

# ord():アスキーコードを取得する
# chr():アスキーコードから文字に変換する
# islower():小文字のみ、または大文字小文字の区別のない文字列ならTrue

sentence = "I love ○○さん very much."
print("元の文字列   :", sentence)
encryption = cipher(sentence)
print("暗号化した文字列:", encryption)
decryption = cipher(encryption)
print("復号化した文字列:", decryption)

# 元の文字列   : I love ○○さん very much.
# 暗号化した文字列: I olev ○○さん evib nfxs.
# 復号化した文字列: I love ○○さん very much.

09. Typoglycemia

スペースで区切られた単語列に対して,各単語の先頭と末尾の文字は残し,それ以外の文字の順序をランダムに並び替えるプログラムを作成せよ.ただし,長さが4以下の単語は並び替えないこととする.適当な英語の文(例えば"I couldn't believe that I could actually understand what I was reading : the phenomenal power of the human mind .")を与え,その実行結果を確認せよ.

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

def Typoglycemia(sentence):
    import random
    words = sentence.split(' ')
    output = []
    for word in words:
        if len(word) <= 4:
            pass
        else:
            move = list(word[1:len(word)-1])
            random.shuffle(move)
            word = word[0] + "".join(move) + word[len(word)-1]
        output.append(word)
    
    return " ".join(output)
    
# random.shuffle():リストの参照先を受け取ってその参照先をシャッフルする。
# 破壊的操作なので、リストそのものをシャッフルする。関数とは挙動が違うのに注意。
# 参照:http://hayataka2049.hatenablog.jp/entry/2016/12/17/230638

Typoglycemia(sentence)

# "I cundo'lt belviee that I cuold alalucty unsrnatded what I was rdneiag : the pnaheenmol peowr of the hmuan mind ."