मैं matplotlib के साथ कई सबप्लॉट्स के लिए एक एकल किंवदंती कैसे बनाऊं?


166

मैं एक ही प्रकार की जानकारी की साजिश रच रहा हूं, लेकिन विभिन्न देशों के लिए, मैटलपोटलिब के साथ कई सबप्लॉट हैं। यही है, मेरे पास एक 3x3 ग्रिड पर 9 प्लॉट हैं, सभी लाइनों के लिए समान हैं (निश्चित रूप से, प्रति पंक्ति अलग-अलग मूल्य)।

हालाँकि, मुझे यह पता नहीं चला है कि एक बार में केवल एक ही लीजेंड को कैसे रखा जाए (क्योंकि सभी 9 सबप्लॉट्स में समान लाइनें हैं)।

मैं उसको कैसे करू?

जवाबों:


160

एक अच्छा कार्य भी है जिसे get_legend_handles_labels()आप अंतिम अक्ष पर कॉल कर सकते हैं (यदि आप उन पर पुनरावृति करते हैं) जो आपको label=तर्क से आवश्यक सब कुछ एकत्र करेगा :

handles, labels = ax.get_legend_handles_labels()
fig.legend(handles, labels, loc='upper center')

13
यह शीर्ष उत्तर होना चाहिए।
n

1
यह वास्तव में बहुत अधिक उपयोगी उत्तर है! यह मेरे लिए एक और अधिक जटिल मामले की तरह ही काम करता था।
गमरेल

1
सही जवाब!
डोरघम

4
मैं सबप्लॉट्स के लिए किंवदंती को कैसे हटाऊं?
बीएनडी

5
बस इस महान जवाब में जोड़ने के लिए। यदि आपके पास अपने भूखंडों पर द्वितीयक y अक्ष है और उन्हें इन दोनों को मिलाने की आवश्यकता है, तो इसका उपयोग करें:handles, labels = [(a + b) for a, b in zip(ax1.get_legend_handles_labels(), ax2.get_legend_handles_labels())]
Bill

114

अनुमान लगाया जा सकता है कि आप क्या देख रहे हैं: http://matplotlib.org/api/pyplot_api.html#matplotlib.pyplot.figlegend

यहाँ उदाहरण: http://matplotlib.org/examples/pylab_examples/figlegend_dem2.html

एक और उदाहरण:

plt.figlegend( lines, labels, loc = 'lower center', ncol=5, labelspacing=0. )

या:

fig.legend( lines, labels, loc = (0.5, 0), ncol=5 )

1
मैं उन पंक्तियों को जानता हूं, जिन्हें मैं किंवदंती में रखना चाहता हूं, लेकिन मुझे linesतर्क के लिए चर कैसे मिलेगा legend?
patapouf_ai 12

1
@patapouf_ai linesपरिणामों की एक सूची है जो axes.plot()(यानी, प्रत्येक axes.plotया समान दिनचर्या एक "लाइन" देता है) से लौटा दी जाती है। इससे जुड़ा उदाहरण भी देखें।

17

figureकई कुल्हाड़ियों के साथ एक एकल किंवदंती की स्वचालित स्थिति के लिए , जैसे कि उन लोगों के साथ प्राप्त किया जाता है subplots(), निम्नलिखित समाधान वास्तव में अच्छी तरह से काम करता है:

plt.legend( lines, labels, loc = 'lower center', bbox_to_anchor = (0,-0.1,1,1),
            bbox_transform = plt.gcf().transFigure )

के साथ bbox_to_anchorऔर bbox_transform=plt.gcf().transFigureआप के figureलिए एक संदर्भ के एक नए बाउंडिंग बॉक्स को परिभाषित कर रहे हैं loc(0,-0.1,1,1)किंवदंती को अन्य कलाकारों पर रखे जाने से रोकने के लिए इस गुलदस्ते बॉक्स को थोड़ा नीचे की ओर ले जाते हुए उपयोग करें।

OBS: इस समाधान का उपयोग तब करें जब आप उपयोग करते हैं fig.set_size_inches()और इससे पहले कि आप उपयोग करते हैंfig.tight_layout()


1
या simpy loc='upper center', bbox_to_anchor=(0.5, 0), bbox_transform=plt.gcf().transFigureऔर यह सुनिश्चित करने के लिए ओवरलैप नहीं होगा।
जोसिपोविक

2
मुझे अभी भी यकीन नहीं है कि क्यों, लेकिन एवर्ट का समाधान मेरे लिए काम नहीं करता था - किंवदंती कट रही थी। इस समाधान (davor की टिप्पणी के साथ) ने बहुत सफाई से काम किया - किंवदंती को उम्मीद के मुताबिक रखा गया और पूरी तरह से दिखाई दिया। धन्यवाद!
को स्थापित करें

16

आपको बस एक बार किंवदंती के लिए पूछना है, अपने पाश के बाहर।

उदाहरण के लिए, इस मामले में मेरे पास 4 उपप्लॉट हैं, एक ही पंक्तियों के साथ, और एक एकल किंवदंती।

from matplotlib.pyplot import *

ficheiros = ['120318.nc', '120319.nc', '120320.nc', '120321.nc']

fig = figure()
fig.suptitle('concentration profile analysis')

for a in range(len(ficheiros)):
    # dados is here defined
    level = dados.variables['level'][:]

    ax = fig.add_subplot(2,2,a+1)
    xticks(range(8), ['0h','3h','6h','9h','12h','15h','18h','21h']) 
    ax.set_xlabel('time (hours)')
    ax.set_ylabel('CONC ($\mu g. m^{-3}$)')

    for index in range(len(level)):
        conc = dados.variables['CONC'][4:12,index] * 1e9
        ax.plot(conc,label=str(level[index])+'m')

    dados.close()

ax.legend(bbox_to_anchor=(1.05, 0), loc='lower left', borderaxespad=0.)
         # it will place the legend on the outer right-hand side of the last axes

show()

3
figlegend, जैसा कि एवर्ट द्वारा आत्मसात किया गया था, एक बेहतर समाधान प्रतीत होता है;)
कार्ला

11
की समस्या fig.legend()यह है कि इसके लिए सभी रेखाओं (भूखंडों) की पहचान की आवश्यकता होती है ... जैसा कि, प्रत्येक उपप्लॉट के लिए, मैं लाइनों को उत्पन्न करने के लिए एक लूप का उपयोग कर रहा हूं, एकमात्र समाधान जो मैंने इसे दूर करने के लिए लगाया है, वह है खाली सूची बनाने से पहले दूसरा लूप, और फिर लाइनों को जोड़ दें क्योंकि वे बनाए जा रहे हैं ... फिर मैं इस सूची को fig.legend()फ़ंक्शन के तर्क के रूप में उपयोग करता हूं ।
कार्ला

ऐसा ही एक सवाल यहाँ
emmmphd

dadosवहाँ क्या है ?
श्यामखण्डका

1
@Shyamkkhadka, मेरी मूल स्क्रिप्ट dadosमें netCDF4 फ़ाइल से एक डेटासेट था (सूची में परिभाषित प्रत्येक फ़ाइल के लिए ficheiros)। प्रत्येक लूप में, एक अलग फ़ाइल पढ़ी जाती है और एक सबप्लॉट को आकृति में जोड़ा जाता है।
कार्ला

14

मैंने देखा है कि कोई भी उत्तर किसी भी लीजेंड के साथ एक छवि प्रदर्शित नहीं करता है जो विभिन्न उप-खंडों में कई घटता का संदर्भ देता है, इसलिए मुझे आपको उत्सुक दिखाने के लिए एक ... दिखाना होगा ...

यहां छवि विवरण दर्ज करें

अब, आप कोड को देखना चाहते हैं , है ना?

from numpy import linspace
import matplotlib.pyplot as plt

# Calling the axes.prop_cycle returns an itertoools.cycle

color_cycle = plt.rcParams['axes.prop_cycle']()

# I need some curves to plot

x = linspace(0, 1, 51)
f1 = x*(1-x)   ; lab1 = 'x - x x'
f2 = 0.25-f1   ; lab2 = '1/4 - x + x x' 
f3 = x*x*(1-x) ; lab3 = 'x x - x x x'
f4 = 0.25-f3   ; lab4 = '1/4 - x x + x x x'

# let's plot our curves (note the use of color cycle, otherwise the curves colors in
# the two subplots will be repeated and a single legend becomes difficult to read)
fig, (a13, a24) = plt.subplots(2)

a13.plot(x, f1, label=lab1, **next(color_cycle))
a13.plot(x, f3, label=lab3, **next(color_cycle))
a24.plot(x, f2, label=lab2, **next(color_cycle))
a24.plot(x, f4, label=lab4, **next(color_cycle))

# so far so good, now the trick

lines_labels = [ax.get_legend_handles_labels() for ax in fig.axes]
lines, labels = [sum(lol, []) for lol in zip(*lines_labels)]

# finally we invoke the legend (that you probably would like to customize...)

fig.legend(lines, labels)
plt.show()

दो लाइनें

lines_labels = [ax.get_legend_handles_labels() for ax in fig.axes]
lines, labels = [sum(lol, []) for lol in zip(*lines_labels)]

एक स्पष्टीकरण के लायक है - इस उद्देश्य के लिए मैंने एक फ़ंक्शन में मुश्किल भाग को कूटबद्ध किया है, कोड की सिर्फ 4 पंक्तियाँ लेकिन भारी टिप्पणी की गई है

def fig_legend(fig, **kwdargs):

    # generate a sequence of tuples, each contains
    #  - a list of handles (lohand) and
    #  - a list of labels (lolbl)
    tuples_lohand_lolbl = (ax.get_legend_handles_labels() for ax in fig.axes)
    # e.g. a figure with two axes, ax0 with two curves, ax1 with one curve
    # yields:   ([ax0h0, ax0h1], [ax0l0, ax0l1]) and ([ax1h0], [ax1l0])

    # legend needs a list of handles and a list of labels, 
    # so our first step is to transpose our data,
    # generating two tuples of lists of homogeneous stuff(tolohs), i.e
    # we yield ([ax0h0, ax0h1], [ax1h0]) and ([ax0l0, ax0l1], [ax1l0])
    tolohs = zip(*tuples_lohand_lolbl)

    # finally we need to concatenate the individual lists in the two
    # lists of lists: [ax0h0, ax0h1, ax1h0] and [ax0l0, ax0l1, ax1l0]
    # a possible solution is to sum the sublists - we use unpacking
    handles, labels = (sum(list_of_lists, []) for list_of_lists in tolohs)

    # call fig.legend with the keyword arguments, return the legend object

    return fig.legend(handles, labels, **kwdargs)

पुनश्च मैं पहचानता हूं कि sum(list_of_lists, [])सूचियों की सूची को समतल करने के लिए वास्तव में अक्षम विधि है, लेकिन ness मुझे इसकी कॉम्पैक्टनेस से प्यार है, cur आमतौर पर कुछ सबप्लॉट में कुछ घटता है और pl Matplotlib और दक्षता? ;-)


3

खेल के बजाय देर से, मैं यहाँ एक और समाधान दूंगा क्योंकि यह अभी भी Google पर दिखाने के लिए पहले लिंक में से एक है। Matplotlib 2.2.2 का उपयोग करके, इसे ग्रिडस्पीक फीचर का उपयोग करके प्राप्त किया जा सकता है। नीचे दिए गए उदाहरण में नीचे की ओर दिखाए गए किंवदंती के साथ 2x2 के फैशन में व्यवस्थित चार सबप्लोट्स हैं। किंवदंती को एक निश्चित स्थान पर रखने के लिए तल पर एक 'अशुद्ध' अक्ष बनाया जाता है। 'अशुद्ध' अक्ष तब बंद हो जाता है इसलिए केवल किंवदंती शो। परिणाम: https://i.stack.imgur.com/5LUWM.png

import matplotlib.pyplot as plt
import matplotlib.gridspec as gridspec

#Gridspec demo
fig = plt.figure()
fig.set_size_inches(8,9)
fig.set_dpi(100)

rows   = 17 #the larger the number here, the smaller the spacing around the legend
start1 = 0
end1   = int((rows-1)/2)
start2 = end1
end2   = int(rows-1)

gspec = gridspec.GridSpec(ncols=4, nrows=rows)

axes = []
axes.append(fig.add_subplot(gspec[start1:end1,0:2]))
axes.append(fig.add_subplot(gspec[start2:end2,0:2]))
axes.append(fig.add_subplot(gspec[start1:end1,2:4]))
axes.append(fig.add_subplot(gspec[start2:end2,2:4]))
axes.append(fig.add_subplot(gspec[end2,0:4]))

line, = axes[0].plot([0,1],[0,1],'b')           #add some data
axes[-1].legend((line,),('Test',),loc='center') #create legend on bottommost axis
axes[-1].set_axis_off()                         #don't show bottommost axis

fig.tight_layout()
plt.show()

3

यदि आप बार चार्ट के साथ सबप्लॉट का उपयोग कर रहे हैं, तो प्रत्येक बार के लिए अलग-अलग रंग के साथ। यह अपने आप का उपयोग कर कलाकृतियों को बनाने के लिए तेज़ हो सकता हैmpatches

कहो कि आपके पास अलग-अलग रंगों के साथ चार बार हैं जैसा कि r m c kआप किंवदंती को निम्नानुसार सेट कर सकते हैं

import matplotlib.patches as mpatches
import matplotlib.pyplot as plt
labels = ['Red Bar', 'Magenta Bar', 'Cyan Bar', 'Black Bar']


#####################################
# insert code for the subplots here #
#####################################


# now, create an artist for each color
red_patch = mpatches.Patch(facecolor='r', edgecolor='#000000') #this will create a red bar with black borders, you can leave out edgecolor if you do not want the borders
black_patch = mpatches.Patch(facecolor='k', edgecolor='#000000')
magenta_patch = mpatches.Patch(facecolor='m', edgecolor='#000000')
cyan_patch = mpatches.Patch(facecolor='c', edgecolor='#000000')
fig.legend(handles = [red_patch, magenta_patch, cyan_patch, black_patch],labels=labels,
       loc="center right", 
       borderaxespad=0.1)
plt.subplots_adjust(right=0.85) #adjust the subplot to the right for the legend

1
+1 सबसे अच्छा! मैं इसे इस तरह से सीधे plt.legendअपने सभी सबप्लॉट के लिए एक किंवदंती जोड़ने के लिए उपयोग करता था
उपयोगकर्ता

: यह तेजी से स्वत: हैंडल और हस्तनिर्मित लेबल गठबंधन करने के लिए है handles, _ = plt.gca().get_legend_handles_labels()तो,fig.legend(handles, labels)
एसएमसी

1

यह उत्तर किंवदंती की स्थिति पर @ एवर्ट का पूरक है।

किंवदंती के ओवरलैप और सबप्लॉट के शीर्षक के कारण @ एवर्ट के समाधान पर मेरी पहली कोशिश विफल रही।

वास्तव में, ओवरलैप्स के कारण होता है fig.tight_layout(), जो आंकड़ा किंवदंती पर विचार किए बिना सबप्लॉट्स के लेआउट को बदलता है। हालाँकि, fig.tight_layout()आवश्यक है।

ओवरलैप्स से बचने के लिए, हम fig.tight_layout()आंकड़ा के लीजेंड द्वारा रिक्त स्थान छोड़ने के लिए कह सकते हैं fig.tight_layout(rect=(0,0,1,0.9))

कसौटियों का विवरण () पैरामीटर


1

@ Gboffi और बेन उस्मान के जवाब के शीर्ष पर निर्माण करने के लिए:

ऐसी स्थिति में जहां एक ही रंग और लेबल के साथ अलग-अलग सबप्लॉट में अलग-अलग रेखाएं होती हैं, व्यक्ति लाइनों के साथ कुछ कर सकता है

labels_handles = {
  label: handle for ax in fig.axes for handle, label in zip(*ax.get_legend_handles_labels())
}

fig.legend(
  labels_handles.values(),
  labels_handles.keys(),
  loc="upper center",
  bbox_to_anchor=(0.5, 0),
  bbox_transform=plt.gcf().transFigure,
)
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.