はじめに
自然言語処理を基礎からやり直そうと思い、言語処理100本ノック(http://www.cl.ecei.tohoku.ac.jp/nlp100/ )に取り組み始めました。言語処理100本ノックは、東北大学の乾・岡崎研究室(http://www.cl.ecei.tohoku.ac.jp/ )の岡崎先生が提供されている自然言語処理の練習問題集です。
動作環境
Windows 10
Python 3.6.0
問題 00. 文字列の逆順
スライスで、3つめの数字として、間隔を指定できます。a[::2]のようにすれば「1つ飛ばす」という意味です。
負の値を入れると逆順になります。
文字列"stressed"の文字を逆に(末尾から先頭に向かって)並べた文字列を得よ.
input_ = 'stressed'
ret = input_[::-1]
print(ret)
実行結果
desserts
問題 01. 「パタトクカシーー」
「パタトクカシーー」という文字列の1,3,5,7文字目を取り出して連結した文字列を得よ.
input_ = 'パタトクカシーー'
ret = input_[::2]
print(ret)
実行結果
パトカー
問題 02. 「パトカー」+「タクシー」=「パタトクカシーー」
zipは知っていましたが、長い方に合わせてくれるitertools.zip_longestなるものが存在するのは今回初めて知りました。
「パトカー」+「タクシー」の文字を先頭から交互に連結して文字列「パタトクカシーー」を得よ.
import itertools
input1 = 'パトカー'
input2 = 'タクシー'
ret = ''
for (c1, c2) in itertools.zip_longest(input1, input2, fillvalue=''):
ret += c1 + c2
print(ret)
実行結果
パタトクカシーー
問題 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.'
punctuation_marks = ',.'
counts = [len(word) for word in sentence.translate(str.maketrans('', '', punctuation_marks)).split()]
print(counts)
実行結果
[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.'
punctuation_marks = ',.'
idx_list=[1, 5, 6, 7, 8, 9, 15, 16, 19]
word_pos_dict = dict()
words = sentence.translate(str.maketrans('', '', punctuation_marks)).split()
for i, word in enumerate(words, 1) :
if i in idx_list:
word_pos_dict[word[0]] = i
else:
word_pos_dict[word[0 : 2]] = i
print(word_pos_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
次の問題への繋がりを考えると、文字bigramは、2文字ずつの文字列のリストにするべきなのでしょうが、リストのリストにしたのは個人的な好みです。
与えられたシーケンス(文字列やリストなど)からn-gramを作る関数を作成せよ.この関数を用い,"I am an NLPer"という文から単語bi-gram,文字bi-gramを得よ.
def n_gram(sentence, n, type='word'):
if type == 'word':
words = sentence.split()
elif type == 'character':
words = list(sentence)
else:
raise Exeption
rets = []
for i in range(len(words) - n + 1):
tmp = []
for j in range(i, i + n):
tmp.append(words[j])
rets.append(tmp)
return rets
sentence = 'I am an NLPer'
print('単語bi-gram')
print(n_gram(sentence, 2), end='\n\n')
print('文字bi-gram')
print(n_gram(sentence, 2, 'character'))
実行結果
単語bi-gram
[['I', 'am'], ['am', 'an'], ['an', 'NLPer']]
文字bi-gram
[['I', ' '], [' ', 'a'], ['a', 'm'], ['m', ' '], [' ', 'a'], ['a', 'n'], ['n', ' '], [' ', 'N'], ['N', 'L'], ['L', 'P'], ['P', 'e'], ['e', 'r']]
問題 06. 集合
"paraparaparadise"と"paragraph"に含まれる文字bi-gramの集合を,それぞれ, XとYとして求め,XとYの和集合,積集合,差集合を求めよ.さらに,'se'というbi-gramがXおよびYに含まれるかどうかを調べよ.
str1 = 'paraparaparadise'
str2 = 'paragraph'
X = set([c1 + c2 for c1, c2 in n_gram(str1, 2, 'character')])
Y = set([c1 + c2 for c1, c2 in n_gram(str2, 2, 'character')])
print('X:',X)
print('Y:',Y)
print('和集合:', X.union(Y))
print('積集合:', X.intersection(Y))
print('差集合:', X.difference(Y))
print('X内に\'se\'は存在 : ', 'se' in X)
print('Y内に\'se\'は存在 : ', 'se' in Y)
実行結果
X: {'ap', 'ad', 'ra', 'se', 'is', 'di', 'ar', 'pa'}
Y: {'ap', 'gr', 'ag', 'ra', 'ph', 'ar', 'pa'}
和集合: {'ap', 'ad', 'gr', 'ag', 'ra', 'se', 'ph', 'ar', 'pa', 'is', 'di'}
積集合: {'ap', 'ra', 'ar', 'pa'}
差集合: {'di', 'ad', 'se', 'is'}
X内に'se'は存在 : True
Y内に'se'は存在 : False
問題 07. テンプレートによる文生成
引数x, y, zを受け取り「x時のyはz」という文字列を返す関数を実装せよ.さらに,x=12, y="気温", z=22.4として,実行結果を確認せよ.
def to_sentence(X, Y, Z):
return '{0}時の{1}は{2}'.format(X, Y, Z)
to_sentence(12, '気温', 22.4)
実行結果
'12時の気温は22.4'
問題 08. 暗号文
与えられた文字列の各文字を,以下の仕様で変換する関数cipherを実装せよ.
英小文字ならば(219 - 文字コード)の文字に置換
その他の文字はそのまま出力
この関数を用い,英語のメッセージを暗号化・復号化せよ.
def cipher(sentence):
rets = ''
for c in sentence:
if c.islower():
rets += chr(219 - ord(c))
else :
rets += c
return rets
row_data = 'We are going to use A strategy tomorrow.'
print('row data : ', row_data)
encrypted_data = cipher(row_data)
print('encrypted data : ', encrypted_data)
decrypted_data = cipher(encrypted_data)
print('decrypted data : ', decrypted_data)
実行結果
row data : We are going to use A strategy tomorrow.
encrypted data : Wv ziv tlrmt gl fhv A hgizgvtb glnliild.
decrypted data : We are going to use A strategy tomorrow.
問題 09. Typoglycemia
スペースで区切られた単語列に対して,各単語の先頭と末尾の文字は残し,それ以外の文字の順序をランダムに並び替えるプログラムを作成せよ.ただし,長さが4以下の単語は並び替えないこととする.適当な英語の文(例えば"I couldn't believe that I could actually understand what I was reading : the phenomenal power of the human mind .")を与え,その実行結果を確認せよ.
import numpy as np
def random_swap(sentence):
words = sentence.split()
rets = ''
for word in words:
if len(word) > 4:
rets += word[0]
for i in np.random.permutation(range(1,len(word) - 1)) :
rets += word[i]
rets += word[-1] + ' '
else :
rets += word + ' '
return rets.rstrip(' ')
sentence = 'I couldn\'t believe that I could actually understand what I was reading : the phenomenal power of the human mind .'
print('original : ', sentence)
print('swapped : ', random_swap(sentence))
実行結果
original : I couldn't believe that I could actually understand what I was reading : the phenomenal power of the human mind .
swapped : I cdnulo't bliveee that I cluod aacltuly urstnaednd what I was rnaedig : the panonemhel pwoer of the haumn mind .
おわりに
今回は準備運動ということで、これから難しくなりそうなので、気を引き締めて頑張ろうと思います。
何か間違いを発見された方は、ご指摘頂けると助かります。
#参考資料
素人の言語処理100本ノック:まとめ
【python】100本ノックにチャンレンジ!(これまでの投稿一覧)
Pythonでリストをflattenする方法まとめ