matplotlibのグラフをインタラクティブなまま保存する

savefig() で画像に保存しちゃうと、矩形拡大とかができないし、
show() しちゃうと閉じたら消える。インタラクティブなまま保存できたらよいのにと思って調べてみた。

https://stackoverflow.com/questions/4348733/saving-interactive-matplotlib-figures
https://stackoverflow.com/questions/29160177/matplotlib-save-file-to-be-reedited-later-in-ipython

MATLABにはそういう機能が既にあるみたいですね。まぁ欲しくなるよね。
matplotlibの場合、figureオブジェクトをpickleで保存すればいいみたいです。

日本語でも情報ありました。このエントリを書く意味がほとんど無くなってしまった。
http://kuroneko0208.hatenablog.com/entry/2014/07/28/161453

バッチファイルを作っておいて、pickleファイルを放り込めばすぐに見れる、みたいな感じにしておけばいいのかなー。
pngでもpickleでも保存しておいて、中身を詳しく見たくなったらインタラクティブモードでも見れるみたいな形にしておくと便利かな

保存する側

#! /usr/bin/python2.7
# coding: utf-8

import matplotlib.pyplot as plt
import pickle

xs = range(10)
ys = [x**2 for x in xs]

fig = plt.figure()
ax = fig.add_subplot(111)

ax.plot(xs, ys)

#plt.show()

with open('figure_image.pkl', 'wb') as ofs:
    pickle.dump(fig, ofs)

表示する側

#! /usr/bin/python2.7
# coding: utf-8

import matplotlib.pyplot as plt
import pickle
import sys

filename = 'figure_image.pkl'
if len(sys.argv) > 1:
    filename = sys.argv[1]

with open(filename) as ifs:
    fig = pickle.load(ifs)
    plt.show()
rem plt_viewer.bat
cd %~dp0

python plt_viewer.py %1

pickleファイルをドラッグ&ドロップで放り込むと、グラフをpickleでリストアして表示する。

SeleniumのChromeDriverを自動更新するbashスクリプト

Seleniumの処理でエラーが出てから、chromeのwebdriverが更新されていることに気がついてファイルを置き換える、というのを何度も繰り返してウンザリしていたので自動化することにしました。
スクリプトをcron等に仕掛けておけば、常に最新のchromedriverが起動されるようにできます。

前提となるディレクトリ構成


/home/username/bin/ ... ユーザーusernameのホームディレクトリ配下のbin。あらかじめ作成しておく。
selenium-chromedriver/ ... chromedriverの各バージョンを保持するディレクトリ。あらかじめ作成しておく。
2.27/ ... chromedriverのバージョン。自動で作成される。
chromedriver ... chromedriverの実行可能ファイル。自動で作成される。
2.28/
chromedriver
X.XX/
chromedriver
chromedriver ... 最新のchromedriverバイナリへのシンボリックリンク。自動で作成・更新される。

Python2/3でgmailからメールを送信する(gmailライブラリ使用編)

エントリPython2.7でGMailから日本語メールを送信するではテキストメールを送れるスクリプトを書きましたが、HTMLメールだとか添付ファイルはつけられないので、すぐに不満がでてくると思います。というか出てきました。手元では都度少しづつ改造して使っていますが、ライブラリを使うのが効率が良いと思います。

ということで、PyPIを"gmail"で検索して一番に出てくるライブラリを使ってみました。gmailという名前です。直球ですね。

インストール方法

pip install gmail
続きを読む

redashを使わなかった話、あるいは風呂敷を広げすぎない自戒


結論:新しいツールを導入しなくてもSQLの実行結果をメールで送信するだけで良かった


毎日決まった時間に動作するプログラムを書いて個人で運用しているのですが、動いた実績が日によって違うものなので、簡単に確認する手段が欲しくなりました。

そこで新しいものを触りたい欲が刺激され、redash ( https://redash.io/ ) という可視化ツールを使ってみようか迷いました。使い方に慣れておけば、他でも可視化したいという要望が出てきたときに知識を使い回せると考えたからです。

しかし、すぐにこれまでの経験が浮かび思いとどまりました。

  • だいたい環境構築でつまずいて、(ただでさえ少ない)余暇を奪う
    • インストールでつまずく(特定のディストリでは追加の操作が必要...etc)
    • データソースにつながらない
  • こういうツールって思ったよりマシンリソースを食う
  • 実行結果を細かく修飾したくなってきて設定とかCSSいじりだしたりしそう…

↑これにかける時間でゲームしたりDVD観たりできそう(重要)


そもそも、やりたい事は「SQLの実行結果を1日に1回程度確認したい」というだけなので、実行結果をそのままメールで送る程度で十分な事に気が付きました。しかも、メールを送信するプログラムは既にあるので、SQLと簡単なシェルスクリプトを書けばすぐに実装できます。
ただの表で表示されるより棒グラフや折れ線グラフで綺麗な表示ができる方が良いといえば良いのですが、実際、別になくてもそんなに困らない。

というわけで、SQLを実行してメールを送るだけにしました。具体的には、シェルスクリプトで特定のフォルダ以下にあるSQLファイルすべてについて、psqlを--htmlオプション付きでクエリ実行し、その結果を連結してgmailにメールを送信する対応をして終わらせました。

新しい事を覚えて他にも活かせるようにしよう、というのは良いのですが、新しい事だらけだと疲れるし、目的に対してかける時間や手段が大げさになりすぎる事もよくあります。そこら辺はバランス感覚が大事というか、完璧を求めすぎないほうが良いのかなと思います。

Pythonでpickleを使って永続化する、期限付きメモ化デコレータ

止まっちゃうプログラムの応急処理として、pickleでキャッシュを永続化するメモ化関数を書いたのでついでにgistにあげました。いつかまた使う気がする。
メモ化する関数がコールバック関数を受け取るタイプの関数だったので、キャッシュのキーを作る段階で関数名の文字列に置き換えちゃってます。

windowsでxargs -Pがしたい

windowsでxargs -P 10 -n 2みたいなことをしたかったのですが、

  • powershellとかではそういうことができなさそう
  • cygwinは入れたくない

ということで自分で実装することにしました。
最低限必要な-P(--max-procs) -n(--max-args) {}(プレースホルダ)は実装されてます。

これで並列実行させることで7分掛かってた処理が3分になりました。
multiprocessingは本当に便利。

続きを読む

python pipでインストール用ファイルをローカルに保存しておく

アプリケーションを別のサーバーに載せ替えたい等の理由で、インストールされているpythonパッケージを、別のマシンへ展開したいことがあります。
こういう時pipなら、pip freezeでパッケージ名とバージョンの一覧を出力して、他マシンでその一覧を元にinstallすると簡単です。

[HOST A]$ pip freeze > /nas/pip/requirements.txt
[HOST A]$ cat /nas/pip/requirements.txt
BeautifulSoup==3.2.1
Flask==0.10.1
Jinja2==2.7.3
...
terminado==0.5
toolz==0.7.1
tornado==4.1
wheel==0.24.0
[HOST B]$ pip install -r /nas/pip/requirements.txt

しかし、これだとfreezeで一覧にした後に、ライブラリの特定のバージョンが公開停止されたり、あるいはライブラリ自体が
公開停止されると、うまく展開できなくなるか、一部の機能が動かない(とか、変に動いたりする)はず。

それだと困るので、今の状態で、予め依存するライブラリも含め、全てローカルに保存しておきたい。
しかしPyPIから一つ一つ落としてくるのも手間です。



こういうときに便利なコマンドは無いのか…



installコマンドに--downloadというオプションがありました。

指定すると、インストール用の.tar.gzや.whlファイルをフォルダに放り込んでくれます。
ダウンロードまでで、インストールはされません。これで今あるpython環境をローカルに落としておけます。

[HOST A]$ pip freeze > /nas/pip/requirements.txt
[HOST A]$ pip install --download /nas/pip/archive -r /nas/pip/requirements.txt

あとは展開される側でそのフォルダを参照してpip installすればOKです。

[HOST B]$ pip install --no-index --find-links=file:///nas/pip/archive -r /nas/pip/requirements.txt
...()
Collecting wheel==0.24.0 (from -r ..\requirements.txt (line 34))
Installing collected packages: pyquery, pytz, pyzmq, redis, requests, selenium,
tornado, terminado, toolz, wheel
  Running setup.py install for pyquery
  Running setup.py install for redis
  Running setup.py install for selenium
  Running setup.py install for tornado
  Running setup.py install for terminado
  Running setup.py install for toolz
Successfully installed pyquery-1.2.9 pytz-2014.10 pyzmq-14.6.0 redis-2.10.3 requests-2.5.1 selenium-2.44.0 terminado-0.5 toolz-0.7.1 tornado-4.1 wheel-0.24.0

なお、cx_OracleとかscipyとかpylzmaとかのC拡張が含まれる場合は、関連するコンパイラなども持っておく必要がある気がします。
あと、pip以外からパッケージをインストールしている場合(aptとかyumで入れてる場合)、当然これでは展開できないので要注意です。

※pip 1.5.6(python2.7)で動作確認してます。

参考:
User Guide − pip 6.1.1 documentation
How to use Python's pip to download and keep the zipped files for a package? - Stack Overflow