2013年3月20日水曜日

Matplotlibのグラフテンプレート

概要

matplotlibを使い出してからしばらく立ち,グラフの整形を一括して行いたいと思うようになった.
つまり,グラフ作成過程と,整形過程を分離してたスクリプトを書きたいと考えた.
そこで,テンプレートの設定方法について調べたところ,rcParamsを書き換えれば良いということが分かった.具体的にはここに全て書いてあるので,自分に必要な物を選択して変更すれば良い.
上記のページを元に自分用の仮テンプレートを作成したので上げておく.
こんな感じのグラフを作成する
グラフ軸に分数を使うよう設定するのがちょっと面倒


テンプレートのフォント周りについて

rcParamsの設定ではTex形式出力に利用するフォントとして通常文字に使用しているフォントの選択が可能であるようだ.
Tex形式のデフォルトはTexの数式で使われるフォントはそのままではIllustratorで利用できない.
まあ,Illustratorの設定を弄っても良いのだが,面倒くさいし,フォントを統一しておいたほうが便利かもしれないのでテンプレートではギリシャ文字出力に通常文字を割り当てるようにしている
これにより,ギリシャ文字出力が格段に楽になった(ユニコード調べるの面倒くさすぎ).

rcParamsの限界

rcParamsを使うことで相当細部までグラフの設定ができるが,実現したかった機能の内,幾つかは設定できなかったり,問題があった.(もしくは設定項目を適切に見つけることができなかった)
  1. グラフのマーカー自動設定
  2. 凡例のグラフ外部への配置設定
  3. 凡例の要素の列数設定
  4. Tex形式で分数を使った場合のサイズ縮小化対策
  5. EPSへのtype42フォント埋め込み設定(設定項目はあったがバグが生じた)
  6. マーカーをグラフ外枠より上に表示する方法(イラレでPDFを覗いた感じでは難しそう)
  7. TexフォントにSymbolフォントを利用する方法
  8. 文字列の配置を中揃えにする方法(軸で分数を使う際に軸ごとに設定できると便利)
上記以外にも幾つか問題点があったような気がする.
とりあえずEPSのフォント埋め込みに関連してXPDFはそのうち調べておく予定.

テンプレート



2013年3月18日月曜日

ScipyのFFTテスト

概要

Scipyには様々な科学技術計算用のツールが揃っている。
今回、その中の一つ、FFTを利用したサンプルコードを作成した.
具体的にはノイズの乗ったサンプルデータを作成し,これに対してFFTを行った後,高周波成分を除去し逆FFTを行う.

コード

以下の通り

import numpy as np
from scipy import fftpack #for fft
import matplotlib as mpl
import matplotlib.pyplot as plt
mpl.rcParams['figure.autolayout']=True #グラフのはみ出しを回避するためのコマンド
mpl.rcParams['axes.grid']=True
#データ点数
num = 1000
#時間の最小値と最大値
tscale = (0.0, 4.0)
#x = 0, 001, 0.02, ..., 4.99, 5.00
t = np.linspace(tscale[0],tscale[1], num)
#元のデータ:今回は正弦波
y = np.sin(np.pi*t)
#ノイズの入ったデータ.実際のデータにはノイズが乗ることが多い
yn = y + np.random.normal(scale=1,size=num)
#元データとノイズの乗ったデータをプロットする.
plt.subplot(311)#今回3つのグラフを立てに並べるのでサブプロットを使う
plt.plot(t,yn, color='b', linewidth=2, label="raw")
plt.plot(t,y, color='r', linewidth=1, label='ideal')
plt.xlabel("time [s]")
plt.ylabel("Value")
plt.legend(loc=1,fancybox=True)
#上記のデータをFFTしたもの
freq = fftpack.fftfreq(num, ((tscale[1]-tscale[0])/num))
fftY = fftpack.fft(y)
fftYN = fftpack.fft(yn)
plt.subplot(312)
plt.plot(freq[np.where(freq>=0)],np.abs(fftYN)[np.where(freq>=0)],\
         color='b', linewidth=2, label="raw")
plt.plot(freq[np.where(freq>=0)],np.abs(fftY)[np.where(freq>=0)],\
         color='r', linewidth=1, label="ideal")
plt.xlabel("Freq [Hz]")
plt.xlim(0,128)
plt.legend(loc=1,fancybox=True)
#2Hz以上をカットして逆FFT
fftYN[np.where(freq>=1.)] = 0
fftYN[np.where(freq<-1.)] = 0
yn = fftpack.ifft(fftYN)
plt.subplot(313)
plt.plot(t,yn, color='b', linewidth=2, label="ifft")
plt.plot(t,y, color='r', linewidth=1, label='ideal')
plt.xlabel("time [s]")
plt.ylabel("Value")
plt.legend(loc=1,fancybox=True)
plt.savefig("scipyffttest.png", dpi=300)
plt.show()

2013年2月24日日曜日

EPD(フルパッケージ版)におけるCythonの使い方

概要

EPDのフルパッケージ版にはCythonが含まれている。
CythonはPythonのコードをCコードに変換してくれるパッケージ。
ループ構造や条件分岐などで特に処理の遅いPythonコードをCythonでCに変換しコンパイルすることで処理の高速化が可能となったりするらしい。

とりあえず関数などをCythonでコンパイルすると良いらしい。これにより生成される実行ファイルをPython側で使うことでPythonで作った関数よりも高速に動作させることが可能となる。
もっとも、NumpyやScipyなどのパッケージは処理を計算ライブラリに投げることで高速化を実現しており、CPU資源をかなり使い切っているためCythonの恩恵は少ないはず。
別途記事を作るかもしれないが、行列-行列計算などではほとんどといっていいほど違いは出ない(今のところ時間計測をPythonで行なっているため、アプリケーションの実行時間で比べれば、また違った結果が見えるかもしれないが)。
Cythonのイメージのようなもの
とにかく、条件によっては性能が100倍以上に上昇する場合もあるので、覚えておいて損はないと思う。
EPDの場合、なんにも考えなくとも、パス等が通っているため楽に使うことができる。
ただ、Windows 7 64bitの場合、デフォルトでは32bitの実行ファイルが生成されてしまう。
これを64bitにするためにかなり調べてググったのでメモとしてまとめておく。

使い方

はじめに、C言語化したいPythonの関数を.pyx形式で作成する。
ここで、Cythonを使うためにいくつかのコードを関数に追加する必要がある。
高速化の恩恵を受けられるかどうかにも関わってくるらしいので覚えておきたい。
Pyxのコードは、例えば次のようになる。

#行列のべき乗計算
import numpy as np
cimport numpy as np
cimport cython
DOUBLE = np.float64
INT = np.int64
ctypedef np.float64_t DOUBLE_t
ctypedef np.int64_t INT_t
def MyMatPow(np.ndarray[DOUBLE_t,ndim=2] a, INT_t n):
    cdef int i
    cdef np.ndarray[DOUBLE_t,ndim=2] tempa = a
    for i in xrange(n-1):
        tempa = np.dot(tempa, a)
    return tempa
こんなかんじでソースを作っておく。

Windows 7の場合のコンパイル方法(多分XPとかでも同じ)を以下に記す。

コンパイル@32bit

EPDだと何も考えずにそのままコンパイルできる。
と言っても、いろいろめんどくさいので、setup.pyというファイルをPYXソースと同一のディレクトリに配置しておく。setup.pyの中身はこんな感じで動くと思う。

'''$ python setup.py build_ext --inplace'''
from distutils.core import setup
from distutils.extension import Extension
from Cython.Distutils import build_ext
import numpy as np
setup(
    cmdclass = {'build_ext': build_ext},
    ext_modules = [Extension("出力ファイルの名前(ex: example)", ["PYXソースの名前(ex: example.pyx)"], language="c", include_dirs=[np.get_include()])]
)

とりあえずコマンドライン上でPYXソースのあるディレクトリ上に移動して、
python setup.py build_ext --inplace
と打てばコンパイルしたファイルが生成される。ここで出力ファイルの名前とPYXソースの名前は合わせておく。例えば、example.pyxという名前のソースを作った場合、出力ファイルの名前にはexampleと書く。
後は、同一ディレクトリにpythonソースを置き、
import example
とかいう風にしてやれば、example.pyxの中身の関数を使うことができる。

コンパイル @64bit

64bitでコンパイルする場合、windwosだとVisual Studioと言うか、 Windows SDK C/C++ compilerが必要になる。
とりあえず、本家に記載のあった通り、Microsoft Windows SDK for Windows 7 and .NET Framework 3.5 SP1辺りをインストールしておく。
とりあえず、C:\Program Files\Microsoft SDKs\Windows\v7.0というフォルダができていれば良い。
次に、
バッチファイルでも作って、

CD /d C:\Program Files\Microsoft SDKs\Windows\v7.0
set DISTUTILS_USE_SDK=1
C:\Windows\System32\cmd.exe /E:ON /V:ON /T:0E /K "C:\Program Files\Microsoft SDKs\Windows\v7.0\Bin\Setenv.cmd" /x64 /release
と書き、実行すると、文字が緑色のコマンドラインが出てくる。
そうしたら、コマンドライン上でPYXソースのあるディレクトリに移動して、
python setup.py build_ext --inplace
と打つと64bitの実行ライブラリが生成される。
Python上での使い方は32bitの場合と変わらない。


2013年2月23日土曜日

EPDによるパッケージのアップデート方法


EPDはパッケージ管理を一括して行なってくれる。
パッケージの管理はコマンドプロント(またはターミナル)上で
enpkg
を使って行う。

アカデミック版(恐らく有償版も)ではアップデートをする前に認証が必要になる。以下の手順で認証すれば良い。

>enpkg enstaller

>enpkg --userpass
Please enter the email address (or username) and password for your
EPD or EPD Free subscription.  If you are not subscribed to EPD,
just press Enter.

Email (or username): メールアドレス@を入力する
Password:パスワードを打ち込む
Wrote configuration file: C:\どっか
You are logged in as メールアドレス (ユーザー名).
Subscription level: EPD Basic or above

認証が正常に終了すると次のコマンドでパッケージの最新版がインストールできる。
>enpkg packagename

任意のバージョンをインストールしたい場合は、パッケージ名の後ろにバージョン番号を入れる。
>enpkg packagename 1.1.1(version number)

バージョン番号は、
>enpkg -i packagename
で調べる。

アップデート可能なパッケージを調べるには、
>enpkg --whats-new
で一覧が表示される。

分からないことがあれば、
>enpkg -h
でコマンドの説明が表示される。

2013年2月22日金曜日

Python,Matplotlibでギリシャ文字出力方法2

概要

matplotlibのギリシャ文字出力方法については以前書いたが、Tex形式で出力する方法('$\mu$'など)とフォントの保有しているギリシャ文字をUnicodeから指定する方法(u'\u03bc'等)がある。
使用するフォントにも依るが、両者でグラフの見た目が結構変わる。
特にArialやそれ系のフォント(Helvetica欲しい……)等、サンセリフ体のものを使う場合、Tセリフ体の文字が出力されるためTex方式では、ギリシャ文字に対してそれ以外の英数字が浮いてしまう。
気にならなければ構わないのだが、個人的にはグラフ全体に同一のフォントを使用したほうが統一感が出て良いと思う。
まあ、Times New Romanとかそれ系のセリフフォント(Times欲しい……)を使うならそれほど違和感はないかもしれないが、これらのフォントにもギリシャ文字が同梱されているわけで、それを使ったほうがやはり統一感が出て美しいのではないかと思う。

学会によってはギリシャ文字はSymbolを使えというところもあるようなので、その場合は仕方ないし、ギリシャ文字はSymbolじゃないとという人もいるのかもしれない。
ただ、グラフのフォントをIllustratorなどを使って差し替える場合、Symbolを使用していると一括変換ができないが、Unicodeを使えば変換先のフォントがコードに対応したフォントを持っていれば問題なく変換できてしまうというメリットがある。

比較

ギリシャ文字をTex形式とUnicode形式で出力したグラフを作成した。英数字のフォントをArialとした場合とTimes New Romanとした場合の結果を以下に示す。

Arial

Arial、ギリシャ文字はUnicode形式

Arial、ギリシャ文字はTex形式

Times New Roman

Times New Roman、ギリシャ文字はUnicode形式

Times New Roman、ギリシャ文字はTex形式

考察

Arial等のサンセリフフォントを利用する場合、ギリシャ文字をTex形式で出力するとセリフ体とサンセリフ体が1つのグラフに混在することになる。また、Tex形式で出力したデータのギリシャ文字のフォントはCmmi10及びCmr10となる。これらは通常Illustratorで編集することができない。そのため、Illustratorで加工する場合、自分で適宜Symbolにするなりしなければならない事になる。(ただし、これらのTexフォントをIllustratorで扱えるようにする方法もあるらしい) matplotlibはグラフの描画機能が優秀であるため、Illustratorを介さずに十分な品質のグラフを直接出力することが可能だが、フォントを埋め込む場合はpdf形式しか出力できない(epsだとフォントがアウトライン化されてしまう。何とかepsにフォントを埋め込む方法はないかは調査中)。そのため、面倒なくTexに埋め込むにはIllustratorでeps形式に出力し直さなければならない。Unicode方式ではそのまま保存できるが、Tex方式ではTexのフォントをIllustratorで扱えるようにしないと、フォント調整に一手間かかることになる。 一方で、Unicode形式の場合、出力したい文字のUnicodeを一々調べねばならず面倒である。また、グラフ中に数式を描きたい場合はやはりTex形式の方が優れているように思う。 どちらの方法も一長一短であるという毒にも薬にもならない戯言が結論になるだろうか。

付録

グラフ作成に使用したデータとPythonコードを以下に示す。なお、Pythonコードはあちこちのホームページやブログを参考にさせていただいているが、どこがどこを参考にしたのか全く把握できないため、特に引用の記載はない。誠に申し訳ない。

データ:


sample.txt
# This is a sample data
0 0 5
0.314159265 2.938926261 4.484011233
0.628318531 4.755282581 0.71019761
0.942477796 4.755282581 0.71019761
1.256637061 2.938926261 4.484011233
1.570796327 6.12574E-16 5
1.884955592 -2.938926261 -1.39384129
2.199114858 -4.755282581 -8.800367553
2.513274123 -4.755282581 -8.800367553
2.827433388 -2.938926261 -1.39384129
3.141592654 -1.22515E-15 5
3.455751919 2.938926261 4.484011233
3.769911184 4.755282581 0.71019761
4.08407045 4.755282581 0.71019761
4.398229715 2.938926261 4.484011233
4.71238898 1.83772E-15 5
5.026548246 -2.938926261 -1.39384129
5.340707511 -4.755282581 -8.800367553
5.654866776 -4.755282581 -8.800367553
5.969026042 -2.938926261 -1.39384129
6.283185307 -2.4503E-15 5

Pythonコード

#コメントは#マークを使うか"""hogehoge"""とする.

#ライブラリをインポート
import numpy as np
import matplotlib.pyplot as plt
import matplotlib as mpl
#import datetime #ここらへんのパッケージは昔必要になったことがあったようななかったような
#import matplotlib.colors as colors
#import matplotlib.finance as finance
#import matplotlib.dates as mdates
#import matplotlib.ticker as mticker
#import matplotlib.mlab as mlab
#import matplotlib.font_manager as font_manager


pi = np.pi # 円周率が必要な場合はnumpy(インポート設定からnp.~とする),このように出力できる
e = np.e # 自然対数とかも出力可能
###############################################################################
#                               Graph Setting                                 #
###############################################################################
###############################################################################
mpl.rcParams['font.family'] = 'Arial' #使用するフォント名
mpl.rcParams['pdf.fonttype'] = 42 #このおまじないでフォントが埋め込まれるようになる
params = {'backend': 'ps', # バックエンド設定
          'axes.labelsize': 8, # 軸ラベルのフォントサイズ
          'text.fontsize': 8, # テキストサイズだろう      
          'legend.fontsize': 8, # 凡例の文字の大きさ
          'xtick.labelsize': 8, # x軸の数値の文字の大きさ
          'ytick.labelsize': 8, # y軸の数値の文字の大きさ
          'text.usetex': False, # 使用するフォントをtex用(Type1)に変更
          'figure.figsize': [10/2.54, 6/2.54]} # 出力画像のサイズ(インチなので2.54で割る)
mpl.rcParams.update(params)

###############################################################################
#                                 Data Input                                  #
###############################################################################
###############################################################################
#データ読み込みにはnumpyのloadtxtを使用すると便利
filename = "sample.txt"
data = np.loadtxt(filename, #ファイルの名前
                  skiprows=1, #この場合ファイルの一行目をスキップする
                  dtype={'names':('data1',
                                  'data2',
                                  'data3'),
                        'formats':('f8',
                                   'f8',
                                   'f8'
                               )})

###############################################################################
#                                 Make Graph                                  #
###############################################################################
###############################################################################
plt.rc('axes', grid=True)#
plt.rc('grid', color='0.8', linestyle='-.', linewidth=0.5)#
bs = plt.figure(facecolor='white')
base_ax = plt.axes([0.15,0.15,0.75,0.75])
ax1 = plt.plot(data['data1'], data['data2'], 'ro-', label='Sample1')#散布図の描画.凡例のラベル設定はここで行う
ax2 = plt.plot(data['data1'], data['data3'], 'bs--', label='Sample2')
lx = plt.xlabel(u'x line [\u03bcm]')#x軸のラベル
ly = plt.ylabel(u'y line [\u03bcm]')#y軸のラベル
tx = plt.xticks(np.arange(0, 2.1*pi, 0.5*pi),#np.arange(0, 2*pi, 0.5*pi)とすると2*piが含まれない
                [u'0',u'0.5\u03C0',u'\u03C0',u'1.5\u03C0',u'2\u03C0']) #xラベルの設定
                #$\pi$表記でTex形式出力も可能
limx = plt.xlim(0, 2*pi) # xの範囲
leg = plt.legend(loc='best', shadow=True, fancybox=True)#凡例出力
leg.get_frame().set_alpha(0.5)#凡例を半透明にする
plt.savefig('sample.pdf')# PDF形式で保存する場合,フォントが埋め込まれる
plt.savefig('sample.eps')# EPS形式で保存する場合,フォントはアウトライン化される
plt.savefig('sample.png', dpi=100)# png形式ではdipの調整ができる.デフォルトでは80だったはず
plt.show()#グラフ出力.最後に持ってくる(savefigがうまく動かなかったりするため)

2013年2月19日火曜日

Matplotlibの凡例設定

昔やったはずなのに忘れていたからメモ。
物忘れが激しくて困る。

import matplotlib.pyplot as plt
plt.legend(loc='best', fancybox=True, shadow=True, ...)
要はmatplotlib.pyplot.legend()関数内で色々設定する。決まった定型文のようなものをコンマで区切って入れていく。
以下、自分が使いそうな関連をまとめておく。

凡例の位置:

基本的には
 loc='~'
と言った感じで作成。本家のページを探したらこんなのがあった。数字を入れてもいいし、loc='best'
のように書いてもいい。
Location StringLocation Code
‘best’0
‘upper right’1
‘upper left’2
‘lower left’3
‘lower right’4
‘right’5
‘center left’6
‘center right’7
‘lower center’8
‘upper center’9
‘center’10
更にbbox_to_anchor=(0.5,0.5)と書くと凡例の位置が指定した箇所からずれる。
どんな風にずれるかはプロットしながら調整すれば良いと思う。

凡例の表示方法:

numpoints=? :
 凡例に表示する点の数を指定する。デフォルトでは2になっているが、点同士を結ばない場合、2つ点が並んで違和感がでるのでよく使うとおもう。
ncol=?:
 凡例の列数。デフォルトでは1。たまに使いたくなる。
mode=“expand”:
 凡例をグラフの横いっぱいに広げるらしい。一度使ったことがある
 fancybox=True:
 凡例の角を丸くする。最初は違和感があったが、なんとなく見映えがいいような気がする
shadow=True:
 凡例に影をつける。これも違和感があったが、かっこいい気がしてきた。
title='~'
 凡例のタイトルを指定する

凡例を半透明にする

関数内でalpha=?としたらエラーが出た。この構文はpyplot.plotやpyplot.xlabelなどに対応しているがpyplot.legend関数には対応していないらしい。
そこで、今のところ、凡例を半透明にするには次のように書いている。
他に方法がないのかはそのうち調べるかも(たぶんしない)
leg = plt.legend('''色々指定する''')
leg.get_frame().set_alpha(0.5)

2013年2月17日日曜日

Matplotlibの高解像度画像保存


概要

PNG形式でグラフを保存する場合解像度はデフォルトで80dpi位になっている。
Texを使う場合はベクターデータとしてPDF形式(フォント埋め込み可)かEPS(フォントのアウトライン化)で保存して適宜加工すれば良いが、MS Wordなどを使う場合はPNGで直接出力したほうが早い。
プリント出力する場合、グラフの解像度を上げたいことがある。
しかし、普通にfigure内でdpiを宣言しても上手く行かなかった。
ちょっと調べれば方法は分かったが、メモとして残しておく。
デフォルトの解像度


高解像度(300dpi)


方法

次のように書けば良い。要はsavefig関数の中でdpiを宣言する。
import matplotlib.pyplot as plt
plt.savefig("sample.png",format = 'png', dpi=300)