LoginSignup
22
25

More than 1 year has passed since last update.

【最強の英単語帳爆誕ww】Pythonでエンジニア必須の英単語帳を自動生成-後編

Last updated at Posted at 2020-04-26

はじめに

こちらは後編の記事になりますので、まずは前編をご覧下さい。

↓ 今回作成した単語データを使用した学習の様子(記事の最後にダウンロードリンクがあります!)
anki_2.gif

実行結果だけ見たい!という方はこちら→実行結果

目次

  • 前回の振り返り
  • 改善方法について
    • プログラム内で使用する単語が多い
    • case insensitiveになっていない
    • stackoverflowのページにオーバフィットしている
  • コードの全体像
  • 改善点と追加機能
    • プログラム内で使用する単語が多い
    • case insensitiveになっていない
    • stackoverflowのページにオーバフィットしている
    • 単語の翻訳
    • その他
  • 実行結果
  • 結論
  • 【重要】英単語帳を作るには
  • データのダウンロード

前回の振り返り

仮説と検証結果

previously.jpg
前回は前編ということで、プログラムで自動生成してしまえば

もう英単語帳購入の必要はないんじゃないか?


という仮説の簡単な検証を行なっていきました。


前回の実行結果の一部がこちら。
rank:0 word: ('i', 96)
rank:1 word: ('python', 86)
rank:2 word: ('ago', 50)
rank:3 word: ('bronze', 36)
rank:4 word: ('have', 29)
rank:5 word: ('×', 25)

問題点

meeting_1.jpg

前回の検証結果として下記3つの問題点があることが見えてきました。

  1. プログラムのみで使用する単語が存在する
  2. case insensitiveになっていない
  3. stackoverflowのページにオーバフィットしている

今回はこれらの課題を解決し、

エンジニアにとって必要な英単語を最効率で学習


できる英単語帳の作成を目指していきます。

改善方法について

プログラム内で使用する単語が多い

plain_code.jpg

def、jやkなどの単語のことになるのですが、プログラム的に除去していく方法としては以下のようなものが挙げられます。

  1. プログラムのみで使用するような単語のリストデータを作成し、フィルタリング
  2. 自然言語として意味のなさないものを除去する+2文字以下は単語とみなさない※1
  3. 品詞の解読精度を向上させる

1と3は時間がかかるので、TextBlobの機能が生かせる2を採用していきます。

まずは、こちらをご覧下さい。


>>from textblob import Word
>>print(Word("list").definitions)
>>['a database containing an ordered array of items (names or topics)',...]

こちらで分かる通り、単語に意味が存在する場合definitionsというアトリビュートを呼ぶと、定義をリストで返してくれます。

同様に'def'の定義を見てみると


>>from textblob import Word
>>print(Word("def").definitions)
>>[]

のように、空のリストが返されますので、こちらを利用して本来の英語には存在しない単語を除去していきます。

case insensitiveになっていない

これは下記のようにstringのメソッドlowerを呼んで、全て小文字にすることで対応していきます。

'string'.lower()

stackoverflowのページにオーバフィットしている

AI.jpg

ここが良い単語帳を作成するにのに最も重要なポイントとなりそうです。「エンジニア」をターゲットとすると少し幅が広くてデータをとるのが大変なので、下記2点を行っている人にフォーカスした単語帳を作成することとして5つのwebページをピックアップしてみました。

  1. Pythonを使用している
  2. AIを使用したバックエンド開発を行っている

1/5 AWS Lamda
https://aws.amazon.com/lambda/
AWS.png

2/5 Python3.8 What's new
https://docs.python.org/3/whatsnew/3.8.html
python.png

3/5 Docker what is container
https://www.docker.com/resources/what-container
docker.png

4/5 Wikipedia Artificial intelligence
https://en.wikipedia.org/wiki/Artificial_intelligence
AI.png

コードの全体像

基本的には前回のコードを使いまわしていますが、今回は複数のサイトにアクセスするためのfor文が必要となったので、一部関数の切り出しと修正を行っています。

from enum import Enum, unique
from typing import List, Tuple, Dict
import requests

from bs4 import BeautifulSoup as bs
from textblob import TextBlob, Word
from textblob.exceptions import NotTranslated

URLS = (
    ('stackoverflow', 'https://stackoverflow.com/questions/tagged/python'),
    ('AWS', 'https://aws.amazon.com/lambda/'),
    ('wikipedia AI', 'https://en.wikipedia.org/wiki/Artificial_intelligence'),
    ('Docker', 'https://www.docker.com/resources/what-container'),
    ('Python3.8 doc', 'https://docs.python.org/3/whatsnew/3.8.html'),
)

PARSER = "html.parser"
UNACCEPTABLE_LENGTH_OF_WORD = 2


@unique
class PartOfSpeechToLearn(Enum):
    JJ = 'Adjective'
    VB = 'Verb'
    NN = 'Noun'
    RB = 'Adverb'


def single_page_text_from_url(url: str) -> str:
    res = requests.get(url)
    raw_html = bs(res.text, PARSER)
    texts_without_html: str = raw_html.text
    return texts_without_html


def filter_for_wordbook(morph: TextBlob) -> List[str]:
    word_and_tag: List[Tuple[str, str]] = morph.tags
    part_of_speech_to_learn = tuple(pos.name for pos in PartOfSpeechToLearn)

    filtered_words = []
    for wt in word_and_tag:
        word: str = wt[0].lower()
        part_of_speech: int = wt[1]
        if part_of_speech not in part_of_speech_to_learn:
            continue
        if word in filtered_words:
            continue
        if (
                len(word) <= UNACCEPTABLE_LENGTH_OF_WORD or
                len(Word(word).definitions) == 0
        ):
            continue
        filtered_words.append(word)
    return filtered_words


def show_top_50_common_and_uncommon_words(word_and_count: Dict[str, int]) -> None:
    words_by_descending_order: List[Tuple[str, int]] = sorted(
        word_and_count.items(),
        key=lambda x: x[1],
        reverse=True
    )

    print('the most common words')
    for i, word_and_count in enumerate(words_by_descending_order[:10]):
        word: str = word_and_count[0]
        count: int = word_and_count[1]
        try:
            meaning_jp = str(TextBlob(word).translate(to='ja'))
        except NotTranslated:
            meaning_jp = word
        print(f'rank:{i + 1} word: {word} count: {count} meanings: {meaning_jp}')

    print('the most uncommon words')
    for j, word_and_count in enumerate(words_by_descending_order[:-11:-1]):
        word: str = word_and_count[0]
        count: int = word_and_count[1]
        try:
            meaning_jp = str(TextBlob(word).translate(to='ja'))
        except NotTranslated:
            meaning_jp = word
        print(f'rank:{j + 1} word: {word} count: {count} meanings: {meaning_jp}')


if __name__ == '__main__':
    word_and_count: Dict[str, int] = {}
    for url in URLS:
        print(f'website: {url[0]} url: {url[1]}')

        texts_without_html = single_page_text_from_url(url[1])

        morph = TextBlob(texts_without_html)
        filtered_words = filter_for_wordbook(morph)

        for word in filtered_words:
            count = morph.words.count(word, case_sensitive=False)
            if count == 0:
                continue
            if word not in word_and_count.keys():
                word_and_count[word] = count
            else:
                word_and_count[word] += count

        print(f'length of word_and_count: {len(word_and_count)}')

    show_top_50_common_and_uncommon_words(word_and_count)

それでは、前回から改良した点をみていきましょう!

改善点と追加機能

プログラム内で使用する単語が多い

前回内包表記でフィルタリングしていた部分を全て関数に切り出してifでフィルターするようにしています。シンプルな処理であれば内包表記がnamespaceの節約と処理効率の面で便利ですが、複数の条件でデータを処理する場合にはif文を使用する方がシンプルでベターかと思います。

ifに関してはネストを避けるために、処理が軽そうなものから順にcontinueしていくようにしています。

def filter_for_wordbook(morph: TextBlob) -> List[str]:
    word_and_tag: List[Tuple[str, str]] = morph.tags
    part_of_speech_to_learn = tuple(pos.name for pos in PartOfSpeechToLearn)

    filtered_words = []
    for wt in word_and_tag:
        word = wt[0].lower()
        part_of_speech = wt[1]
        if part_of_speech not in part_of_speech_to_learn:
            continue
        if word in filtered_words:
            continue
        if (
            len(word) <= UNACCEPTABLE_LENGTH_OF_WORD or
            len(Word(word).definitions) == 0
        ):
            continue
        filtered_words.append(word)
    return filtered_words

上記コードのここで、プログラムでしか使用しない単語をフィルタリングしています。

if (
    len(word) <= UNACCEPTABLE_LENGTH_OF_WORD or 
    len(Word(word).definitions) == 0
):
    continue

case insensitiveになっていない

先ほどのコードの下記部分で、大文字小文字が違うだけで同じ単語を同一データとして重複を避ける処理を入れています。

for wt in word_and_tag:
    word = wt[0].lower()
    if word in filtered_words:
        continue

stackoverflowのページにオーバフィットしている

先ほど紹介したwebサイトをtuple型でURLSとして定義しています。

URLS = (
    ('stackoverflow', 'https://stackoverflow.com/questions/tagged/python'),
    ('AWS', 'https://aws.amazon.com/lambda/'),
    ('wikipedia AI', 'https://en.wikipedia.org/wiki/Artificial_intelligence'),
    ('Docker', 'https://www.docker.com/resources/what-container'),
    ('Python3.8 doc', 'https://docs.python.org/3/whatsnew/3.8.html'),
)

単語の翻訳

中身はGoogle翻訳ですが、単語の翻訳もTextBlobライブラリを使用しています。
NotTranslatedは、pythonという単語のように、翻訳してもpythonとなるようなものを翻訳しようとした時にraiseされるものになります。

try:
    meaning_jp = str(TextBlob(word).translate(to='ja'))
except NotTranslated:
    meaning_jp = word

その他

お気づきになった方もいらっしゃるかもしれませんが、下記のコードで


for word in filtered_words:
    count = morph.words.count(word)
    if count == 0:
        continue
    if word not in word_and_count.keys():
        word_and_count[word] = count
    else:
        word_and_count[word] += count

このように、カウントがゼロの場合、次のループへ移行する文があります。

count = morph.words.count(word)
if count == 0:
    continue

具体的にはe.g.,のような文字列を解析した際、トークナイズ時はe.g.、品詞毎に分解した際はe.gとなるため、e.g.,のような単語がきた時にcount==0となるのでこちらのコードを挿入しています。

Screen Shot 2020-04-24 at 15.12.09.png

自然言語処理とTextBlobの使い方に関してまだまだ勉強不足ですので、ある程度知見がたまったら別途TextBlobの使い方まとめ、みたいな形で記事を出した際に解説していきたいと思います。

実行結果

surprise.jpg

前回の問題点は無事改良されているのでしょうか................?

実行結果はこちら..................................!

the most common words
rank:1 word: intelligence count: 327 meanings: 知性
rank:2 word: artificial count: 280 meanings: 人工的な
rank:3 word: help count: 190 meanings: 助けて
rank:4 word: python count: 186 meanings: python
rank:5 word: error count: 184 meanings: エラー
rank:6 word: target count: 168 meanings: 目標
rank:7 word: new count: 134 meanings: 新着
rank:8 word: learning count: 129 meanings: 学習する
rank:9 word: machine count: 125 meanings: 機械
rank:10 word: have count: 120 meanings: 持ってる

the most uncommon words
rank:1 word: donate count: 1 meanings: 寄付
rank:2 word: math count: 1 meanings: 数学
rank:3 word: inspect count: 1 meanings: 調べる
rank:4 word: ignore count: 1 meanings: 無視する
rank:5 word: processor count: 1 meanings: プロセッサー
rank:6 word: queue count: 1 meanings: キュー
rank:7 word: depend count: 1 meanings: 依存する
rank:8 word: pretend count: 1 meanings: ふり
rank:9 word: decrease count: 1 meanings: 減少
rank:10 word: allocator count: 1 meanings: アロケータ

おおおおおおおおっ!

結論

target_goal.jpg

まずはもう一度仮説に振り返ってみたいと思います。今回のシリーズでの仮説は
Pythonで自動的に作成すれば、

もう英単語帳購入の必要はないんじゃないか?


というものでした。


こちらの仮説に対しての結論としては、

英単語帳は購入しなくても、プログラムで作れる!


といえそうです!!!※2

【重要】市販のような英単語帳を作るには

tips.jpg

実行結果をみていただければわかりますが、今回抽出できた単語だけでもかなり有用なことが分かります。ただ、市販の単語帳と同等のものを作るにはインターフェースと3つの追加情報が必要になってきます。

インターフェース

市販の単語帳には、本、もしくはアプリといった問題形式で覚えたり、内容の確認をしやすくするためのインターフェースを備えています。

今回のように自作英単語データを学習に使用したいという方には、私のブログでも紹介していますAnkiアプリが超おすすめです!!素のAtom使ってる人がIDEを使いこなした時ぐらい感動します!

先日Twitterでたまたま見かけたのですが、連続起業家で有名な家入一真さんもAnkiを使用しているようです。

PC版は無料でインストールできるので、PCだけであれば完全無料で英単語学習ができそうです。※3
【英語学習に必須】Ankiアプリとは?
anki.png

3つの情報

市販の英単語帳には通常、下記の3つの情報がほぼ確実に含まれています。

  1. 単語の翻訳詳細(品詞、他の意味)
  2. 例文
  3. 例文の翻訳

1.単語の翻訳詳細(品詞、他の意味)や2.例文に関してはTextBlobの機能を使えばうまく引っ張ってくることができるのですが、例文の翻訳が問題となってきます。

こちらの記事でも紹介しましたが、Google翻訳を使用して文章を翻訳するとコンテクストの問題やそもそもの翻訳が違うなどの問題がでてくるので、正しい学習ができない可能性があります。
まだGoogle翻訳で消耗してるの?
Screen Shot 2020-04-23 at 18.14.07.png

しかし

人力であれば、例文の自然な翻訳をしていくことはまだ可能です。

もし、私のTwitterのフォロワーが1000を超えたら、今回作成したプログラムをさらに改良し例文も含めた

エンジニア向けの最強英単語帳


のデータを配布していきたいと思います。


英語学習に関するTipsや万が一英単語帳の配布が決定した場合のお知らせなどはこちらのTwitterで随時お知らせしていきますので、この機会にぜひフォローして頂けると幸いです。

(会社のPCだからフォローし辛いという方は、スマホで「hossyan-blog.com」もしくは「ほっしゃん エンジニア 英語」で検索してみてください。)

Twitter:ほし@オランダ在住プログラマー
Screen Shot 2021-08-27 at 10.44.44.png

ブログ: ほし@オランダ在住プログラマーのブログ
Screen Shot 2021-08-27 at 10.43.50.png

データのダウンロード

anki_2.gif

私のブログのこちらのページから単語データのダウンロードと、10秒でできるAnkiへのデータインポート方法を紹介していますので、参考にしてみて下さい。

https://hossyan-blog.com/2020/04/26/how-to-download-text-data/
Screen Shot 2021-08-27 at 10.42.02.png

注釈
※1 下記のサイトを見た感じだと無視して良さそうです
https://en.wikibooks.org/wiki/Scrabble/Two_Letter_Words
https://en.wiktionary.org/wiki/Category:English_one-letter_words
※2 英単語帳を英単語とその翻訳のデータセットとして定義すれば
※3 Ankiさん、広告費貰えるわけでもないのに激推ししておきましたよ!

22
25
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
22
25