matplotlib getting labels to show decimal - python

So for the life of me i can't figure out how to get the labels to show decimal places and not just 0,1,2
i need them to be in decimal form below is my code in python 3
#ROE and Growth
Tax_Burden = stock.loc['Net Income']/stock.loc['Pre-Tax Income']
Interest_Burden= stock.loc['Pre-Tax Income']/stock.loc['Operating Income']
Operating_Margin= stock.loc['Operating Income']/stock.loc['Revenue']
Asset_Turnover= stock.loc['Revenue']/stock.loc['Total Assets Average']
Leverage_Ratio= stock.loc['Total Assets Average']/stock.loc['Total Equity Average']
roe=Tax_Burden*Interest_Burden*Operating_Margin*Asset_Turnover*Leverage_Ratio
Growth = roe * (1-stock.loc['Dividend Payout Ratio'])
astart = 21
aend = 31
annual = [Operating_Margin[astart:aend],Tax_Burden[astart:aend],Interest_Burden[astart:aend],Asset_Turnover[astart:aend],Leverage_Ratio[astart:aend],roe[astart:aend],Growth[astart:aend]]
N = len(annual[0])
ind = np.arange(N) # the x locations for the groups
width = .12 # the width of the bars
fig, ax = plt.subplots(figsize=(20,10))
rects1 = ax.bar(ind, annual[0], width, color='y')
rects2 = ax.bar(ind+width, annual[1], width, color='r')
rects3 = ax.bar(ind+width*2, annual[2], width, color='b')
rects4 = ax.bar(ind+width*3, annual[3], width, color='k')
rects5 = ax.bar(ind+width*4, annual[4], width, color='c')
rects6 = ax.bar(ind+width*5, annual[5], width, color='k')
rects7 = ax.bar(ind+width*6, annual[6], width, color='r')
# add some text for labels, title and axes ticks
ax.set_ylabel('Percentage')
ax.set_title('ROE Annual')
ax.set_xticks(ind + width / 2)
ax.set_xticklabels(list(stock.loc['Fiscal Period'][astart:aend]))
#ax.legend((rects1[0], rects2[0]), ('workinprogress'))
def autolabel(rects, ax):
# Get y-axis height to calculate label position from.
(y_bottom, y_top) = ax.get_ylim()
y_height = y_top - y_bottom
for rect in rects:
height = rect.get_height()
# Fraction of axis height taken up by this rectangle
p_height = (height / y_height)
# If we can fit the label above the column, do that;
# otherwise, put it inside the column.
if p_height > 0.95: # arbitrary; 95% looked good to me.
label_position = height - (y_height * 0.05)
else:
label_position = height + (y_height * 0.01)
ax.text(rect.get_x() + rect.get_width()/2, label_position,
'%d' % int(height),
ha='center', va='bottom')
autolabel(rects1,ax)
autolabel(rects2,ax)
autolabel(rects3,ax)
autolabel(rects4,ax)
autolabel(rects5,ax)
autolabel(rects6,ax)
autolabel(rects7,ax)
plt.show()
i am aware it is not pretty as of now and not lazy need to make sore more functions but can't seem to get past this issue. thanks for looking at.
EDIT: For those looking in the future the issue was the S operator here matplotlib documentation. Jay helped clarify below. i am attaching my code and new chart so can be copied for ease. still needs a little tweaking but that is personal preference.
astart = 21
aend = 31
annual = [Operating_Margin[astart:aend],Tax_Burden[astart:aend],Interest_Burden[astart:aend],Asset_Turnover[astart:aend],Leverage_Ratio[astart:aend],roe[astart:aend],Growth[astart:aend]]
N = len(annual[0])
ind = np.arange(N) # the x locations for the groups
width = .12 # the width of the bars
fig, ax = plt.subplots(figsize=(20,10),facecolor='#c8f2e5')
rects1 = ax.bar(ind, annual[0], width, color='#f29ca2')
rects2 = ax.bar(ind+width, annual[1], width, color='#61eaf2')
rects3 = ax.bar(ind+width*2, annual[2], width, color='#6da4d9')
rects4 = ax.bar(ind+width*3, annual[3], width, color='#f2bb12')
rects5 = ax.bar(ind+width*4, annual[4], width, color='c')
rects6 = ax.bar(ind+width*5, annual[5], width, color='#ce44f2')
rects7 = ax.bar(ind+width*6, annual[6], width, color='r')
ax.set_facecolor('#a7cff2')
# add some text for labels, title and axes ticks
ax.set_ylabel('Percentage',size=20)
ax.set_title('ROE Annual',size=30)
ax.set_xticks(ind + width / 2)
ax.set_xticklabels(list(stock.loc['Fiscal Period'][astart:aend]),size=14)
vals = ax.get_yticks()
ax.set_yticklabels(['{:3.2f}%'.format(x*100) for x in vals])
ax.legend((rects1[0], rects2[0], rects3[0], rects4[0], rects5[0], rects6[0], rects7[0]),('Operating Margin', 'Tax Burden','Interest Burden','Asset Turnover', 'Leverage Ratio','ROE','Growth'))
def autolabel(rects, ax):
# Get y-axis height to calculate label position from.
(y_bottom, y_top) = ax.get_ylim()
y_height = y_top - y_bottom
for rect in rects:
height = rect.get_height()
# Fraction of axis height taken up by this rectangle
p_height = (height / y_height)
# If we can fit the label above the column, do that;
# otherwise, put it inside the column.
if p_height > 0.95: # arbitrary; 95% looked good to me.
label_position = height - (y_height * 0.05)
else:
label_position = height + (y_height * 0.01)
ax.text(rect.get_x() + rect.get_width()/2, label_position,
'%.2f' % float(height),
ha='center', va='bottom',color='k',fontsize=12)
#
autolabel(rects1,ax)
autolabel(rects2,ax)
autolabel(rects3,ax)
autolabel(rects4,ax)
autolabel(rects5,ax)
autolabel(rects6,ax)
autolabel(rects7,ax)
plt.show()

I think problem is with below statement. Instead of int, use float
ax.text(rect.get_x() + rect.get_width()/2, label_position,
'%.2f' % float(height),
ha='center', va='bottom')

The solution by Jay did not work for me, but this did:
for p in ax.patches:
ax.annotate(str(p.get_height()), (p.get_x() * 1.005, p.get_height() * 1.002))

Related

Percentage Annotation Matplotlib Bars side-by-side

I have created the following figure, but I want as annotation to add the percentage for each bar above, not the numbers. Can someone help how to do this in this case of three bars side-by-side. Note: I want the sum of the percentages to be 100% per group of three bars. I know only how to do this only for each category...
fig = plt.figure(figsize=(15, 8));
ax5 = fig.add_subplot(1, 1, 1);
ax5.set_xticklabels(ff.index.tolist(), size=12)
# Custom Y axis-ticks
y_ticks = np.arange(0, 400, 50)
yrange = (y_ticks[0], y_ticks[-1])
ax5.set_ylim(yrange)
ax5.set_yticklabels(y_ticks, size = 12)
ax5.set_yticks(y_ticks)
# set the xlim
ax5.set_xlim(0, len(labels))
# get your locations
dim = np.arange(0.35,len(labels),1);
# set the locations of the xticks to be on the integers
ax5.set_xticks(dim)
# Custom X - label
ax5.set_xlabel('Mode of Information', size=16, fontweight='bold')
# Custom X - label
ax5.set_ylabel('Number of volunteers', size=16, fontweight='bold')
rects1 = ax5.bar(x + width, Rem, width, label='Remained the same', color='coral',
edgecolor='black')
rects2 = ax5.bar(x + (width*2), Inc, width, label='Increased', color='forestgreen',
edgecolor='black')
rects3 = ax5.bar(x + (width*3), Dec, width, label='Decreased', color='royalblue',
edgecolor='black')
for i,rect in enumerate(rects1): # for each bar
height = rect.get_height()
ax5.text(rect.get_x() + rect.get_width() / 2, rect.get_height() + 3, '%s'% (height),
ha='center', va='bottom', color = 'black', size = 12)
for i,rect in enumerate(rects2): # for each bar
height = rect.get_height()
ax5.text(rect.get_x() + rect.get_width() / 2, rect.get_height() + 3, '%s'% (height),
ha='center', va='bottom', color = 'black', size = 12)
for i,rect in enumerate(rects3): # for each bar
height = rect.get_height()
ax5.text(rect.get_x() + rect.get_width() / 2, rect.get_height() + 3, '%s'% (height),
ha='center', va='bottom', color = 'black', size = 12)
You could make a list of totals which gives you the total within each group:
totals = [x.get_height() + y.get_height() + z.get_height()
for x, y, z in zip(rects1, rects2, rects3)]
And then in the three annotation loops, divide the height text by totals[i]:
# '%s' % (height) # old
'%.1f%%' % (100 * height / totals[i]) # new

Python : Bar out of the figure

I currently develop an application with matplotlib
Is it possible to ensure that if we have (for example) 30 bar charts, 20 are visible directly and the other 10 are hidden while waiting for the graph to scroll?
Here a picture of what I would like
My code
x = np.arange(len(list_machine)) # the label locations
width = 0.20 # the width of the bars
fig, ax = plt.subplots()
rects1 = ax.bar(x - (width*3)/2, list_uturn_time_forward_X_300, width, label = "Axe : X / Velocity : 300")
rects2 = ax.bar(x - width/2, list_uturn_time_forward_X_600, width, label = "Axe : X / Velocity : 600")
rects2 = ax.bar(x + width/2, list_uturn_time_forward_Y_300, width, label = "Axe : Y / Velocity : 300")
rects2 = ax.bar(x + (width*3)/2, list_uturn_time_forward_Y_600, width, label = "Axe : Y / Velocity : 600")
# Add some text for labels, title and custom x-axis tick labels, etc.
ax.set_ylabel('U turn value')
ax.set_title('Uturn by Machine, Axis and Velocity')
ax.set_xticks(x)
ax.set_xticklabels(list_machine)
ax.legend()
fig.tight_layout()
plt.show()
thank you in advance for your help.

How can I adapt the autolabel function in matplotlib so that it displays negative values correctly?

I have been playing around with Python for the last couple of days and found a lot of good resources about labelling, but I am failing to make it properly display negative values. Because the autolabel() function takes the height of the bar, which seems to always be a positive value, the labels are displayed way up in the graph and are of course not displayed as negative values. Can I somehow get the values that make up these bars or how do I get these labels down where they belong and show them as negative?
import pandas as pd
import matplotlib.pyplot as plt
from builtins import list
import matplotlib
matplotlib.style.use('ggplot')
import numpy as np
n_groups = 2
# create plot
fig, ax = plt.subplots()
fig.canvas.set_window_title('Mindestlohn Bundesweit')
index = np.arange(n_groups)
bar_width = 0.20
opacity = 0.8
list_reallohn_week_vollzeit = [-8.159698443426123, 11.395025597733763]
list_reallohn_week_teilzeit = [-1.048913873322391, 28.99318154295449]
list_reallohn_week_mini = [-7.552596893170488, 7.959096278017519]
rects1 = plt.bar(index + 0.00, list_reallohn_week_vollzeit, bar_width,
alpha=opacity,
color='b',
label='Vollzeit')
rects2 = plt.bar(index + bar_width, list_reallohn_week_teilzeit, bar_width,
alpha=opacity,
color='g',
label='Teilzeit')
rects3 = plt.bar(index + bar_width * 2,list_reallohn_week_mini, bar_width,
alpha = opacity,
color='c',
label='Mini Job')
label_week_lists = ('2014 vor MdL', '2015 Nicht MdL berechtigt', '2015 mit MdL')
plt.ylabel('EUR')
plt.title('Reallöhne pro Woche')
plt.xticks(index + bar_width, label_week_lists)
plt.legend(bbox_to_anchor=(1, 1),
bbox_transform=plt.gcf().transFigure)
def autolabel(rects, ax):
# Get y-axis height to calculate label position from.
(y_bottom, y_top) = ax.get_ylim()
y_height = y_top - y_bottom
for rect in rects:
height = rect.get_height()
# Fraction of axis height taken up by this rectangle
p_height = (height / y_height)
# If we can fit the label above the column, do that;
# otherwise, put it inside the column.
if p_height > 0.95: # arbitrary; 95% looked good to me.
label_position = height - (y_height * 0.05)
else:
label_position = height + (y_height * 0.01)
ax.text(rect.get_x() + rect.get_width() / 2., label_position,
'%d' % int(height),
ha='center', va='bottom')
autolabel(rects1, ax)
autolabel(rects2, ax)
autolabel(rects3, ax)
plt.show()
mathplotlib is not very well documented in that department. Try using the dir() function to reveal the available options you have on the container you're working on. i found there is a .get_y() function which retuns negative numbers in that case
try this code instead
import pandas as pd
import matplotlib.pyplot as plt
from builtins import list
import matplotlib
matplotlib.style.use('ggplot')
import numpy as np
n_groups = 2
# create plot
fig, ax = plt.subplots()
fig.canvas.set_window_title('Mindestlohn Bundesweit')
index = np.arange(n_groups)
bar_width = 0.20
opacity = 0.8
list_reallohn_week_vollzeit = [-8.159698443426123, 11.395025597733763]
list_reallohn_week_teilzeit = [-1.048913873322391, 28.99318154295449]
list_reallohn_week_mini = [-7.552596893170488, 7.959096278017519]
rects1 = plt.bar(index + 0.00, list_reallohn_week_vollzeit, bar_width,
alpha=opacity,
color='b',
label='Vollzeit')
rects2 = plt.bar(index + bar_width, list_reallohn_week_teilzeit, bar_width,
alpha=opacity,
color='g',
label='Teilzeit')
rects3 = plt.bar(index + bar_width * 2,list_reallohn_week_mini, bar_width,
alpha = opacity,
color='c',
label='Mini Job')
label_week_lists = ('2015 Nicht MdL berechtigt', '2015 mit MdL')
plt.ylabel('EUR')
plt.title('Reallöhne pro Woche')
plt.xticks(index + bar_width, label_week_lists)
plt.legend(bbox_to_anchor=(1, 1),
bbox_transform=plt.gcf().transFigure)
def autolabel(rects, ax):
# Get y-axis height to calculate label position from.
(y_bottom, y_top) = ax.get_ylim()
y_height = y_top - y_bottom
for rect in rects:
# print(dir(rect))
height = 0
if rect.get_y() < 0:
height = rect.get_y()
else:
height = rect.get_height()
print(rect.get_height())
print( str(rect.get_y()) )
# Fraction of axis height taken up by this rectangle
p_height = (height / y_height)
# If we can fit the label above the column, do that;
# otherwise, put it inside the column.
if p_height > 0.95: # arbitrary; 95% looked good to me.
label_position = height - (y_height * 0.05)
else:
label_position = height + (y_height * 0.01)
ax.text(rect.get_x() + rect.get_width() / 2., label_position,
'%d' % int(height),
ha='center', va='bottom')
autolabel(rects1, ax)
autolabel(rects2, ax)
autolabel(rects3, ax)
plt.show()
If you print rect.get_height() values you get something like:
-8.159698443426123
11.395025597733763
-1.048913873322391
28.99318154295449
-7.552596893170488
7.959096278017519
Therefore height of a bar maybe a negative.
To mark negative bars modify if statement in auto label function as follow:
if p_height > 0.95: # arbitrary; 95% looked good to me.
label_position = height - (y_height * 0.05) if (height > 0) else height + (y_height * 0.05)
else:
label_position = height + (y_height * 0.01) if (height > 0) else height - (y_height * 0.05)
You have to ajust coefficients in else branch (0.05) manually because position of a below label depends on a font size of a label. (Position of a label for positive bar does not affected by font size because a label is above the bar).
Finale for my font settings:

Bar graph doesn't fill the Axis

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

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