morikomorou’s blog

自分が学んだことなどの備忘録的なやつ

【python】クリックでmatplotlibのグラフの色や透明度を変える

はじめに

以前の記事で、matplotlibで描画したグラフにマウスクリックイベントを紐づける方法について紹介しました。
【python】matplotlibでマウスクリックイベントを紐づける - morikomorou’s blog

クリックしたその位置に直接点を打ったりしましたが、
今回は、クリックした座標周辺のグラフの要素(点や直線)を取得するような、
ピックイベントを紐づける方法について説明します。

ピックイベントを使って今回やってみることは以下です。

  • グラフをクリックして線の透明度を変える
  • グラフをクリックして線の色を変える


実装に関して下記のブログが非常によくまとまっておりましたので参考にさせていただきました。
[matplotlib] 103. legendのクリックイベントでプロットの表示/非表示きりかえ – サボテンパイソン

実際に見せたほうがわかりやすいと思いますので、下記に今回の目標を示します。

ピックイベントについて

過去記事でmatplotlibで使えるイベントについてまとめましたが、
【python】matplotlibでマウスクリックイベントを紐づける - morikomorou’s blog
今回は「要素を選択する」という'pick_event'をグラフに紐づけます。

グラフをクリックして透明度を変える

さっそく実装してみます。

import matplotlib.pyplot as plt
import numpy as np

def onpick(event):
    gco = event.artist
    alpha = gco.get_alpha()
    if alpha == 0.2:
        gco.set_alpha(1.0)
    else:
        gco.set_alpha(0.2)
    plt.draw()

fig, ax = plt.subplots()
ax.plot(np.random.rand(10),"r-",picker=10,label='a')
ax.plot(np.random.rand(10),"b-",picker=10,label='b')
ax.plot(np.random.rand(10),"g-",picker=10,label='c')
plt.legend()

fig.canvas.mpl_connect('pick_event', onpick)
plt.show()

以下に動きを載せておきます。

コード解説

ピックイベントでは'pick_event'を使うので下記のようにfigureと関数を紐づけます。

fig.canvas.mpl_connect('pick_event', onpick)

また、クリックして取得したい要素にはpickerプロパティを指定します

ax.plot(np.random.rand(10),"r-",picker=10,label='a')
ax.plot(np.random.rand(10),"b-",picker=10,label='b')
ax.plot(np.random.rand(10),"g-",picker=10,label='c')

pickerに指定する数値は、要素のピッキング範囲になります。この範囲内をクリックするとその要素を取得できます。
取得した要素はevent.artistに入ります。

pick_eventで呼ばれる関数は以下です。

def onpick(event):
    gco = event.artist
    alpha = gco.get_alpha()
    if alpha == 0.2:
        gco.set_alpha(1.0)
    else:
        gco.set_alpha(0.2)
    plt.draw()

取得した要素に対し、.get_alpha()メソッドで現在の透明度を呼び出し、
その値に応じて.set_alpha()で指定する透明度を変えることで、
クリックの度に透明度を上げたり下げたりできるようにしています。

凡例の線も透明度を変える

先ほどの動画で気づいたかと思いますが、このままだと凡例の透明度は変わっていません。
どうせなら凡例のほうも変わるように設定したいと思います。

import matplotlib.pyplot as plt
import numpy as np

def onpick(event):
    gco = event.artist
    alpha = gco.get_alpha()
    l = gco.get_label()
    if alpha == 0.2:
        gco.set_alpha(1.0)
        legdic[l].set_alpha(1.0)
    else:
        gco.set_alpha(0.2)
        legdic[l].set_alpha(0.2)
    plt.draw()

fig, ax = plt.subplots()
ax.plot(np.random.rand(10),"r-",picker=10,label='a')
ax.plot(np.random.rand(10),"b-",picker=10,label='b')
ax.plot(np.random.rand(10),"g-",picker=10,label='c')

leg = plt.legend()
leglines = leg.get_lines()      # プロットしたラインのリスト
leglabels = [i.get_text() for i in leg.get_texts()]      # ラベルのリスト

legdic = {}      # ラベルとラインを紐づけた辞書
for line, label in zip(leglines, leglabels):
    legdic[label] = line

fig.canvas.mpl_connect('pick_event', onpick)
plt.show()

結果は以下です。

ちゃんと凡例も変わりましたね。

解説

まずは各要素のラベルと凡例の線のクラスを紐づけて辞書にし、
ラベルから凡例の線のクラスを呼び出せるようにしておきます。
ラベルの取得はplt.legend().get_texts()で出来ますが、このままだとTextクラスのリストが返ってくるので、返ってきたリストの要素に対し.get_text()メソッドで文字列だけ抜き出しています。

leg = plt.legend()
leglines = leg.get_lines()      # 凡例のラインのリスト(初めにプロットした順)
leglabels = [i.get_text() for i in leg.get_texts()]      # 凡例のラベルのリスト(['a', 'b', 'c'])

legdic = {}      # ラベルとラインを紐づけた辞書
for line, label in zip(leglines, leglabels):
    legdic[label] = line

次に、onpick関数内で取得した要素のラベルを読み取ります。.get_label()メソッドでピックした要素のラベルを文字列で取得できます。

def onpick(event):
    gco = event.artist
    alpha = gco.get_alpha()
    l = gco.get_label()    # 取得した要素のラベルをget
    if alpha == 0.2:
        gco.set_alpha(1.0)
        legdic[l].set_alpha(1.0)
    else:
        gco.set_alpha(0.2)
        legdic[l].set_alpha(0.2)
    plt.draw()



グラフをクリックして色を変える

つづいて色を変えてみましょう。
透明度を変える時のコードがほとんど流用できます。

コード

先程までのコードを参考に実装してみましょう。
凡例の色も変えれるようにします。

import matplotlib.pyplot as plt
import numpy as np

def onpick(event):
    gco = event.artist
    c = gco.get_color()
    l = gco.get_label()
    if c != 'k':
        gco.set_color('k')
        legdic[l].set_color('k')
    elif l == 'a':
        gco.set_color('r')
        legdic[l].set_color('r')
    elif l == 'b':
        gco.set_color('b')
        legdic[l].set_color('b')
    else:
        gco.set_color('g')
        legdic[l].set_color('g')
    plt.draw()

fig, ax = plt.subplots()
ax.plot(np.random.rand(10),"r-",picker=10,label='a')
ax.plot(np.random.rand(10),"b-",picker=10,label='b')
ax.plot(np.random.rand(10),"g-",picker=10,label='c')

leg = plt.legend()
leglines = leg.get_lines()
leglabels = [i.get_text() for i in leg.get_texts()]
legdic = {}
for line, label in zip(leglines, leglabels):
    legdic[label] = line
fig.canvas.mpl_connect('pick_event', onpick)
plt.show()

結果は以下のようになります。

解説

透明度を変えたコードに対し、.get_alpha()、.set_alpha()メソッドを.get_color()、.set_color()メソッドに置き換えるだけです。

おわりに

いかがだったでしょうか?透明度をインタラクティブににかえられることでゴチャゴチャしたグラフも見やすくなる気がします。