Offset in Vertical Alignment of Legend Text and Symbol (Matplotlib) - python

I was trying to plot some data and include linear trends. I wanted the legend to include the function of the trend but somehow the legend always shows a weird vertical offset of the symbol relative to the text (see Screenshot marked with light blue). Does anybody know why and could maybe also include a fix?
Thank you!
l_y1_1 = ax.plot(x, y1, 'ro', label='Lithium', markersize=10)
l_y1_2 = ax.plot(x_extrapolate, y1_extrapolate,'r:', label = "%s" % poly1)
l_y2_1 = ax.plot(x, y2, 'bx', label='Chlor', markersize=10)
l_y2_2 = ax.plot(x_extrapolate, y2_extrapolate,'b:', label = "%s" % poly2)
ax2 = ax.twinx()
l_y3_1 = ax2.plot(x, y3, 'gD', label='Fluor', markersize=10)
l_y3_2 = ax2.plot(x_extrapolate, y3_extrapolate,'g:',label = "%s" % poly3)
l_y_tot = l_y1_1 + l_y1_2 + l_y2_1 + l_y2_2 + l_y3_1 + l_y3_2
y_labs = [l.get_label() for l in l_y_tot]
legend = ax.legend(l_y_tot, y_labs, loc = 'upper left')

Related

matplotlib - Change text colour on bar chart labels

I have the following code which I use to do a stacked bar plot, and then plot the percentage labels on the bars as text:
dfLev = pd.DataFrame({"year":['2017/16','2015/16','2014/15','2013/14','2012/13', '2011/12', '2010/11'],
"a":[1158,1091,1029,1062,929,922,725],
"b":[3713,3319,3395,3773,3684,4215,4177]})
df_total = [4871,4410,4424,4835,4613,5137,4902]
dfLevFinal = dfLev.iloc[:, 0:3]
plotBar = dfLevFinal.plot(x = 'year', kind='barh',stacked = True, color = ['#8C4799','#008275'], title = '', mark_right = True)
df_rel = dfLevFinal[dfLevFinal.columns[1:3]].div(df_total, 0)*100
#plot the labels on the bars
for n in df_rel:
for i, (cs, ab, pc) in enumerate(zip(dfLevFinal.iloc[:, 1:].cumsum(1)[n], dfLevFinal[n], df_rel[n])):
plt.text(cs - ab/2, i, str(int(np.round(pc))) + '%', va='center', ha='center')
However, the labels on the bars are in 'black' font, which is difficult to see. How do I change it to 'white'?
I have tried the following but it doesnt work:
import matplotlib as mpl
mpl.rcParams['text.color'] = 'White'
Any suggestions?
I found it. It was a simple fix:
I added color = 'white' to the bottom line:
#plot the labels on the bars
for n in df_rel:
for i, (cs, ab, pc) in enumerate(zip(dfLevFinal.iloc[:, 1:].cumsum(1)[n], dfLevFinal[n], df_rel[n])):
plt.text(cs - ab/2, i, str(int(np.round(pc))) + '%', va='center', ha='center', color = 'white')

Angle between two lines in Matplotlib at the right scale

I do same thing that answered here
def get_angle_plot(line1, line2, offset = 1, color = None, origin = [0,0], len_x_axis = 1, len_y_axis = 1):
l1xy = line1.get_xydata()
slope1 = (l1xy[1][1] - l1xy[0][1]) / float(l1xy[1][0] - l1xy[0][0])
angle1 = abs(math.degrees(math.atan(slope1))) # Taking only the positive angle
l2xy = line2.get_xydata()
slope2 = (l2xy[1][1] - l2xy[0][1]) / float(l2xy[1][0] - l2xy[0][0])
angle2 = abs(math.degrees(math.atan(slope2)))
theta1 = min(angle1, angle2)
theta2 = max(angle1, angle2)
angle = theta2 - theta1
if color is None:
color = line1.get_color() # Uses the color of line 1 if color parameter is not passed.
return Arc(origin, len_x_axis*offset, len_y_axis*offset, 0, theta1, theta2, color=color, label = str(angle)+u"\u00b0")
And when Line2D-s same as in example. All work just fine.
fig = plt.figure()
line_1 = Line2D([0,1], [0,4], linewidth=1, linestyle = "-", color="green")
line_2 = Line2D([0,4.5], [0,3], linewidth=1, linestyle = "-", color="red")
ax = fig.add_subplot(1,1,1)
ax.add_line(line_1)
ax.add_line(line_2)
angle_plot = get_angle_plot(line_1, line_2, 1)
ax.add_patch(angle_plot) # To display the angle arc
plt.show()
But when i change slightly to this
line_1 = Line2D([10,30], [1,2], linewidth=1, linestyle = "-", color="green")
line_2 = Line2D([10,30], [1,1], linewidth=1, linestyle = "-", color="red")
I get white empty plot 1*1 size. Not 2*30 if be more refined
You need to set the origin to the point where the two lines intersect. In this case origin = [10,1].
Then you may want to relimit the axes, because the lines you want to show are outside the range [0,1]. You may use ax.relim(); ax.autoscale_view() for that purpose.
line_1 = Line2D([10,30], [1,2], linewidth=1, linestyle = "-", color="green")
line_2 = Line2D([10,30], [1,1], linewidth=1, linestyle = "-", color="red")
ax = fig.add_subplot(1,1,1)
ax.add_line(line_1)
ax.add_line(line_2)
angle_plot = get_angle_plot(line_1, line_2, 1, origin = [10,1])
ax.add_patch(angle_plot) # To display the angle arc
ax.relim()
ax.autoscale_view()
plt.show()
Note that the easier option is surely to not define the lines as Line2D but simply as usual plot. That makes it unnecessary to relimit the axes.
ax = fig.add_subplot(1,1,1)
line_1, = ax.plot([10,30], [1,2], linewidth=1, linestyle = "-", color="green")
line_2, = ax.plot([10,30], [1,1], linewidth=1, linestyle = "-", color="red")
angle_plot = get_angle_plot(line_1, line_2, 5, origin = [10,1])
ax.add_patch(angle_plot) # To display the angle arc
plt.show()
Because the x and y scales are so differently the result will look more like a straight line.

pyplot barchart is plotted, but saved to empty file

I have a strange behaviour in my python code, where I plot a bar-chart and it will be showed in a window, but pressing the "save"-button does nothing and .savefig() creates an empty file with 0KB.
Even more strange, I have a for-loop before the barchart, which is creating several complicated plots and all of them are capable to be plotted and then saved, except the barchart, which is only plotted.
I do not receive any errors from the python-console.
This is my code from the plot-loop before the bar-chart:
for l in range(NumberOfResults):
plottitle=groupLimits[0][l+1]+' - Result Width and Probability Distribution of ' + str(numberCombinations) + ' possible Combinations'
fig=plt.figure(plottitle,figsize=(GetSystemMetrics(0)/96,GetSystemMetrics(1)/96),dpi=96)
plt.clf()
ax=fig.add_subplot(1,1,1)
ResultColumn=range(NumberOfInputs,NumberOfInputs+NumberOfResults)[l]
positions=range(len(groupResults))
#positions=list(map(int, np.array(groupLimits[1:])[:,0]))
#plt.grid(1)
y1=[]
x1=[]
for i in range(len(groupResults)):
y1.append(groupResults[i][:,ResultColumn])
x1.append([positions[i]]*len(groupResults[i]))
y=list(np.array(y1).flatten())
s=[]
for i in range(len(groupResults)):
smin=min(groupResults[i][:,ProbabilityColumn])
smax=max(groupResults[i][:,ProbabilityColumn])
s.append((groupResults[i][:,ProbabilityColumn]-smin)/(smax-smin)*25000)
plt.scatter(x1,y1,s=s,marker='_',color='cyan',alpha=0.45)
bp=plt.boxplot(y1, positions=positions, widths=0.5,
labels=list(np.array(groupLimits[1:])[:,0]),
patch_artist=True,whis=[0,100])
#plt.xlim(min(positions)*0.9,max(positions)*1.1)
ax.xaxis.set_tick_params(labelsize=24)
ax.xaxis.grid(True,'major',color='k', linestyle='-', linewidth=0.75)
ax.xaxis.grid(True,'minor',color='k', linestyle=':', linewidth=0.25)
ax.xaxis.set_minor_locator(MultipleLocator(50));
ax.yaxis.set_tick_params(labelsize=24)
ax.yaxis.grid(True,'major',color='k', linestyle='-', linewidth=0.25)
ax.yaxis.grid(True,'minor',color='k', linestyle=':', linewidth=0.25)
ax.yaxis.set_minor_locator(MultipleLocator(0.1));
ax.yaxis.set_ticks(np.arange(int(min(y)),int(max(y))*1.1,0.5))
## change outline color, fill color and linewidth of the boxes
for box in bp['boxes']:
# change outline color
box.set( color='blue', linewidth=3)
# change fill color
box.set( facecolor = 'None')
## change color and linewidth of the whiskers
for whisker in bp['whiskers']:
whisker.set(color='blue', linewidth=3)
## change color and linewidth of the caps
for cap in bp['caps']:
cap.set(color='blue', linewidth=3,alpha=0.8)
## change color and linewidth of the medians
for median in bp['medians']:
median.set(color='magenta', linewidth=3)
y2=[]
x2=[]
for i in range(1,len(groupResults)+1):
if groupLimits[i][l+1]=='-':
pass
else:
y2.append(float(groupLimits[i][l+1]))
if FailedCombinations[i-1][l] is None:
failureString = 'No Results below the Limit'
else:
failureString= str(len(FailedCombinations[i-1][l]))+' failed # ' + "{:.3g}".format(FailureProbability[i-1][l]*100) + '%'
plt.text(positions[i-1],float(groupLimits[i][l+1])*0.9,
failureString,fontsize=18,horizontalalignment='center',
bbox=dict(facecolor='yellow', edgecolor='black'))
x2.append(positions[i-1])
plt.plot(x2,y2,marker='x',color='red', mew=4,ms=15,linestyle='None')
blue_patch = mpatches.Patch(color='blue', label='Result width')
red_patch = mlines.Line2D([],[],marker='x',color='red', mew=4,ms=15,linestyle='None', label='Limit')
magenta_patch = mpatches.Patch(color='magenta', label='Median')
cyan_patch = mpatches.Patch(color='cyan', label='Probability of Result')
plt.legend(handles=[blue_patch, red_patch, magenta_patch, cyan_patch],
loc='upper left',ncol=1,fontsize=24)
plt.title(plottitle,fontsize=24)
plt.xlabel('Engine Rotation Speed [rpm]',fontsize=24)
plt.ylabel('Absolute Pressure [bar]',fontsize=24)
labelsize = 24
rcParams['xtick.labelsize'] = labelsize
rcParams['ytick.labelsize'] = labelsize
plt.savefig(plottitle)
plt.show()
In between I run some calculations to prepare data for the bar-chart plot. However, the code for the bar-chart is:
# Plot group-wise combined failures with all-combined failures at end
plottitle = 'Failed combinations per group - and engine-all combined - TOTAL combinations: ' + str(len(groupResults[0]))
plt.figure(plottitle,figsize=(GetSystemMetrics(0)/96,GetSystemMetrics(1)/96),dpi=96)
plt.clf()
ax=plt.subplot(1,1,1)
y=[]
ind = np.arange(len(groupFailureProbabilities))
for i in range(len(groupFailures)):
y.append(len(groupFailures[i]))
y=tuple(y)
for i in range(len(y)):
if y[i]==0:
pass
else:
string=str(y[i])+' failed # ' + "{:.3g}".format(groupFailureProbabilities[i]*100)+' %'
plt.text(ind[i],y[i]+100,
string,fontsize=18, horizontalalignment='center',
bbox=dict(facecolor='yellow', edgecolor='black'))
width = 0.35
y=list(y)
plt.bar(ind, y, align='center', color='b')
labels=list(np.array(groupLimits[1:])[:,0])
labels.append('engine \n complete')
ax.set_xticks(ind)
ax.set_xticklabels(tuple(labels))
ax.yaxis.set_tick_params(labelsize=24)
ax.yaxis.grid(True,'major',color='k', linestyle='-', linewidth=0.25)
ax.yaxis.grid(True,'minor',color='k', linestyle=':', linewidth=0.25)
ax.yaxis.set_ticks(np.arange(int(min(y)),int(max(y))*1.1,500))
plt.title(plottitle,fontsize=24)
plt.xlabel('Engine Rotation Speed [rpm]',fontsize=24)
plt.ylabel('Number of Failed Combinations',fontsize=24)
plt.savefig(plottitle)
plt.show()
What is the reason why I cannot save my bar-chart ?
It's kinda difficult to write the shortest code necessary to reproduce the problem, if I have no idea why it happened in the first place.
However, I managed to get it working with a rearrangement of the code lines:
plottitle = 'Failed combinations per group - TOTAL combinations ' + str(len(groupResults[0]))
plt.figure(plottitle,figsize=(GetSystemMetrics(0)/96,GetSystemMetrics(1)/96),dpi=96)
plt.clf()
ax=plt.subplot(1,1,1)
plt.bar(ind, y, align='center')
labels=list(np.array(groupLimits[1:])[:,0])
labels.append('engine \n complete')
ax.set_xticks(ind)
ax.set_xticklabels(tuple(labels))
ax.yaxis.set_tick_params(labelsize=24)
ax.yaxis.grid(True,'major',color='k', linestyle='-', linewidth=0.25)
ax.yaxis.grid(True,'minor',color='k', linestyle=':', linewidth=0.25)
ax.yaxis.set_ticks(np.arange(int(min(y)),int(max(y))*1.1,500))
plt.title(plottitle,fontsize=24)
plt.xlabel('Engine Rotation Speed [rpm]',fontsize=24)
plt.ylabel('Number of Failed Combinations',fontsize=24)
plt.savefig(plottitle+'.png')
plt.show()

Does anyone know how to get rid of the black 'y' axis to the left in Matplotlib plot?

After moving all of my 'y' axes to subplots I get an unwanted axis. It's the black one on the left. Does anyone know how to get rid of it? I'm sure it's getting plotted when I call the figure, however I'm not sure how to get rid of it.
def mpl_plot(self, plot_page, replot = 0): #Data stored in lists
if plot_page == 1: #Plot 1st Page
#plt0 = self.mplwidget.axes
fig = self.mplwidget.figure #Add a figure
if plot_page == 2: #Plot 2nd Page
#plt0 = self.mplwidget_2.axes
fig = self.mplwidget_2.figure #Add a figure
if plot_page == 3: #Plot 3rd Page
#plt0 = self.mplwidget_3.axes
fig = self.mplwidget_3.figure #Add a figure
#Clears Figure if data is roplotted
if replot == 1:
fig.clf()
par0 = fig.add_subplot(111)
par1 = fig.add_subplot(111)
par2 = fig.add_subplot(111)
#Add Axes
plt = par0.twinx()
ax1 = par1.twinx()
ax2 = par2.twinx()
impeller = str(self.comboBox_impellers.currentText()) #Get Impeller
fac_curves = self.mpl_factory_specs(impeller)
fac_lift = fac_curves[0]
fac_power = fac_curves[1]
fac_flow = fac_curves[2]
fac_eff = fac_curves[3]
fac_max_eff = fac_curves[4]
fac_max_eff_bpd = fac_curves[5]
fac_ranges = self.mpl_factory_ranges()
min_range = fac_ranges[0]
max_range = fac_ranges[1]
#Plot Chart
plt.hold(True)
plt.plot(fac_flow, fac_lift, 'b', linestyle = "dashed", linewidth = 1)
ax1.plot(fac_flow, fac_power, 'r', linestyle = "dashed", linewidth = 1)
ax2.plot(fac_flow, fac_eff, 'g', linestyle = "dashed", linewidth = 1)
#Move spines
ax2.spines["right"].set_position(("outward", 25))
self.make_patch_spines_invisible(ax2)
ax2.spines["right"].set_visible(True)
#Plot x axis minor tick marks
minorLocatorx = AutoMinorLocator()
ax1.xaxis.set_minor_locator(minorLocatorx)
ax1.tick_params(which='both', width= 0.5)
ax1.tick_params(which='major', length=7)
ax1.tick_params(which='minor', length=4, color='k')
#Plot y axis minor tick marks
minorLocatory = AutoMinorLocator()
plt.yaxis.set_minor_locator(minorLocatory)
plt.tick_params(which='both', width= 0.5)
plt.tick_params(which='major', length=7)
plt.tick_params(which='minor', length=4, color='k')
#Make Border of Chart White
fig.set_facecolor('white')
#Plot Grid
plt.grid(b=True, which='both', color='k', linestyle='-')
#set shaded Area
plt.axvspan(min_range, max_range, facecolor='#9BE2FA', alpha=0.5) #Yellow rectangular shaded area
#Set Vertical Lines
plt.axvline(fac_max_eff_bpd, color = '#69767A')
#BEP MARKER *** Can change marker style if needed
bep = fac_max_eff * 0.90 #bep is 90% of maximum efficiency point
bep_corrected = bep * 0.90 # We knock off another 10% to place the arrow correctly on chart
ax2.annotate('BEP', xy=(fac_max_eff_bpd, bep_corrected), xycoords='data', #Subtract 2.5 shows up correctly on chart
xytext=(-50, 30), textcoords='offset points',
bbox=dict(boxstyle="round", fc="0.8"),
arrowprops=dict(arrowstyle="-|>",
shrinkA=0, shrinkB=10,
connectionstyle="angle,angleA=0,angleB=90,rad=10"),
)
#Set Scales
plt.set_ylim(0,max(fac_lift) + (max(fac_lift) * 0.40)) #Pressure
#plt.set_xlim(0,max(fac_flow))
ax1.set_ylim(0,max(fac_power) + (max(fac_power) * 0.40)) #Power
ax2.set_ylim(0,max(fac_eff) + (max(fac_eff) * 0.40)) #Effiency
plt.yaxis.tick_left()
# Set Axes Colors
plt.tick_params(axis='y', colors='b')
ax1.tick_params(axis='y', colors='r')
ax2.tick_params(axis='y', colors='g')
# Set Chart Labels
plt.yaxis.set_label_position("left")
plt.set_xlabel("BPD")
plt.set_ylabel("Feet" , color = 'b')
#ax1.set_ylabel("BHP", color = 'r')
#ax1.set_ylabel("Effiency", color = 'g')
# Set tight layout
fig.set_tight_layout
# Since we moved Feet Axis to subplot, extra unneeded axis was created. This Removes it
# Refresh
fig.canvas.update()
fig.canvas.draw()
Well it looks like you have three y-axes, referencing the one you want to not be shown, you could try adding:
ax.yaxis.set_tick_params(labelsize=0, length=0, which='major')
to just make invisible the labels and ticks. I think it's ax2 you want gone?

add text to a legend in python

I am trying to add some text to the legend. It is about the text in the box. Another solution is that the textbox stays beneath the legend and doesn't move when you enlarge the graph.
plt.scatter(stageheight,discharge,color='b',label='measured data')
plt.plot(stageheight_hecras,discharge_hecras,'y^',label='modeled with HEC-RAS')
plt.plot(stageheight_masked,discharge_predicted,'r-',label='regression line measured data')
plt.plot(stageheight_hecras,discharge_predicted_hecras,'g-',label='regression line HEC-RAS')
plt.plot(stageheight_masked,upper,'r--',label='15% error measured data')
plt.plot(stageheight_masked,lower,'r--')
plt.plot(stageheight_hecras,upper_hecras,'g--',label='30% error HEC-RAS')
plt.plot(stageheight_hecras,lower_hecras,'g--')
plt.fill_between(stageheight_masked,upper,lower,facecolor='red',edgecolor='red',alpha=0.5,label='test')
plt.fill_between(stageheight_hecras,upper_hecras,lower_hecras,facecolor='green',alpha=0.5)
plt.axhline(y=0.6,xmin=0,xmax=1,color='black',linewidth = 4.0,label='measuring range')
plt.text(0.02,0.7,'measured rating curve $Q = 1.37H^2 + 0.34H - 0.007$\nmodeled ratign curve $Q = 2.71H^2 - 2.20H + 0.98$',bbox=dict(facecolor='none',edgecolor='black',boxstyle='square'))
plt.title('Rating curve Catsop')
plt.ylabel('discharge')
plt.ylim(0,2.5)
plt.xlim(0,1.2)
plt.xlabel('stageheight[m]')
plt.legend(loc='upper left', title='Legend')
plt.grid(True)
plt.show()
This is the graph I have now:
Rather than plotting a fake rectangle, you can use a Patch, which doesn't show up on the figure or axes:
import matplotlib.patches as mpatches
extraString = 'measured rating curve $Q = 1.37H^2 + 0.34H - 0.007$\nmodeled rating curve $Q = 2.71H^2 - 2.20H + 0.98$'
handles, labels = plt.get_legend_handles_labels()
handles.append(mpatches.Patch(color='none', label=extraString))
plt.legend(handles=handles)
This method has the bonus effect that you first get whatever is already in the legend, so you don't have to explicitly build it by hand.
This adds the text to the legend (inspired by this answer):
from matplotlib.patches import Rectangle
plt.plot(range(10))
p = plt.axhline(y=0.6,xmin=0,xmax=1,color='black',linewidth = 4.0,label='measuring range')
plt.ylabel('discharge')
plt.ylim(0,2.5)
plt.xlim(0,1.2)
plt.xlabel('stageheight[m]')
text1 = 'measured rating curve $Q = 1.37H^2 + 0.34H - 0.007$'
text2 = 'modeled ratign curve $Q = 2.71H^2 - 2.20H + 0.98$'
extra = Rectangle((0, 0), 1, 1, fc="w", fill=False, edgecolor='none', linewidth=0)
plt.legend([p, extra, extra],[p.get_label(), text1, text2], loc='upper left', title='Legend')
plt.grid(True)
plt.show()
Try to use transform in plt.text. Now the first two coordinates are relative to the axis.
ax = plt.gca()
ax.text(0.3,0.05,'measured rating curve $Q = 1.37H^2 + 0.34H - 0.007$\nmodeled ratign curve $Q = 2.71H^2 - 2.20H + 0.98$',transform=ax.transAxes, bbox=dict(facecolor='none',edgecolor='black',boxstyle='square'))

Categories