2012-04-11 5 views
139

ことで、それはカットオフ私は、次の質問に精通しています:移動matplotlibの伝説がフィギュアボックス

Matplotlib savefig with a legend outside the plot

How to put the legend out of the plot

これらの質問で回答を持っているようです凡例が収まるように、軸の正確な縮みを聞くことができます。

しかし、軸を縮小することは、データを小さくして実際に解釈するのが困難になるため、理想的な解決策ではありません。特にその複合体があり、多くのことが進行中であるために大きな凡例が必要な場合

図の複雑な凡例の例は、プロットの凡例が実際には複数のデータポイントを完全に隠すため、 。

http://matplotlib.sourceforge.net/users/legend_guide.html#legend-of-complex-plots

私は何をできるようにしたいことは、動的に拡大図の凡例に対応するために、図のボックスのサイズを拡大しています。

import matplotlib.pyplot as plt 
import numpy as np 

x = np.arange(-2*np.pi, 2*np.pi, 0.1) 
fig = plt.figure(1) 
ax = fig.add_subplot(111) 
ax.plot(x, np.sin(x), label='Sine') 
ax.plot(x, np.cos(x), label='Cosine') 
ax.plot(x, np.arctan(x), label='Inverse tan') 
lgd = ax.legend(loc=9, bbox_to_anchor=(0.5,0)) 
ax.grid('on') 

最終ラベル「インバース・タンは」フィギュアボックスの外側を実際にどのように注意してください(とひどくカットオフになります! - ない出版品質) enter image description here

最後に、私はこれがあると言われてきましたRとLaTeXの通常の動作ですので、なぜこれがPythonでは難しいのか少し混乱しています...歴史的な理由はありますか?この問題に関してMatlabは同等に貧しいですか?

私はペーストビンhttp://pastebin.com/grVjc007上のこのコードの(わずかに)長いバージョン

+6

限り理由など、matplotlibのはしばらくR、インタラクティブなプロットを目指しているので、それはだだとして、そうではありません。 Matlabは正しく機能するには、図のサイズ変更、ズーム、または凡例の位置が更新されるたびに、軸のサイズ変更を心配する必要があります。 Ggplotなどは静的なので、matplotlibとmatlabはデフォルトでこれを行う傾向があります。伝えられるところによれば、 'tight_layout()'は伝説を考慮に入れて変更する必要があります。 –

+2

この質問については、matplotlibのユーザーメーリングリストでも議論しています。そこで、私は、savefig行を次のように調整することを提案しています: fig.savefig( 'samplefigure'、bbox_extra_artists =(lgd、)、bbox = 'tight') – jbbiomed

+3

私はmatplotlibがすべてがしかし、この伝説のものはあまりにも良いものです。私が伝説を外側に置くと、明らかにそれが見えるようにしたい。ウィンドウは、この巨大なリスケーリングの面倒を作るのではなく、収まるように拡大する必要があります。少なくとも、このオートスケーリングの動作を制御するには、デフォルトのTrueオプションが必要です。ユーザーがコントロールの名前でスケール番号を取得しようとすると、その逆を達成するために、うまくいかない数の再レンダリングを行うように強制します。 – Elliot

答えて

190

申し訳ありませんが、私は実際にはmatplotlib maillingリストから別の応答を得ました(Benjamin Rootに感謝します)。

私はsavefigコールを調整している探していますコード:

fig.savefig('samplefigure', bbox_extra_artists=(lgd,), bbox_inches='tight') 
#Note that the bbox_extra_artists must be an iterable 

これはtight_layout呼び出すと明らかに似ていますが、代わりにあなたはsavefigは計算に余分なアーティストを検討することができます。これは、実際には希望通りにFigureボックスのサイズを変更しました。

import matplotlib.pyplot as plt 
import numpy as np 

x = np.arange(-2*np.pi, 2*np.pi, 0.1) 
fig = plt.figure(1) 
ax = fig.add_subplot(111) 
ax.plot(x, np.sin(x), label='Sine') 
ax.plot(x, np.cos(x), label='Cosine') 
ax.plot(x, np.arctan(x), label='Inverse tan') 
handles, labels = ax.get_legend_handles_labels() 
lgd = ax.legend(handles, labels, loc='upper center', bbox_to_anchor=(0.5,-0.1)) 
ax.grid('on') 
fig.savefig('samplefigure', bbox_extra_artists=(lgd,), bbox_inches='tight') 

これが生成します。ここでは

enter image description here

+1

/!\ matplotlib> = 1.0以降でしか動作しないようです(Debian squeezeが0.99で動作しません) –

+1

これを動作させることができません:(lgdをsavefigに渡しますが、サイズは変更されません。私はサブプロットを使用していないかもしれません。 – 6005

+4

ああ!私はちょうどbbox_inches = "tight"を使用する必要がありました。ありがとう! – 6005

13

追加しました:を私はすぐにトリックを行う必要があります何かを見つけましたが、以下のコードの残りの部分はまた、代替手段を提供しています。

は、サブプロットの下部を上に移動する subplots_adjust()機能を使用します。

fig.subplots_adjust(bottom=0.2) # <-- Change the 0.02 to work for your plot. 

は、その後、あなたがそれをしたい凡例ボックスを取得するには、伝説のコマンドの伝説bbox_to_anchor部分的に相殺して遊びます。 figsizeを設定し、subplots_adjust(bottom=...)を使用すると、品質のプロットが得られるはずです。

オルタナティブ:に

fig = plt.figure(1) 

私は単に行を変更し

fig = plt.figure(num=1, figsize=(13, 13), dpi=80, facecolor='w', edgecolor='k') 

lgd = ax.legend(loc=9, bbox_to_anchor=(0.5,0)) 

を変更しました

lgd = ax.legend(loc=9, bbox_to_anchor=(0.5,-0.02)) 

これは私の画面(24インチCRTモニター)で正常に表示されます。

ここでfigsize=(M,N)は、FigureウィンドウをMインチ×Nインチに設定します。それはあなたのために右に見えるまでこれで遊ぶ。よりスケーラブルな画像形式に変換し、必要に応じてGIMPを使用して編集するか、グラフィックスを含めるときにLaTeX viewportオプションを使用してクロップします。

+0

これは現時点で最良の解決策であると思われますが、オートレポートジェネレータには適していません。実際にはすでにこのソリューションを使用していますが、本当の問題はmatplotlibが軸のbboxの外側にある凡例を動的に補正しないことです。 @Joeは言ったように、tight_layout *は、軸、タイトル、寓話だけでなく、もっと多くの機能を考慮する必要があります。私はこれをmatplotlibの機能要求として追加するかもしれません。 – jbbiomed

+0

も私のために、以前は切断されていたxlabelsに合うように十分な大きさの画像を得るために働いています –

+1

[ここ](http://matplotlib.org/examples/pylab_examples/subplots_adjust.html)は、matplotlib.org – Yojimbo

11

は別の、非常にマニュアルソリューションです。軸のサイズを定義することができ、それに従って詰め物が考慮されます(凡例と目盛りを含む)。それが誰かに役立つことを望みます。

例(軸のサイズが同じである!):

enter image description here

コード:

#================================================== 
# Plot table 

colmap = [(0,0,1) #blue 
     ,(1,0,0) #red 
     ,(0,1,0) #green 
     ,(1,1,0) #yellow 
     ,(1,0,1) #magenta 
     ,(1,0.5,0.5) #pink 
     ,(0.5,0.5,0.5) #gray 
     ,(0.5,0,0) #brown 
     ,(1,0.5,0) #orange 
     ] 


import matplotlib.pyplot as plt 
import numpy as np 

import collections 
df = collections.OrderedDict() 
df['labels']  = ['GWP100a\n[kgCO2eq]\n\nasedf\nasdf\nadfs','human\n[pts]','ressource\n[pts]'] 
df['all-petroleum long name'] = [3,5,2] 
df['all-electric'] = [5.5, 1, 3] 
df['HEV']   = [3.5, 2, 1] 
df['PHEV']   = [3.5, 2, 1] 

numLabels = len(df.values()[0]) 
numItems = len(df)-1 
posX = np.arange(numLabels)+1 
width = 1.0/(numItems+1) 

fig = plt.figure(figsize=(2,2)) 
ax = fig.add_subplot(111) 
for iiItem in range(1,numItems+1): 
    ax.bar(posX+(iiItem-1)*width, df.values()[iiItem], width, color=colmap[iiItem-1], label=df.keys()[iiItem]) 
ax.set(xticks=posX+width*(0.5*numItems), xticklabels=df['labels']) 

#-------------------------------------------------- 
# Change padding and margins, insert legend 

fig.tight_layout() #tight margins 
leg = ax.legend(loc='upper left', bbox_to_anchor=(1.02, 1), borderaxespad=0) 
plt.draw() #to know size of legend 

padLeft = ax.get_position().x0 * fig.get_size_inches()[0] 
padBottom = ax.get_position().y0 * fig.get_size_inches()[1] 
padTop = (1 - ax.get_position().y0 - ax.get_position().height) * fig.get_size_inches()[1] 
padRight = (1 - ax.get_position().x0 - ax.get_position().width) * fig.get_size_inches()[0] 
dpi  = fig.get_dpi() 
padLegend = ax.get_legend().get_frame().get_width()/dpi 

widthAx = 3 #inches 
heightAx = 3 #inches 
widthTot = widthAx+padLeft+padRight+padLegend 
heightTot = heightAx+padTop+padBottom 

# resize ipython window (optional) 
posScreenX = 1366/2-10 #pixel 
posScreenY = 0 #pixel 
canvasPadding = 6 #pixel 
canvasBottom = 40 #pixel 
ipythonWindowSize = '{0}x{1}+{2}+{3}'.format(int(round(widthTot*dpi))+2*canvasPadding 
              ,int(round(heightTot*dpi))+2*canvasPadding+canvasBottom 
              ,posScreenX,posScreenY) 
fig.canvas._tkcanvas.master.geometry(ipythonWindowSize) 
plt.draw() #to resize ipython window. Has to be done BEFORE figure resizing! 

# set figure size and ax position 
fig.set_size_inches(widthTot,heightTot) 
ax.set_position([padLeft/widthTot, padBottom/heightTot, widthAx/widthTot, heightAx/heightTot]) 
plt.draw() 
plt.show() 
#-------------------------------------------------- 
#================================================== 
+0

最初の 'plt.draw()'を 'ax.figure.canvas.draw()'に変更するまで、これはうまくいきませんでした。私は理由は分かりませんが、変更前は凡例のサイズが更新されていませんでした。 –

+0

これをGUIウィンドウで使用する場合は、 'fig.set_size_inches(widthTot、heightTot)'を 'fig.set_size_inches(widthTot、heightTot、forward = True)'に変更する必要があります。 –