0

I have some really simple figures that I'm trying to generate for an experiment. They're just supposed to be bars with 3 colors, each color representing a different probability for an event.

I approached this by creating a horizontal stacked barplot in matplotlib, then I tried removing the gridlines and margins and everything extra just so that I could see only the bars. The following

df = pd.DataFrame({"Risk":[95], "No Effect" : [3], "Gain":[2]})
df.plot(kind='barh', legend = False, stacked=True, color=['#FFFF00', '#808080', '#4169e1'])
plt.axis('off')
plt.margins(x=0)
plt.margins(y=0)
plt.grid(b=None)
plt.savefig('static/images/debug3red.png', bbox_inches='tight', pad_inches=-1)
plt.show()

This code snippet is basically what I compiled from reviewing a bunch of posts about people trying to accomplish the same task.

It's almost up to par.

Here's the image I get from plt.show(). There are still margins present, but this technically shouldn't be a problem because my savefig() call ideally should have the correct parameters to remove those margins.

enter image description here

Now here's the image that's saved from the savefig() call.

enter image description here

The image is slightly zoomed in on, and cropped partially. You can't tell that the image has been zoomed in slightly from this image, there are other instances that I've seen that better showcase that property. But you can clearly see that the image is being cropped. There are no margins at least, but...

I'm close, but what's going wrong here and how can I actually accomplish my goal?

Edit: Also for those who might be wondering (because I've heard that "pad_inches=-1" isn't elegant)...

pad_inches = 0 produces the following (for a different set of probabilities)

enter image description here

Edit: Based off of this answer, Removing white space around a saved image in matplotlib

The following code removes the vertical margins, but doesn't remove the horizontal margins.

plt.gca().set_axis_off()
plt.subplots_adjust(top = 1, bottom = 0, right = 1, left = 0, 
            hspace = 0, wspace = 0)
plt.margins(0,0)
plt.savefig('static/images/debug5.png', bbox_inches='tight', pad_inches=0.0)

Resulting in... enter image description here

Byron Smith
  • 587
  • 10
  • 32
  • 1
    `pad_inches=-1` means that you crop one inch on each side of the figure. If you haven't ensured that you margin is exactly one inch previously, that can lead to just any result. Given that you also use the `"tight"` option, it's almost guaranteed that you will crop too much. Consider [this answer](https://stackoverflow.com/a/53310715/4124317). – ImportanceOfBeingErnest Sep 16 '19 at 20:00
  • @ImportanceOfBeingErnest That makes sense, so I won't take the `pad_inches=-1` approach. Trying the code from the answer you linked removes the vertical margins, but it doesn't remove the horizontal margins. – Byron Smith Sep 16 '19 at 20:08
  • @ImportanceOfBeingErnest See the edit I just made – Byron Smith Sep 16 '19 at 20:11
  • That answer I linked to does not use `bbox_inches='tight', pad_inches=0.0`, remove that bit. – ImportanceOfBeingErnest Sep 16 '19 at 20:14
  • @ImportanceOfBeingErnest Removing those parameters still leads to the same resulting image actually – Byron Smith Sep 16 '19 at 20:17
  • @ImportanceOfBeingErnest Check the edit I just made. Does that meet the criteria for a minimal reproducible example? That's exactly what I have for my code right now. – Byron Smith Sep 16 '19 at 20:23
  • Oh, ok, I missed that pandas plotting function sets the axis limits to fixed numbers. Hence `plt.margins(0,0)` only affects the horizontal axis. You can use `plt.ylim(None,None); plt.autoscale()` to undo this fixing by pandas. This also means that you can use any solution with `pad_inches=0` (not negative though) when undoing this pandas stuff. – ImportanceOfBeingErnest Sep 16 '19 at 20:29
  • @ImportanceOfBeingErnest Alrighty. I added those two lines after the `subplots_adjust()` call and that worked! – Byron Smith Sep 16 '19 at 20:33
  • There is a bit of a tangle of issues here. `get_tightbbox` cares if objects are visible or not. `axis('off')` amazingly doesn't make the artists have `visible==False`, it just doesn't draw them. Unless I'm misunderstanding, this is just a bug/inconsistency. You could make your life easier if you had a handle to your bars, but you are using pandas and all they give you is the axes. If you had the handle to your bars you could just do `bbox_extra_artists=bar_handles` in the savefig. But thats not trivial to extract. – Jody Klymak Sep 16 '19 at 20:33
  • What about not using matplotlib for this, but writing directly to a bitmap or SVG? Even Powerpoint can solve this problem (with proper print coords), so you could automate Powerpoint. – roadrunner66 Sep 16 '19 at 21:07

0 Answers0