Bar graph doesn't fill the Axis - python

I'm trying to make a stacked bar chart of a list of a variable number of "Accumulators", which have a person's name and three percentages which always add up to 100. But when I have a large number of entries in the list, all the bars are crowded to the left side of the graph.
Here's the code:
per_unreviewed = np.array([p.accum_per_unreviewed for p in accumulators])
per_reviewed = np.array([p.accum_per_reviewed for p in accumulators])
per_signed_off = np.array([p.accum_per_signed_off for p in accumulators])
fig = Figure(facecolor="w", figsize=(15, 7))
ax = fig.add_subplot(111)
ind = np.arange(len(accumulators))
logger.debug("len(acc) = %d, ind = %s", len(accumulators), ind)
width = 0.45
p1 = ax.bar(ind, per_signed_off, width, color="g")
p2 = ax.bar(ind, per_reviewed, width, color="b", bottom=per_signed_off)
p3 = ax.bar(ind, per_unreviewed, width, color="r",
bottom=per_signed_off + per_reviewed)
ax.set_title(title)
ax.set_ylabel("Percent by status")
ax.set_yticks(np.arange(0, 101, 20))
ax.set_xticks(ind + width / 2.0)
ax.set_xticklabels(
[p.person for p in accumulators],
rotation='vertical', clip_on=False)
fig.tight_layout()
box = ax.get_position()
ax.set_position([box.x0, box.y0, box.width * 0.7, box.height])
if (len(p1) > 0 or len(p2) > 0 or len(p3) > 0):
ax.legend(
(p1[0], p2[0], p3[0]),
('Signed Off', 'Reviewed', 'Unreviewed'),
loc="upper left", bbox_to_anchor=(1.05, 1), borderaxespad=0
)
canvas = FigureCanvas(fig)
outstr = StringIO.StringIO()
canvas.print_png(outstr)
And the result

Have you tried playing with the x axis range? You have the ticks and a figure size, but nothing that tells the plot the range of x.
I don't use subplots myself, but is there something like ax.set_xlim([]) or ax.xlim() that does this?
Update from Paul Tomblin: I tried those suggestions and they didn't help, but they did point me to the right idea:
ax.set_xbound(lower=0, upper=len(accumulators))

Related

Want to plot graph side by side

I wan to two plots side by side instead of this vertically, right now it's showing one by one
def scatter_plot(surrogate, building, actual, pred,index):
#calculating max and min x axis range
min_range=pred.min()-10
max_range=pred.max()+10
min_domain=actual.min()-10
max_domain=actual.max()+10
#scaling and creating scatter plot
plt.axes([0, 0, 2, 2])
plt.scatter(x=actual,y=pred, marker="o") #(y = predicted)
#plt.gca().set_aspect('equal', adjustable='box')
plt.grid()
plt.xlabel('Actual Values', fontsize = 20)
plt.ylabel('Predicted Values', fontsize = 20)
plt.title(f'{building.idf}_{building.epw}_{variable} Scatter Plot of NN vs E+', fontsize= 25)
#adding regression line
plt.plot([min_domain, max_domain], [min_range, max_range], color='g', linestyle='-', linewidth=1,label='regression')
#adding line passing minimum and maximum actual points
plt.plot([min_domain, max_domain],[min_domain, max_domain],color='r',linestyle='-',linewidth=1,label='actual point line')
#adding legend
plt.legend(loc='lower right')
#calculating error metrics
location = building.metadata['building_attributes']['Location']
building_type = building.idf
df = csv.loc[(csv['id'] == surrogate.surrogate_id) &
(csv['Location'] == location) & (csv['Building Type'] == building_type)]
rmse = df[f'{variable} RMSE'].values[0]
r2 = df[f'{variable} R2'].values[0]
#Adding Error metric annotations
textstr = '\n'.join((r'Total Error Metrics', r'$RMSE=%.2f$' % (rmse, ),r'$R2=%.2f$' % (r2, )))
props = dict(boxstyle='round', facecolor='ivory', alpha=0.5)
plt.text(max_range, max_domain, textstr, fontsize=20, verticalalignment='top', bbox=props)
#calculating x and y range
axes = plt.gca()
y_min, y_max = axes.get_ylim()
x_min, x_max = axes.get_xlim()
#Coordinates of interested area
percentile = 10
nth_percentile = np.percentile(actual,percentile)
bottom, left, width, height = 0, 0, nth_percentile,nth_percentile
try:
x_hist = x_min +(x_max - x_min)/9 #may have to change value 9
#calculating lines for selected area
x1, y1 = [left, x_hist], [bottom+height, (y_max + y_min)/2]
x2, y2 = [left + width, x_hist], [bottom + height, (y_max + y_min)/2]
L_act = []
L_pred = []
for x, y in zip(actual, pred):
if left <= x <= width+left:
if bottom<= y <= height + bottom:
L_act.append(x)
L_pred.append(y)
#adding rectangle for selected area
rect=mpatches.Rectangle((left, bottom),width, height, fill = False, color = "black",linewidth = 2)
plt.gca().add_patch(rect)
#calculating error metrics for selected area
rmse = RMSE(L_act, L_pred)
r2 = R2(L_act, L_pred)
#adding lines to indicated the selected area
plt.plot(x1, y1, x2, y2, color = 'black', linewidth = 2)
#adding histogram
plt.axes([0.2, 1, .6, .6], facecolor='w')
plt.hist(L_act, 30)
plt.xticks([])
plt.yticks([])
textstr = '\n'.join((r'Selected Section Error Metrics', r'$RMSE=%.2f$' % (rmse, ),r'$R2=%.2f$' % (r2, )))
props = dict(boxstyle='round', facecolor='ivory', alpha=0.8)
#adding error metrics annotations for selected area
axes = plt.gca()
y_min, y_max = axes.get_ylim()
x_min, x_max = axes.get_xlim()
plt.text(x_min + x_min/10, y_max - y_max/30, textstr, fontsize=10, verticalalignment='top', bbox=props)
except ValueError:
print("Selected section doesn't contain any data points")
plt.show()
I tried using a subplot but that didn't work
def s_plot(surrogate,building):
figure, axis = plt.subplots(1, 2)
actual, pred = np.array(surrogate.test_samples[variable].values[:]), np.array(surrogate.training_samples[variable].values[:])
actual_train, pred_train = np.array(surrogate.train_actual[variable].values[:]), np.array(surrogate.train_pred[variable].values[:])
data =[[actual,pred],[actual_train, pred_train]
for ax,i in zip(axes.flatten(),data):
scatter_plot(surrogate,building,i[0],i[1],ax)
Here I am using axes instead of plt but there are so many parameters that axes doesn't have such as gca, scaling using axes, etc. and I am not able to plot histogram by subplot use
Is there any way to plot this side by side
here is a simple example of using histogram with subplots:
def func(ax):
# example data
mu = 100 # mean of distribution
sigma = 15 # standard deviation of distribution
x = mu + sigma * np.random.randn(437)
num_bins = 50
# the histogram of the data
n, bins, patches = ax.hist(x, num_bins, density=True)
# add a 'best fit' line
y = ((1 / (np.sqrt(2 * np.pi) * sigma)) *
np.exp(-0.5 * (1 / sigma * (bins - mu))**2))
ax.plot(bins, y, '--')
ax.set_xlabel('Smarts')
ax.set_ylabel('Probability density')
ax.set_title(r'Histogram of IQ: $\mu=100$, $\sigma=15$')
fig, (ax1, ax2) = plt.subplots(1, 2)
func(ax1)
func(ax2)

Matpolitlib Stacked Barchart - Adding Labels for the Total Value on each of my bars [duplicate]

I have a grouped bar chart and each bar is stacked.
I have annotated each section of the stack with its individual value and now I would like to sum those values and annotate the total value(height) of each bar. I would like this annotation to be on top of each bar.
This is one of the two dataframes I am working from:
df_title = pd.DataFrame(index=['F','M'],
data={'<10':[2.064897, 1.573255], '10-12':[3.933137, 4.326450], '13-17':[9.242871, 16.715831],
'18-24':[10.226155, 12.487709], '18-24':[8.161259, 10.717797], '35-44':[5.801377, 4.916421],
'45-54':[3.539823, 2.851524], '55+':[1.671583, 1.769912]})
I convert both dataframes (df_title and df_comps) into numpy arrays before plotting.
df_title_concat = np.concatenate((np.zeros((len,1)), df_title.T.values), axis=1)
Here is the full code:
df_title
df_comps
len = df_title.shape[1]
df_title_concat = np.concatenate((np.zeros((len,1)), df_title.T.values), axis=1)
df_comps_concat = np.concatenate((np.zeros((len,1)), df_comps.T.values), axis=1)
fig = plt.figure(figsize=(20,10))
ax = plt.subplot()
title_colors = ['skyblue', 'royalblue']
comps_colors = ['lightgoldenrodyellow', 'orange']
for i in range(1,3):
for j in list(range(0, df_title.shape[1]-1)):
j += 1
ax_1 = ax.bar(j, df_title_concat[j,i], width=-0.4, bottom=np.sum(df_title_concat[j,:i]), color = title_colors[i-1],
edgecolor='black', linewidth=3, align='edge')
for p in ax_1.patches:
width, height = p.get_width(), p.get_height()
x, y = p.get_xy()
if height > 2:
ax.annotate('{:.2f}%'.format(height), (p.get_x()+0.875*width, p.get_y()+.4*height),
fontsize=16, fontweight='bold', color='black')
ax_2 = ax.bar(j, df_comps_concat[j,i], width=0.4, bottom=np.sum(df_comps_concat[j,:i]), color = comps_colors[i-1],
edgecolor='black', linewidth=3, align='edge')
for p in ax_2.patches:
width, height = p.get_width(), p.get_height()
x, y = p.get_xy()
if height > 2:
ax.annotate('{:.2f}%'.format(height), (p.get_x()+0.15*width, p.get_y()+.4*height),
fontsize=16, fontweight='bold', color='black')
Here is a solution:
df_title = pd.DataFrame(index=['F','M'],
data={'<10':[2.064897, 1.573255], '10-12':[3.933137, 4.326450], '13-17':[9.242871, 16.715831],
'18-24':[10.226155, 12.487709], '18-24':[8.161259, 10.717797], '35-44':[5.801377, 4.916421],
'45-54':[3.539823, 2.851524], '55+':[1.671583, 1.769912]})
df_title_concat = np.concatenate((np.zeros((len(df_title),1)), df_title.T.values), axis=1)
fig = plt.figure(figsize=(12,8))
ax = plt.subplot()
title_colors = ['skyblue', 'royalblue']
for i in range(1,3):
for j in list(range(0, df_title.shape[1]-1)):
j += 1
bottom=np.sum(df_title_concat[j,:i])
ax_1 = ax.bar(j, df_title_concat[j,i], width=-0.4, bottom=bottom, color = title_colors[i-1],
edgecolor='black', linewidth=3, align='edge')
for p in ax_1.patches:
width, height = p.get_width(), p.get_height()
if bottom != 0:
ax.annotate('{:.2f}%'.format(height+bottom), (p.get_x()+0.875*width, (height+bottom)+0.3),
fontsize=16, fontweight='bold', color='black')
However, I would suggest you to rethink the whole approach you are following and change the plot to something like:
plt.bar(df_title.columns,df_title.loc['M'])
plt.bar(df_title.columns,df_title.loc['F'],bottom=df_title.loc['M'])

matplotlib text is not on top of patches even with a higher zorder

I've plotted data for females on one axes, and males on another axes. Each plot was made with zorder=0, but with position=1 and position=2 respectively. I label the bars with text with zorder=1, but as you can see, the bars overlap the text. Is it because they are on separate axes? In which case, how can I have text in one axes be higher than the highest zorder in another axes?
def get_ages():
df = pd.read_csv('surveydata.csv', low_memory=False)
fems = df.loc[df['gender'] == 1]
males = df.loc[df['gender'] == 2]
fdata = fems['age'].value_counts()
mdata = males['age'].value_counts()
fdata.sort_index(inplace=True)
mdata.sort_index(inplace=True)
print(fdata)
print(mdata)
fdata2 = fdata[0:14]
mdata2 = mdata[0:14]
fdata2['>31'] = sum(fdata[14:])
mdata2['>31'] = sum(mdata[14:])
fig = plt.figure() # Create matplotlib figure
ax = fig.add_subplot(111) # Create matplotlib axes
ax2 = ax.twinx() # Create another axes that shares the same x-axis as ax.
fdata2.plot(kind='bar', figsize=(10, 5.7), width=.4, color='pink', position=0, ax=ax,zorder=0)
mdata2.plot(kind='bar', figsize=(10, 5.7), width=.4, color='lightskyblue', position=1, ax=ax2, zorder=0)
ax.set_title("Ages", fontsize=18)
ax.set_ylabel("Occurrence", fontsize=18)
ax.set_facecolor('snow')
ax.set_xlim(ax.patches[0].get_x() - 1, ax.patches[-1].get_x() + 1)
ax2.set_yticks([])
totals = []
for i in ax.patches:
totals.append(i.get_height())
total = sum(totals)
for i in ax.patches:
ax.text(i.get_x() , i.get_height() + .5,
str(round((i.get_height() / total) * 100, 2)) + '%', fontsize=8,
color='black', horizontalalignment='left', zorder=9)
totals = []
for i in ax2.patches:
totals.append(i.get_height())
total = sum(totals)
for i in ax2.patches:
t = ax2.text(i.get_x()+ i.get_width(), i.get_height() + .5,
str(round((i.get_height() / total) * 100, 1)) + '%', fontsize=8,
color='black', horizontalalignment='right', zorder=10)
for x in ax.texts: #Shifts text up and down in case they overlap.
bb2 = x.get_window_extent(ax.get_figure().canvas.get_renderer())
bb = t.get_window_extent(ax.get_figure().canvas.get_renderer())
while bb2.overlaps(bb):
t.set_y(a._y - .01)
bb2 = x.get_window_extent(ax.get_figure().canvas.get_renderer())
bb = t.get_window_extent(ax.get_figure().canvas.get_renderer())

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?

Organizing text on pie charts at matplotlib

I've learned the basics of plotting pie charts (through the tutorial and examples here), but I don't manage to put the suptitle above the chart (I need maybe to reduce the pie chart size, but how do I do it?). I also want to place the extra text box that I added in the bottom right or left side of the pie chart. If someone can give a hint it would be great!
(The function takes a string which is the name of the channel, then a list with 4 percentages, an int for the mass and a flag save_figures if I want to save the figure)
def plot_channel(channel,percentages, mass, save_figures):
# build a rectangle in axes coords
left, width = .25, .5
bottom, height = .25, .5
right = left + width
top = bottom + height
channel = ''.join(i for i in channel if i in 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ')
nu_energy , d_plus_p_energy, e_energy, gamma_energy = percentages
# The slices will be ordered and plotted counter-clockwise.
labels = [r'$E_{\nu} / E_{tot}$ = %.3f'%nu_energy,
r'$E_{d+p} / E_{tot}$ = %.3f'%d_plus_p_energy,
r'$E_{e} / E_{tot}$ = %.3f'%e_energy,
r'$E_{\gamma} / E_{tot}$ = %.3f'%gamma_energy]
sizes = [nu_energy , d_plus_p_energy, e_energy, gamma_energy]
colors = ['gold','red','green', 'lightskyblue']
explode = (0.1, 0,0,0)
patches, texts = plt.pie(sizes, colors=colors)#, startangle=90) ** not working for some reason
plt.legend(patches, labels, loc = "best")
E_gamma_e = e_energy + gamma_energy
plt.text(right, bottom,
r'$E_{\gamma + e} / E_{tot}$ = %.3f'%E_gamma_e,
horizontalalignment='left',
verticalalignment='bottom',
bbox=dict(facecolor='white', alpha=0.5), fontsize=30)
#plt.pie(sizes, explode=explode, labels=labels, colors=colors,
#autopct='%1.1f%%', shadow=True)
# Set aspect ratio to be equal so that pie is drawn as a circle.
plt.axis('equal')
plt.suptitle(r'DM DM $\rightarrow$ $%s$ + $%s$'%(channel,channel),position=(left,top),
bbox=dict(facecolor='0.8',), fontsize=30)
plt.tight_layout()
if save_figures:
plt.savefig("./figures/energy_distribution_for_channel_{}.png".format(channel))
else:
plt.show()
plt.close()
Try this:
import matplotlib.pyplot as plt
channel,percentages, mass = "ab",[0.2,0.2,0.1,0.5], 10
# build a rectangle in axes coords
left, width = .25, .5
bottom, height = .25, .5
right = left + width
top = bottom + height
channel = ''.join(i for i in channel if i in 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ')
nu_energy , d_plus_p_energy, e_energy, gamma_energy = percentages
# The slices will be ordered and plotted counter-clockwise.
labels = [r'$E_{\nu} / E_{tot}$ = %.3f' % nu_energy,
r'$E_{d+p} / E_{tot}$ = %.3f' % d_plus_p_energy,
r'$E_{e} / E_{tot}$ = %.3f' % e_energy,
r'$E_{\gamma} / E_{tot}$ = %.3f' %gamma_energy]
sizes = [nu_energy , d_plus_p_energy, e_energy, gamma_energy]
colors = ['gold','red','green', 'lightskyblue']
explode = (0.1, 0,0,0)
patches, texts = plt.pie(sizes, colors=colors)#, startangle=90) ** not working for some reason
plt.legend(patches, labels, loc = "best")
E_gamma_e = e_energy + gamma_energy
#plt.pie(sizes, explode=explode, labels=labels, colors=colors,
#autopct='%1.1f%%', shadow=True)
# Set aspect ratio to be equal so that pie is drawn as a circle.
plt.axis('equal')
plt.title(r'DM DM $\rightarrow$ $%s$ + $%s$'%(channel,channel),position=(0.5,1),bbox=dict(facecolor='0.8',), fontsize=30)
plt.text(-1,-0.98, r'$E_{\gamma + e} / E_{tot}$ = %.3f'%E_gamma_e, bbox=dict(facecolor='white', alpha=0.5), fontsize=14)
plt.tight_layout()
plt.show()

Categories