tan(x) などの不連続点をつなげないグラフを描く

 

初等関数のグラフを描く際,$y=\tan x$ や $y=1/x$ などの不連続点を無造作に線でつなぐケースがあったので,不連続点を無造作につなげないようなグラフの描き方をまとめてみた。

gnuplot 編

$y = \tan x$ のグラフ

$y \rightarrow +\infty$ から $y \rightarrow -\infty$ に不連続に変化する箇所があっても,gnuplot では特に問題なく plot してくれる。

In [1]:
# なめらかな曲線のために
set samples 200
# x軸 y軸を描く
set zeroaxis
# グリッド(格子)を描く
set grid
#
set xtics ("-2π" -2*pi, "-3π/2" -1.5*pi, "-π" -pi, "-π/2" -0.5*pi, "0" 0, \
            "2π"  2*pi,  "3π/2"  1.5*pi,  "π"  pi,  "π/2"  0.5*pi)
set xlabel "x"
set ylabel "tan(x)"
set title "gnuplot で tan(x)"
plot [-2*pi:2*pi][-10:10] tan(x) lc 'blue' lw 2 notitle

$y = x^{-1}$ のグラフ

$x = 0$ で $ y \rightarrow -\infty$ から $y \rightarrow +\infty$ に不連続に変化するが,gnuplot では特に問題なく plot してくれる。

In [2]:
reset
# なめらかな曲線のために
set samples 200
# x軸 y軸を描く
set zeroaxis
# グリッド(格子)を描く
set grid
#
set xtics 1
set xlabel "x"
set ylabel "1/x"

set title "gnuplot で 1/x"
plot [-5:5][-10:10] x**(-1) lc 'red' lw 2 notitle

Maxima 編

plot2d() で $ y = \tan x$ のグラフ

特に問題なくグラフを描いてくれる。

In [1]:
plot2d(
  tan(x), [x, -2*%pi, 2*%pi], 
  [style, [lines, 2, blue]],
  [y, -10, 10], grid2d, 
  [title, "Maxima plot2d() で tan(x)"], 
  [xtics, %pi/2], [gnuplot_preamble, "set format x '%3.1P π';"]
)$

draw2d() で $y = \tan x$ のグラフ

特に問題なくグラフを描いてくれる。

In [2]:
draw2d(
  line_width = 2, color = "blue",
  explicit(tan(x), x, -2*%pi, 2*%pi),
  xrange = [-2*%pi, 2*%pi], yrange = [-10, 10], 
  xaxis = true, yaxis = true, 
  title = "Maxima draw2d() で tan(x)",
  xlabel = "x", ylabel = "tan(x)",
  grid = true, 
  xtics = %pi/2, user_preamble = "set format x '%3.1P π'"
)$

plot2d() で $y = x^{-1}$ のグラフ

ちょっと警告が出るが,まぁ問題なくグラフを描いてくれる。

In [3]:
plot2d(
  1/x, [x, -5, 5], [style, [lines, 2, red]],
  [y, -10, 10], grid2d, [title, "Maxima plot2d() で 1/x"], 
  [xtics, 1]
)$
plot2d: expression evaluates to non-numeric value somewhere in plotting range.

draw2d() で $y = x^{-1}$ のグラフ

特に問題なくグラフを描いてくれる。

In [4]:
draw2d(
  line_width = 2, color = "red",
  explicit(1/x, x, -5, 5),
  xrange = [-5, 5], yrange = [-10, 10], 
  xaxis = true, yaxis = true, 
  title = "Maxima draw2d() で 1/x",
  xlabel = "x", ylabel = "1/x",
  grid = true, xtics = 1
)$

Matplotlib 編

モジュールの import

In [1]:
import matplotlib.pyplot as plt
import numpy as np

# 以下はグラフを SVG で Notebook にインライン表示させる設定
%config InlineBackend.figure_formats = ['svg']

# グラフのサイズ,mathtext のフォント設定
plt.rcParams["figure.figsize"] = (6, 4)
plt.rcParams['mathtext.fontset'] = 'cm'

$y = \tan x$ のグラフ

普通に $y = \tan x$ を plt.plot() すると,以下のグラフのように,$y \rightarrow \infty$ から $y \rightarrow -\infty$ となる不連続なところもつないでグラフにしてしまう。

In [2]:
x = np.linspace(-2*np.pi, 2*np.pi, 1000)
plt.plot(x, np.tan(x), color = 'blue')

# 横軸縦軸の表示範囲
plt.xlim(-2*np.pi, 2*np.pi)
plt.ylim(-10, 10);
np.nan を使った対処法とオプション設定例

不連続な箇所は無理に線でつながずにグラフにする例。たとえば,y の絶対値がある程度以上の大きい値なら,その点はグラフに描かないように「欠損値」np.nan (Not A Number) に置き換える。

In [3]:
x = np.linspace(-2*np.pi, 2*np.pi, 1000)
y = np.tan(x)
# y の絶対値がある程度以上なら「欠損値」np.nan とする例
y[abs(y) > 100] = np.nan

plt.plot(x, y, color = 'blue')

# 横軸縦軸の表示範囲
plt.xlim(-2*np.pi, 2*np.pi)
plt.ylim(-10, 10)
# グリッド(格子線)
plt.grid(linestyle='dotted')

plt.xlabel('$x$', fontsize=12)
plt.ylabel(r'$\tan x$', fontsize=12)
plt.title('Matplotlib で $\\tan x$', fontsize=14)

plt.xticks([-2*np.pi,-1.5*np.pi,-np.pi,-0.5*np.pi, 0,
            2*np.pi,  1.5*np.pi, np.pi, 0.5*np.pi], 
           ['$-2\pi$','$-3\pi/2$','$-\pi$','$-\pi/2$','$0$',
            '$2\pi$',  '$3\pi/2$', '$\pi$', '$\pi/2$'], fontsize=12)
# x軸 y軸
plt.axhline(0, color='black', dashes=(5, 5), linewidth=0.5)
plt.axvline(0, color='black', dashes=(5, 5), linewidth=0.5);

$y = x^{-1}$ のグラフ

$y = x^{-1}$ を普通に plt.plot() すると,$\displaystyle \lim_{x\rightarrow -0} \frac{1}{x} \rightarrow -\infty$ と $\displaystyle \lim_{x\rightarrow +0} \frac{1}{x} \rightarrow +\infty$ の不連続点をつないでグラフにしてしまう。

In [4]:
x = np.linspace(-5, 5, 1000)
plt.plot(x, x**(-1), color = 'red')

# 横軸縦軸の表示範囲
plt.xlim(-5, 5)
plt.ylim(-10, 10);
np.nan を使った対処法とオプション設定例

不連続な箇所は無理に線でつながずにグラフにする例。例えば,$y = x^{-1}$ は $x=0$ 以外では単調減少関数なので,$y$ の値が増加するようなら,その点はグラフに描かないように「欠損値」np.nan に置き換える。

In [5]:
x = np.linspace(-5, 5, 1000)
y = x**(-1)

# y が増加したら「欠損値」np.nan とする例
y[1:][np.diff(y) > 0] = np.nan

plt.plot(x, y, color = 'red')

# 横軸縦軸の表示範囲
plt.xlim(-5, 5)
plt.ylim(-10, 10)
# x の目盛を 1 刻みに。linspace を使う例。
plt.xticks(np.linspace(-5, 5, 11))
# グリッド(格子線)
plt.grid(linestyle='dotted')

plt.xlabel('$x$', fontsize=12)
plt.ylabel(r'$1/x$', fontsize=12)
plt.title('Matplotlib で $1/x$', fontsize=14)

# x軸 y軸
plt.axhline(0, color='black', dashes=(5, 5), linewidth=0.5)
plt.axvline(0, color='black', dashes=(5, 5), linewidth=0.5);

補足説明:np.nan に置き換える方法

ある条件を満たす配列の要素の値を np.nan に置き換える方法の補足説明。

以下のように,6個の要素をもつ配列 y を考える。

In [6]:
y = np.array([1., 2., 5., -30., -1., 3.])

np.diff() は各要素間の差を表す階差数列を生成する。たとえば y の差分は…

In [7]:
np.diff(y)
Out[7]:
array([  1.,   3., -35.,  29.,   4.])

と,5個の成分をもつ配列を生成するが,これは以下の計算をしていることになる。

In [8]:
for i in range(1,len(y)):
    print('%7.0f' % (y[i]-y[i-1]), end='')
      1      3    -35     29      4

np.diff(y) が負の値かどうかを判定するには…

In [9]:
np.diff(y) < 0
Out[9]:
array([False, False,  True, False, False])

y の値のうち,np.diff(y) < 0 となる値を抽出するには,len(np.diff(y)) = len(y)-1 であることに注意して…

In [10]:
y[1:][np.diff(y)<0]
Out[10]:
array([-30.])

または,

In [11]:
y[:-1][np.diff(y)<0]
Out[11]:
array([5.])

y の要素のうち,np.diff(y) < 0 となる値を「欠損値」np.nan にするには…

In [12]:
y = np.array([1., 2., 5., -30., -1., 3.])
# 置き換え前
print('置き換え前')
for i in y:
    print('%5.0f' % i, end='')
print('')

y[1:][np.diff(y) < 0] = np.nan

# 置き換え後
print('置き換え後')
for i in y:
    print('%5.0f' % i, end='')
print('')
置き換え前
    1    2    5  -30   -1    3
置き換え後
    1    2    5  nan   -1    3

同様にして,y の要素のうち,絶対値が $5$ 以上,つまり abs(y) >= 5 となる値を「欠損値」np.nan に置き換えるには…

In [13]:
y = np.array([1., 2., 5., -30., -1., 3.])
# 置き換え前
print('置き換え前')
for i in y:
    print('%5.0f' % i, end='')
print('')

y[abs(y) >= 5] = np.nan

# 置き換え後
print('置き換え後')
for i in y:
    print('%5.0f' % i, end='')
print('')
置き換え前
    1    2    5  -30   -1    3
置き換え後
    1    2  nan  nan   -1    3

SymPy Plotting Backends 編

モジュールの import

In [1]:
from sympy.abc import *
from sympy import *
# SymPy Plotting Backends (SPB) 
from spb import *

Pi = float(pi)

# グラフを SVG で Notebook にインライン表示
%config InlineBackend.figure_formats = ['svg']
In [2]:
# グラフを描くためではなくデフォルト設定のため
import matplotlib.pyplot as plt

plt.rcParams['mathtext.fontset'] = 'cm'

$y = \tan x$ のグラフ

普通に $y = \tan x$ を plot() すると,(バックエンドが Matplotlib であることから)以下のグラフのように,$y \rightarrow \infty$ から $y \rightarrow -\infty$ となる不連続なところもつないでグラフにしてしまう。

In [3]:
plot(tan(x),(x, -2*pi, 2*pi), {"color":"blue"},
     xlim = (-2*pi, 2*pi), ylim=(-10, 10));

detect_poles=True で対処

detect_poles=True オプションをつけて,さらに n の値を大きめにすると,以下のようになる。

In [4]:
plot(tan(x),(x, -2*pi, 2*pi), {"color":"blue"},
     xlim = (-2*pi, 2*pi), ylim=(-10, 10), 
     detect_poles=True, n=1e05);

Matplotlib 風のオプション設定
In [5]:
p = plot(tan(x),(x, -2*pi, 2*pi), {"color":"blue"},
         detect_poles=True, n=1e05, show = False)

fig, ax = p.fig, p.ax
# fig.tight_layout()

# 横軸縦軸の表示範囲
ax.set_xlim(-2*Pi, 2*Pi)
ax.set_ylim(-10, 10)

# グリッド(格子線)
ax.grid(linestyle='dotted')
ax.set_xlabel('$x$', fontsize=12)
ax.set_ylabel(r'$\tan x$', fontsize=12)
ax.set_title('SymPy Plotting Backends で $\\tan x$', fontsize=14)

ax.set_xticks([-2*Pi,-1.5*Pi,-Pi,-0.5*Pi, 0,
                2*Pi, 1.5*Pi, Pi, 0.5*Pi], 
              ['$-2\pi$','$-3\pi/2$','$-\pi$','$-\pi/2$','$0$',
               '$ 2\pi$','$ 3\pi/2$','$ \pi$','$ \pi/2$'], fontsize=12)
# x軸 y軸
ax.axhline(0, color='black', dashes=(5, 5), linewidth=0.5)
ax.axvline(0, color='black', dashes=(5, 5), linewidth=0.5);

$y = x^{-1}$ のグラフ

$y = x^{-1}$ を普通に plot() すると,$\displaystyle \lim_{x\rightarrow -0} \frac{1}{x} \rightarrow -\infty$ と $\displaystyle \lim_{x\rightarrow +0} \frac{1}{x} \rightarrow +\infty$ の不連続点をつないでグラフにしてしまうなぁ。

In [6]:
plot(x**(-1), (x, -5, 5), {'color':'red'},
     xlim = (-5, 5), ylim=(-10, 10));

detect_poles=True で対処

detect_poles=True オプションをつけて,さらに n の値を大きめにすると,以下のようになる。

In [7]:
plot(x**(-1), (x, -5, 5), {'color':'red'},
     xlim = (-5, 5), ylim=(-10, 10), 
     detect_poles=True, n=1e04);

exclude オプションで対処

$x=0$ は除くことにして exclude = [0] オプションを使う例。xlabel などのオプションは plot() 内で設定できる。

In [8]:
plot(x**(-1), (x, -5, 5), {'color':'red'},
     xlim = (-5, 5), ylim=(-10, 10), 
     exclude = [0], 
     xlabel = '$x$', ylabel = '$1/x$', 
     title = 'SymPy Plotting Backends で $1/x$');