Python_matplotlib の凡例は外側に保存すると不完全になる
前回も触れましたが、以下のコードでベクトルプロットを保存すると、外側に配置された凡例が不完全に表示されることがよくあります。
import numpy as np
import matplotlib.pyplot as plt
fig, ax = plt.subplots()
x1 = np.random.uniform(-10, 10, size=20)
x2 = np.random.uniform(-10, 10, size=20)
#print(x1)
#print(x2)
number = []
x11 = []
x12 = []
for i in range(20):
number.append(i+1)
x11.append(i+1)
x12.append(i+1)
plt.figure(1)
# you can specify the marker size two ways directly:
plt.plot(number, x1, 'bo', markersize=20,label='a') # blue circle with size 20
plt.plot(number, x2, 'ro', ms=10,label='b') # ms is just an alias for markersize
lgnd=plt.legend(bbox_to_anchor=(1.05, 1), loc=2, borderaxespad=0,numpoints=1,fontsize=10)
lgnd.legendHandles[0]. _legmarker.set_markersize(16)
lgnd.legendHandles[1]. _legmarker.set_markersize(10)
plt.show()
fig.savefig('scatter.png',dpi=600)
scatter.pngとして保存した後の効果は以下の通りです。
画像の右上に配置された凡例は、左側のほんの一部しか表示されていないことがわかります。
その理由は簡単で、savefig()関数を使ってベクター画像を保存するとき、その画像はバウンディングボックス(bbox)で囲まれ、その中に入る画像だけが保存されるので、凡例がそのボックス内に完全に入らない場合、保存されないからです。
仕組みが分かれば、問題解決も簡単です。
ここで、2つの解決策を提案します。
1. bboxに完全に入らない画像を取り、完全に入るように移動させれば、bboxに遮られた画像が完成する(画像をbboxの中に移動させる)。
2. 特にbboxの外に落ちやすい凡例については、その画像を完全に含むようにbboxのサイズを変更する(bboxを拡張して画像を完全に含むようにする)。
この問題を解決するために、それぞれの考え方に基づく2つのアプローチを以下に紹介します。
1. 関数を使用する subplots_adjust()
公式ドキュメントにあるように、subplots_adjust()関数はサブプロットのレイアウトを調整するために使用されます。6つのパラメータを持ち、そのうち4つはleft, right, bottom, topでそれぞれサブプロットの左、右、下、上の位置を調整し、残りの2つのパラメータwspace, hspaceでサブグラフ間の左右および上下の距離を調整する。
初期値は以下の通りです。
上の図を例にして、今度は凡例から考えてみましょう。 右側 が表示されていない場合、subplots_adjust()関数のrightパラメータを調整して、その位置を少し左にずらし、rightパラメータのデフォルト値を0.9から0.8に変更すると、次のように完全な凡例が得られます。
import numpy as np
import matplotlib.pyplot as plt
fig, ax = plt.subplots()
x1 = np.random.uniform(-10, 10, size=20)
x2 = np.random.uniform(-10, 10, size=20)
#print(x1)
#print(x2)
number = []
x11 = []
x12 = []
for i in range(20):
number.append(i+1)
x11.append(i+1)
x12.append(i+1)
plt.figure(1)
# you can specify the marker size two ways directly:
plt.plot(number, x1, 'bo', markersize=20,label='a') # blue circle with size 20
plt.plot(number, x2, 'ro', ms=10,label='b') # ms is just an alias for markersize
lgnd=plt.legend(bbox_to_anchor=(1.05, 1), loc=2, borderaxespad=0,numpoints=1,fontsize=10)
lgnd.legendHandles[0]. _legmarker.set_markersize(16)
lgnd.legendHandles[1]. _legmarker.set_markersize(10)
fig.subplots_adjust(right=0.8)
plt.show()
fig.savefig('scatter1.png',dpi=600)
scatter1.png として保存した後、scatter.png との比較は次のようになります。
scatter1.pngの凡例が全体的に表示されていることがわかります。これは、画像の右側の位置を左にずらして、全体として保存された画像に含まれることで行われます。
同様に、凡例が画像の下側にある場合、savefig() で保存すると不完全になるので、関数 subplots_adjust() を修正して、保存のために画像を横取りするボックス、すなわち後述の bbox に含まれるように上に移動する必要があります。
import numpy as np
import matplotlib.pyplot as plt
fig, ax = plt.subplots()
x1 = np.random.uniform(-10, 10, size=20)
x2 = np.random.uniform(-10, 10, size=20)
#print(x1)
#print(x2)
number = []
x11 = []
x12 = []
for i in range(20):
number.append(i+1)
x11.append(i+1)
x12.append(i+1)
plt.figure(1)
# you can specify the marker size two ways directly:
plt.plot(number, x1, 'bo', markersize=20,label='a') # blue circle with size 20
plt.plot(number, x2, 'ro', ms=10,label='b') # ms is just an alias for markersize
lgnd=plt.legend(bbox_to_anchor=(0.4, -0.1), loc=2, borderaxespad=0,numpoints=1,fontsize=10)
lgnd.legendHandles[0]. _legmarker.set_markersize(16)
lgnd.legendHandles[1]. _legmarker.set_markersize(10)
plt.show()
fig.savefig('scatter#1.png',dpi=600)
というのは subplots_adjust() はデフォルトの底値が0.1なので、底を上に移動するために fig.subplots_adjust(bottom=0.2) を追加し、次のように修正します。
import numpy as np
import matplotlib.pyplot as plt
fig, ax = plt.subplots()
x1 = np.random.uniform(-10, 10, size=20)
x2 = np.random.uniform(-10, 10, size=20)
#print(x1)
#print(x2)
number = []
x11 = []
x12 = []
for i in range(20):
number.append(i+1)
x11.append(i+1)
x12.append(i+1)
plt.figure(1)
# you can specify the marker size two ways directly:
plt.plot(number, x1, 'bo', markersize=20,label='a') # blue circle with size 20
plt.plot(number, x2, 'ro', ms=10,label='b') # ms is just an alias for markersize
lgnd=plt.legend(bbox_to_anchor=(0.4, -0.1), loc=2, borderaxespad=0,numpoints=1,fontsize=10)
lgnd.legendHandles[0]. _legmarker.set_markersize(16)
lgnd.legendHandles[1]. _legmarker.set_markersize(10)
fig.subplots_adjust(bottom=0.2)
plt.show()
fig.savefig('scatter#1.png',dpi=600)
効果の比較
他の場所でも凡例は同じです。
2. 関数を使用する savefig()
前回のブログで紹介したように、savefig()関数のfname、dpi、formatの3つの引数でベクター画像を保存することができますが、今度は関数のもう一つの引数である bbox_inches を作る
グラフに保存されていない凡例を含める。
下図に示すように、bbox_inchesはグラフのbbox、すなわちバウンディングボックスを調整する役割を果たします。
ご覧のように、bbox_inchesを「tight」に設定すると、その画像からよりタイトな(きつい)バウンディングボックスのbboxを計算し、その選択したボックスで画像を保存することができます。
ここでいうタイトなバウンディングボックスは、画像を完全に含む矩形であるべきですが、画像から一定のパディング距離を置いて、同じように 最小バウンディングボックス (ミニマムバウンディングボックス)ですが、個人的には、若干の違いがあります。単位もインチです。
こうすることで、凡例がbboxに含まれるようになり、その結果、保存されます。
フルコードです。
import numpy as np
import matplotlib.pyplot as plt
fig, ax = plt.subplots()
x1 = np.random.uniform(-10, 10, size=20)
x2 = np.random.uniform(-10, 10, size=20)
#print(x1)
#print(x2)
number = []
x11 = []
x12 = []
for i in range(20):
number.append(i+1)
x11.append(i+1)
x12.append(i+1)
plt.figure(1)
# you can specify the marker size two ways directly:
plt.plot(number, x1, 'bo', markersize=20,label='a') # blue circle with size 20
plt.plot(number, x2, 'ro', ms=10,label='b') # ms is just an alias for markersize
lgnd=plt.legend(bbox_to_anchor=(1.05, 1), loc=2, borderaxespad=0,numpoints=1,fontsize=10)
lgnd.legendHandles[0]. _legmarker.set_markersize(16)
lgnd.legendHandles[1]. _legmarker.set_markersize(10)
#fig.subplots_adjust(right=0.8)
plt.show()
fig.savefig('scatter2.png',dpi=600,bbox_inches='height')
scatter2.pngとして保存された、scatter.png、scatter1.png、scatter2.pngの比較画像です。
ご覧のように、最初の方法であるscatter1.pngの考え方は、切片を保存するために使用したbboxをそのままにして、画像の右端を左に移動させることであり、2番目の方法であるscatter2.pngの考え方は、切片を保存するために使用したbboxをそのまま画像全体に拡大しながら、すべてを含めることである。
備考 : savefig() には、さらに2つの指定パラメータがあります。
そのうちの1つは パッドインチス これは、直前の bbox_inches が 'tight' である場合に、画像と bbox の間のパディングの距離を調整するために使用されますが、ここでは設定する必要がなく、デフォルト値を選択するだけです。
個人的には、pad_inches パラメータを 0、つまり pad_inches=0 にすると、切片が保存される bbox は最小バウンディングボックスとなります。
もうひとつは bbox_extra_artists これは、画像のbboxを計算して他の要素も含めるために使用されます。
例として、画像の左側に別のテキストボックスのテキストを追加し、画像保存時にそのテキストボックスをbboxに含めたい場合、パラメータbbox_extra_artistsを使ってテキストを含めることができます(実際には、bbox_extra_artistsが使われていなくても保存画像にテキストは含まれます)。
import numpy as np
import matplotlib.pyplot as plt
fig, ax = plt.subplots()
x1 = np.random.uniform(-10, 10, size=20)
x2 = np.random.uniform(-10, 10, size=20)
#print(x1)
#print(x2)
number = []
x11 = []
x12 = []
for i in range(20):
number.append(i+1)
x11.append(i+1)
x12.append(i+1)
plt.figure(1)
# you can specify the marker size two ways directly:
plt.plot(number, x1, 'bo', markersize=20,label='a') # blue circle with size 20
plt.plot(number, x2, 'ro', ms=10,label='b') # ms is just an alias for markersize
lgnd=plt.legend(bbox_to_anchor=(1.05, 1), loc=2, borderaxespad=0,numpoints=1,fontsize=10)
lgnd.legendHandles[0]. _legmarker.set_markersize(16)
lgnd.legendHandles[1]. _legmarker.set_markersize(10)
text = ax.text(-0.3,1, "test", transform=ax.transAxes)
#fig.subplots_adjust(right=0.8)
plt.show()
fig.savefig('scatter3.png',dpi=600, bbox_extra_artists=(lgnd,text),bbox_inches='tight')
効果を表示します。
bboxに含まれない要素がある場合、このパラメータを使用することを検討してください。
関連
-
TypeErrorの解決策:Unicodeエラーへの強制力
-
concat を使用して 2 つのデータフレームを結合する際のエラー
-
pythonBug:AttributeError: タイプオブジェクト 'datetime.datetime' は属性 'datetime' を持たない。
-
ImportError: Windows の Django でプロジェクトを作成するとき、django.core solution という名前のモジュールがない。
-
Python27 PILソリューションという名前のモジュールがない
-
ImportError を解決します。pandas をインストールした後に 'pandas' という名前のモジュールがない。
-
Pythonエラー解決] 'urllib2'という名前のモジュールがない解決方法
-
Python3+BeautifulSoupがUnicodeEncodeErrorを報告:'charmap' codec can't encode characters in position
-
パイソン] Python パイソンミニゲーム - 欲張りスネークアドベンチャー
-
解決策 UnicodeDecodeError: 'gbk' コーデックは、位置 21804 のバイト 0x8b をデコードできません: 不正なマルチバイト配列です。
最新
-
nginxです。[emerg] 0.0.0.0:80 への bind() に失敗しました (98: アドレスは既に使用中です)
-
htmlページでギリシャ文字を使うには
-
ピュアhtml+cssでの要素読み込み効果
-
純粋なhtml + cssで五輪を実現するサンプルコード
-
ナビゲーションバー・ドロップダウンメニューのHTML+CSSサンプルコード
-
タイピング効果を実現するピュアhtml+css
-
htmlの選択ボックスのプレースホルダー作成に関する質問
-
html css3 伸縮しない 画像表示効果
-
トップナビゲーションバーメニュー作成用HTML+CSS
-
html+css 実装 サイバーパンク風ボタン
おすすめ
-
Pythonです。pandasのiloc, loc, ixの違いと連携について
-
Ubuntu pip AttributeError: 'module' オブジェクトに '_main' 属性がない。
-
ImportError: scipy'という名前のモジュールがありません。
-
Pythonモジュールの簡単な説明(とても詳しいです!)。
-
エラーの原因の1つ: 'encoding'はこの関数の無効なキーワード引数です。
-
プログラム実行中にPythonの例外が発生しました。TypeError: 'NoneType' オブジェクトは呼び出し可能ではありません。
-
Pythonのsum関数でTypeError: unsupported operand type(s) for +: 'int' and 'list' エラーを解決する。
-
Python で実行 TypeError: + でサポートされていないオペランド型: 'float' および 'str'.
-
Pythonソケットプログラミング [WinError 10061] ターゲットコンピュータがアクティブに拒否しているため、接続できない。
-
Python2.7のエンコード問題:UnicodeDecodeError: 'ascii' codec can't decode byte 0xe8 in position... 解決方法