これの続きでーす。
Python未経験者が言語処理100本ノックをやってみる00~04
https://qiita.com/earlgrey914/items/fe1d326880af83d37b22
続きはこちら
Python未経験者が言語処理100本ノックをやってみる07~09
https://qiita.com/earlgrey914/items/a7b6781037bc0844744b
#05. n-gram
###与えられたシーケンス(文字列やリストなど)からn-gramを作る関数を作成せよ.この関数を用い,"I am an NLPer"という文から単語bi-gram,文字bi-gramを得よ.
n-gramって何だっけかなぁ・・・なんか聞いたことあるけど。
ってこの問題n-gramがなにか理解できてないと解けないじゃん!まずそこからかよ!!
~2分ググった~
N-gramとは自然言語(テキスト)を連続するN個の文字、もしくはN個の単語単位で単語を切り出す手法のことです。
<参考>
https://www.pytry3g.com/entry/N-gram
なるほど。
じゃあ1が渡されたら1文字ずつ区切って、2が渡されたら2文字ずつ区切るような関数を作ればいのね。
単語bi-gramってのは単語単位で2文字区切り
文字bi-gramってのは文字単位で2文字区切りすばいいってことかしらね。
だから解答は
■単語bi-gram
["I", "am", "an", "NL", "Pe", "r"]
■文字bi-gram
["I ","ma","an","NL","Pe","r"]
って出力すればいいのかな?間違ってたらゴメンネ。この前提で解くよ。
ふつーに間違っていたので解答の出力結果のみチラ見した。
[['I', 'am'], ['am', 'an'], ['an', 'NLPer']]
['I ', ' a', 'am', 'm ', ' a', 'an', 'n ', ' N', 'NL', 'LP', 'Pe', 'er']
このように出力されればOKらしい。
なるほど。
ウィ。とりあえず単語bigramができた。
s = "I am an NLPer"
tango_bigram= []
def bigram(s):
counter = 0
list = s.split()
for i in list:
if counter < len(list)-1:
tango_bigram.extend([[list[counter],list[counter+1]]])
counter += 1
return tango_bigram
print(bigram(s))
[['I', 'am'], ['am', 'an'], ['an', 'NLPer']]
ここらでそろそろお気づきだと思うが、コードを書いていく上で色々とよろしくない部分が見え始めた。
- 変数の命名が適当すぎる。英語と日本語が混ざってるし、
s
やi
といった1文字変数を使うところもあれば、counter
などの変数名を使っているところもある - ここではスネークケースである
tango_bigram
という書き方をしているが、前(演習4)はキャメルケースでichimoziList
と書いており、バラバラ。 - 改行ルールが謎。半角スペースを入れるルールが謎。
今後直していきたいが、今はまだ目をつむっていく。一人で書いているだけなので。
まぁそのうち自分で書いたコードが自分で「見づれえええええ」ってなって直さざるをえなくなるんだけどね。
前の演習ではリストへの追加はappend()
を使ったが、ここではextend()
を使った。
複数要素をいっきにリストに入れたい場合はextend()
を使えばいいらしい。
l += [1, 2, 3]
といった+=
を使う記法もあるらしいが、extend()
のほうがわかりやすいなぁという印象。
<参考URL>
https://qiita.com/tag1216/items/416314cc75a099ad6149
で、
似たような感じで文字bigramも書いた。
s = "I am an NLPer"
tango_bigram= []
moji_bigram = []
def bigram(s):
tango_counter = 0
moji_counter = 0
#単語gram処理
list = s.split()
for i in list:
if tango_counter < len(list)-1:
tango_bigram.extend([[list[tango_counter],list[tango_counter+1]]])
tango_counter += 1
#文字gram処理
for i in s:
if moji_counter < len(s)-1:
moji_bigram.append(s[moji_counter] + s[moji_counter+1])
moji_counter += 1
return tango_bigram,moji_bigram
print(bigram(s))
([['I', 'am'], ['am', 'an'], ['an', 'NLPer']], ['I ', ' a', 'am', 'm ', ' a', 'an', 'n ', ' N', 'NL', 'LP', 'Pe', 'er'])
可読性がゴミだな!!!
まぁいいや。
Pythonの「関数は関数呼び出し処理より上に書かないといけない」っていう規約?慣れないなぁ・・・
個人的な印象だが、Pythonは動的型付けである点と、インデントによるブロック区切りのおかけで、
「書くのは楽だが読むのは大変」という印象がある。
Javaみたいな静的型付け言語で{}
を使ったブロック区切りに慣れているからかしら・・・
Javaもインデントが適当だと著しく可読性が落ちるけどね。
#ちょっと休憩
なんかいい変数名の付け方ないの?と思ってググったらこういう記事もあった。
<参考URL>
https://qiita.com/Ted-HM/items/7dde25dcffae4cdc7923
#06. 集合
###"paraparaparadise"と"paragraph"に含まれる文字bi-gramの集合を,それぞれ, XとYとして求め,XとYの和集合,積集合,差集合を求めよ.さらに,'se'というbi-gramがXおよびYに含まれるかどうかを調べよ.
なんかもうまず日本語が難しいんだが。
いや一応わかるけどさ。プログラム以前に日本語の解読が大変。
この問題見た瞬間に「え?集合?もうそういうの計算できるライブラリをインポートして使っていい?」と思った。さっきのN-gramもライブラリであるでしょ絶対・・・
筆者は世の中において**「自分で作らなければいけない」ものは「自分だけが思いついたもの」**と思っているので、誰かが作ってくれたものがあるならそれを活用したほうがいいと思っている。
が、今回は目的が学習であるため、ちゃんと自分で作ります。
演習05のbigram関数をちょっといじれば2つの文字列bigramを取得するのは容易。
(bigram
関数とmoji_bigram
のスコープが無茶苦茶だったので修正してます)
str_paradise = "paraparaparadise"
str_paragraph = "paragraph"
def bigram(s):
moji_bigram = []
moji_counter = 0
#文字gram処理
for i in s:
if moji_counter < len(s)-1:
moji_bigram.append(s[moji_counter]+s[moji_counter+1])
moji_counter += 1
return moji_bigram
print(bigram(str_paradise))
print(bigram(str_paragraph))
['pa', 'ar', 'ra', 'ap', 'pa', 'ar', 'ra', 'ap', 'pa', 'ar', 'ra', 'ad', 'di', 'is', 'se']
['pa', 'ar', 'ra', 'ag', 'gr', 'ra', 'ap', 'ph']
で、集合を求めるにはどうすんのかなーと「Python 集合 計算」みたいな感じで適当にググったら
list型ではなくset型というものを使えばいいらしい。
<参考URL>
https://note.nkmk.me/python-set/
set型ってどんなもの?と思いググッたら、
・重複した要素がない
・要素に順番がない
とのこと。パーフェクトじゃん。
<参考URL>
https://note.nkmk.me/python-set/
てことでサクサクっと完成。
str_paradise = "paraparaparadise"
str_paragraph = "paragraph"
#文字bigramのリストを返す関数
def bigram(s):
moji_bigram = []
moji_counter = 0
for i in s:
if moji_counter < len(s)-1:
moji_bigram.append(s[moji_counter]+s[moji_counter+1])
moji_counter += 1
return moji_bigram
#リストをsetに変換する関数
def listToSet(list):
moji_bigram_set = {}
moji_bigram_set = set(list)
return moji_bigram_set
#bigramのリストを作成
str_paradise_list = bigram(str_paradise)
str_paragraph_list = bigram(str_paragraph)
#bigramのリストをsetに変換し重複を削除
paradise_set_X = listToSet(str_paradise_list)
paragraph_set_Y = listToSet(str_paragraph_list)
print("paradise_set_X")
print(paradise_set_X)
print("paragraph_set_Y")
print(paragraph_set_Y)
print("和集合")
print(paradise_set_X | paragraph_set_Y)
print("積集合")
print(paradise_set_X & paragraph_set_Y)
print("差集合")
print(paradise_set_X - paragraph_set_Y)
paradise_set_X
{'ap', 'ar', 'pa', 'di', 'is', 'ra', 'se', 'ad'}
paragraph_set_Y
{'ap', 'ar', 'pa', 'ph', 'ag', 'ra', 'gr'}
和集合
{'ap', 'ar', 'gr', 'pa', 'di', 'ph', 'is', 'ag', 'ra', 'se', 'ad'}
積集合
{'ra', 'pa', 'ap', 'ar'}
差集合
{'is', 'di', 'se', 'ad'}
うん、簡単ですね。
答えがあってるのかチェックが大変だぜ・・・
続きは明日!!!!
05~06でここまで2時間かかりました!!!!!!!!!!!!!!!(重要)