Local Optima

数理最適化を趣味的に楽しむ。

こんなふうにPythonプログラムを育てたい(願望)

Pythonプログラムを実装・開発した先に「展開」のステップが待ち構えていることだろう。つまり,ただの小さなスクリプトが,他の人の環境に配布されていったり,アプリケーションやシステムになっていったりする日が来るかもしれない。

そこで覚えたい(願望)ものをひとまず列挙してみた。じっくり勉強するか。

Pyinstaller

Python環境非依存で人に配るときに必要になる。

使うだけならあまり難しくなさそうだが凝ったことをしようとすると手間がかかる。

Sphinx

パッケージを作っているのなら,読みやすいAPIリファレンスを作るのに必須となる。

Tkinter

GUIを着せるなら,なんだかんだ言ってこれがいいだろう。

Tkinterで足りないようなオシャレなGUIが作りたいのであれば,Webアプリを志向するか,完全Python開発は諦めたほうがいいのかもしれない。

頑張ることを止めはしないが。

Inno Setup

Pyinstallerでonefileのexe化とすると何かと遅いという評判である。

一方で,onefolderのアプリケーションにビルドすると正しく使ってくれない人が出てくるもんだ。

onefolderにビルドしたPythonアプリケーションを,Inno Setupでインストーラにこしらえてあげようという算段だ。

標準ライブラリのソースを読んでPython力を鍛えよう

Pythonの標準ライブラリは,開発された時代が古い場合があることを除けば,汎用性が高く,背景知識への依存度が少なく,他のライブラリとの依存関係も少ないため,ソースコードを読むことにより,「きれいで機能的なPythonのコーディング」を学ぶことができると思います。

なかでも私の一押しは,日付や時間を操作するdatetimeモジュールです。

docs.python.org

リファレンスのページにGitHubへのリンクが付いているので,そこから飛んでひたすら読みます。面倒くさい方はここから飛んでください。

おすすめポイント

  • 「うるう年」がきれいに実装されていて参考になる。
  • コメントが充実していて何をしているか,何を意図しているかが分かりやすい。
  • クラス定義も丁寧できれいに行われている。
  • 基本的に上から順番に読んでいけばよい(どこまで理解しながら読み進められるかが自分の実力のバロメータ

モジュールの公開プロパティと非公開プロパティをどのように整理しておくべきかなど,お手本とすべき点は他にもたくさんあると思います。

私とPythonのバージョン

私が最初にPythonを触ったのは2017年頃だった。当時すでにPython 3系の開発も進んではいたが,まだまだPython 2.7の人気が盤石だった時代である。インターネット上のPython関係の記事も,あるにはあったが現在の掃いて捨てるほどあるような様子に比べればまだまだ少なかった。Python 3系よりもPython 2系の情報のほうが量的に充実していたのでPython 2から始めたものだった。特に,かの時点で目下使う必要のあったライブラリの参考書がPython2系ベースで執筆されていたこともあり,Python 3を選ぶ理由は当時見当たらなかった。

しかしほどなくして,Python 3系に乗り換えた。特にハッキリしたきっかけは覚えていない。たしかそのときはPython 3.4にしたような気がする。

今はPython 3.8をベースに,Python 3.7を時々使っているといった調子である。利用可能なtensorflowのバージョンが3.7と3.8では代わるので,そのあたりに依存して切り替えている。しかし自分で機械学習のコードを書くことは今のところないので,ほとんどすべてPython 3.8で済ませている。

しかし,どういうわけだか知らないがPython 3.6を使ったままバージョンを上げる気配のない人が身近にいるのである。Python 3.6以前だと対応しているnumpy, pandasの機能にだいぶ差があるような気がしている。私が書いたPythonプログラムを渡したら動かないと言われたことがあるが,それは私のせいじゃない。

Python 3.9は型ヒントの書き方がずいぶんシンプルになったというのでぜひ取り入れたいところではあるが,abcモジュール等,ほかに熟知しないといけない事柄が多すぎて,便利になるのか手間が増えるのか分からず,乗り換える動機に欠ける。3.10か3.11がstableリリースになって,公式マニュアルに日本語の記述が十分になるまで待ってもよいかと思っていたところである。

python-mip.Model.add_constr について

本日もPython MIPの話題だが,その前に,PuLPの話から。

PuLPのモデルに制約を追加するときの記法

# PuLP
model += 2*x + 3*y<= 6

という記法が,個人的にあまり好みでない。完全に好みの問題だ。

Python MIPでは,もともとPuLPに触発されて開発が始まったという経緯もあるそうなので,PuLP流の書き方もできるし,モデルオブジェクトがadd_constr()という関数を公開しているので,

#python-mip
model.add_constr(2*x + 3*y <= 6)

という,gurobipy風の書き方が可能である。(これを薦めている人があまり見当たらないが。)

ついでに言えば,

#python-mip
model.add_constr(2*x + 3*y <= 6, name='hogehoge')

のように,制約オブジェクトに名前をもたせることができ,これは,.lpや.mpsファイルに書き出したときに反映されるらしい。

詳しくは(英語だが)公式ドキュメントを参照されたし。

docs.python-mip.com

Python MIPについて

オープンソースの数理計画ソルバーでなかなかの地位にいるのが,Coin-ORプロジェクトCBCソルバーである。

CBCのドキュメント (readme.md) にあるように,このソルバーを利用する方法はいろいろある。 なかでも,CBCを使えるPythonモジュールとして,cbcpy,Google OR-tools,PuLPPython MIPがある。

cbcpyはCBCPythonネイティブインターフェイスとあり,おそらく色々なことができるがこれを使うくらいなら,CかC++で同じプログラムを書いても難易度が変わらないものと思われる。

Google OR-toolsは,色々な機能の一部として,混合整数計画を定式化して解くことができるのだがその際に内部的にCBCソルバーを使う仕組みだ。ところが,これもあまりPythonぽいコードを書くのには適していない。(他言語の実装から自動生成したのだろうか。)

PuLPPython MIPは,当初からPythonをターゲットにしてAPIが設計されていて,Pythonicなコードで,定式化と求解ができる。

coin-or.github.io

PuLPは日本国内でそこそこ有名になったので,日本語文献が割と探しやすく,出版されている書籍でも,「Pythonではじめる数理最適化」「Pythonによる数理最適化入門」で扱われている。

ところが,PuLPは,定式化して解くことはできるのだけれど,CBCソルバーの機能をフルに活用した手の込んだ解法が実装できない。安いソルバーを安っぽく使っても,大した性能は期待できない。

www.python-mip.com

一方,Python MIPは,分枝カット法の実装が可能で,いろいろと手の込んだことができる。分枝カット法というと難しい手法でとっつきづらいのだが,それ以外のお手軽な機能として,ユーザーが任意の初期解を与えることができるので,問題構造から自明に得られる初期解や別の貪欲法アルゴリズムで見つけてきた初期解を与えて高速化をねらうなど,実務で役に立ちそうだ。

Pythonで信号の平滑化

MATLABドキュメントにある「信号の平滑化」は、日本語の文献としては、情報の量と粒度が適度にまとまっているようです。

jp.mathworks.com

本稿では、上記ドキュメントで行われていること(の一部)を、Pythonベースで、numpy, pandas, scipyなどのライブラリも上手に使いつつ、なぞってみたいと思います。信号の平滑化処理のサンプルプログラム集のようなつもりでお読みいただければ幸いです。

使用データ

Pythonで利用可能なローガン空港の気温のデータが見当たらなかったので、Rdatasetsで見つけたメキシコ湾の水温データ を使用します。1時間毎に1年分の水温(華氏)が記録されています。

import numpy as np 
import pandas as pd 

data = pd.read_csv("https://vincentarelbundock.github.io/Rdatasets/csv/Stat2Data/KeyWestWater.csv", index_col=0)
print(data)
        DateTime     WaterTemp   t
1   10/3/2016 0:00  86.2    1
2   10/3/2016 1:00  86.2    2
3   10/3/2016 2:00  86.2    3
4   10/3/2016 3:00  86.2    4
5   10/3/2016 4:00  86.0    5
... ... ... ...
6568    10/2/2017 20:00 85.6    6568
6569    10/2/2017 21:00 85.6    6569
6570    10/2/2017 22:00 86.0    6570
6571    10/2/2017 23:00 85.8    6571
6572    10/3/2017 0:00  85.8    6572
6572 rows × 3 columns

まずは、データの現波形を見てみます。

data.plot('t', 'WaterTemp')

f:id:local_optima:20200914152034p:plain

10月スタートなのではじめに冬に向かって水温が下がり、夏になると水温が上がる様子が確認できます。データには日中と夜間の水温差が記録されているので、拡大してみてみると24時間周期でギザギザしています。(ご自身の環境でお試しください!)

移動平均

平滑化でもっともポピュラーなのは移動平均法 (moving average) でしょう。連続したN個の平均をとり、それをどんどんずらしていきます。

pandas.Series.rolling().mean()を使います。

pandas.Series.rolling — pandas 1.2.0 documentation

pandas.core.window.rolling.Rolling.mean — pandas 1.2.0 documentation

"""移動平均"""
data['MovAveTemp'] = data['WaterTemp'].rolling(24, center=True).mean()

# グラフは最初の1ヶ月=720時間ぶんのみ表示
ax = data[0:720].plot('t', 'WaterTemp')
data[0:720].plot('t', 'MovAveTemp', c='r', ax=ax)

f:id:local_optima:20200914185212p:plain

rolling(24, center=True)で、各点を中心とし、ウィンドウを前後にN=24で取ってくれます。24時間周期の変動を除去して平滑化できています。

Savitzkey-Golayフィルタ

scipy.signal.savgol_filterが使えるようですが、使用目的がよくわからないので、ドキュメントを紹介するだけに留めておきます。

scipy.signal.savgol_filter — SciPy v1.6.0 Reference Guide

識者の方はコメント欄等で教えてくださるとありがたいです。

メディアンフィルタ

メディアンフィルタは、移動平均とほとんど同じ。pandas.Series.rolling().median() です。かんたんなので省略。

pandas.core.window.rolling.Rolling.median — pandas 1.2.0 documentation

ところで。

Savitzkey, Golay, Hampel の正しい発音が分からんw

(包絡線を引くのはうまくいきませんでした……。)

Pythonでグラフを書く & レポートで使うには

グラフ描き、していますか。Excelでグラフを作るのも悪くはないけど、データ量が多いとか、Excelよりはもう少し気の利いたことを「何か」にやって欲しいなあというときには、Pythonが便利です。

  • Pythonを使ってグラフを描くツール
  • グラフの書式をレポート向きにカスタマイズする
  • 印刷にも適したsvg形式でグラフを保存する

以上のポイントについて書いていきます。

  • 便利なライブラリたち
    • matplotlibとseabornの使い分け
  • 基本的な使い方は…
  • matplotlibで日本語を使うなら
  • レポートも見据えてseabornで最初にやっておきたい設定いろいろ
  • レポートに貼る図
  • 使用例1
  • 使用例2
  • See also
続きを読む