【matplotlib】軸の単位をカスタマイズして科学記数法以外に設定する
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と表示)で表示されます。useOffset
をFalse
に設定すると、この挙動を無効にし、各ラベルをその実際の値で表示します。
orderOfMagnitude
orderOfMagnitude
は、軸の数値のオーダー(10のべき乗)を表します。例えば、軸の値が1,000から10,000の範囲にある場合、orderOfMagnitude
は3(10^3)になります。これを使用して、数値のスケールに応じたフォーマットを選択することができます。
__init__
メソッド
コンストラクタでは、super().__init__()
で親クラスのコンストラクタを呼び出した後、useOffset
をFalse
に設定しています。これにより、オフセットを使用しないようにし、数値をそのままのスケールで表示するようにしています。
get_offset
メソッド
このメソッドは、軸のオフセット(単位)を決定します。orderOfMagnitude
の値に基づいて、表示するべき単位(「千」、「百万」、「十億」など)を選択し、その文字列を返すようにしています。
get_offset
メソッドは、軸ラベルのフォーマット処理中に、Matplotlibによって内部的に呼び出されます。具体的には、軸のオフセット(単位)を決定する際に使用されます。このメソッドは、CustomScalarFormatter
のインスタンスが軸のフォーマッタとして設定された後、グラフが描画されるプロセスの中で、Matplotlibの描画システムによって自動的に呼び出されます。
例えば、グラフのy軸の数値が数百万の範囲にある場合、get_offset
は「百万」という文字列を返し、これがy軸のラベルに「百万」という単位として表示されます。
__call__
メソッド
__call__
メソッドは、軸の各数値ラベルをフォーマットするために使用されます。ここでは、orderOfMagnitude
に基づいて数値を適切なスケールで表示します。また、f'{x * scale_factor:.0f}'
を使用して、数値を標準的な整数形式でフォーマットし、科学記数法の表示を避けています。
__call__
メソッドのpos
引数は、そのラベルが軸上のどの位置に対応するかを示す引数です。通常、この引数は内部的に使用されるため、カスタムフォーマッタを作成する際には特に意識する必要はありません。Matplotlibが軸のラベルを描画する際に、各ラベルの位置情報を伝えるために内部的に使用されるものです。
処理の流れの概要
ここまで変数やメソッド個別の処理を説明しましたが、CustomScalarFormatter
を実行した際の処理の流れは以下のようになります。
CustomScalarFormatter
のインスタンスが作成され、__init__
が実行されてuseOffset
にFalse
が設定されます。- グラフが描画される際、
CustomScalarFormatter
の__call__
メソッドが各軸ラベルの数値に対して呼び出され、数値を適切なフォーマットで文字列に変換します。 - 同時に、
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()