forpy を使って Fortran から matplotlib を呼び出す

MacOS 11.6 Big Sur 上で、forpy を使って Fortran から matplotlib を呼び 出し、作図するためのメモです。python は使いたくないし、長年お世話になっ た PGPLOT ももはや弱すぎるし…というところです。

matplotlib/forpy を入れる
  1. homebrew で matplotlib をインストールする。しばらく brew を放置して あった場合は、homebrew 自体を再インストールしてしまうほうがいいよう です。以下を参考に、python3 を入れます。
    MacにPython3をインストールし環境構築【決定版】
    matplotlib は、
    pip3 install matplotlib
    でいけます。

  2. GitHub から forpy をダウンロードします。GitHub の使い方はこちらなど。
    今さら聞けない! GitHubの使い方【超初心者向け】
    git clone https://github.com/ylikx/forpy.git
    とするだけです(自動的 に forpy というディレクトリが作られます)
    GitHub の Forpy のページはこちら。
    Forpy (GitHub)

  3. 以下のサイトを参考に、.mod や .o を作っていきます。
    Fortran からPythonを使いたい!:forpy
    私の環境では以前 Anaconda を入れたため若干ひっかかりました。GitHub の Forpy のページに書いてあるとおり、
    gfortran -c forpy_mod.F90
    gfortran intro_to_forpy.F90 forpy_mod.o -fno-lto `python3-config --ldflags --embed`
    
    で行けました。ただし、--embed は当該ページにはありませんが Python3.8以降 では必要なようです。
    今後は、
    gfortran test.F90 forpy_mod.o `python3-config --ldflags --embed`
    のようにすればコンパイルでき、a.out と実行すればグラフが表示されます(グ ラフのパネルの×を押して終了しないといけないのが面倒です。Ctrl-C では終わっ てくれません)。
基本的な使い方
  1. たとえば python で plt.plot(x, y) のようにして線を引きたい場合は以下のよ うにします。
    type(tuple) :: args
    type(ndarray) :: x_nd, y_nd
    type(dict) :: kwargs
    real, allocatable :: x(:), y(:)
    
    x = ...
    y = ...
    
    ierr = ndarray_create(x_nd, x)
    ierr = ndarray_create(y_nd, y)
    ierr = tuple_create(args, 2)
    ierr = args%setitem(0, x_nd)
    ierr = args%setitem(1, y_nd)
    ierr = dict_create(kwargs)
    ierr = kwargs%setitem("color", 'blue')
    ierr = call_py_noret(plt, 'plot', args, kwargs)
    ierr = call_py_noret(plt, 'show')
    
    配列 x, y は何かプロットしたい数値データの x, y座標が入っています。コー ド中で定義するなり、read文でファイルから読み取ったりします。 これを ndarray に変換します。どうも一つの配列について一行づつ書かないと いけないようなので面倒ですが、まずは普通の配列 x, y を ndarray_create() を使って x_nd, y_nd の ndarray に変換します。

    次にこれらの ndarray を tuple 変数に格納します。これも面倒ですが、まず tuple を要素が2つであると宣言し、ひとつめ(0から始まるのが Fortran 使いとしては嫌ですね…)に x_nd を、二つめに y_nd を setitem を用いて 格納します。これだけあれば最低限の線は引けます。

    色を変えたいなどのオプションについては、dict 変数を利用します。ここでは 例として色だけを示していますが、オプションがわかれば似たようにすればOKで す。まず dict 変数 kwargs を作ります。そして setitem を用いて "color" に 'blue' を定義します(このように dict 変数は対になっています)。

    たとえばさらに線種を変えたい、というときは、上の "color" を定義した行に 続けて、

    ierr = kwargs%setitem("linestyle", '--')
    
    のようにすれば破線になります。追加したいオプションを、このようにどんどん 行を追加することで同じ変数(いまの場合は kwargs)に定義していけばいいよう です。

    ここまでやって、python での

    plt.plot(x_nd, y_nd, color='blue', linestyle='--')
    
    と同じことができるようです。

  2. 散布図の場合は、マーカーを指定しなければなりません。この場合は、 tuple 変数のほうにマーカーの情報を入れます。上の核心の部分が以下の ように修正されます。
    ierr = ndarray_create(x_nd, x)
    ierr = ndarray_create(y_nd, y)
    ierr = tuple_create(args, 3)
    ierr = args%setitem(0, x_nd)
    ierr = args%setitem(1, y_nd)
    ierr = args%setitem(2, 'o')
    ierr = dict_create(kwargs)
    ierr = kwargs%setitem("markersize", 1)
    ierr = kwargs%setitem("markerfacecolor", 'black')
    ierr = kwargs%setitem("markeredgecolor", 'black')
    ierr = call_py_noret(plt, 'plot', args, kwargs)
    
    tuple 変数 args の引数の数を"3"に変更し、3番目の引数として 'o' を設定し ています。
boxplot (箱ひげ図)の描き方
自分の研究で箱ひげ図を描くことはまずないのですが、雑用で作る必要があって (涙)、ネットを調べてもなかなかわからなかったのでメモ。
  1. 変数の宣言等は上を参考にしてください。下の例では、複数の box を横に 並べるプロットの一部ですが、横軸の位置に定量的な意味を持たせたいた め、通常の箱ひげ図のような名義尺度をx軸にするのとは異なるやり方にし ています。下のほうの positions を設定しなければ、通常の箱ひげ図のよ うな名義尺度になります(これは様々なウェブサイトに載っているのですぐ わかると思います)。

    ややこしい点は、boxplot のオプションは上と同様に辞書型変数に格納するので すが、箱や中央値のそれぞれに color='black', lw=2.,... みたいなオプション を定義していかないといけないので、箱のオプションを入れた辞書型変数、ひげ のオプションを入れた辞書型変数、,,, を設定し、その上でそれらの辞書型変数 をまとめた辞書型変数を定義して、boxplot に喰わせる、という手続きが必要な ことです。

    この例では、kwargs2 という変数が boxplot に喰わせる変数で、その中に、 boxprops や whiskerprops などのオプションを定義した変数が入っています。 中ほどで kwargs2b とか kwargs2c とかを定義し、下のほうでそれらを kwargs2 に入れこんでいます。

    このように辞書型変数を入れ子にすれば良いということに気付くまでには結構苦 労しました…。

    ierr = dict_create(kwargs2)                   !boxplotのオプション;辞書型変数の中に別の辞書型変数を入れる
    ierr = kwargs2%setitem("widths",0.05)         ! boxの幅の設定
    ierr = kwargs2%setitem("manage_ticks",'False')! x軸を名義尺度ではなく数値として意味のあるものにしたいため
    
    ierr = ndarray_create(y_nd, [0,100])       ! 外れ値を設定しない(ひげの範囲で全部含むようにする)
    ierr = kwargs2%setitem("whis",y_nd)        ! 外れ値を設定しない
    
    ierr = kwargs2%setitem("showmeans",'True') ! 平均値も表示
    
    color = 'green'
    ierr = kwargs2%setitem("patch_artist",'True') ! 詳細な設定を可能にする
    
    ierr = dict_create(kwargs2b)               ! boxplotのオプションは複雑なので辞書変数を複数定義することが必要
    ierr = kwargs2b%setitem("facecolor",'none')! 箱の内部の色なし
    ierr = kwargs2b%setitem("color",color)     ! 色の設定
    ierr = kwargs2b%setitem("lw",'0.5')        ! 箱の線の色
    
    ierr = dict_create(kwargs2c)               ! ひげ用、ひげの先端用の変数(同じなので一つの変数を使いまわしている)
    ierr = kwargs2c%setitem("color",color)
    ierr = kwargs2c%setitem("lw",'0.5')
    
    ierr = dict_create(kwargs2d)               ! 中央値用の変数
    ierr = kwargs2d%setitem("color",color)
    ierr = kwargs2d%setitem("lw",'2.')
    
    ierr = dict_create(kwargs2e)               ! 平均値用の変数
    ierr = kwargs2e%setitem("marker",'d')
    ierr = kwargs2e%setitem("markeredgecolor",color)
    ierr = kwargs2e%setitem("markerfacecolor",'none')
    
    ierr = kwargs2%setitem("boxprops",kwargs2b)     ! 箱のオプション
    ierr = kwargs2%setitem("whiskerprops",kwargs2c) ! ひげのオプション
    ierr = kwargs2%setitem("capprops",kwargs2c)     ! ひげの先端のオプション
    ierr = kwargs2%setitem("medianprops",kwargs2d)  ! 中央値のオプション
    ierr = kwargs2%setitem("meanprops", kwargs2e)   ! 平均値のオプション
    
    ierr = ndarray_create(x_nd, [real(i)])  ! x軸の数値の設定; ここではx軸上で変数iの数値の位置に箱が描かれるようにしている
    ierr = ndarray_create(y_nd, y)          ! 数値データ
    ierr = kwargs2%setitem("positions",x_nd)! x軸を名義尺度ではなく数値で位置を決めるようにする
    ierr = tuple_create(arg2, 1)
    ierr = arg2%setitem(0, y_nd)            ! tuple変数 arg2 に y_nd を入れる
    ierr = call_py_noret(plt, 'boxplot', arg2, kwargs2) ! arg2 に入っている配列の分布に従って、kwargs2 のオプションで箱ひげ図を描く
    
グラフの出力について
  1. 画面に出力する際には、他のサイトにもある通り、以下のようにすればOKで す。
    ierr = call_py_noret(plt, 'show')
    
    これはいちいちパネルのバッテンを押して消去しないといけないので面倒ですよ ね…。保存する際には下のフロッピーディスクのアイコンをクリックしてやるこ とになります。

  2. 直接ファイルに出力する場合は以下のようにします。
    ierr = call_py_noret(plt, 'savefig', 'figure.pdf')
    
    もちろんファイル名を変数にすることも可能です(スクリプトを回してファイル を複数出力したいときはもちろんこうした方が便利ですよね)。
    ierr = tuple_create(arg_outfile,1)
    ierr = arg_outfile%setitem(0, "figure.pdf")
    ierr = call_py_noret(plt, 'savefig', arg_outfile)
    
  3. どちらのやり方にしても、拡張子を png にしたら png file として保存さ れましたので、ファイル形式は拡張子で自動的に判断してくれるようです。
参考になりそうなサイト

なんでこんな面倒な思いまでして Fortran でやりたいか、って…? それは、 Fortran は生き方だからですよ、生き方!!
戻る
Last Modified:
since 24 April 2003
astro antenna