Matplotlib | Scale Classの概要(Python軸スケール3)

データサイエンス

みなさんこんにちは!このブログでは主に

の4つのトピックについて発信しています。

今回はPythonデータ解析です!Matplotlib、軸のスケールシリーズ第3回として、Scaleオブジェクト(class)の概要を解説します!Pythonで作図する際、軸のスケールはScaleオブジェクトを使って変更することができます。Scaleオブジェクトの使い方を知っておくと、オリジナルのスケールを定義したり、変換・逆変換関数を簡単に呼び出せるようになります。

この記事を読めば、Matplotlibで予め用意されたスケールについてのScaleオブジェクト、変換関数や逆変換関数を知ることができます!ぜひ最後までご覧ください。

Kaiko
Kaiko

この記事はこんな人のお悩み解決に役立ちます!

  • Pythonの作図で軸のスケールを変えたいけどどうやる?
  • 負の値も対数スケールでプロットできないの?
  • 散布図のデータ点が潰れて見にくいのはどうしたら良い?



Abstract | Scaleオブジェクトを理解すればプロット方法の幅が広がる

Scaleオブジェクトを理解しておくと、Matplotlibで作図方法の幅が広がります。Matplotlibでは

ax.set_yscale("log")

などとすれば、軸のスケールを設定することができます。しかし、背後で呼び出されているLogScaleなどのScaleオブジェクトについて理解しておくと、

  • 変換関数や逆変換関数を呼び出せる
  • オリジナルのスケールを定義して作成できる
  • スケール変換を絡めた処理を実装できる

など、Matplotlibを使ったプロットに関してできることの幅が広がります。

特に自分でオリジナルのスケールを定義するCustom Scaleや散布図作成にスケール変換を絡める処理を入れたいといった場合に、今回紹介するScaleオブジェクト、その構成要素であるTransformオブジェクトとInvertedTransformオブジェクトについて理解しておくことが必要です。



Background | PythonのスケールとScaleオブジェクト

散布図やヒストグラム、その他さまざまな図を作る際、軸のスケールを適切に設定する必要があります。Pythonの作図ライブラリMatplotlibでは、デフォルトの線形スケール以外に様々スケールでプロットすることができます。プロットしたいデータの分布に合わせて適切なスケールを選ぶことが必要です。

本シリーズの過去記事

では、線形スケールでは図を綺麗に表示できない例や、Matplotlibに予め定義されたスケールを使ってプロットする方法を解説しました。

今回の記事では、Matplotlibで用意されているScaleオブジェクトと、Scaleオブジェクトの構成要素であるTransformオブジェクト、InvertedTransformオブジェクトの概要について解説します。より詳しく知りたい方はMatplotlibのドキュメントページは(matplotlib.scale)もご覧ください。

Scaleオブジェクトの概要を理解していると、

  • 変換・逆変換関数を呼び出したい
  • 自作のスケールを定義したい
  • スケール変換を絡めた処理を実装したい

といった場合に役立ちます。今回は概要の解説だけとしますが、次回以降で実際にコードを書いてScaleオブジェクトについての理解を深めていきます。



Scaleオブジェクトの概要

Matplotlibでは、xxxScaleというオブジェクト(class)が用意されています。xxxの部分にはMatplotlibで予め用意されているLog, Logit, Symlog, Asinhなどが入ります。`xxxScale`のうち、予め用意されているのは

  1. LinearScale
  2. LogScale
  3. LogitScale
  4. SymmetricalLogScale
  5. AsinhScale
  6. FuncScale
  7. FuncScaleLog

の7つです。これら予め用意されている7つの他に、自分でオリジナルのスケール(Custom Scale)を定義することもできます。

図1. 予め用意されたScaleオブジェクトの一覧



Scaleオブジェクトの構成要素

Scaleオブジェクト(class)は概ね

  • __init__(): オブジェクト生成
  • get_transform(): 変換関数xxxTransformのオブジェクトを返す
  • set_default_locators_and_formatters(): 軸の目盛りをセットする

という3つの関数で構成されています。

例としてLogスケールを見てみましょう。LocScaleはMatplotlibライブラリのmatplotlib.scale.pyの276行目に下記のように定義されています(単純化のためコメント行は削除してあります)。

class LogScale(ScaleBase):
    name = 'log'

    def __init__(self, axis, *, base=10, subs=None, nonpositive="clip"):
        self._transform = LogTransform(base, nonpositive)
        self.subs = subs

    base = property(lambda self: self._transform.base)

    def set_default_locators_and_formatters(self, axis):
        axis.set_major_locator(LogLocator(self.base))
        axis.set_major_formatter(LogFormatterSciNotation(self.base))
        axis.set_minor_locator(LogLocator(self.base, self.subs))
        axis.set_minor_formatter(
            LogFormatterSciNotation(self.base,
                                    labelOnlyBase=(self.subs is not None)))

    def get_transform(self):
        return self._transform

    def limit_range_for_scale(self, vmin, vmax, minpos):
        if not np.isfinite(minpos):
            minpos = 1e-300  # Should rarely (if ever) have a visible effect.

        return (minpos if vmin <= 0 else vmin,
                minpos if vmax <= 0 else vmax)

class定義class LogScale(ScaleBase):の直後に

name = 'log'

と定義されており、オブジェクト.nameでスケールの名前を取得できることがわかります。Logスケールの場合は、前述の3つの関数に加えてlimit_range_for_scaleが追加されています。こちらの関数は描画する値を正の値のみに絞るために設けられています。

__init__()

Logスケールの例から簡単に情報を読み取ってみましょう。オブジェクト生成部分の__init__()の引数に着目すると、必須の引数としてaxisがあります。予めMatlitlibのaxisオブジェクトを

ax = plt.subplot(111)

などとして生成しておき、引数として渡す必要があります。このあたりの具体的な使い方はResultの章で取り扱います。

また、__init__()の中の処理では、

self._transform = LogTransform(base, nonpositive)

と`_transform `という要素が定義されています。Scaleオブジェクトでは予めTransformオブジェクト(変換関数)とInvertedTransformオブジェクト(逆変換関数)を用意しておく必要があります。

get_transform()

関数get_transform()では、

return self._transform

となっているのでself._transformを返します。つまり、Transformオブジェクト(変換関数)を返します。

set_default_locators_and_formatters

関数set_default_locators_and_formatters()では、プロットするときの軸の目盛りが設定されています。

axis.set_major_locator(LogLocator(self.base))         axis.set_major_formatter(LogFormatterSciNotation(self.base))         axis.set_minor_locator(LogLocator(self.base, self.subs))
axis.set_minor_formatter(LogFormatterSciNotation(self.base, labelOnlyBase=(self.subs is not None)))

と、大目盛りと小目盛りの位置とフォーマットが定義されています。



Transform(変換関数)とInvertedTransform(逆変換関数)

Scaleオブジェクト(class)の前提となるTransform(変換関数)とInvertedTransform(逆変換関数)オブジェクトについても触れておきます。

Transform(変換関数)オブジェクトの概要

Transformオブジェクトでは変換関数が定義されています。Logの場合を例にLogTransformオブジェクトを見てみましょう。matplotlib.scale.pyの218行目には

class LogTransform(Transform):
    input_dims = output_dims = 1

    def __init__(self, base, nonpositive='clip'):
        super().__init__()
        if base <= 0 or base == 1:
            raise ValueError('The log base cannot be <= 0 or == 1')
        self.base = base
        self._clip = _api.check_getitem(
            {"clip": True, "mask": False}, nonpositive=nonpositive)

    def __str__(self):
        return "{}(base={}, nonpositive={!r})".format(
            type(self).__name__, self.base, "clip" if self._clip else "mask")

    def transform_non_affine(self, values):
        with np.errstate(divide="ignore", invalid="ignore"):
            log = {np.e: np.log, 2: np.log2, 10: np.log10}.get(self.base)
            if log:  # If possible, do everything in a single call to NumPy.
                out = log(values)
            else:
                out = np.log(values)
                out /= np.log(self.base)
            if self._clip:
                out[values <= 0] = -1000
        return out

    def inverted(self):
        return InvertedLogTransform(self.base)

ここで重要なのは

  • transform_non_affine()
  • inverted()

の2つです。transform_non_affine()では変換関数が定義されています。transform_non_affine()で変換を定義しておくことで、オブジェクト.transform()で変換関数を呼び出すことができます。Resultの章で使い方の例を見せるので、ここではそんなもんかと思ってもらえばOKです。逆変換関数が定義されるinverted()では、InvertedLogTransform()を返しているだけです。

InvertedTransform(逆変換関数)オブジェクトの概要

InvertedTransformオブジェクトでは逆変換関数が定義されています。Logの場合を例にInvertedLogTransformオブジェクトを見てみましょう。matplotlib.scale.pyの259行目には

class InvertedLogTransform(Transform):
    input_dims = output_dims = 1

    def __init__(self, base):
        super().__init__()
        self.base = base

    def __str__(self):
        return f"{type(self).__name__}(base={self.base})"

    def transform_non_affine(self, values):
        return np.power(self.base, values)

    def inverted(self):
        return LogTransform(self.base)

ここでも重要なのは

  • transform_non_affine()
  • inverted()

の2つです。これら2つの定義はTransformオブジェクトのときと同じですが、transform_non_affine()ではLog変換の逆変換関数が定義され、inverted()ではLog変換が定義されることになります。



Scale、Transform、InvertedTransformの全体像

Scaleオブジェクト(class)の概要とその前提となるTransformオブジェクト、InvertedTransformオブジェクトについて解説してきました。この章の最後に、Scale、Transform、InvertedTransformの全体像をまとめておきます。

Scaleオブジェクトから変換・逆変換関数を参照する仕組み

Scale、Transform、InvertedTransformの相互関係を図2に示します。図2の参照関係に着目すると、Scaleオブジェクトから変換・逆変換関数を参照する仕組みがわかります。まず、Scaleオブジェクトの中のget_transform()からTransformが参照されます。Transformには変換関数に加えて、逆変換オブジェクトを呼び出すinveted()が定義されています。Transformのinverted()からはInvertedTransformが参照され、InvertedTransformのinverted()からはTransformが参照されるという相互参照の構造になっています。したがって、Scaleにget_transform()さえ定義されていれば、変換関数も逆変換関数も参照することができる仕組みになっています。

図2. Scale・Transform・InvertedTransformの相互関係

予め用意されたScale・Transform・InvertedTransformの一覧

Matplotlibで予め用意されたスケールについて、Scale・Transform・InvertedTransformの一覧を見ておきましょう。図3にScale・Transform・InvertedTransformの一覧を示します。Matplotlibで予め用意されたスケールについて、Scaleオブジェクトとその前提となるTransform、InvertedTransformは図3のように用意されています。Log変換、Logit変換、Logisic変換など、図3に記載された変換・逆変換関数はScaleオブジェクトから呼び出すことも可能です。その具体的なやりかたは次回以降に解説します。

図3. 予め用意されたScale・Transform・InvertedTransformの一覧



Conclusion | まとめ

最後までご覧頂きありがとうございます!
Pythonの作図ライブラリMatplotlibのScaleオブジェクトについて解説しました!

Matplotlibでは、Scale Classのオブジェクトを使ってスケール変換をすることができます。Scaleオブジェクトは変換関数を定義する部分と軸の目盛を定義する部分で構成されます。変換関数はTransform ClassのオブジェクトとInvetedTransform Classのオブジェクトによって定義されます。Matplotlibで予め用意されている7つのスケールについては、Scale・Transform・InvertedTransformがすべて定義されています。Scale・Transform・InvertedTransformの関係を理解しておくことは、次回以降解説するCustom Scale(自作のスケール)を作る際にも必要です。Scaleオブジェクトについて理解し、プロット方法の幅を広げましょう!

以上「Matplotlib | Scaleオブジェクトの使い方(Python軸スケール3)」でした!
またお会いしましょう!Ciao!



References | 参考

参考に本シリーズの記事と外部リンクをまとめます。

本シリーズの記事

外部リンク

コメント

タイトルとURLをコピーしました