【matplotlib】なにかプロットされているか確認する関数
matplotlibで描画する関数を自分で定義する際、すでになにかがプロットされている場合は新しいAxesにプロットし、Axesは存在するけれど何もプロットされていない場合はそのAxesにプロットする、のような関数を定義したかったのだが、少しハマったのでメモ。
最終的に最善と思われる関数を定義した。
Sponsored by Google AdSense
実装
# モジュールのインポート
import sys
import matplotlib
import matplotlib.patches
# Pythonのバージョンを表示
print(sys.version)
3.11.6 | packaged by conda-forge | (main, Oct 3 2023, 10:37:07) [Clang 15.0.7 ]
# matplotlibのバージョンを表示
matplotlib.__version__
'3.8.0'
is_plotted関数
matplotlibでCurrent Axesに何かプロットされているかを判定する関数を定義した。
なにかプロットされているときはTrue
、なにもプロットされていないときはFalse
が返る (はず)。
from typing import Optional
import matplotlib.pyplot as plt
import matplotlib.axes
def is_plotted(ax: Optional[matplotlib.axes.Axes] = None) -> bool:
"""Check if an axes has been plotted on.
Parameters
----------
ax : matplotlib.axes.Axes | None
The axes to check. If None, the current axes will be used.
optional, by default None
Returns
-------
bool
True if the axes has been plotted on, False otherwise.
"""
ax = plt.gca() if ax is None else ax
return any(
len(getattr(ax, _key))
for _key in dir(ax)
if isinstance(getattr(ax, _key), matplotlib.axes.Axes.ArtistList)
)
仕様確認
何もプロットしてないとき
is_plotted()
False
fig, ax = plt.subplots(facecolor="w")
is_plotted(ax)
False
scatter
ax.scatter([1, 2, 3], [1, 2, 3])
ax.scatter([3, 2, 1], [2, 1, 3])
display(fig)
is_plotted(ax)
True
# プロットを消去
plt.close()
plot
plt.plot([1, 2, 3], [1, 2, 3])
is_plotted()
True
text系
fig, ax = plt.subplots(facecolor="w")
ax.annotate("test", xy=(0.5, 0.5))
is_plotted(ax)
True
# プロットを消去
plt.close()
fig, ax = plt.subplots(facecolor="w")
ax.text(0.5, 0.5, "test")
is_plotted(ax)
True
# プロットを消去
plt.close()
図形
fig, ax = plt.subplots(facecolor="w")
ax.add_artist(matplotlib.patches.Circle((0.5, 0.5), 0.5))
is_plotted()
True
# プロットを消去
plt.close()
画像
# fmt: off
img = [
[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],
[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],
[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],
[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],
[0,0,0,0,0,0,0,0,11,130,166,209,253,84,18,18,0,0,0,0,0,0,0,0,0,0,0,0],
[0,0,0,0,0,0,0,8,171,252,253,252,166,21,106,199,21,0,0,0,0,0,0,0,0,0,0,0],
[0,0,0,0,0,0,0,171,252,252,223,91,2,0,9,200,202,11,0,0,0,0,0,0,0,0,0,0],
[0,0,0,0,0,0,0,179,252,226,35,0,0,0,0,148,252,21,0,0,0,0,0,0,0,0,0,0],
[0,0,0,0,0,0,0,4,42,24,0,0,0,0,0,192,252,21,0,0,0,0,0,0,0,0,0,0],
[0,0,0,0,0,0,0,0,0,0,0,0,0,0,124,255,204,9,0,0,0,0,0,0,0,0,0,0],
[0,0,0,0,0,0,0,0,0,0,0,0,0,57,225,253,89,0,0,0,0,0,0,0,0,0,0,0],
[0,0,0,0,0,0,0,0,0,0,31,162,232,246,252,253,202,30,7,0,0,0,0,0,0,0,0,0],
[0,0,0,0,0,0,0,0,0,71,218,252,252,252,155,102,189,247,170,37,0,0,0,0,0,0,0,0],
[0,0,0,0,0,0,0,0,0,62,253,252,199,77,7,0,0,47,217,235,45,0,0,0,0,0,0,0],
[0,0,0,0,0,0,0,0,0,0,18,106,9,0,0,0,0,0,27,167,237,55,0,0,0,0,0,0],
[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,27,218,196,7,0,0,0,0,0],
[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,96,252,29,0,0,0,0,0],
[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,43,252,126,0,0,0,0,0],
[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,43,252,126,0,0,0,0,0],
[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,61,253,127,0,0,0,0,0],
[0,0,0,0,0,0,0,8,28,0,0,0,0,0,0,0,0,0,0,36,183,252,91,0,0,0,0,0],
[0,0,0,0,0,0,0,215,142,13,0,0,0,0,0,0,0,20,92,179,253,201,11,0,0,0,0,0],
[0,0,0,0,0,0,0,135,252,217,152,64,64,64,64,126,169,246,252,252,199,21,0,0,0,0,0,0],
[0,0,0,0,0,0,0,4,113,147,209,252,252,252,252,253,252,155,147,59,18,0,0,0,0,0,0,0],
[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],
[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],
[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],
[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],
]
# fmt: on
fig, ax = plt.subplots(facecolor="w")
ax.imshow(img, cmap="gray")
ax.axis("off")
is_plotted(ax)
True
なぜ判定できるのか
matplotlib.axes.Axes.ArtisList
オブジェクトは、Axesに紐づくArtistのリストで、ここに描画されたものが保存されているため。
ArtistList
をAxes
オブジェクトのAttributeの中から全て取り出して、1つでも何かArtistが保存されていれば何かが描画されていると判定している。
最初に提示したis_plotted
関数は内包表記で書いているが、これを書き下すと以下のように書ける。
from typing import Optional
import matplotlib.pyplot as plt
import matplotlib.axes
def is_plotted(ax: Optional[matplotlib.axes.Axes] = None) -> bool:
"""Check if an axes has been plotted on.
Parameters
----------
ax : matplotlib.axes.Axes | None
The axes to check. If None, the current axes will be used.
optional, by default None
Returns
-------
bool
True if the axes has been plotted on, False otherwise.
"""
ax = plt.gca() if ax is None else ax
# ax: matplotlib.axes.Axesオブジェクトの属性の名称を列挙
for _key in dir(ax):
# axオブジェクトの属性の値を取得
_attribute = getattr(ax, _key)
# axオブジェクトの属性の値がmatplotlib.axes.Axes.ArtistList型の場合
if isinstance(_attribute, matplotlib.axes.Axes.ArtistList):
# axオブジェクトの属性の値が空でない場合
if len(_attribute) > 0:
# なにか入っているのでTrueを返す
return True
# for文が最後まで実行された場合
# = すべてのArtistListが空の場合
else:
# 何も描画されていないのでFalseを返す
return False
コメント
もっと適切な関数などありましたらぜひ教えてください。
どうでもいいけれど、blackで整形してほしくないところを無視するマジックコマンド (# fmt: off
と# fmt: on
で囲う) の存在を初めて知った。
flake8でいうところの# noqa
のような存在で、重宝しそう(厳密にはそれらは使わない方が理想なのだろうが。。。)。