Plotting Module
SymPy の Plotting Module をインポートすると、Matplotlib をバックエンドで使って 2Dグラフや 3Dグラフを表示させることができます。数値データを使わずに、SymPy のシンボル(記号)で表された数式をそのままグラフとして視覚化できるのが特徴です。
Matplotlib はバックエンドで動くので、特にFigure の設定を直接変更するのでなければ、sympy をインポートするだけでグラフを描画することができます。2Dグラフを描くためには sympy.plotting.plot() を呼び出す必要がありますが、簡略して sympy.plot() と記述することもできます。例として ガウス関数
# SYMPY_PLOTTING_PLOT
# In[1]
import sympy
# 記号xを定義
sympy.var('x')
# y = exp(x)
y = sympy.exp(-x**2)
# グラフ設定
# x軸の範囲:[-5, 5], y軸ラベル:'y'
p1 = sympy.plot(y, (x, -5, 5), ylabel="y")
# In[2]
# 円周率
pi = sympy.pi
# f=sinx, g=cosx
f = sympy.sin(x)
g = sympy.cos(x)
# sinxとcosxを重ねて描画
p = sympy.plot(f, g, (x, -10, 10), legend=True, show=False)
# gの線の色をredに変更
p[1].line_color = "red"
# Figureを表示
p.show()
sympy.plotting.plot_parametric()
plot_parametric() を使うと 媒介変数 (パラメータ) 表示された関数のグラフを描くことができます。以下のサンプルコードでは媒介変数方程式
を使って円を描きます。Figureサイズを変更するので、matplotlib.pyplot 本体もインポートしておきます。
# SYMPY_PLOTTING_PLOT_PARAMETRIC
# In[1]
import matplotlib.pyplot as plt
# Figureサイズを6×6に設定
plt.rcParams['figure.figsize'] = 6, 6
# sympyから必要な関数をインポート
from sympy import cos, sin, var
# sympy.plottingから媒介変数プロット関数をインポート
from sympy.plotting import plot_parametric
# 記号を定義
var('t')
# 円の媒介変数(パラメータ)方程式
x = cos(t)
y = sin(t)
# tを-5から5まで動かして円を描画
p = plot_parametric(x, y, (t, -5, 5))
sympy.plotting.plot3d()
plot3d() は 3次元データを視覚化するための関数です。
サンプルコードは楕円放物面
# SYMPY_PLOTTING_PLOT_3D
# In[1]
from IPython.display import clear_output
# Google Colab では上の一行を消して以下のコードを記述
# from google.colab import output
import matplotlib.pyplot as plt
from sympy import var
from sympy.plotting import plot3d
var('x y')
# 楕円放物面の方程式
z = x**2 + y**2
# 楕円放物面を描画
p = plot3d(z, (x, -5, 5), (y, -5, 5))
# pの出力をキャンセル
clear_output("True")
# Google Colab では上の一行を消して以下のコードを記述
# output.clear()
# バックエンドのFigureオブジェクトを取得
q = p._backend.fig
# Axes3DSubplotオブジェクトを取得してアスペクト比を設定
q.axes[0].set_box_aspect((1, 1, 1))
display(q)
2023 年 3 月時点の Jupyter Notebook や Google Colab の環境では、SymPy の三次元グラフの 3 軸のアスペクト比が固定されていて、うまく調整されない (グラフが z 軸方向に潰れてしまう) 問題が生じています。そのため、上のコードでは sympy.plotting のバックエンドで動く Matplotlib の Axes3DSubplot オブジェクトを取得してアスペクト比を設定しています。
sympy.plotting.plot3d_parametric_surface()
plot3d_parametric_surface() はパラメータで表された 曲面の方程式 を可視化する関数です。下のサンプルコードではパラメータ方程式
を使って球面を表示させます。
# SYMPY_PLOTTING_PLOT3D_PARAMETRIC_SURFACE
# In[1]
from IPython.display import clear_output
# Google Colab では上の一行を消して以下のコードを記述
# from google.colab import output
import matplotlib.pyplot as plt
from sympy import var, cos, sin
from sympy.plotting import plot3d_parametric_surface as pps
# 記号u,vを定義
var('u v')
# 球面方程式のパラメータ表示
x = sin(u) * cos(v)
y = sin(u) * sin(v)
z = cos(u)
# 球面を描画
p = pps(x, y, z, (u, 0, 8), (v, 0, 8))
# pの出力をキャンセル
clear_output("True")
# Google Colab では上の一行を消して以下のコードを記述
# output.clear()
# バックエンドのFigureオブジェクトを取得
q = p._backend.fig
# Axes3DSubplotオブジェクトを取得してアスペクト比を設定
q.axes[0].set_box_aspect((1, 1, 1))
display(q)
コメント
PLOTTING_MODULE_03, 04 プログラムの 3d プロットでは、記事の実行結果と比べるとだいぶ z 軸方向が つぶれてしまいます。rcParams で figsize を変えてみても効果なく、オプション引数の aspect では ‘auto’ しかサポートされていないようです。もう少し z 軸方向を伸ばす方法はないものでしょうか。
SymPy の Plotting Module は、Matplotlib をバックエンドに動く簡易インターフェースなので、詳細な設定をするためには、_backend 属性でバックエンドにはたらきかける必要があります。以下のコードを試してみてください。
◆◆◆◆◆
# In[1]
import matplotlib.pyplot as plt
from sympy import var
from sympy.plotting import plot3d
var(‘x y’)
z = x**2 + y**2
p = plot3d(z, (x, -5, 5), (y, -5, 5))
# In[2]
# pのバックエンドにあるFigureオブジェクトを取得
q = p._backend.fig
# アスペクト比を1:1:1.5に設定
q.axes[0].set_box_aspect((1, 1, 1.5))
display(q)
◆◆◆◆◆
ご回答ありがとうございました。PLOTTING_MODULE_04に、
q = p._backend.fig
q.axes[0].set_box_aspect((1, 1, 1))
display(q)
の 3 行を加えて、見事な球体が表示されたときは感動モノでした。でも、何とか
p = pps(x, y, z, (u, 0, 8), (v, 0, 8))
が表示されないようにできないかと思い、オプション引数で show=False を追加してみましたが以下のエラーが発生しました。
AttributeError: ‘Plot’ object has no attribute ‘_backend’
そこで、Google Colab の output.clear 命令を使ってみたら消すことができました。追加したのは下記の 2 行です。
from google.colab import output
p = pps(x, y, z, (u, 0, 8), (v, 0, 8)) の直後に、
output.clear()
あまりきれいではありませんが、pが表示された後にその出力を強制的に消して、qを表示させています。
なるほど! google.colab の関数を使うとは、上手いことやりましたね。これは思いつきませんでした。後で記事のほうにも加筆しておこうと思います。貴重な情報をありがとうございます。m(_ _)m
output.clear には ( ) が必要ですので、修正をお願いいたします。
output.clear → output.clear( ) (2ヶ所)
申し訳ないです。
修正しておきました。m(_ _)m