linuxBean14.04(79)mpldatacursorで散布図のマーカーのラベルをポップアップ表示する

ラベル: ,

前の関連記事:linuxBean14.04(78)Jupyter Notebookの内容を直接Bloggerに貼り付ける方法


linuxBean14.04(76)MySQL5.6からPython3.4で散布図をプロットするの散布図にlinuxBean14.04(75)matplotlibのインタラクティブなグラフでインストールしたmpldatacursorパッケージを使って、マーカーをクリックすると値がポップアップするようにします。Jupyter NotebookではポップアップはでませんのでPyCharmから実行しています。

マーカーのx,y座標の値を使ってラベルをポップアップする

#!~/anaconda3/bin/python3.4
# -*- coding: utf-8 -*-
import mysql.connector
from mysql.connector import errorcode
import pandas as pd
import matplotlib.pyplot as plt
import matplotlib
from mpldatacursor import datacursor
matplotlib.rcParams['toolbar'] = 'None'  # ツールバーを抑制。
config = {
    'user': 'User01',
    'password': 'user01_pass',
    'database': 'world'
}
try:
    cnx = mysql.connector.connect(**config)  # MySQLサーバに接続する。
except mysql.connector.Error as err:
    if err.errno == errorcode.ER_ACCESS_DENIED_ERROR:
        print("ユーザー名かパスワードが間違っています。")
    elif err.errno == errorcode.ER_BAD_DB_ERROR:
        print("データベース「{}」が存在しません。".format(config['database']))
    else:
        print(err)
else:
    query = 'SELECT Name, Continent, Population, LifeExpectancy, GNP FROM Country'  # MySQLに渡すSQL文。
    df=pd.read_sql(query,cnx) # pandasのデータフレームにテーブルを取得。
    cnx.close()  # MySQLとの接続を閉じる。
    df.rename(columns={'Name': '国', 'Continent': '大陸', 'Population': '人口', 'LifeExpectancy': '平均余命', 'GNP':'国民総生産'},inplace=True) # 列インデックス名を変更。
    df.dropna(inplace=True)  # 欠損値がある行を削除。
    df = df.ix[df['国民総生産']>100]  # 国民総生産が100より大きい行のみ取得。
    fig, ax = plt.subplots(figsize=(8,6))  # 8x6インチの図に設定。
    ax.grid(True)  # グリッドの表示を有効にする。
    ax.scatter(x=df['国民総生産'], y=df['平均余命'], s=df['人口']/300000, alpha=0.5)  # 散布図をプロットする。
    ax.set_xlim((100, 20000000))  # x軸の目盛りを100から20000000に制限する。
    ax.set_xlabel('国民総生産',size=15)  # x軸のラベルを設定。
    ax.set_xscale('log')  # x軸の目盛りをlogにする。
    ax.set_ylabel('平均余命',size=15)  # y軸のラベルを設定。
    ax.set_title('平均余命 対 国民総生産', size=20)  # プロットのタイトルを設定。
    ax.tick_params(labelsize=15)  # 目盛りラベルのフォントサイズを設定。
    label = '国民総生産:{x:,}\n平均余命:{y}'  # マウスカーソル近くのマーカーの座標x,yを使ってラベルを作成。
    datacursor(hover=True, formatter=label.format)
    plt.show()

マウスカーソルの近くにあるマーカーの座標がx, yで得られることを利用して40行目でポップアップあせるラベルの書式を作っています。

formatterオプションを指定しない場合はmatplotlib.axes.scatter()の引数のx, y, sの値が表示されました。でもxの値がおかしいですね。


マーカーのx,y座標の値を使ってデータフレームの同じ行の値を表示させる


これが結構悩みました。

Nullege: A Search Engine for Python source codeの例をいくつか眺めてようやくひとつの解を得ました。

datacursor()のprops_overrideオプションを使ってformatterオプションに渡す引数を関数で修飾してラベルを作成しました。
from mpldatacursor import datacursor
?datacursor()  
Jupyter Notebookで?をつけて実行するとヘルプが表示されます。

例がないとヘルプを読んだだけではなかなか理解できませんね。
#!~/anaconda3/bin/python3.4
# -*- coding: utf-8 -*-
import mysql.connector
from mysql.connector import errorcode
import pandas as pd
import matplotlib.pyplot as plt
import matplotlib
from mpldatacursor import datacursor
matplotlib.rcParams['toolbar'] = 'None'  # ツールバーを抑制。
config = {
    'user': 'User01',
    'password': 'user01_pass',
    'database': 'world'
}
try:
    cnx = mysql.connector.connect(**config)  # MySQLサーバに接続する。
except mysql.connector.Error as err:
    if err.errno == errorcode.ER_ACCESS_DENIED_ERROR:
        print("ユーザー名かパスワードが間違っています。")
    elif err.errno == errorcode.ER_BAD_DB_ERROR:
        print("データベース「{}」が存在しません。".format(config['database']))
    else:
        print(err)
else:
    query = 'SELECT Name, Continent, Population, LifeExpectancy, GNP FROM Country'  # MySQLに渡すSQL文。
    df=pd.read_sql(query,cnx) # pandasのデータフレームにテーブルを取得。
    cnx.close()  # MySQLとの接続を閉じる。
    df.rename(columns={'Name': '国', 'Continent': '大陸', 'Population': '人口', 'LifeExpectancy': '平均余命', 'GNP':'国民総生産'},inplace=True) # 列インデックス名を変更。
    df.dropna(inplace=True)  # 欠損値がある行を削除。
    df = df.ix[df['国民総生産']>100]  # 国民総生産が100より大きい行のみ取得。(logグラフに表示しやすいため)
    df = df.set_index(['国民総生産','平均余命'])  # ('国民総生産','平均余命')をインデックスを変更。
    idx = df.index  # インデックスオブジェクトを取得。
    fig, ax = plt.subplots(figsize=(8,6))  # 8x6インチの図に設定。
    ax.grid(True)  # グリッドの表示を有効にする。
    ax.scatter(x=idx.get_level_values(0), y=idx.get_level_values(1), s=df['人口']/300000, alpha=0.5)  # 散布図をプロットする。
    ax.set_xlim((100, 20000000))  # x軸の目盛りを100から20000000に制限する。
    ax.set_xlabel('国民総生産',size=15)  # x軸のラベルを設定。
    ax.set_xscale('log')  # x軸の目盛りをlogにする。
    ax.set_ylabel('平均余命',size=15)  # y軸のラベルを設定。
    ax.set_title('平均余命 対 国民総生産', size=20)  # プロットのタイトルを設定。
    ax.tick_params(labelsize=15)  # 目盛りラベルのフォントサイズを設定。
    def override(**kwargs):  # datacursorのformatterに渡す変数を変更する関数。
        s = df.ix[(kwargs['x'], kwargs['y'])]  # (x,y)座標をインデックスとしてdfの行のデータを取得。
        kwargs['label'] = '{0}\n{1}\n国民総生産:{2:,.0f}\n平均余命:{3}\n人口:{2:,.0f}'.format(s[0], s[1], kwargs['x'], kwargs['y'], s[2])
        return kwargs
    datacursor(hover=True, props_override=override, formatter='{label}'.format)
    plt.show()
31行目でデータフレームdfのインデックスを('国民総生産','平均余命')のタプルに変更しています。

こうすることによって43行目でマーカーの座標(xが国民総生産、yが平均余命)をインデックスにしてデータフレームdfから他の列のデータを得ています。


難点はプロットがでてくるまで時間がかかることです。

プロットがでたあとはマウスカーソルを動かすとでてくるポップアップは素早く切り替わります。

参考にしたサイト


mpldatacursor/README.rst at master · joferkington/mpldatacursor · GitHub
ヘルプはJupyter Notebookで?datacursor()で表示されるdocstringに詳しいです。

axes — Matplotlib 1.4.3 documentation
プロットはこのaxesオブジェクトを操作します。

Nullege: A Search Engine for Python source code
このサイトはPythonのコード例がたくさんあって便利に使えそうです。

pandas: powerful Python data analysis toolkit — pandas 0.17.0 documentation
私はPythonによるデータ分析入門 ―NumPy、pandasを使ったデータ処理の本も参考にしています。

次の関連記事:linuxBean14.04(80)mpld3でツールチップ

PR

0 件のコメント:

コメントを投稿