2017年9月17日日曜日

【Python】「言語処理100本ノック 2015」を始めてみました

夏の一コマ
α6500 & SIGMA 35mm F1.4 DG HSM Art

 言語処理100本ノック 2015を始めてみました。
 まずは「第1章: 準備運動」からですが、準備運動でもプログラミング素人にはなかなか大変です。
 環境は、Windows10 + Anaconda3.4.4 です。

00. 文字列の逆順

文字列”stressed”の文字を逆に(末尾から先頭に向かって)並べた文字列を得よ.

s = "stressed"
ans = s[::-1]
print(ans)

# > desserts

文字列のスライスのステップを使ってみました。
ステップに-数値を指定すると逆順にステップします。
ちなみに、スライスで指定する数字は、要素(文字列なら文字)の位置ではなく、要素と要素の間(ナイフを入れて切るところ)と考えると理解しやすかったです。

01. 「パタトクカシーー」

「パタトクカシーー」という文字列の1,3,5,7文字目を取り出して連結した文字列を得よ.

s = "パタトクカシーー"
ans = s[::2]
print(ans)

# > パトカー

むしろこちらの方が1問目に相応しいような気がする問題。
文字列のスライスのステップを知っていれば簡単ですね。

02. 「パトカー」+「タクシー」=「パタトクカシーー」

「パトカー」+「タクシー」の文字を先頭から交互に連結して文字列「パタトクカシーー」を得よ.

s1 = "パトカー"
s2 = "タクシー"
ans = "".join([c1 + c2 for c1, c2 in zip(s1, s2)])
print(ans)

# > パタトクカシーー

リスト内包表記を使用しています。for文より処理速度が速いとのことなので、使えるときは内包表記を使用するように心がけています。
“区切り文字列”.join(文字列のリスト)で区切り文字列を挟んで文字列のリストを結合した文字列を得られます(上の例は空文字列で結合)。
zip()は複数のシーケンスオブジェクトを同時にループしたいときに便利。要素数が違う場合は一番少ないものに合わせられます。

03. 円周率

“Now I need a drink, alcoholic of course, after the heavy lectures involving quantum mechanics.”という文を単語に分解し,各単語の(アルファベットの)文字数を先頭から出現順に並べたリストを作成せよ.

s = "Now I need a drink, alcoholic of course, after the heavy lectures involving quantum mechanics."
ans = [len(word.strip(",.")) for word in s.split()]
print(ans)

# > [3, 1, 4, 1, 5, 9, 2, 6, 5, 3, 5, 8, 9, 7, 9]

文字列をjoin()で区切ってリストにしてから”,”と”.”をstrip()で除去。リスト内包表記でリスト化しています。
“文字列A”.split(“区切り文字”)で文字列Aを区切り文字列で区切った文字列のリストを得ることができます(区切り文字列は除去される)。区切り文字を指定しない場合は空白文字(半角スペース、全角スペース、改行、改ページ、タブ、垂直タブ、復帰)が区切り文字として扱われます。
strip()は文字列の両端から指定した文字列を削除するメソッドです。
len()はシーケンスの要素数を返す関数ですね。

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文字を取り出し,取り出した文字列から単語の位置(先頭から何番目の単語か)への連想配列(辞書型もしくはマップ型)を作成せよ.

s = "Hi He Lied Because Boron Could Not Oxidize Fluorine. New Nations Might Also Sign Peace Security Clause. Arthur King Can."
ans = {word[:1] if i in [1, 5, 6, 7, 8, 9, 15, 16, 19] else word[:2]: i for i, word in enumerate(s.split(), start=1)}
print(ans)

# > {'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}

split()メソッドでリスト化してから条件式(三項演算子)と内包表記で辞書型にまとめています。
enumerate関数はfor文でループ処理をするときカウンターと要素の両方を取得できる関数です。startでカウンターの初期値を指定できます(何も指定しないと0からスタート)。

05. n-gram

与えられたシーケンス(文字列やリストなど)からn-gramを作る関数を作成せよ.この関数を用い,”I am an NLPer”という文から単語bi-gram,文字bi-gramを得よ.

# 単語n-gram
def w_n_gram(n, text):
    words = [word.strip(",.") for word in text.split()]
    return [words[i:i + n] for i in range(len(words) - n + 1)]

# 文字n-gram
def c_n_gram(n, text):
    return [text[i:i + n] for i in range(len(text) - n + 1)]

s = "I am an NLPer"
ans1 = c_n_gram(2, s)
ans2 = w_n_gram(2, s)
print(ans1)
print(ans2)

# > ['I ', ' a', 'am', 'm ', ' a', 'an', 'n ', ' N', 'NL', 'LP', 'Pe', 'er']
# > [['I', 'am'], ['am', 'an'], ['an', 'NLPer']]

n-gramの意味が分からなくて検索してしまいました。
問題で指定された文字列だけでいうなら、単語n-gramのstrip(“,.”)は要らないですね。逆により汎用化するなら”;”や”:”など必要な記号を列記する必要があります。

06. 集合

“paraparaparadise”と”paragraph”に含まれる文字bi-gramの集合を,それぞれ, XとYとして求め,XとYの和集合,積集合,差集合を求めよ.さらに,’se’というbi-gramがXおよびYに含まれるかどうかを調べよ.

s1 = "paraparaparadise"
s2 = "paragraph"

# 文字bi-gram
def get_bi_gram(text):
    return {text[i:i + 2] for i in range(len(text) - 1)}

x = get_bi_gram(s1)
y = get_bi_gram(s2)

print("X: {0}".format(x))
print("Y: {0}".format(y))
print("和集合: {0}".format(x | y))
print("積集合: {0}".format(x & y))
print("差集合: {0}".format(x - y))
print("Xに'se'は含まれるか? {0}".format('se' in x))
print("Yに'se'は含まれるか? {0}".format('se' in y))

# > X: {'ad', 'ra', 'is', 'ar', 'se', 'ap', 'pa', 'di'}
# > Y: {'ra', 'ag', 'ar', 'ap', 'ph', 'pa', 'gr'}
# > 和集合: {'is', 'se', 'gr', 'ad', 'ra', 'ag', 'ar', 'ap', 'ph', 'pa', 'di'}
# > 積集合: {'pa', 'ap', 'ar', 'ra'}
# > 差集合: {'ad', 'is', 'di', 'se'}
# > Xに'se'は含まれるか? True
# > Yに'se'は含まれるか? False

題名のとおり集合型を使う問題ですね。
今回はbi-gram限定ですので、集合型のbi-gramを返す関数を作って対応しています。内包表記を使えば一行で済むものなので、関数にする必要性も薄いような気もしますが。
あとは集合型の演算方法さえ知っていればOK。

07. テンプレートによる文生成

引数x, y, zを受け取り「x時のyはz」という文字列を返す関数を実装せよ.さらに,x=12, y=”気温”, z=22.4として,実行結果を確認せよ.

def xyz(x, y, z):
    return "{0}時の{1}は{2}".format(x, y, z)

x = 12
y = "気温"
z = 22.4
print(xyz(x, y, z))

# > 12時の気温は22.4

なんだか拍子抜けする問題。
format()メソッドの使用方法さえ知っていれば簡単。

08. 暗号文

与えられた文字列の各文字を,以下の仕様で変換する関数cipherを実装せよ.

  • 英小文字ならば(219 - 文字コード)の文字に置換
  • その他の文字はそのまま出力

この関数を用い,英語のメッセージを暗号化・復号化せよ.

def cipher(text):
    return "".join([chr(219-ord(c)) if c.islower() else c for c in text])

s = "I couldn't believe that I could actually understand what I was reading : the phenomenal power of the human mind."
s_ciphered = cipher(s)
print("原文: {0}".format(s))
print("暗号化後: {0}".format(s_ciphered))
print("復号化後: {0}".format(cipher(s_ciphered)))

# > 原文: I couldn't believe that I could actually understand what I was reading : the phenomenal power of the human mind.
# > 暗号化後: I xlfowm'g yvorvev gszg I xlfow zxgfzoob fmwvihgzmw dszg I dzh ivzwrmt : gsv ksvmlnvmzo kldvi lu gsv sfnzm nrmw.
# > 復号化後: I couldn't believe that I could actually understand what I was reading : the phenomenal power of the human mind.

リスト内包表記と条件式(三項演算子)で文字列を1文字ずつ指定の処理をしてリスト化したものをjoin()メソッドで結合しています。
islower()は文字列がアルファベットの小文字かどうかを判定するメソッド。
chrは文字コードから文字を返す関数、ord()は文字から文字コードを返す関数です。

09. Typoglycemia

スペースで区切られた単語列に対して,各単語の先頭と末尾の文字は残し,それ以外の文字の順序をランダムに並び替えるプログラムを作成せよ.ただし,長さが4以下の単語は並び替えないこととする.適当な英語の文(例えば”I couldn’t believe that I could actually understand what I was reading : the phenomenal power of the human mind .”)を与え,その実行結果を確認せよ.

from random import shuffle

def randomize_text(text):
    words = [randomize_word(word) for word in text.split()]
    return " ".join(words)

def randomize_word(word):
    if len(word) <= 4:
        return word
    else:
        mid_char = [c for c in word[1:-1]]
        shuffle(mid_char)
        return word[:1] + "".join(mid_char) + word[-1:]

s = "I couldn't believe that I could actually understand what I was reading : the phenomenal power of the human mind ."
print(randomize_text(s))

# > I cd'nolut bvlieee that I cluod atlaulcy unendatsrd what I was radneig : the pomnaheenl pweor of the hmuan mind .

問題文から”.”や”:”もスペースで区切られていることを前提に作成。
まずsplit()で文を単語のリスト化。
次に4文字以上の単語の先頭と末尾を除いたものを文字のリストとしてから、randomモジュールのshuffleを使ってランダムに並び替え。
先頭と末尾を戻して再結合して単語を作り、最後にスペースを区切りにして単語のリストを結合して文に再構築しています。

2017年9月16日土曜日

夏のバラ

夏のバラ
α6500 & Sonnar T* E 24mm F1.8 ZA SEL24F18Z

 本日も旅行先での写真。
 夏なのにそれなりの形で咲いているのは、やはり涼しいところだからなのでしょうね。

夏のバラ
α6500 & Sonnar T* E 24mm F1.8 ZA SEL24F18Z

夏のバラ
α6500 & Sonnar T* E 24mm F1.8 ZA SEL24F18Z

2017年9月10日日曜日

名残のかき氷

かき氷
α6500 & Sonnar T* E 24mm F1.8 ZA SEL24F18Z

 天候不順の影響もあって、かき氷を食べたのは旅行先での1回だけだった今年の夏。
 そんな年もあります。

2017年9月5日火曜日

『Pythonクローリング&スクレイピング』を購入しました

夏の湖
Ricoh GR

 より実践的なPythonのスクレイピングのテクニックを知りたくて、『Pythonクローリング&スクレイピング -データ収集・解析のための実践開発ガイド-』という本を購入しました。
 基本的なスクレイピング処理から自動での処理まで、相手先への負荷への配慮とか、エラーが出た場合の処理方法なども含めて載っています。以前に購入した『Pythonによるスクレイピング&機械学習 開発テクニック BeautifulSoup,scikit-learn,TensorFlowを使ってみよう』のスクレイピング部分をより実践的にした内容。
 とりあえずこれがあれば、私がしたいと思っていたことはできそうです。
 体系的な本があるとWebで個別に情報を探すより効率的で良いですね。

2017年9月3日日曜日

ディズニーランド

ディズニーランド
α6500 & Sonnar T* E 24mm F1.8 ZA SEL24F18Z

 お久しぶりのディズニーランド。
 お盆時期に行ったのですが、意外と空いていました。
 天気が今ひとつだったのが功を奏したのかのもしれません。

 家族旅行直後に行ってきたので、さらに疲労が蓄積。
 もう9月に入りましたが、未だに疲れが抜けない感じです。

ディズニーランド
α6500 & Sonnar T* E 24mm F1.8 ZA SEL24F18Z

2017年9月2日土曜日

ヘラクレスオオカブト

ヘラクレスオオカブト
α6500 & Sonnar T* E 24mm F1.8 ZA SEL24F18Z

 今年の夏に初めて体験したことは幾つかありますが、そのうちのひとつがヘラクレスオオカブトです。
 生きているヘラクレスオオカブトを初めて見ました、触りました。
 とても立派な生き物ですね。

ヘラクレスオオカブト
α6500 & Sonnar T* E 24mm F1.8 ZA SEL24F18Z

2017年8月27日日曜日

奥入瀬渓流 銚子大滝

銚子大滝
α6500 & Sonnar T* E 24mm F1.8 ZA SEL24F18Z

 今回の夏の旅行の目玉となる予定だった奥入瀬渓流。
 残念なことに小雨でしたが、折角なので奥入瀬渓流本流唯一の滝である銚子大滝ぐらいは、ということで眺めてきました。
 看板には「スモールナイアガラ」と呼ばれているというようなことが書かれていましたが、本当に地元の人々はそんな風に呼称しているのでしょうかね?
 比較対象としてはあまりにもスケールが違いすぎて、スモールというにも程があると思うのですが。

2017年8月19日土曜日

家族旅行

白い花


 岩手県、秋田県、青森県と3県にまたがっての新幹線とレンタカーでの家族旅行。
 移動が多く、疲れました。
 そろそろ、一箇所にとどまってのんびりとした時間を愉しむバケーションの方が似合う年齢になってきたのかもね。

2017年8月11日金曜日

Python始めました

柚子
α6500 & SIGMA 35mm F1.4 DG HSM Art

 プログラミング言語「Python」の勉強を始めました。
 プログラミング素人の備忘録としてメモを残しておきます。

きっかけ

 Webサイトからデータをダウンロードして加工、というような作業を捗らせる方法ないかと探しているうちにPythonに遭遇。
 プログラミング素人にとって学習コストが低そうなのと、ライブラリが充実してコードを書く手間がいろいろと省けそうなので、試してみることにしました。

Anacondaのインストール

 とりあえず書籍を買って読んでみたら(購入した書籍については後述)、学習を進めていくうちにいろいろと追加のライブラリを入れることになりそうなので、最初から必要なものが概ね揃っているAnaconda(「Continuum | Home」)をインストール。
 ダウンロードしてインストーラーを起動し、説明に従うだけなので簡単。

PyCharm

 開発環境はPyCharmを選択。無料の「Community edition」の方です。OSはWindows10です。
 Anaconda付属のIDE(統合開発環境)であるSpyderや、AtomにPython用のパッケージを入れる、など幾つか試してみましたが、PyCharmが一番使いやすく、デザインも良かったです。
 とにかく補完機能が強力なので省力化する上にミスも減少できますし(おまけにスペルチェックもある)、Ctrl + Alt + LでソースコードがPEP8(Pythonのコーディング規約)に準じて美しく自動整形されるのも良いです。何事も見た目が大切。

 PyCharmは 日本語化して使っています。「Android Studio / IntelliJ IDEA の日本語化と設定 - Qiita」を参考に日本語化。環境変数の設定を忘れずに。
 ただし、日本語化した方がいいのかは微妙なところです。情報量は英語の方が圧倒的に多いので、英語のまま使って用語に慣れた方がよいのかも。

学習用のテキスト

 前述のとおり、当面の目標はWebからのデータのダウンロードと加工。そのためには、当然のことながらPythonの基本的な文法事項等の学習も必要。
 基礎的な本は図書館で適当に借りようかと思ってたのですが、Pythonは意外と人気があるのか、めぼしい本は予約待ちの列になっていたので、書店で見比べた上で『実践力を身につける Pythonの教科書』『Pythonによるスクレイピング&機械学習 開発テクニック BeautifulSoup,scikit-learn,TensorFlowを使ってみよう』の2冊を購入しました。
 どちらもわかりやすいですが、広く浅くというタイプですので、いずれより詳しい本も探してみようと思います。

その他

 開発著しい分野なのか、数年前の情報ですら古くなっていて使えなかったり、もっと便利な方法が生まれていたりするので、情報源はなるべく新しいものを用意したほうが良さそうです。
 上で紹介した本も出版されてからまだ一年も経っていないですが、現在は変わってしまっている点がありました。

2017年8月5日土曜日

Huawei Mediapad M3をAndroid7.0にアップデート

ひまわり
α6500 & Sonnar T* E 24mm F1.8 ZA SEL24F18Z

 私のHUAWEI MediaPad M3にもようやくAndroid7.0アップデートが降りてきました。
 Wi-fi版は3万円もしない機種なのでアップデートはないかと思っていたのですが、ちゃんとアップデートされて嬉しいです。
 指紋認証をはじめ動作はサクサクだし、電池の持ちは良いし、附属アプリはホームアプリも含めて使い勝手が良いし、価格はお手頃だし、こんな高コストパフォーマンスなタブレットってそうはないですね。

 Android7.0になって、これでようやくマルチウィンドウが使えます。
 小型タブレットとはスクリーンサイズは8.4インチあるのでそれなりに有効に使えます。
 ブラウザでWebサイトの情報を見ながら、エディタで入力するとかね。