第2回:▼ 複数のグラフを描く

■ リテラル

リテラル(literal)とは, 「文字の並び」の通りに解釈される量をいう.

11.1 はリテラルである. それぞれ,整数 1 ,小数 1.1 という値として評価されるからである.

■ 文字列

「文字の並び」として表される量が,文字列である. 文字列のリテラルは,ダブルクォート " で囲まれた文字の並びである.

julia> "Hello world"
"Hello world"

文字列を連結するには,演算子 * を用いる.

julia> h = "Hello"
"Hello"

julia> w = "world"
"world"

julia> h * w
"Helloworld"

julia> h * " " * w
"Hello world"
Note

演算子 * は,数同士に対して用いると乗算の意味になる.文字列同士に用いると文字列の連結の意味になる.このように,同じ演算子 * に対して,複数の意味があり,適用する値の型(の組合せ)に応じて,適切な意味が選ばれて,計算される.

数字を表す文字列を作るには,string 関数を用いる.

julia> string(0)
"0"

julia> string(1)
"1"

julia> string(1.1)
"1.1"

▼ グラフに凡例を加える

グラフの凡例(lengend)は,グラフに描かれた曲線を区別するための説明である. PyPlotパッケージで書かれたグラフに凡例を追加するには,以下のようにする.

まず,plot 関数に label=文字列 の形式で, その曲線に付与する文字列を指定する. 全ての曲線を描いた後に,legend 関数を実行すると,グラフに凡例が追加される.

using PyPlot

xs = -1:0.1:1
plt.plot(xs, -1 * xs, label = "y=-x")
plt.plot(xs, 2 * xs .- 1, label = "y=2x-1")
plt.legend()

比例関係 $y = ax$(ただし $a = 1, 2, 3, 4, 5$ )のグラフを描こう.

xs = -1:0.1:1
plt.plot(xs, xs, label = "y= x")
plt.plot(xs, 2 * xs, label = "y=2x")
plt.plot(xs, 3 * xs, label = "y=3x")
plt.plot(xs, 4 * xs, label = "y=4x")
plt.plot(xs, 5 * xs, label = "y=5x")
plt.legend()

▼ グラフに,水平線・垂直線を加える

$y=ax$ は全て原点 $(0,0)$ を通る. 水平線や垂直線の補助線を引いて,これを見やすくする.

PyPlotパッケージに用意された関数 axhline(y) は, 縦座標 $y$ で水平線(horizontal line)を描く. 最初の引数には,水平線を引く $y$ 座標を指定する. キーワード引数 color="k" は,黒色(black)で描くことを指定し,lw=0.5 は線幅(linewidth)を指定する.

また,関数 axvline(x) は,横座標 $x$ で垂直線(vertical line)を描く. 最初の引数には,垂直線を引く $x$ 座標を指定する.

上のプログラムに続けて

using PyPlot
# 水平線 y=0
plt.axhline(0, color = "k", lw = 0.5)
# 垂直線 x=0
plt.axvline(0, color = "k", lw = 0.5)

▼ グラフの描画範囲を指定する

plot 命令は,全ての点を表示しようとする. グラフの描画範囲を調整するには,関数 xlimylim を用いる.

  • 関数 xlim(a,b) は,x軸の描画を a から b の範囲に限定する.
  • 関数 ylim(a,b) は,y軸の描画を a から b の範囲に限定する.

上のプログラムに続けて

# 描画範囲
plt.xlim(-3, 3) # <=
plt.ylim(-3, 3) # <=

別の描画範囲を指定してみる. 上のプログラムに続けて

# 描画範囲
plt.xlim(-0.5, 0.5) # <=
plt.ylim(-0.5, 0.5) # <=

▼ グラフのアスペクト比を等しくする

グラフの縦横の長さの比をアスペクト比(aspect ratio)という.

関数 plt.axes().set_aspect() は,アスペクト比を指定する命令である.

何も指定しない場合は,plt.axes().set_aspect("auto") であり, アスペクト比を適当に調整する.

関数 plt.axes().set_aspect("equal") は,アスペクト比を等しくする命令である. 通常は,前項の描画範囲の指定と同時に用いる.

アスペクト auto の場合

using PyPlot
# plt.axes().set_aspect("auto")  # PyPlot起動時は指定しなくてもよい

xs = -1:0.2:1
plt.plot(xs, xs)
plt.xlim(-1, 1)
plt.ylim(-1, 1)
plt.axhline(0, color = "k", lw = 0.5)
plt.axvline(0, color = "k", lw = 0.5)

アスペクト equal を指定する.上のプログラムに続けて

plt.axes().set_aspect("equal")

for

Repeated Evaluation: Loops (section)

一つずつ要素を取り出すことができる量をコレクションという. ベクトルや範囲は,コレクションである.

for 文を用いると,コレクションから要素を一つづつ取り出して, end 文が出現するまでの文を繰り返して,計算を行うことができる. この繰り返される部分をブロック(block)という. ブロックは,字下げ(indent)で表記される. が,字下げは見やすさのためだけである.

繰り返しをループ(loop)ともいう.

Note

ブロックは,for 文以外にも出現する(例えば,■ if文). 区別したいなら for ブロックとも称する.

次の例では,変数 i に,ベクトルの各要素を入れて,end 文までの計算を繰り返す. コレクションの各要素が入る変数をループ変数(loop variable)という.

julia> for i in [1, 3, 2]
          @show i   # 式 i の値を表示する
       end
i = 1
i = 3
i = 2

@show i は,式 i の値を表示するマクロである.

範囲を用いた for 文の例を示す.

julia> for i = 1:5
          println(string(i))
       end
1
2
3
4
5

string 関数の結果を表示する. println 関数は,印字してから,改行する命令である.

▼ for 文でパラメータを変えて,複数のグラフを描く

▼ グラフに凡例を加える の後半のプログラムを再掲する.

using PyPlot

xs = -1:0.1:1
plt.plot(xs, xs, label = "y= x")
plt.plot(xs, 2 * xs, label = "y=2x")
plt.plot(xs, 3 * xs, label = "y=3x")
plt.plot(xs, 4 * xs, label = "y=4x")
plt.plot(xs, 5 * xs, label = "y=5x")
plt.legend()

上のプログラムを,for 文を用いた繰り返しで書き直してみよう.

次の例の for 文では, ループ変数 a に,1, 2, 3, 4, 5 の値が順番に入って,end までの文が実行される. すなわち,直前のプログラムと同等である.

using PyPlot

xs = -1:0.1:1
for a = 1:5
   plt.plot(xs, a * xs, label = "y=" * string(a) * "x")
end
plt.legend()

▼ 冪乗関数を描く

Base.:^ - Method

x^y は,冪(べき,power)ないし冪乗(べきじょう)$x^{y}$ を表す. $x$ を底(base),$y$ を冪指数(exponent)という.

julia> 2^2
4

julia> 2^3
8

julia> 2^4
16

スカラー c とベクトル v とに演算子 .^ を適用する c .^ vと, 各々の冪指数に対して冪乗を計算したベクトルが得られる.

底が整数の場合は .^ の前に空白を入れる.

julia> 2 .^[2,3,4]
3-element Array{Int64,1}:
  4
  8
 16
Note

整数(底)の直後に .^ と書くと例外が出るので注意しよう.

julia> 2.^[2,3,4]
ERROR: syntax: invalid syntax "2.^"; add space(s) to clarify

底が小数の場合は,その直後に .^ と書いてよい.

julia> 2.0.^[2, 3, 4]
3-element Array{Float64,1}:
  4.0
  8.0
 16.0

ベクトルとスカラーとに演算子 .^ を適用する v .^ cと, 各々の底に対して,冪乗を計算したベクトルが得られる.

julia> [2, 3, 4] .^ 2
3-element Array{Int64,1}:
  4
  9
 16

julia> [2, 3, 4] .^ 2
3-element Array{Int64,1}:
  4
  9
 16

範囲 v とスカラー c とに演算子 .^ を適用する v .^ c と, 各々の底に対して,冪乗を計算したベクトルが得られる.

julia> (2:4) .^ 2
3-element Array{Int64,1}:
  4
  9
 16

julia> 2:4 .^ 2    # `^`は `:` よりも優先度が高い
2:16

区間 $x= [0,1]$ で,冪乗 $y=x^a$ (ただし $a = 2,3,4,5$ )のグラフを描こう.

plt.axes().set_aspect("equal") は, グラフの縦横比(アスペクト比 aspect ratio)を等しくする命令である.

using PyPlot
plt.axes().set_aspect("equal")

xs = 0:0.1:1
plt.plot(xs, xs .^ 2)
plt.plot(xs, xs .^ 3)
plt.plot(xs, xs .^ 4)
plt.plot(xs, xs .^ 5)

for 文を使って,繰り返しの処理をまとめる. 区間を $x= [0,2]$ に拡大して,凡例を追加しよう.

using PyPlot
plt.axes().set_aspect("equal")
xs = 0:0.05:2
for a = 2:5
   plt.plot(xs, xs .^ a, label = "y=x^" * string(a))
end
plt.legend()
# 描画範囲を設定
plt.xlim(-0.05, 2)
plt.ylim(-0.05, 2)

冪乗 $y=x^a$ (ただし $a = 2, 3, 4, 5 $)は全て,点 $(1,1)$ を通る. 直線 $x = 1$$y = 1$ を付与して,これを見やすくしよう.

using PyPlot
plt.axes().set_aspect("equal")

# 間隔を狭めた
xs = 0:0.05:2
for a = 2:5
   plt.plot(xs, xs .^ a, label = "y=x^" * string(a))
end
plt.legend()
# 描画範囲を設定
plt.xlim(-0.05, 2)
plt.ylim(-0.05, 2)
# 水平線 y=1
plt.axhline(1, color = "k", lw = 0.5)
# 垂直線 x=1
plt.axvline(1, color = "k", lw = 0.5)

■ ベクトルの要素の加減算

ベクトル v とベクトル u とに 演算子 .+.- を適用する v .+ u, v .- u と, 対応する要素同士を加減算した要素を持つベクトルが得られる.

julia> xs = [1, 2, 2, 1]
4-element Array{Int64,1}:
 1
 2
 2
 1

julia> ys = [1, 1, 3, 1]
4-element Array{Int64,1}:
 1
 1
 3
 1

julia> xs .+ ys
4-element Array{Int64,1}:
 2
 3
 5
 2

julia> xs .- ys
4-element Array{Int64,1}:
  0
  1
 -1
  0

■ ベクトルと範囲との加減算

範囲v とベクトル u とに 演算子 .+.- を適用する v +. u, v .- u と, 範囲とベクトルとの寸法(要素の数)が等しいなら, 対応する要素同士を加減算した要素を持つベクトルが得られる. ベクトルと範囲に演算子 .+.- を適用しても同様である. 要素の数が異なると,例外(exception, エラー)となる.

julia> xs = 1:1:5
1:1:5

julia> ys = [11, 13, 15, 17, 19]
5-element Array{Int64,1}:
 11
 13
 15
 17
 19

julia> xs .+ ys
5-element Array{Int64,1}:
 12
 15
 18
 21
 24

julia> ys .- xs
5-element Array{Int64,1}:
 10
 11
 12
 13
 14
Note

範囲をベクトルに変換してしまうと,元が等差数列であるという情報が欠落する.どうしても,ベクトルでないと困る場合だけ,ベクトルに変換しよう.

▼ ローレンツ関数を描く

Lorentzian Function

以下の曲線を, ローレンツ関数(Lorentzian function)という.

\[\begin{gathered} y = \dfrac{\dfrac{\gamma}{2}}{(x-x_0)^2+\left(\dfrac{\gamma}{2}\right)^2}, \\ \gamma > 0 \end{gathered}\]

パラメータを $x_0 = 0, \gamma = 2$ のように選ぶと, 以下のように簡単な形となる.

\[y = \dfrac{1}{x^2+1}\]

まず,この曲線を描いてみる.

範囲の各要素に対して加算や除算を行うため,1 / (xs.^2 +1) では 駄目である. /+ の前にピリオド . を付与する.

julia> xs = -3:0.5:3
-3.0:0.5:3.0

julia> 1 ./ (xs .^ 2 .+ 1)
13-element Array{Float64,1}:
 0.1
 0.13793103448275862
 0.2
 0.3076923076923077
 0.5
 0.8
 1.0
 0.8
 0.5
 0.3076923076923077
 0.2
 0.13793103448275862
 0.1

上のグラフを描こう.

using PyPlot

xs = -3:0.1:3
plt.plot(xs, 1 ./ (xs .^ 2 .+ 1))

以下のように,パラメータ $\gamma$ を追加する.

\[y = \dfrac{\dfrac{\gamma}{2}}{x^2+\left(\dfrac{\gamma}{2}\right)^2}\]

三つのパラメータ $\gamma=0.5, 1, 2$ について,この曲線を描く.

using PyPlot

xs = -3:0.05:3
gamma = 0.5
plt.plot(xs, (gamma / 2) ./ (xs .^ 2 .+ (gamma / 2)^2), label = gamma)
gamma = 1.0
plt.plot(xs, (gamma / 2) ./ (xs .^ 2 .+ (gamma / 2)^2), label = gamma)
gamma = 2.0
plt.plot(xs, (gamma / 2) ./ (xs .^ 2 .+ (gamma / 2)^2), label = gamma)
plt.legend()

gamma の値が変わっても,それぞれの曲線を描くための命令は変わらない. for 文を用いて,gamma の値を変えてみよう(結果のグラフは変わらないので,省略する).

using PyPlot

xs = -3:0.05:3
for gamma in [0.5, 1.0, 2.0]
   plt.plot(xs, (gamma / 2) ./ (xs .^ 2 .+ (gamma / 2)^2), label = gamma)
end
plt.legend()

ローレンツ関数には,次の性質がある.

  • $x = 0$ で 最大値 $y = \dfrac{2}{\gamma}$
  • $x = \pm\dfrac{\gamma}{2}$ で,最大値の半分 $y = \dfrac{1}{2}\dfrac{2}{\gamma}$

2つ目の性質を観察するため,最大値に対する比を描いてみる.

using PyPlot

xs = -3:0.05:3
for gamma in [0.5, 1.0, 2.0]
   plt.plot(
      xs,
      (gamma / 2) ./ (xs .^ 2 .+ (gamma / 2)^2) / (2 / gamma),
      label = gamma,
   )
end
plt.legend()
plt.axhline(1 / 2, color = "k", lw = 0.5)

Note

plot 関数の文は,複数の行に渡って記述しているが,行が更に続くことを示す記法は,特に用意されていない.構文が行末で終わらなければ,次の行まで読みに行くことになっている.

パラメータ $\gamma$ は,半値全幅(Full Width of Half Maximum, FWHM)と呼ばれる. $\gamma$ を非常に小さくすると,Diracのデルタ関数(Dirac delta function)の近似(の一つ)となる.

■ 更新演算子

Updating operators (section)

変数に四則演算などを行って,元の変数に再代入する場合には, 更新演算子を用いるとよい. 演算子の直後に = の文字が入る.

julia> x = 1
1

julia> # 再代入
       x = x + 1
2

julia> # 更新演算子
       x += 1
3

演算子 +, -, *, /, ^ に対して, 更新演算子 +=, -=, *=, /=, ^= が用意されている.

以下の例は,変数 gamma を 2 で繰り返し割り算する.

julia> gamma = 2
2

julia> for i = 1:5
          global gamma
          gamma /= 2
          @show gamma
       end
gamma = 1.0
gamma = 0.5
gamma = 0.25
gamma = 0.125
gamma = 0.0625

更新演算子 /= を利用して,ローレンツ関数のパラメータ gamma を変えてみる.

for 文の次の global gamma に注目してほしい. 対話型で実行する場合,for 文の外側で定義された変数の値を変更することは, 安全のために通常禁止されている.これを可能とするのが global 文である.

using PyPlot
xs = -3:0.05:3
gamma = 2
for i = 1:5
   global gamma
   plt.plot(
      xs,
      (gamma / 2) ./ (xs .^ 2 .+ (gamma / 2)^2) / (2 / gamma),
      label = gamma,
   )
   gamma /= 2
end
plt.legend()
axhline(1 / 2, color = "k", lw = 0.5)

ベクトルをスカラー倍して更新するには,更新演算子 *= を用いる. ベクトルにスカラーを加減して更新するには,更新演算子 .+=または .-= を用いる.

julia> xs = [1, 2, 2, 1]
4-element Array{Int64,1}:
 1
 2
 2
 1

julia> xs .+= 1
4-element Array{Int64,1}:
 2
 3
 3
 2

julia> xs *= 2
4-element Array{Int64,1}:
 4
 6
 6
 4

前節の三角形を描く例題で,更新演算子 .+= を用いて,図形を繰り返し並行移動してみよう.

for 文の中で,変数 xsys を更新するので,global xs, ys の文が必要である.

using PyPlot
plt.axes().set_aspect("equal")

xs = [1.0, 2.0, 2.0, 1.0]
ys = [1.0, 1.0, 3.0, 1.0]
for i = 1:5
   global xs, ys
   plt.plot(xs, ys)
   xs .+= 0.5
   ys .+= 0.5
end
plt.xlim(0, 6)
plt.ylim(0, 6)

▲ 練習

ローレンツ関数において,パラメータ $x_0$ を変えたグラフを作成せよ. for 文を使う方法,更新演算子を使う方法の,二つで描いてみよ.

★ 今回のまとめ

  • 文字列
  • for
  • グラフに凡例を加える
  • 冪乗関数
  • ローレンツ関数
  • 更新演算子