This question already has answers here:
Saving a figure after invoking pyplot.show() results in an empty file
(4 answers)
Closed 5 years ago.
me very new programmer, I having problem with saving bar chart to png, bars aren't showing up.
My Code:
import numpy as np
import matplotlib.pyplot as plt
N = 3
ind = np.arange(N)
width = 0.35
Start_means = (100, 50, 50)
Start_std = (2, 3, 4)
End_means = (80, 30, 30)
End_std = (3, 5, 2)
fig, ax = plt.subplots()
rects1 = ax.bar(ind, Start_means, width, color='xkcd:red', yerr=Start_std)
rects2 = ax.bar(ind+width, End_means, width, color='xkcd:black', yerr=End_std)
ax.legend((rects1[0], rects2[0]), ('Start', 'End'))
ax.set_ylabel('Available')
ax.set_title('Travel availability, by tour')
ax.set_xticks(ind + width/2)
countries = ['Italy', 'China', 'France']
ids = ['ID:12345', 'ID:13579', 'ID:24680']
xlabels = []
for i, j in zip(countries, ids):
xlabels.append(i + '\n' + j)
ax.set_xticklabels(xlabels)
def autolabel(rects):
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')
autolabel(rects1)
autolabel(rects2)
plt.show()
plt.savefig('barchart.png')
What it should look like: here
I'd like to save it as a png file, but it just comes up blank without the bars.
You simply need to swap the order in which the plt.show() appears and the plt.savefig('barchart.png')
plt.savefig('barchart.png')
plt.show()
The reason that plt.savefig doesn't work after calling show is that
the current figure has been reset.
Source: https://stackoverflow.com/a/21884187/1577947
Related
This question already has answers here:
How to add value labels on a bar chart
(7 answers)
Closed last year.
I have the following code:
import matplotlib.pyplot as plt
import numpy as np
plt.style.use('ggplot')
def autolabel(rects):
"""Attach a text label above each bar in *rects*, displaying its height."""
for rect in rects:
height = int(rect.get_height())
ax.annotate('{}'.format(height),
xy=(rect.get_x() + rect.get_width() / 2, height),
xytext=(0, 3), # 3 points vertical offset
textcoords="offset points",
ha='center', va='bottom', fontsize=6)
data = [108, 140.9, 187, 237.6, 299.2, 360.9, 413.3, 431.9, 437.2, 441.9]
set1 = [140.973, 161.588, 202.391, 213.57, 408.55, 442.648, 491.883, 517.456, 534.018, 545.594]
set2 = [140.386, 156.932, 200.106, 213.789, 401.426, 440.09, 490.252, 516.478, 533.255, 545.232]
set3 = [141.046, 162.663, 202.05, 213.613, 408.678, 442.685, 491.894, 517.552, 534.028, 545.858]
stage = [1,2,3,4,5,6,7,8,9,10]
x = np.arange(len(stage)) # the label locations
y = [50, 100, 150, 200, 250, 300, 350, 400, 450, 500, 550, 600]
width = 0.20 # the width of the bars
fig, ax = plt.subplots()
rects1 = ax.bar(x - 3*width/2, data, width, color = "tab:gray", label='Data')
rects2 = ax.bar(x - width/2, set1, width, color = "tab:red", label='Set 1')
rects3 = ax.bar(x + width/2, set2, width, color = "tab:blue", label='Set 2')
rects4 = ax.bar(x + 3*width/2, set3, width, color = "tab:purple", label='Set 3')
ax.set_yticks(y)
ax.set_yticklabels(y, fontsize=10)
ax.set_ylim(0,580)
ax.set_xticks(x)
ax.set_xticklabels(stage, fontsize=10)
ax.legend(fontsize=8)
ax.grid(True)
autolabel(rects1)
autolabel(rects2)
autolabel(rects3)
autolabel(rects4)
fig.savefig("plot.png", dpi=300)
which gives me the following bar plot:
Bar Plot
Can anyone help me to plot this with readable text on top of all the bars? I tried increasing the width for more text visibility but then the bars at second point on the x-axis and so on are overlapped with previous bars.
matplotlib now has a method for adding labels to bars, bar_label. You can replace your custom function with that and add your options there. To make your labels fit, without reducing the size of the text further, you can rotate by 90 degrees e.g.
ax.bar_label(rects1, fmt="%d", fontsize=6, rotation=90, padding=3)
i want visualyze with seaborn and add the text. this my code:
# barplot price by body-style
fig, ax = plt.subplots(figsize = (12,8))
g = data[['body-style','price']].groupby(by = 'body-
style').sum().reset_index().sort_values(by='price')
x = g['body-style']
y = g['price']
ok = sns.barplot(x,y, ci = None)
ax.set_title('Price By Body Style')
def autolabel(rects):
for idx,rect in enumerate(ok):
height = rect.get_height()
g.text(rect.get_x() + rect.get_width()/2., 0.2*height,
g['price'].unique().tolist()[idx],
ha='center', va='bottom', rotation=90)
autolabel(ok)
but i go error:
You need a few changes:
As you already created the ax, you need sns.barplot(..., ax=ax).
autolabel() needs to be called with the list of bars as argument. With seaborn you get this list via ax.patches.
for idx,rect in enumerate(ok): shouldn't use ok but rects.
You can't use g.text. g is a dataframe and doesn't have a .text function. You need ax.text.
Using g['price'].unique().tolist()[idx] as the text to print doesn't have any relationship with the plotted bars. You could use height instead.
Here is some test code with toy data:
import matplotlib.pyplot as plt
import seaborn as sns
import numpy as np
fig, ax = plt.subplots(figsize=(12, 8))
g = data[['body-style','price']].groupby(by = 'body-style').sum().reset_index().sort_values(by='price')
x = g['body-style']
y = g['price']
# x = list('abcdefghij')
# y = np.random.randint(20, 100, len(x))
sns.barplot(x, y, ci=None, ax=ax)
ax.set_title('Price By Body Style')
def autolabel(rects):
for rect in rects:
height = rect.get_height()
ax.text(rect.get_x() + rect.get_width() / 2., 0.2 * height,
height,
ha='center', va='bottom', rotation=90, color='white')
autolabel(ax.patches)
plt.show()
PS: You can change the fontsize of the text via a parameter to ax.text: ax.text(..., fontsize=14).
I'm trying to create a break in the y axis for the plot below. I've tried using the brokenaxis method (but I end up being unable to have headings on my bars and things didn't look great) and from the documentation here, but I can't seem to get it working. I either end up creating two figures with the exact plot I want but with no data and the other figure exactly the same as before. Can somebody help me out? Thanks
from matplotlib import pyplot as plt
import numpy as np
from brokenaxes import brokenaxes
system_x = ['a', 'b', 'c', 'd', 'e', 'f', 'g']
x_indexes = np.arange(len(system_x))
width = 0.2
fig, (ax) = plt.subplots()
cof_1 = [15, 0.0798, 0.0696, 0.0540, 0.0616, 0.0601, 0.0590]
cof_2 = [0.3856, 0.1428, 0.1803, 0.1694, 0.1172, 0.1913, 0.1474]
cof_3 = [1, 1, 2, 3, 1, 2, 2]
cof_4 = [0.0874, 0.0846, 0.0730, 0.1114, 0.0541, 0.0823, 0.0803]
r0 = ax.bar(x_indexes - 1.5*width, cof_1, label='1', color='crimson', width=width)
r1 = ax.bar(x_indexes - 0.5*width, cof_2, label='2', color='slategrey', width=width)
r2 = ax.bar(x_indexes + 0.5*width, cof_3,
label='3', color='yellowgreen', width=width)
r3 = ax.bar(x_indexes + 1.5*width, cof_4, label='3', color='orange', width=width)
def autolabel(rects):
for rect in rects:
height = rect.get_height()
ax.annotate('{}'.format(height),
xy=(rect.get_x() + rect.get_width() / 2, height),
xytext=(0, 3),
textcoords="offset points",
ha='center', va='bottom', rotation='vertical',
fontweight='bold')
autolabel(r0)
autolabel(r1)
autolabel(r2)
autolabel(r3)
plt.xticks(ticks=x_indexes, labels=system_x)
plt.xlabel('Test')
plt.ylabel('Test1')
plt.title('Mean Test')
axes = plt.gca()
axes.set_ylim([0, 10])
leg = plt.legend()
leg_lines = leg.get_lines()
leg_texts = leg.get_texts()
plt.setp(leg_lines, linewidth=4)
plt.grid(False)
plt.tight_layout()
plt.show()
Since your data are different scale, why don't you use log scale on y:
# modify auto label function
def autolabel(rects, vals):
for rect,val in zip(rects,vals):
height = rect.get_height()
ax.annotate('{}'.format(val),
xy=(rect.get_x() + rect.get_width() / 2, height),
xytext=(0, 3),
textcoords="offset points",
ha='center', va='bottom', rotation='vertical',
fontweight='bold')
autolabel(r0,cof_1)
autolabel(r1,cof_2)
autolabel(r2,cof_3)
autolabel(r3,cof_4)
# other codes
# ...
plt.xlabel('Test')
plt.ylabel('Test1')
plt.title('Mean Test')
plt.yscale('log')
# other codes
# ...
Output:
Also, consider plotting horizontal bars.
I am almost brand-new to Python and matplotlib, and so have been working on adapting an example from the Python documentation for a graph that I need to complete. However, I get undefined name errors for the rect1 and rect2 calls and for ax in the ax.text. I have a feeling that it has something to do with the values not transferring across function definitions, but I can't figure out the proper syntax. Any ideas?
P.S. I can supply additional information if necessary; this is my first post of this sort.
from inventoryClass import stockItem
import numpy as np
import matplotlib.pyplot as plt
def plotInventory(itemRecords):
stockBegin = []
stockFinish = []
stockID = []
stockItems = []
for rec in itemRecords.values() :
stockBegin.append(rec.getStockStart)
stockFinish.append(rec.getStockOnHand)
stockID.append(rec.getID)
stockItems.append(rec.getName)
N = len(stockBegin)
ind = np.arange(N) # the x locations for the groups
width = 0.35 # the width of the bars
fig, ax = plt.subplots()
rects1 = ax.bar(ind, stockBegin, width, color='r')
rects2 = ax.bar(ind + width, stockFinish, width, color='y')
# add some text for labels, title and axes ticks
ax.set_ylabel('Inventory')
ax.set_title('Stock start and end inventory, by item')
ax.set_xticks(ind + width)
ax.set_xticklabels((str(stockID[0]), str(stockID[1]), str(stockID[1])))
ax.legend((rects1[0], rects2[0]), ('Start', 'End'))
def autolabel(rects) :
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')
autolabel(rects1)
autolabel(rects2)
plt.show()
The variables rects1 and rects2 only exist in the scope of plotInventory and so python doesn't know what you're referring to by 'rects1'. There are two possible ways to fix this:
You can return the values so they are available in the global scope:
def plotInventory(itemRecords):
# ... code ... #
return rects1, rects2
rects1, rects2 = plotInventory(records)
autolabel(rects1)
autolabel(rects2)
plt.show()
You can just call autolabel from inside plotInventory:
def plotInventory(itemRecords):
# ... code ... #
autolabel(rects1)
autolabel(rects2)
As for ax, you have the same problem and the same solutions, except that you need to pass ax into autolabel, eg:
def autolabel(ax, rects):
# ... code ... #
ax, rects1, rects2 = plotInventory(records)
autolabel(ax, rects1)
autolabel(ax, rects2)
Remember to return ax from plotInventory as well!
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))