Aligning markers for individual points in matplotlib and removing "double" legend for markers in matplotlib in Python

StackOverflow https://stackoverflow.com/questions/18163666

  •  24-06-2022
  •  | 
  •  

Вопрос

I am trying to figure out the alignment of Markers in "ax.plot" . Apart from plotting 2 bar graphs, I also need to plot 2 points, one per bar graph. Here's what I am looking for -:

  1. Centering/alignment of markers ('o' and '' here, in the center of each bar, rather than at the edge of the bar graph. "o" should come at the center of the 1st bar graph and "" should come at the center of the 2nd bar graph, their individual heights will differ though, as on the scale "Performance" -the "o" and "" are "Performance" objects (right hand side scale, as in the figure) - centering, thus means, overlay of the markers("o" and "" against its respective stacked graph.

  2. Removing the duplicate marker symbols, with 'o' and '*' in the legend on the upper right corner. And, understanding why that happens for par2.plot , but not for ax.bar object. Could I have done this without using ax.twinx(), which generates two scales (one for "#candidates" and other for "Performance" - and if this double entry of legend is related to using the 2 scales ? (I hope not)

    For (2), I also used plt.legend(numpoints=1) just before the last line, plt,show() according to the answer here, multiple markers in legend , but that didn't seem to remove the "duplicate markers" in this context.

Also attached is the graph, with (1) and (2) highlighted enter image description here

Tip -: Ignore the looping constructs, they are a part of the larger piece, and did not want to change that while pasting, focus on this snippet of the entire code (IMO, this should narrow the problem?)

rects1 = ax.bar(ind, Current_Period, width, color=colors)
rects2 = ax.bar(ind+width, Next_Period, width, color='c')
lines_1=par1.plot(perform_1,linestyle='', marker='H', markerfacecolor ='k')
lines_2=par1.plot(perform_2,linestyle='', marker='*',markerfacecolor ='m')
ax.legend((rects1[0], rects2[0],lines_1[0],lines_2[0]), ('Current time period', 'Next time Period','Current Period Performance', 'Next Period Performance'),prop=dict(size=10) )

Here is the complete code that I used -:

#Final plotting file 
import numpy as np
import matplotlib.pyplot as plt
from matplotlib import rc
#placing anchored text within the figure
from mpl_toolkits.axes_grid.anchored_artists import AnchoredText
rc('mathtext', default='regular')

history_P=[[1.4155322812819471, 4.9723842851306213, 3.6831354714462456, 3.0345047089322521, 5.3355879766963819], [2.3240101637275856, 4.7804345245879354, 7.0829471987293973, 6.1050663075245852, 3.6087166298399973], [3.5770722538162265, 3.4516290562530587, 4.4851829512197678, 5.1158026103364733, 3.7873662329909235], [4.7137003352158136, 5.0792119756378593, 4.4624078437179504, 3.1790266221827754, 4.8711126648436895], [4.8043291762010414, 5.6979872315568576, 3.4869780377350339, 3.892755123606721, 3.8142509389863095], [4.8072846135271492, 4.2055137431209033, 5.0441056822018417, 4.1014759291893306, 5.327936039526822]]
history_C=[[14000, 14000, 14000, 14000, 14000], [5373, 18874, 13981, 11519, 20253], [6806, 14001, 20744, 17880, 10569], [12264, 11834, 15377, 17540, 12985], [14793, 15940, 14004, 9977, 15286], [15500, 18384, 11250, 12559, 12307]]

N = 5
ind = np.arange(N)  # the x locations for the groups
width = 0.35

def make_patch_spines_invisible(ax):
    ax.set_frame_on(True)
    ax.patch.set_visible(False)
    for sp in ax.spines.itervalues():
        sp.set_visible(False)

def autolabel(rects):
    # attach some text labels
    for rect in rects:
        height = rect.get_height()
        ax.text(rect.get_x()+rect.get_width()/2., 1.05*height, '%d'%int(height),ha='center', va='bottom')

alphab = ['M1', 'M2', 'M3', 'M4', 'M5', 'M6']

for k in range(0,5):
    colors=[]
    Current_Period=history_C[k]
    Next_Period = history_C[k+1]
    perform_1=history_P[k]
    perform_2=history_P[k+1]

    for i in range(0,5):
        if perform_1[i]==max(perform_1) :
            colors.append('g')
            best=i
        elif perform_1[i]==min(perform_1):
            colors.append('r')
            worst=i
        elif (perform_1[i] != min(perform_1) or perform_1[i] != max(perform_1)):
            colors.append('b')

    fig, ax = plt.subplots()
    fig.subplots_adjust(right=0.75)

    par1 = ax.twinx()
    make_patch_spines_invisible(par1)

    rects1 = ax.bar(ind, Current_Period, width, color=colors)
    rects2 = ax.bar(ind+width, Next_Period, width, color='c')
    lines_1=par1.plot(perform_1,linestyle='', marker='H', markerfacecolor ='k')
    lines_2=par1.plot(perform_2,linestyle='', marker='*',markerfacecolor ='m')
    ax.set_xlabel("Model #",style='italic',size='large')
    ax.set_ylabel("Candidate #",style='italic',size='large')
    par1.set_ylabel("Performance",style='italic',size='large')


    ax.set_title('Aggregated Performace Rolled out to candidates, per period',style='italic')
    #fontdict=dict('fontsize':rcParams['axes.titlesize'],'verticalalignment': 'baseline', 'horizontalalignment': loc)
    ax.set_xticks(ind+width)
    ax.set_xticklabels( ('M1', 'M2', 'M3', 'M4', 'M5') )
    ax.annotate('Worst Performer', xy=(worst,0),  xycoords='data',xytext=(-30, 30), textcoords='offset points',size=12, va="center", ha="center",arrowprops=dict(arrowstyle="simple", connectionstyle="arc3,rad=-0.2"))
    ax.annotate('Best Performer', xy=(best,0),  xycoords='data',xytext=(-30, 30), textcoords='offset points',size=12, va="center", ha="center",arrowprops=dict(arrowstyle="simple", connectionstyle="arc3,rad=-0.2"))
    ax.legend((rects1[0], rects2[0],lines_1[0],lines_2[0]), ('Current time period', 'Next time Period','Current Period Performance', 'Next Period Performance'),prop=dict(size=10) )
    #placing anchored text within the figure, per Period
    at = AnchoredText("Time Period :"+str(k+1),prop=dict(size=10), frameon=True,loc=2,)
    at.patch.set_boxstyle("round,pad=0.,rounding_size=0.2")
    ax.add_artist(at)
    par1.set_ylim(0, 10)

    autolabel(rects1)
    autolabel(rects2)
    plt.show()
Это было полезно?

Решение

  1. You have to provide the plot method with x-coordinate arguments. If given only one list-like object, matplotlib will use this list as the y-coordinates and use x = np.arange(len(y)) (where y are the given y-coordinates).

  2. You should not call the legend method several times for each Axes; include the numpoints kwarg in your original legend call.

In other words, replace the lines

lines_1=par1.plot(perform_1,linestyle='', marker='H', markerfacecolor ='k')
lines_2=par1.plot(perform_2,linestyle='', marker='*',markerfacecolor ='m')
ax.legend((rects1[0], rects2[0],lines_1[0],lines_2[0]), ('Current time period', 'Next time Period','Current Period Performance', 'Next Period Performance'),prop=dict(size=10) )

with

lines_1=par1.plot(ind + 0.5*width, perform_1,linestyle='', marker='H', markerfacecolor ='k')
lines_2=par1.plot(ind + 1.5*width, perform_2,linestyle='', marker='*',markerfacecolor ='m')
ax.legend((rects1[0], rects2[0],lines_1[0],lines_2[0]), ('Current time period', 'Next time Period','Current Period Performance', 'Next Period Performance'),prop=dict(size=10), numpoints=1 )

This gives the desire output:

enter image description here

Другие советы

I think it is slightly better style to use bar(..., align='center'), as that is what you really want here:

    rects1 = ax.bar(ind, Current_Period, width, color=colors, align='center')
    rects2 = ax.bar(ind+width, Next_Period, width, color='c', align='center')
    lines_1=par1.plot(ind, perform_1,linestyle='', marker='H', markerfacecolor ='k')
    lines_2=par1.plot(ind+width, perform_2,linestyle='', marker='*',markerfacecolor ='m')
    ax.set_xticks(ind + width/2)
    ax.set_xticklabels( ('M1', 'M2', 'M3', 'M4', 'M5') )
    ax.legend((rects1[0], rects2[0],lines_1[0],lines_2[0]), ('Current time period', 'Next time Period','Current Period Performance', 'Next Period Performance'),prop=dict(size=10), numpoints=1 )

From a philosophical point of view, it it better to tell the plotting library to do what you want, than to contort your self (and inject details of how the plotting library does things internally) to accommodate the fact that you only are using part of the api.

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top