matplotlib

【matplotlib】軸の単位をカスタマイズして科学記数法以外に設定する

MAX

matplotlibやseabornで、1000以上の値のデータに対してグラフを書いていると、y軸上に1e3、1e7などの文字が表示されることがあります。

1e3は10の3乗、1e7は10の7乗とすぐに分かる人が多いと思いますが、データを見慣れていない人に説明する場合等だと、1e7とかだと分かりにくかったりします。

一方で、ビジネスや会計などでは、1e3や1e6などではなく「千」や「百万」(KやMなども)などと記載されていることもあります。

本記事では、ビジネスなどの現場で分析結果を可視化して説明しやすくするためにy軸に表示される単位を科学記数法から「千」「百万」などに変更する方法を説明します。

スポンサーリンク

y軸のフォーマット変更なしのグラフ

何もフォーマットを変更しない場合、例えば以下のサンプルデータのように1000万単位のデータであれば、y軸の上に1e7と表示され、y軸の値には2, 4, 6, 8などの値が表示されます。

1import matplotlib.pyplot as plt
2
3x = range(10)
4y = [i * 10_000_000 for i in x]  # 1000万単位のデータを生成
5
6# グラフを普通に描画するとy軸の上に1e7と表示され、y軸の値は2, 4, 6, 8となる
7plt.plot(x, y)
8plt.tight_layout()
9plt.show()
10

これでも悪くはないのですが、10の7乗が1000万と認識するよりも、10の6乗が100万と認識する方が、個人的にはラクな気がしています。

y軸のフォーマットを変更したグラフ

y軸の単位を「百万」などが表示されるように変更します。

以下の例では「千」「百万」「十億」としていますが、もちろん「K」「M}「B」などでもOKです。

カスタムフォーマットクラスの作成

matplotlibのScalarFormatterを継承して軸のフォーマットをカスタムします。

1from matplotlib.ticker import ScalarFormatter
2
3class CustomScalarFormatter(ScalarFormatter):
4    def __init__(self, *args, **kwargs):
5        super().__init__(*args, **kwargs)
6        self.useOffset = False
7
8    def get_offset(self):
9        # 軸の数値のオーダーに応じて表示する単位を返す
10        if self.orderOfMagnitude >= 9:
11            return ' 十億'
12        elif self.orderOfMagnitude >= 6:
13            return ' 百万'
14        elif self.orderOfMagnitude >= 3:
15            return ' 千'
16        else:
17            return ''
18
19    def __call__(self, x, pos=None):
20        # スケーリングを調整
21        if self.orderOfMagnitude >= 9:
22            scale_factor = 1e-9
23        elif self.orderOfMagnitude >= 6:
24            scale_factor = 1e-6
25        elif self.orderOfMagnitude >= 3:
26            scale_factor = 1e-3
27        else:
28            scale_factor = 1
29
30        # 科学記数法を使わずにフォーマット
31        return f'{x * scale_factor:.0f}'
32

このクラスでは、ScalarFormatterを継承して、get_offsetメソッドと__call__メソッドをオーバーライドします。

get_offsetメソッドは、軸のオフセット(つまり単位)を返し、__call__メソッドは各軸のラベルの実際の数値をフォーマットします。

処理内容の詳細は後述します。

__call__もオーバーライドしているため、このクラスの使い方例を説明します。

CustomScalarFormatterでy軸のフォーマットを変更する

使い方はシンプルで、軸を取得してset_major_formatterでフォーマットを適用するだけです。

1import matplotlib.pyplot as plt
2# 軸の単位に日本語を使うのでimportせずにフォントもデフォルトだと、文字化けする
3import japanize_matplotlib
4
5x = range(10)
6y = [i * 10_000_000 for i in x]  # 1000万単位のデータを生成
7
8plt.plot(x, y)
9# y軸のフォーマットを実行
10plt.gca().yaxis.set_major_formatter(CustomScalarFormatter())
11plt.tight_layout()
12plt.show()
13

y軸の上に「百万」と表示されるようになりました。

CustomScalarFormatterクラスの説明

CustomScalarFormatterクラスについてもう少し詳しく説明します。

以下のコードは先ほどのコードの再掲となります。

(再掲)CustonScalarFormatterクラスのコード

1from matplotlib.ticker import ScalarFormatter
2
3class CustomScalarFormatter(ScalarFormatter):
4    def __init__(self, *args, **kwargs):
5        super().__init__(*args, **kwargs)
6        self.useOffset = False
7
8    def get_offset(self):
9        # 軸の数値のオーダーに応じて表示する単位を返す
10        if self.orderOfMagnitude >= 9:
11            return ' 十億'
12        elif self.orderOfMagnitude >= 6:
13            return ' 百万'
14        elif self.orderOfMagnitude >= 3:
15            return ' 千'
16        else:
17            return ''
18
19    def __call__(self, x, pos=None):
20        # スケーリングを調整
21        if self.orderOfMagnitude >= 9:
22            scale_factor = 1e-9
23        elif self.orderOfMagnitude >= 6:
24            scale_factor = 1e-6
25        elif self.orderOfMagnitude >= 3:
26            scale_factor = 1e-3
27        else:
28            scale_factor = 1
29
30        # 科学記数法を使わずにフォーマット
31        return f'{x * scale_factor:.0f}'
32

CustomScalarFormatterの概要

このクラスは、MatplotlibのScalarFormatterを継承しており、軸の数値ラベルのフォーマットをカスタマイズするために用います。

useOffset

useOffsetは、MatplotlibのFormatterクラスにある属性で、デフォルトではTrueに設定されています。この属性がTrueの場合、軸のラベルはオフセット(基準値)を使用して表示されます。例えば、すべての値が1000以上であれば、1000を基準値とし、各ラベルはその差分(例:1020は20と表示)で表示されます。useOffsetFalseに設定すると、この挙動を無効にし、各ラベルをその実際の値で表示します。

orderOfMagnitude

orderOfMagnitudeは、軸の数値のオーダー(10のべき乗)を表します。例えば、軸の値が1,000から10,000の範囲にある場合、orderOfMagnitudeは3(10^3)になります。これを使用して、数値のスケールに応じたフォーマットを選択することができます。

__init__メソッド

コンストラクタでは、super().__init__()で親クラスのコンストラクタを呼び出した後、useOffsetFalseに設定しています。これにより、オフセットを使用しないようにし、数値をそのままのスケールで表示するようにしています。

get_offsetメソッド

このメソッドは、軸のオフセット(単位)を決定します。orderOfMagnitudeの値に基づいて、表示するべき単位(「千」、「百万」、「十億」など)を選択し、その文字列を返すようにしています。

get_offsetメソッドは、軸ラベルのフォーマット処理中に、Matplotlibによって内部的に呼び出されます。具体的には、軸のオフセット(単位)を決定する際に使用されます。このメソッドは、CustomScalarFormatterのインスタンスが軸のフォーマッタとして設定された後、グラフが描画されるプロセスの中で、Matplotlibの描画システムによって自動的に呼び出されます。

例えば、グラフのy軸の数値が数百万の範囲にある場合、get_offsetは「百万」という文字列を返し、これがy軸のラベルに「百万」という単位として表示されます。

__call__メソッド

__call__メソッドは、軸の各数値ラベルをフォーマットするために使用されます。ここでは、orderOfMagnitudeに基づいて数値を適切なスケールで表示します。また、f'{x * scale_factor:.0f}'を使用して、数値を標準的な整数形式でフォーマットし、科学記数法の表示を避けています。

__call__メソッドのpos引数は、そのラベルが軸上のどの位置に対応するかを示す引数です。通常、この引数は内部的に使用されるため、カスタムフォーマッタを作成する際には特に意識する必要はありません。Matplotlibが軸のラベルを描画する際に、各ラベルの位置情報を伝えるために内部的に使用されるものです。

処理の流れの概要

ここまで変数やメソッド個別の処理を説明しましたが、CustomScalarFormatterを実行した際の処理の流れは以下のようになります。

  1. CustomScalarFormatterのインスタンスが作成され、__init__が実行されてuseOffsetFalseが設定されます。
  2. グラフが描画される際、CustomScalarFormatter__call__メソッドが各軸ラベルの数値に対して呼び出され、数値を適切なフォーマットで文字列に変換します。
  3. 同時に、get_offsetメソッドも内部的に呼び出され、軸の単位(オフセット)を決定し、これが軸ラベルに適用されます。

このように、CustomScalarFormatterは軸の数値と単位の表示方法をカスタマイズするために、__call__get_offsetの両方を利用しています。

まとめ

1import matplotlib.pyplot as plt
2import japanize_matplotlib
3from matplotlib.ticker import ScalarFormatter
4
5# カスタムフォーマットクラスを定義
6class CustomScalarFormatter(ScalarFormatter):
7    def __init__(self, *args, **kwargs):
8        super().__init__(*args, **kwargs)
9        self.useOffset = False
10
11    def get_offset(self):
12        # 軸の数値のオーダーに応じて表示する単位を返す
13        if self.orderOfMagnitude >= 9:
14            return ' 十億'
15        elif self.orderOfMagnitude >= 6:
16            return ' 百万'
17        elif self.orderOfMagnitude >= 3:
18            return ' 千'
19        else:
20            return ''
21
22    def __call__(self, x, pos=None):
23        # スケーリングを調整
24        if self.orderOfMagnitude >= 9:
25            scale_factor = 1e-9
26        elif self.orderOfMagnitude >= 6:
27            scale_factor = 1e-6
28        elif self.orderOfMagnitude >= 3:
29            scale_factor = 1e-3
30        else:
31            scale_factor = 1
32
33        # 科学記数法を使わずにフォーマット
34        return f'{x * scale_factor:.0f}'
35
36# サンプルデータ生成
37x = range(10)
38y = [i * 10_000_000 for i in x]  # 1000万単位のデータを生成
39
40# グラフ作成
41plt.plot(x, y)
42# y軸のフォーマットを実行
43plt.gca().yaxis.set_major_formatter(CustomScalarFormatter())
44plt.tight_layout()
45plt.show()

スポンサーリンク
ABOUT ME
MAX
MAX
ITエンジニア、データサイエンティスト
新卒でSIerに入社し、フリーランスになってWEB系へ転向。
その後AIの世界へ足を踏み入れ、正社員に戻る。 テーブルデータの分析がメイン。
スポンサーリンク
記事URLをコピーしました