Port 53

明日のための技術メモ

言語処理100本ノック 2020 第3章 前半

nlp100.github.io

第3章 正規表現の前半(20-24まで)解説書きます。
ついにJSONが出てきた。そして一気に書くの大変なので解けたら都度更新します。

第3章 後半はこちら

目次

この章で使うファイル

Wikipediaの記事をJSONにしてgzで固めたもの。
途中、抽出してほしい箇所を指定されるが、WikipediaのHelp:早見表を見て確認のこと。

                   title                                               text
0                   エジプト  {{otheruses|主に現代のエジプト・アラブ共和国|古代|古代エジプト}}\n{{基礎...
1                 オーストリア  {{基礎情報 国\n|略名 = オーストリア\n|日本語国名 = オーストリア共和国\n|公...
2                 インドネシア  {{基礎情報 国\n| 略名 =インドネシア\n| 日本語国名 =インドネシア共和国\n| ...
3                    イラク  {{複数の問題\n| 参照方法 = 2011年8月\n| 独自研究 = 2012年10月\n...
4                    イラン  {{半保護}}\n{{未検証|date=2010年3月}}\n{{基礎情報 国\n | 略名...
..                   ...                                                ...
243  スヴァールバル諸島およびヤンマイエン島  '''スヴァールバル諸島およびヤンマイエン島''' (Svalbard and Jan Ma...
244               シンガポール  {{参照方法|date=2012年2月}}\n{{基礎情報 国\n|略名 = シンガポール\...
245                マレーシア  {{基礎情報 国\n|略名 =マレーシア\n|日本語国名 =マレーシア\n|公式国名 ={{...
246                BES諸島                     #転送 [[ボネール、シント・ユースタティウスおよびサバ]]
247               キュラソー島                             #転送 [[キュラソー (オランダ王国)]]

20. JSONデータの読み込み

f:id:saturn-glave:20200503202503p:plain

pandasならread_csv、read_jsonで圧縮されたままdfに取り込めることを知った。
超便利じゃないですか。

ポイント

  • pandas.read_json()で、gz等で圧縮されたままのjsonファイルを読み込める
    • compression='infer'でどの圧縮ファイル(.gz, .bz2, .zip, .xz)か自動判定してくれる
    • orient='records', lines=Trueで1レコードがJSONになっている形式(JSONL)を扱える
  • pandas.DataFrameでの値参照はdf.valuesを使う
  • pandas.DataFrameにあるレコードを条件で検索するにはdf.query('条件')を使う
    • 条件を書く時、文字列を含む場合は、文字列を""で囲む

解答

# coding:utf-8
import pandas as pd

path = './jawiki-country.json.gz'

df = pd.read_json(path, compression='infer', orient='records', lines=True)
# print(df)

ans = df.query('title == "イギリス"')['text'].values[0]


print(ans)

参考記事

note.nkmk.me

note.nkmk.me

note.nkmk.me

21. カテゴリ名を含む行を抽出

f:id:saturn-glave:20200503202529p:plain

正規表現の出番かなーと思い、reモジュールを入れる。
pandas.query()を使わず指定した条件の行を抽出できるので、そのやり方で書いてみた。
この問題で指定されている条件にて文字を抽出するのに使った正規表現は、^\[\[Category:.+\]\]$
正規表現慣れてなくて結構時間かかってしまった。

ポイント

  • 文字列について先頭に限らず正規表現にマッチするかどうかを確認する時はre.search('パターン', 文字列)を使う
  • 正規表現にマッチして返ってきたマッチオブジェクトについて、文字列を取りたい時group()メソッドで取れる

解答

問題20で抽出した文字列について、改行コードで区切っておく。
1行ずつ読み込んで、正規表現に引っかかったら確保していく。

# coding:utf-8
import pandas as pd
import re


def uk_load():
    path = './jawiki-country.json.gz'
    wiki = pd.read_json(path, compression='infer', orient='records', lines=True)
    uk = wiki[wiki['title'] == 'イギリス']['text'].values[0]
    return(uk)


def main():
    uk_txt = uk_load().split('\n')
    ans = []
    # print(uk_txt)

    for item in uk_txt:
        result = re.search(r'^\[\[Category:.+\]\]$', item)
        if result:
            ans.append(result.group(0))

    for r in ans:
        print(r)


if __name__ == "__main__":
    main()

出力はこんな感じ。

 $ python CH3-21.py
[[Category:イギリス|*]]
[[Category:イギリス連邦加盟国]]
[[Category:英連邦王国|*]]
[[Category:G8加盟国]]
[[Category:欧州連合加盟国|元]]
[[Category:海洋国家]]
[[Category:現存する君主国]]
[[Category:島国]]
[[Category:1801年に成立した国家・領域]]

参考記事

note.nkmk.me

docs.python.org

note.nkmk.me

22. カテゴリ名の抽出

f:id:saturn-glave:20200503202600p:plain

問題21の続き。余計な括弧とか抜きで出力する。
問題21の行抽出の時、lambda式とfilterできれいに書けることに気づいたので、
今回はそっちで書いてみることにした。

エンジニアは面倒くさがらないといけない。鉄則。

ポイント

  • filterとlambda式を使って対象を絞り込むことができる
  • 正規表現を使った文字列置換をしたい時は、re.sub('置換元パターン', '置換後の文字', 対象の文字列)を使う

解答

# coding:utf-8
import pandas as pd
import re


def uk_load():
    path = './jawiki-country.json.gz'
    wiki = pd.read_json(path, compression='infer', orient='records', lines=True)
    uk = wiki[wiki['title'] == 'イギリス']['text'].values[0]
    return(uk)


def main():
    uk_txt = uk_load().split('\n')
    uk_category = list(filter(lambda x: "[[Category:" in x, uk_txt))
    # print(uk_category)

    for item in uk_category:
        item = item.replace('[[Category:', '').replace(']]', '')
        item = re.sub(r'\|.$', '', item)
        print(item)


if __name__ == "__main__":
    main()

出力はこんな感じ

$ python CH3-22.py
イギリス
イギリス連邦加盟国
英連邦王国
G8加盟国
欧州連合加盟国
海洋国家
現存する君主国
島国
1801年に成立した国家・領域

参考記事

note.nkmk.me

23. セクション構造

f:id:saturn-glave:20200503202613p:plain

Wikipediaのヘルプ見ると、==でセクション2になっているが、
問題文に従ってセクション1として実装した。

解答

# coding:utf-8
import pandas as pd


def uk_load():
    path = './jawiki-country.json.gz'
    wiki = pd.read_json(path, compression='infer', orient='records', lines=True)
    uk = wiki[wiki['title'] == 'イギリス']['text'].values[0]
    return(uk)


def main():
    uk_txt = uk_load().split('\n')
    section = list(filter(lambda x: '==' in x, uk_txt))
    # print(section)

    for item in section:
        if item[:5] == '=====':
            print(item.replace('=====', ''), 4)
        elif item[:4] == '====':
            print(item.replace('====', ''), 3)
        elif item[:3] == '===':
            print(item.replace('===', ''), 2)
        elif item[:2] == '==':
            print(item.replace('==', ''), 1)


if __name__ == "__main__":
    main()

出力は以下の通り

国名 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. ファイル参照の抽出

f:id:saturn-glave:20200503202625p:plain

filterとlambda式で絞ったら余計なものがくっついてきたので、
改行コードで区切らず1つの文字列扱いにして、おとなしくreモジュールで抽出することに。
そしてもう少しWikipediaのマークアップ仕様を見ていたら、
[[ファイル:ファイル名|オプション]] でも、[[File:ファイル名|オプション]]でも 添付ファイルを貼り付けることができると分かった。

ポイント

  • re.findall('正規表現パターン', 対象の文字列)正規表現パターンにマッチした部分文字列をリスト(要素は文字列)で返す
  • ( )正規表現のグループ化(キャプチャあり)を行い、文字列をまとめて扱える
  • (?: )の場合、グループ化のみを行うという明示になる

解答スクリプト

ファイル名とオプションをキャプチャしているので、 1つのファイルにつき2要素ずつのタプルがリストに格納されて返ってくる。
ほしいのはファイル名だけなので、指定してあげること。

# coding:utf-8
import pandas as pd
import re


def uk_load():
    path = './jawiki-country.json.gz'
    wiki = pd.read_json(path, compression='infer', orient='records', lines=True)
    uk = wiki[wiki['title'] == 'イギリス']['text'].values[0]
    return(uk)


def main():
    uk_txt = uk_load()
    file = re.findall(r'\[\[(?:ファイル|File):(.+?)\|(.+?)\]\]', uk_txt)

    for item in file:
        print(item[0])


if __name__ == "__main__":
    main()

出力は以下の通り。

$ python CH3-24.py
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

参考記事

note.nkmk.me

userweb.mnet.ne.jp

参考

qiita.com