Juliaで色を徐々に変えながらプロットする方法


やりたいこと

グラフを複数同じキャンバス(figureのほうが適切?)に描くとき、それぞれの線の色を変えたいですよね。
通常、PythonもJuliaも自動で色を変えてくれますが、それをきれいなグラデーションで少しづつ変化させるときれいです。
で、その方法はpythonだと簡単に見つかるんですが、Juliaだとなかなか見つからなかったので、記事にしようというわけです。

問題設定

 f(x) = x^{n},  n = 1, 2, 3\cdots 10, x \in [-1, 1]

をプロットしたいとします。ただし、

  • プロットは同じfigure中に描く
  • nが変わったとき、色を変える
  • 色は徐々に(連続的に)変化させる

としましょう。
最終的にこんなプロットができればゴールです。

Pythonだと

Python(正確にはmatplotlib)なら、以下のようにかける。

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


x = np.linspace(-1,1,100)

N_max = 10
for n in range(1,N_max+1,1):
    y = x**n
    color = cm.viridis(float(n/N_max))
    plt.plot(x,y, color=color, label = 'x to the {}th'.format(n))
plt.legend(loc=0)

ポイントは変化する指数$n$を$N_max$で割った値を、カラーマップスキームに引数として渡すところかと思う。


color = cm.viridis(0~1のfloatを与える)

こうすると、指定したカラーマップスキームの中から、色を0~1のスカラー値でピックアップすることができる。

Juliaだと

Julia(ここではPlots+GRやPlotlyを想定)だと少し難しくなる。
なぜかといえば、カラーマップスキームに引数として0~1のfloatを渡すことができないからである。

一応、カラーマップスキームはc=:[なにかのscheme]で渡すことができるらしいので、試してみる。

# cを指定
# 違う、そうじゃない
x = LinRange(-1,1,100)
p = plot()
N_max = 10

for n in 1:N_max
    y = map(xx-> xx^n, x)
    p = plot!(p,x,y,
              c=:viridis,
              label= string("x to the ", n,"th" ),
              legend=:bottomright)
end
display(p)

色が全部おなじになっちゃった(´;ω;`)

解決策

このExampleが解決策だった。

さきに解決策を書こう。


# cgradをうまくつかうのか〜
x = LinRange(-1,1,100)
p = plot()

N_max = 10

#ここで色の候補を作っておく
C(g::ColorGradient) = RGB[g[z] for z=LinRange(0,1,N_max)]
# colormap schemeの指定
g = :viridis
# 色のリスト作成(パイプ使用)
colorlist  = cgrad(g) |> C 

for n in 1:N_max
    y = map(xx-> xx^n, x)
    p = plot!(p,x,y, c=colorlist[n], # ここで色をピックアップ
         label= string("x to the ", n,"th" ),            legend=:bottomright) 

end
display(p)

やったね。

何してるの

何してるんだろう。
正直な話、

#ここで色の候補を作っておく
C(g::ColorGradient) = RGB[g[z] for z=LinRange(0,1,N_max)]

は何が起きているのかよくわからない。
関数CをRGBのリストとして定義しているけれど、その中にColorGradient型のgがあって、そのスライスとしてzが使われていて、、、、

正直良くわからない¯_(ツ)_/¯
ただ、機能としては「指定のカラーマップ(g)を N_max 分割して、そのリストを作る関数を作成」みたいなことをしている。

ポイントはこの関数の引数に直接gを指定しているのではなく、cgrad関数の返り値を入れることである。

# colormap schemeの指定
g = :viridis
# 色のリスト作成(パイプ使用)
colorlist  = cgrad(g) |> C 

ここは実際にカラーマップスキームを指定して、色のリストを実際に作成している。
|>はいわゆるパイプであることに注意。関数Cの引数としてcgrad(g)の値が入っている。

 最後に

Matplotlibだったらできるのに・・・というときには、BackendとしてPyPlot使う手もあるわけだし、どうしても必要というわけではないかもしれないし、よくわかってないけど、とりあえずできたからヨシ!