matplotlib - Change text colour on bar chart labels - python

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')

Related

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

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')

Add a single unifying Legend over many plots in matplotlib

I am plotting several lines in the same figure (which is a football pitch) like so:
fig, ax = create_pitch(120, 80,'white')
for index, pass_ in cluster5.iterrows():
if (index < 0):
continue
x, y = pass_['x'], pass_['y']
end_x, end_y = pass_['end_x'], pass_['end_y']
y = 80 - y
end_y = 80 - end_y
color = color_map[pass_['possession']]
ax.plot([x, end_x], [y, end_y], linewidth = 3, alpha = 0.75, color = color, label = pass_['possession'])
ax.legend(loc = 'upper left')
There are several groups and I would like to plot a single legend for them.
However, I now have a legend of repeated items (one for each call to ax plot for each label).
How can I just plot a single legend item for each label?
Thanks a lot in advance!
I solved this by adding a proxy plot and handle:
for c in color_labels:
ax.plot(x, y, linewidth = 3, color = color_map[c], alpha = 0.75, label = c)
with x, y being the last used one. such that the final color is the same.

Matching the colour of a legend to the bars in a bargraph python?

I'm trying to match the colours of my legend to the bars in a graph. I've specifically highlighted these bars as points of interest, since they are outside of my ylim. Problem is, my legend is displaying the colours as black as opposed to the colours that I want it to.
Below is the function I'm using to graph, as well as the image of the graph.
def seaborn_plot(dataset,times):
sns.set_style('darkgrid')
sns.set_color_codes("muted")
data_ = dataset
time_list = []
data_list = []
for i, v in enumerate(data_):
if data_[i] > 80000:
data_list.append(('ED={:.2f}'.format(data_[i])))
time_list.append(("Hour {}:".format(times[i])))
df = pd.DataFrame(data = {'times_new':time_list,
'data_list':data_list})
red = 'r'
blue = 'b'
colors = []
for i in range(len(data_)):
if data_[i] > 80000:
color = red
colors.append(color)
else:
color2 = blue
colors.append(color2)
graph = sns.barplot(x=times, y=data_ , palette = colors, label = time_list)
graph.set_xlabel("Time (Hours)", fontsize = 10, fontweight = 'bold');
graph.set_ylabel("Euclidean Distance", fontsize = 10, fontweight = 'bold');
graph.set_ylim([0, 80000])
leg = mp.gca().legend(labels = df["times_new"] + df["data_list"])
return graph
The resulting image:
You can loop through the generated bars and use the bars that satisfy the condition as handles for the legend. As seaborn doesn't return a list of bars (in contrast to plt.bars()), the bars can be obtained from the returned ax (supposing no other bars are drawn yet in the same plot):
import matplotlib.pyplot as plt
import numpy as np
import seaborn as sns
sns.set_style('darkgrid')
sns.set_color_codes("muted")
data_ = np.random.randint(20000, 100000, 24)
times = np.arange(0, 24)
y_limit = 80000
colors = ['r' if d > y_limit else 'b' for d in data_]
ax = sns.barplot(x=times, y=data_, palette=colors)
ax.set_xlabel("Time (Hours)", fontsize=10, fontweight='bold')
ax.set_ylabel("Euclidean Distance", fontsize=10, fontweight='bold')
ax.set_ylim([0, y_limit])
handles = [bar for bar in graph.containers[0] if bar.get_height() > y_limit]
labels = [f'Hour {" " if h < 10 else ""}{h}: ED={ed:,.0f}' for ed, h in zip(data_, times) if ed > y_limit]
ax.legend(handles, labels, bbox_to_anchor=[1.02, 1], loc='upper left')
plt.tight_layout()
plt.show()
Note that by using the bars as legend handles, this approach would also work when each bar would have an individual color.

Pandas/Geopandas: Showing Latitude/Longitude Points from two different dataframes on the same plot

I've been following this tutorial:https://towardsdatascience.com/geopandas-101-plot-any-data-with-a-latitude-and-longitude-on-a-map-98e01944b972 and have successfully plotted one map but am having trouble adding a second layer of points to the same plot.
I think it's a general misunderstanding of how plots work.
The following works:
fig,ax = plt.subplots(figsize=(15,15))
nycmap.plot(ax=ax)
geometry = [Point(xy) for xy in zip( df['LONGITUDE'], df['LATITUDE'])]
geo_df = gpd.GeoDataFrame(df, crs = {'init':'epsg:4326'}, geometry = geometry)
fig,ax = plt.subplots(figsize=(15,15))
nycmap.plot(ax = ax, alpha = 0.4, color="blue")
geo_df.plot(ax=ax, markersize=1, color = "yellow", marker = 'o', label = "permit")
plt.legend(prop={'size': 15})
Good Plot
Now I have a second dataframe that I'd like to plot on top of the first but I'm not quite sure how to make it work. Depending on the range of values, they'll have different colors.
Here's what I've tried interwoven with the above code:
geometry2 = [Point(xy) for xy in zip( df2['Longitude'], df2010['Latitude'])]
fig,ax = plt.subplots(figsize=(15,15))
geo_df2 = gpd.GeoDataFrame(df2, crs = {'init':'epsg:4326'}, geometry = geometry2)
fig,ax = plt.subplots(figsize=(15,15))
nycmap.plot(ax = ax, alpha = 0.4, color="blue")
geo_df2[geo_df2['sum'] <= 1236].plot(ax=ax, markersize=1, color = 'red', marker = 'x', label = "Below")
geo_df2[(geo_df2['sum'] > 1237) & (geo_df2['sum'] <= 1606)].plot(ax=ax, markersize=1, color = 'orange', marker = 'x', label = "Average")
geo_df2[(geo_df2['sum'] < 1607) & (geo_df2['sum'] <= 2087)].plot(ax=ax, markersize=1, color = 'green', marker = 'x', label = "Max")
plt.legend(prop={'size': 15})
Could someone tell me where I'm going wrong? It doesn't show the good plot, only a fraction of an empty plot

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?

Categories