nano_exit

基礎的なことこそ、簡単な例が必要だと思うのです。

ラグランジュの未定乗数法と等高線プロット

ラグランジュの未定乗数法を直感的に非常に分かりやすく描いてあるサイト様を発見。
ラグランジュの未定乗数法の解説と直感的な証明

せっかくなので、実際に等高線プロット使って図示してみた。
f:id:koideforest:20171116003805p:plain

図で考えているのは、

 f(x,y) = ( x - 3 )^2 + y^2
 g(x,y) = x^2 + y^2 - 4

のような、何かポテンシャル的な場が f(x,y)で与えられていて、軌道 g(x,y)=0の上で f(x,y)を最大もしくは最小にする (x,y)は何でしょう?という問題。
数学の問題っぽく言えば、束縛条件 g(x,y) = 0の下で f(x,y)を最大(最小)にする (x,y)を求める問題。
文章を読むと、最大最小化する f(x,y)が主役な感じがするが、図を見るとわかる様に、(当たり前だが) g(x,y)=0の軌道の中の点で良さそうなのを探す方がイメージに近い。
ちなみに、二次元の今の問題において得られる最大(最小)の座標群がなぜ零次元(つまり点)なのかというと g(x,y)=0の時点で x yは独立ではなく、既に一次元の問題になっているところが味噌である。そこに f(x,y)を介して条件式が加わると、零次元になるというわけである。三次元でやると得られる座標群は線(つまり軌道・一次元)になる。
単純に変数 x, yそれぞれに対して微分するから、方程式が未定乗数を含めた変数の数だけ出てきて、最終的に値がカッチリ決まるというもの。未定乗数の微分では g(x,y) = 0がそのまま出てくる。
この意味で、主役はなんとなく g(x,y)=0のような気がしてならない。

図を見れば明らかに
最大: ( -2, 0 )
最小: ( 2, 0 )
であるが、ラグランジュの未定乗数法を使うと、

 L = ( x - 3 )^2 + y^2 - \lambda [ x^2 + y^2 - 4 ]
 \frac{ \partial L }{ \partial x } = 2 ( 1 - \lambda ) x - 6 = 0
 \frac{ \partial L }{ \partial y } = 2 ( 1 - \lambda ) y = 0
 \frac{ \partial L }{ \partial \lambda } = - ( x^2 + y^2 - 4 )= 0

ここから、

 x  = - 2 / \lambda
 ( y, \lambda ) = ( 0, a ) \; or \; ( a, 1 ) \; for \; \forall a \in \mathbb{R}
 x^2 + y^2 = 4

三つ目のは元々の束縛条件まんまなので、当たり前は当たり前。
 y = 0を入れれば、 x = \pm 2が求まって、図から類推した答えと合っている。
 \lambda = 1 x = -2と等価であり、その時も y=0となるから、全ての答えが得られている。

以下、ソースの中身。

import numpy as np
import matplotlib.pyplot as pet

N = 100
x = y = np.linspace( -5, 5, N )
X, Y = np.meshgrid( x, y )
F = ( X - 3 )**2 + Y**2
G = X**2 + Y**2 - 4

plt.text( 0, 0, 'g(x,y)=0' )
plt.text( -2.5, 3.5, 'f(x,y)', color='#0068b7')

plt.contour( X, Y, G, [0] )
plt.gray()
plt.pcolor( X, Y, F )
plt.cool()
plt.gca().set_aspect( 'equal', adjustable='box' )
plt.show()

以下のサイト様にお世話になりました。

等高線プロット
matplotlibカラープロット - 週末はいつも晴れ

陰関数
Pythonのmatplotlibでグラフを簡単に表示する方法part2 | 東大卒ニートと普通のサラリーマンのお金の稼ぎ方

matplotlibの等高線プロットでCDのジャケットを作る

等高線プロットを練習してたら何かカッコイイのが出来たので、CDジャケット風にしてみた。
f:id:koideforest:20171115232319p:plain

import numpy as np
import matplotlib.pyplot as plt

N = 5

x = y = np.arange( N )
X, Y = np.meshgrid( x, y )

z = np.random.rand( N * N )
Z = z.reshape( N, N )

plt.figure( figsize = ( 4.72, 4.72 ) )
plt.tick_params( left='off', right='off', bottom='off', top='off' \
                 labelleft='off', labelright='off', labelbottom='off', labeltop='off' )
plt.contour( X, Y, Z )
plt.viridis()

plt.show()

CDジャケットのサイズは約4.72平方インチらしい。
敢えて小さめにして枠を残して印刷してもカッコイイかも。

python: matplotlib.pyplot: インスタンスを使わない一通りのまとめ

いつも忘れるので、個人的なメモ

参考にしたサイトの皆様
グラフの体裁を整える — matplotlib 1.0 documentation
matplotlibでの凡例(ラベル)の表示場所・形式を変更する - ゆるふわめも
matplotlibで忘れがちな文法まとめ - Qiita
【Python】matplotlibによるグラフ描画時のColormapのカスタマイズ - Qiita
matplotlibの凡例を半透明に - Qiita
matplotlib入門 - りんごがでている
[Python] matplotlib: 論文用に図の体裁を整える - Qiita
【Python】matplotlibのフォントを変えた際にクリアする必要のあるキャッシュ - 俺言語。
matplotlib と Seaborn の軸の日本語設定 - Qiita
api example code: font_family_rc.py — Matplotlib 2.0.2 documentation
matplotlibでグラフの文字サイズを大きくする - Qiita
自分用メモ: matplotlibを使ったグラフの調整 - Qiita
Matplotlib 超入門(3)文字の大きさ,グリッド幅
matplotlibで忘れがちな文法まとめ - Qiita
matplotlibで色付きのベクトルを作成 - ゴルゾンの日記
matplotlib/plot - memoring
matplotlibで、系列の凡例を枠外に表示する - Symfoware
matplotlibでrectangleを描く | mwSoft
matplotlib.axes.Axes.tick_params — Matplotlib 2.1.0.post851+g2fd479a documentation
Customizing matplotlib — Matplotlib 2.0.2 documentation

import matplotlib as mpl

# change font
mpl.get_cachedir()
mpl.rcParams[ "font.family" ] = [ "Arial" ]

# change font size
mpl.rcParams[ "font.size" ] = 18

# change the line width of the frame/
mpl.rcParams[ "axes.linewidth" ] = 2.0

# adjust the positon of ticks
mpl.rcParams[ "xtick.major.pad" ] = 3.5
mpl.rcParams[ "ytick.major.pad" ] = 3.5


import numpy as np
import matplotlib.pyplot as plt
import matplotlib.cm as cm

# set figure size
plt.figure( figsize = ( 8, 6 ) )

# plot data
for id, data in enumerate( data_list ):
  x, y = data[0], data[1]
  #cy = "#00a0e9"
  #ma = "#e4007f"
  #gr = "#009944"
  #or = "#f39800"
  #bl = "#0068b7"
  #clolar_list = [ cy, ma, gr, or, bl ]
  #color = cm.tab10( id  )
  #color = cm.viridis( id / len( data_list )  )
  color = cm.cool( id / len( data_list )  )
  plt.plot( x, y, lw = 2, c = color, label = label_list[ id ] )

# add comments
plt.text( xt, yt, "comment", fontsize = 10 )

# add arrows
plt.quiver( x_position, y_position, \
            x_component, y_component, \
            angles = "xy", scale_units = "xy" )

# add rectangular
plt.gca().add_patch( plt.rectangle( [ x_left, y_bottom ], width, height, fill=False ) )

# add horizontal and/or vertical lines
# when you use hlines or vlines, added lines are behind the plot.
#plt.hlines( y_list, xmin, xmax, linestyle = "dashed" )
#plt.vlines( x_list, ymin, ymax, linestyle = "dashed" )
plt.plot( [ xmin, xmax ], [ y, y ], linestyle = "dashed"  )
plt.plot( [ x, x ], [ ymin, ymax ], linestyle = "dashed"  )

# change representation of labels of ticks
plt.xticks( [ x1, x2, x3 ], [ 'X1', 'X2', 'X3' ] )
plt.yticks( np.linspace( ymin, ymax, dividing_number ) )

# change parameters of ticks
plt.tick_params( direction = 'out', width = 1, length = 8, labelsize = 12)

# set each minimum and maximum
plt.xlim( xmin, xmax )
plt.ylim( ymin, ymax )

# set labels for each axis
plt.xlabel( 'horizontal', fontsize = 14 )
plt.ylabel( 'vertical', fontsize = 14 )

# allow to appear legends
#plt.legend( loc = 'upper left', bbox_to_anchor = ( 1.05, 1.00 ),  frameon = False, fontsize = 10 )
#plt.subplots_adjust( right = 0.7 )
plt.legend( loc = 'best', frameon = False, fontsize = 10 )

# avoid lacking of characters of the labels
plt.tight_layout()

# save figure
plt.savefig( 'filename.eps', format='eps', transparent=True, dpi=600 )

ちなみに調べたところ、tickという単語そのものには目盛りの意味は無く、「瞬間」という意味らしい。
tick markで目盛りという意味になるらしいので、そこから来ていると思われる。

カラーマップの実際の色は下記のサイトで見れる。
Choosing Colormaps — Matplotlib 2.0.2 documentation

色の選択には下記のサイトを参照
統計グラフの色

f:id:koideforest:20171115080506p:plain
左が通常、右がphotoshop仕様

でも matplotlib が version 2 になってからdefaultの色の設定でも十分見易くなったと思う。color map の viridis は default で設定されているが、全然悪くない。
今度論文書くときは安直だがそれで図を作ってみることにする。

Seabornをimportするだけでは使えない件について(Mac OSX)

Seabornをpip経由でインストールしたが、importしても変わった感じがせず、決定的だったのがseaborn.pairplotで図が出てこないというエラー。

下記のサイトの下の方に解決法が。
簡単に美しいグラフ描画ができるPythonライブラリSeaborn入門 - MyEnigma

どうやらmatplotlibが古いからupdate (upgrade?) しましょうということでした。

sudo pip install matplotlib -U

これで一応 seaborn.pairplot で図が出るようになりました。
ただし、どうもデフォルトの設定が seaborn.set_style( "white" ) かつ棒グラフの縁の黒線がない状態なので、いろんなサイト様に載っている図と同じにするには多少弄らないといけない模様。

とりあえず、これで色々弄ってみる気にはなった。

MacPortsを使ってMacBook AirにPOV-Rayをインストールしてみた

第一原理計算に必要な原子座標を作るのに便利なpython module "ASE"でPOV-Rayが使われていたので、MacPorts経由でインストールしてみた。

sudo port -install povray

ここまでで、特にエラーメッセージは出ず。
気になるメッセージとしては、pythonのバージョンを以下のコマンドで変更しとくように指示するような文が最後に出て来た(若干コマンドはうろ覚えです)。

sudo port select python python2.7
sudo port select python2 python2.7

python3でなくてすみません。
とりあえず言われた通りに変更すると、numpyやらなんやらかんやらが全くimport出来なくなって焦った。
原因は、もともとはpython2.7-appleというバージョンを使っていたのに、違うバージョンにした結果、モジュールのパスが変わってしまったということでした。
どうやってpythonを入れたかもはや覚えていませんが、anacondaで突っ込む前に自分で手で入れたような気もしてきて、その名残が2.7なのだろうかとボンヤリ思っています。
とりあえず、以下のコマンドで元に戻しました。

sudo port select python python2.7-apple
sudo port select python2 python2.7-apple

この状態で、ASEのモジュールをpythonで読んでサンプルコードを動かすと、ちゃんとPOV-Rayを経由して画像が生成されます。

f:id:koideforest:20171111015504p:plain
サンプルコード:
Using the spacegroup subpackage — ASE documentation


povrayのPATHも自動で通っているので、aseを経由して出て来た*.povを引数に動かしてみても画像が生成されました。

povray *.pov

なかなかツヤツヤしていてカッコイイ図が出来るので、POV-Rayそのものをもうちょっと知りたいと思い、以下のサイトを閲覧。
POV-Ray 初心者向けチュートリアル & Tips

なるほど。自分で色々打ち込んで図を作れると。これはワクテカ
GUIが出てくるらしいので、とりあえず引数無しで起動させてみる。

povray

すると返って来たエラーは以下の通り(usernameは適宜置き換えて読んでください)。

poverty: cannot open the user configuration file /Users/username/.povray/3.7/povray.conf: No such file or directory

Problem with option setting
poverty
No input file provided

無いと怒られたファイルのPATHを辿ると、本当にそんなPATHは存在しないので、どこにpovrayの元ファイルが入っているのか"find"で全ファイル検索をかけたら、etcのディレクトリに何かいました。

/opt/local/etc/povray/3.7/

この中にはpovray.confとpovray.iniが含まれていて、中身を読むと、どうやらpovrayはetcのconfファイルとhomeにあるはずのconfファイル二つを読み行くらしい。
povray.confに当たりをつけて "man povray" でマニュアルを読むと確かにそんなことが書いてあった(最初に読むべきでした。すみません。)。

自分でファイルを作ってconfファイルとiniファイルをそこにコピーして、もう一度起動。

Problem with option setting
poverty
No input file provided

結局、最初のエラーは重要ではなかったらしい。
MacPorts経由で入れると、GUIはついてこない様子。
というか、GUI付きのはunofficialなものしかない。
POV-Ray 3.7.0 final for mac
なので、MacPortsで入れるとGUI無しのコマンドラインで頑張る方がインストールされるという流れになっていました。

しかし、MacBook Airの容量が割と厳しくなって来ているのでもう一個インストールするのも気が引けるし、かといってASEは問題なく動いているのにuninstallするのもなぁ、、、
とりあえずこれで様子を見てたいと思います。
GUIが無いとやっぱり無理だった入れてみます。

ではでは

PyQt: トップダウンで作るラーメンタイマー

下記サイト様を参考にラーメンタイマーを作成する。
PyQtではじめるGUIプログラミング
非常に分かり易く説明されていて、個人的にかなり気に入っている。
基本的にはボトムアップ(端っこの部品から作っていく)で記述して行くので、トップダウンを意識して話を作っても良いかと思い、やってみた。細かい話は参照サイト様をお願いしたい。

使うmoduleの宣言

import sys
import PyQt5.QtWidgets as QWs

ラーメンタイマーのウィンドウを表示させる部分。

def main():
  app = QWs.QApplication( sys.argv )

  # Control outside looking
  panel = QWs.QWidget()

  mw = QWs.QMainWindow()
  mw.setWindowTitle( "Ramen Timer" )
  mw.setCentralWidget( panel )
  mw.show()
  app.exec_()

if __name__ == "__main__":
  main()

QWidgetのインスタンス(ここではpanelという名前)をmw.setCentralWidgetに食わせているため、panelを作り込んで行くことで、どんどんラーメンタイマーになっていく。
一応、ここまででもスクリプトは動くが、panelを何も弄っていないため、Ramen Timerと書かれたWindowだけが出てくる。
でも一応動くから安心感はあると思う。最終段階まで動くか動かないかわからないのは精神衛生上良くないので。

とりあえずスタートボタンを追加する。
ボタンを制御するクラスを記述。

class ButtonBoxWidget( QWs.QWidget ):
  def __init__( self, parent=None ):
    QWs.QWidget.__init__( self, parent=None ):
    self.setup_ui()

  def setup_ui( self ):
    self.start_button = QWs.QPushButton( "START", parent=self )

    layout = QWs.QGridLayout()
    layout.addWidget( self.start_button, 0, 0 )

    self.setLayout( layout )

__init__の中身はまだ勉強不足で理解出来ていない。
setup_uiはソースを整理して見易くするために導入されている。そのため__init__内にベタ打ちしても動く。可読性を上げる関数の使い方は参考になる。

panel周辺は以下の様に変更。

  panel = QWs.QWidget()
  panel_layout = QWs.QVBoxLayout()

  button_box_widget = ButtonBoxWidget( parent=panel )
  panel_layout.addWidget( button_box_widget )

  panel.setLayout( panel_layout )
  • アイテム配置用のインスタンス(panel_layout; 今回は後で"箱"を縦に並べて配置するのでQVBoxLayoutを使用)を用意して、そこに先ほど作ったButtonBoxWidgetのインスタンスをLayoutのクラス関数addWidgetで乗せる。
  • ButtonBoxWidgetはparent=panelと繋げておく(この辺の仕組みはまだ理解できていない)。
  • panel_layoutを弄り終わったら、setLayoutでpanelにちゃんと乗せておく。

この時点でスクリプトを起動すると、"START"と書かれたボタンだけが出てくる。他のボタン(例えば"STOP")はstart_buttonと同様にButtonBoxWidgetに記述することで作れる。

したがって、あとは

  1. classを作成・編集してアイテムを作る・作り込む
  2. 作ったclassのインスタンスをpanelに繋げて記述
  3. panel_layoutでインスタンスを突っ込んで、panelに乗せる

を繰り返す。
アイテムを一つ作るごとにスクリプトを動かして確認出来るため、理解を深め易いと思う。

GUIの外見はこれで割と直感的に作れると思う。
あとは実際の動く部分だが、それぞれの部品の動きは各クラスなりpython関数で記述出来ると思う(カウントダウンで数字を減らしていくところは、PyQtのconnectの概念が必要ではあるが)。
それぞれの部品を繋げるのは、Widgetのクラス関数connectで信号を関数に受け渡す(もしくは関数を呼ぶ)ことで実現する。

# bellow mw.show()
button_box_widget.start_button.clicked.connect( countdown_widget.start_countdown )
# above app_exec_()

カウントダウンのクラスについてここでは記述していないが、カウントダウンを始めるクラス関数を自分で作っておき、その関数start_countdownに信号を渡すようにしている。カウントダウンのクラスの詳細は参照サイトを見て頂きたい。
clickedはクラスQPushButtonの持つ関数(クラス?)で、ボタンをクリックした時にどんなアクションをするかを記述出来る。
つまり、
"START"ボタンをクリックするとstart_countdownが呼ばれる
と言う流れである。

一つ一つすぐに確認しながら出来るのはスクリプト言語の強みだと思うので、ちょっとずつ出来上がっていくはやっぱり楽しい。

PyQt: ボタンの解決の順番

下記サイト様を参考に練習中
PyQtではじめるGUIプログラミング

PyQt5 & python2.7 では、QApplication等の主要なmoduleはQtGuiではなく、QtWidgetsに入っている。
ややこしいことに、QWidgetというmoduleも同時に存在している。

#!/usr/bin/env python

import PyQt5.QtCore as QC
import PyQt5.QtGui as QG
import PyGt5.QtWidegets as QWs

def main():
  #app = QG.QApplication( sys.argv )
  #main_window = QG.QMainWindow()
  #hello_button = QG.QPushButton( "Hello World" )
  app = QWs.QApplication( sys.argv )
  main_window = QWs.QMainWindow()
  hello_button = QWs.QPushButton( "Hello World" )
  app.exec_()

if __name__ == "__main__":
  main()

次を足す。

# above the function main()
def on_click():
  print( "Hello World" )

def on_press():
  print( "Have a nice day" )

def on_release():
  print( "Good Night" )

# just bellow "hello_button"
hello_button.clicked.connect( on_click )
hello_button.pressed.connect( on_press )
hello_button.released.connect( on_release )

実行してボタンを押してみると、

press > release > click

の順で解決されることが分かる。

つまり、

press: ボタンを押した瞬間
release: ボタンを離した瞬間
click: press&releaseが解決された後

という感じ