東北大学知能情報科学講座の自然言語処理学分野の乾・鈴木研究室の新人教育に使われている言語処理100本ノックを解いてみます。現東京工業大学の岡崎教授が作った自然言語の問題集で、2023年2月現在、一般に公開されており、解くだけで自然言語処理の基本がわかってしまうというとても優れものです。いろいろ調べながらやってみましたが、なかなか難易度が高くて骨が折れます。こういうのを研究室で受け継いでいくのってうらやましいです。4章以降はさらに難易度があがるみたいですが、そちらは追々やります。
下の回答は模範解答でもなんでもなく、単なる私の学習記録用に残したものなので、正解かどうかは保証しません。
第1章: 準備運動
プログラミングの基礎的な問題ですが、自然言語処理の前処理で使いそうな小技満載です。
00. 文字列の逆順
s = "stressed"
s[::-1]
'desserts'
01. 「パタトクカシーー」
s="パタトクカシーー"
l = [1,3,5,7]
"".join([s[num-1] for num in l])
'パトカー'
02. 「パトカー」+「タクシー」=「パタトクカシーー」
s1,s2 = "パトカー","タクシー"
"".join([s1[i]+s2[i] for i in range(len(s1))])
'パタトクカシーー'
03. 円周率
s = "Now I need a drink, alcoholic of course, after the heavy lectures involving quantum mechanics."
words_list = s.replace(",","").replace(".","").split()
[len(word) for word in word_list]
[3, 1, 4, 1, 5, 9, 2, 6, 5, 3, 5, 8, 9, 7, 9]
04. 元素記号
s = "Hi He Lied Because Boron Could Not Oxidize Fluorine. New Nations Might Also Sign Peace Security Clause. Arthur King Can."
words_list = s.replace(".","").split()
words_dict = {}
singles = [1,5,6,7,8,9,15,16,19]
for num in range(len(words_list)):
if num+1 in singles:
words_dict[num+1] = words_list[num][0]
else:
words_dict[num+1] = words_list[num][0:2]
words_dict
{1: 'H', 2: 'He', 3: 'Li', 4: 'Be', 5: 'B', 6: 'C', 7: 'N', 8: 'O', 9: 'F', 10: 'Ne', 11: 'Na', 12: 'Mi', 13: 'Al', 14: 'Si', 15: 'P', 16: 'S', 17: 'Cl', 18: 'Ar', 19: 'K', 20: 'Ca'}
05. n-gram
def words_bi_gram(s):
words_list = s.split()
result = []
for i in range(len(words_list)-1):
result.append(" ".join([words_list[i],words_list[i+1]]))
return result
def char_bi_gram(s):
words = s.replace(" ","")
result = []
for i in range(len(words)-1):
result.append(words[i:i+2])
return result
sequence = "I am an NLPer"
print("words: ", words_bi_gram(sequence))
print("chars: ", char_bi_gram(sequence))
words: ['I am', 'am an', 'an NLPer'] char: ['Ia', 'am', 'ma', 'an', 'nN', 'NL', 'LP', 'Pe', 'er']
06. 集合
def char_bi_gram(s):
words = s.replace(" ","")
result = []
for i in range(len(words)-1):
result.append(words[i:i+2])
return result
word1 = "paraparaparadise"
word2 = "paragraph"
X = set(char_bi_gram(word1))
Y = set(char_bi_gram(word2))
print("X: ", X)
print("Y: ", Y)
print("和集合: ", X|Y)
print("積集合: ", X&Y)
print("差集合(X-Y): ", X-Y)
print("差集合(Y-X): ", Y-X)
print("'se' in X: ", 'se' in X)
print("'se' in Y: ", 'se' in Y)
X: {'ar', 'ad', 'pa', 'ap', 'ra', 'se', 'is', 'di'} Y: {'ag', 'ar', 'gr', 'ph', 'pa', 'ap', 'ra'} 和集合: {'ag', 'ar', 'gr', 'ph', 'ad', 'pa', 'ap', 'ra', 'se', 'is', 'di'} 積集合: {'ra', 'ar', 'ap', 'pa'} 差集合(X-Y): {'ad', 'se', 'di', 'is'} 差集合(Y-X): {'ag', 'gr', 'ph'} 'se' in X: True 'se' in Y: False
07. テンプレートによる文生成
x, y, z = 12, "気温", 22.4
print("{}時の{}は{}".format(int(x),y,float(z)))
12時の気温は22.4
08. 暗号文
# 暗号化と復号化は同じ関数
def encryption(s):
s = list(s)
for i in range(len(s)):
if 97 <= ord(s[i]) <= 122:
s[i] = chr(219-ord(s[i]))
return "".join(s)
s= "I have a pen."
encryption(s)
'I szev z kvm.'
s2 = "I szev z kvm."
encryption(s2)
'I have a pen.'
09. Typoglycemia
import random
def typoglycemia(sequence):
words_list = sequence.split()
if len(words_list) <= 4:
result = words_list
else:
head = words_list[0]
end = words_list[-1]
middle = words_list[1:-1]
rand_list = [num for num in range(len(middle))]
random.shuffle(rand_list)
result = [head] + [middle[num] for num in rand_list] + [end]
return " ".join(result)
s = "I couldn’t believe that I could actually understand what I was reading : the phenomenal power of the human mind ."
typoglycemia(s)
'I was mind actually could that of phenomenal I what : couldn’t believe human I the power the understand reading .'
第2章: UNIXコマンド
この章は問題に使うコマンドがヒントとして書いてあるので、調べながらそのコマンドの使い方を学びました。実行Ubuntudで行っています。
10. 行数のカウント
$ wc -l popular-names.txt
2780 popular-names.txt
11. タブをスペースに置換
$ cat popular-names.txt | tr "\t" " " > popular-names-new.txt
12. 1列目をcol1.txtに,2列目をcol2.txtに保存
$ cut -f 1 -d " " popular-names-new.txt >col1.txt
$ cut -f 2 -d " " popular-names-new.txt >col2.txt
13. col1.txtとcol2.txtをマージ
$ cat col1.txt > col_merge.txt col2.txt
14. 先頭からN行を出力
$ head -n 5 popular-names.txt
Mary F 7065 1880
Anna F 2604 1880
Emma F 2003 1880
Elizabeth F 1939 1880
Minnie F 1746 1880
15. 末尾のN行を出力
$ tail -n 3 popular-names.txt
Lucas M 12585 2018
Mason M 12435 2018
Logan M 12352 2018
16. ファイルをN分割する
$ split -n 5 -d popular-names.txt sep
17. 1列目の文字列の異なり
$ cut -f 1 popular-names.txt | sort | uniq
18. 各行を3コラム目の数値の降順にソート
$ sort -k 3nr -t " " popular-names-new.txt
19. 各行の1コラム目の文字列の出現頻度を求め,出現頻度の高い順に並べる
$ cut -f 1 -d " " popular-names-new.txt | sort | uniq -c | sort -r
第3章: 正規表現
ダウンロードしたデータを見ながらゴリゴリと書きました。
20. JSONデータの読み込み
import json
# 1行ずつ読み込んで、json形式で取り出してリストに入れる
articles = []
with open('jawiki-country.json', 'r', encoding="utf-8") as f:
for article in f:
articles.append(json.loads(article))
len(articles)
248
# イギリスのtextの冒頭200文字を表示
for i in range(len(articles)):
if articles[i]['title']=='イギリス':
uk_text = articles[i]['text']
print(uk_text[:200])
break
{{redirect|UK}} {{redirect|英国|春秋時代の諸侯国|英 (春秋)}} {{Otheruses|ヨーロッパの国|長崎県・熊本県の郷土料理|いぎりす}} {{基礎情報 国 |略名 =イギリス |日本語国名 = グレートブリテン及び北アイルランド連合王国 |公式国名 = {{lang|en|United Kingdom of Great Britain and Norther
21. カテゴリ名を含む行を抽出
import re
search_text = '\[\[Category.+\]\]'
result = re.findall(search_text, uk_text)
print(result)
['[[Category:イギリス|*]]', '[[Category:イギリス連邦加盟国]]', '[[Category:英連邦王国|*]]', '[[Category:G8加盟国]]', '[[Category:欧州連合加盟国|元]]', '[[Category:海洋国家]]', '[[Category:現存する君主国]]', '[[Category:島国]]', '[[Category:1801年に成立した国家・領域]]']
22. カテゴリ名の抽出
for i in range(len(result)):
print(re.search("(?<=Category:)(.*)(?=\]\])",result[i]).group())
イギリス|* イギリス連邦加盟国 英連邦王国|* G8加盟国 欧州連合加盟国|元 海洋国家 現存する君主国 島国 1801年に成立した国家・領域
23. セクション構造
for i in range(len(uk_text_list)):
if re.search("(\=\=)(.*)(\=\=)",uk_text_list[i]):
section = re.search("(\=\=)(.*)(\=\=)",uk_text_list[i]).group()
level = section.count("=")//2-1
print(section.replace("=",""), level, end= ", ")
print()
国名 1, 歴史 1, 地理 1, 主要都市 2, 気候 2, 政治 1, 元首 2, 法 2, 内政 2, 地方行政区分 2, 外交・軍事 2, 経済 1, 鉱業 2, 農業 2, 貿易 2, 不動産 2, エネルギー政策 2, 通貨 2, 企業 2, 通信 3, 交通 1, 道路 2, 鉄道 2, 海運 2, 航空 2, 科学技術 1, 国民 1, 言語 2, 宗教 2, 婚姻 2, 移住 2, 教育 2, 医療 2, 文化 1, 食文化 2, 文学 2, 哲学 2, 音楽 2, ポピュラー音楽 3, 映画 2, コメディ 2, 国花 2, 世界遺産 2, 祝祭日 2, スポーツ 2, サッカー 3, クリケット 3, 競馬 3, モータースポーツ 3, 野球 3, カーリング 3, 自転車競技 3, 脚注 1, 関連項目 1, 外部リンク 1,
24. ファイル参照の抽出
search_text = '(?<=ファイル:)(.*?)(?=\|)'
result = re.findall(search_text, uk_text)
print(result)
['Royal Coat of Arms of the United Kingdom.svg', 'Descriptio Prime Tabulae Europae.jpg', "Lenepveu, Jeanne d'Arc au siège d'Orléans.jpg", 'London.bankofengland.arp.jpg', 'Battle of Waterloo 1815.PNG', 'Uk topo en.jpg', 'BenNevis2005.jpg', 'Population density UK 2011 census.png', '2019 Greenwich Peninsula & Canary Wharf.jpg', 'Birmingham Skyline from Edgbaston Cricket Ground crop.jpg', 'Leeds CBD at night.jpg', 'Glasgow and the Clyde from the air (geograph 4665720).jpg', 'Palace of Westminster, London - Feb 2007.jpg', 'Scotland Parliament Holyrood.jpg', 'Donald Trump and Theresa May (33998675310) (cropped).jpg', 'Soldiers Trooping the Colour, 16th June 2007.jpg', 'City of London skyline from London City Hall - Oct 2008.jpg', 'Oil platform in the North SeaPros.jpg', 'Eurostar at St Pancras Jan 2008.jpg', 'Heathrow Terminal 5C Iwelumo-1.jpg', 'Airbus A380-841 G-XLEB British Airways (10424102995).jpg', 'UKpop.svg', 'Anglospeak.svg', "Royal Aberdeen Children's Hospital.jpg", 'CHANDOS3.jpg', 'The Fabs.JPG', 'Wembley Stadium, illuminated.jpg']
25. テンプレートの抽出
results = re.search(r'(?<=基礎情報)(.*)(?=\n\}\}\n)', uk_text, re.DOTALL).group().split('\n|')[1:]
uk_dict = {}
for result in results:
key = re.search('(.*?)(?=\=)', result).group().replace(" ","")
value = re.search('(?<=\=)(.*)', result).group().lstrip()
uk_dict[key] = value
uk_dict
{'略名': 'イギリス', '日本語国名': 'グレートブリテン及び北アイルランド連合王国', '公式国名': '{{lang|en|United Kingdom of Great Britain and Northern Ireland}}<ref>英語以外での正式国名:<br />', '国旗画像': 'Flag of the United Kingdom.svg', '国章画像': '[[ファイル:Royal Coat of Arms of the United Kingdom.svg|85px|イギリスの国章]]', '国章リンク': '([[イギリスの国章|国章]])', '標語': '{{lang|fr|[[Dieu et mon droit]]}}<br />([[フランス語]]:[[Dieu et mon droit|神と我が権利]])', '国歌': "[[女王陛下万歳|{{lang|en|God Save the Queen}}]]{{en icon}}<br />''神よ女王を護り賜え''<br />{{center|[[ファイル:United States Navy Band - God Save the Queen.ogg]]}}", '地図画像': 'Europe-UK.svg', '位置画像': 'United Kingdom (+overseas territories) in the World (+Antarctica claims).svg', '公用語': '[[英語]]', '首都': '[[ロンドン]](事実上)', '最大都市': 'ロンドン', 以下略 }
26. 強調マークアップの除去
results = re.search(r'(?<=基礎情報)(.*)(?=\n\}\}\n)', uk_text, re.DOTALL).group().split('\n|')[1:]
uk_dict = {}
for result in results:
key = re.search('(.*?)(?=\=)', result).group().replace(" ","")
value = re.search('(?<=\=)(.*)', result).group().lstrip()
value = re.sub("'''''","",value)
value = re.sub("'''","",value)
value = re.sub("''","",value)
uk_dict[key] = value
uk_dict
27. 内部リンクの除去
results = re.search(r'(?<=基礎情報)(.*)(?=\n\}\}\n)', uk_text, re.DOTALL).group().split('\n|')[1:]
uk_dict = {}
for result in results:
key = re.search('(.*?)(?=\=)', result).group().replace(" ","")
value = re.search('(?<=\=)(.*)', result).group().lstrip()
value = re.sub("'''''","",value)
value = re.sub("'''","",value)
value = re.sub("''","",value)
value = re.sub('(\{\{)(.*?)(http)(.*?)(\}\})','',value)
value = re.sub('(\[http)(.*?)(\])','',value)
uk_dict[key] = value
uk_dict
28. MediaWikiマークアップの除去
results = re.search(r'(?<=基礎情報)(.*)(?=\n\}\}\n)', uk_text, re.DOTALL).group().split('\n|')[1:]
uk_dict = {}
for result in results:
key = re.search('(.*?)(?=\=)', result).group().replace(" ","")
value = re.search('(?<=\=)(.*)', result).group().lstrip()
value = re.sub("'''''","",value)
value = re.sub("'''","",value)
value = re.sub("''","",value)
value = re.sub('(\<ref)(.*)(\/\>)','',value)
value = re.sub('(\<ref)(.*)(\/ref\>)','',value)
value = re.sub('\<br\s+\/\>','',value)
value = re.sub("\[\[","",value)
value = re.sub("\]\]","",value)
uk_dict[key] = value
uk_dict
{'略名': 'イギリス', '日本語国名': 'グレートブリテン及び北アイルランド連合王国', '公式国名': '{{lang|en|United Kingdom of Great Britain and Northern Ireland}}', '国旗画像': 'Flag of the United Kingdom.svg', '国章画像': 'ファイル:Royal Coat of Arms of the United Kingdom.svg|85px|イギリスの国章', '国章リンク': '(イギリスの国章|国章)', '標語': '{{lang|fr|Dieu et mon droit}}(フランス語:Dieu et mon droit|神と我が権利)', '国歌': '女王陛下万歳|{{lang|en|God Save the Queen}}{{en icon}}神よ女王を護り賜え{{center|ファイル:United States Navy Band - God Save the Queen.ogg}}', '地図画像': 'Europe-UK.svg', '位置画像': 'United Kingdom (+overseas territories) in the World (+Antarctica claims).svg', '公用語': '英語', '首都': 'ロンドン(事実上)', '最大都市': 'ロンドン', 以下略 }
29. 国旗画像のURLを取得する
import requests
S = requests.Session()
URL = "https://ja.wikipedia.org/w/api.php"
PARAMS = {
"action": "query",
"format": "json",
"prop": "imageinfo",
"titles": "ファイル:"+uk_dict['国旗画像'],
"iiprop": "url"
}
R = S.get(url=URL, params=PARAMS)
DATA = R.json()
PAGES = DATA["query"]["pages"]
for k, v in PAGES.items():
print(v["imageinfo"][0]["url"])
https://upload.wikimedia.org/wikipedia/commons/8/83/Flag_of_the_United_Kingdom_%283-5%29.svg
コメント