0

After a few years of finding solutions to all my coding-problems on this site, this is my first post with (as far as I can tell) a new question!

I want to create several bar-charts from one data-set and save them as individual images. I want the image-size to scale automatically so that any given object (e.g. a 1x1 square) appears the same size on every image.

The following code produces two such images in which each 1x1 element is about 60x60 pixel, so far so good:

import matplotlib.pyplot as plt

def barchart(bars,size,title):
    hspace,vspace = (max(size)+1,len(size))

    fig = plt.figure(figsize=(hspace,vspace+1))
    fig.add_axes([0.2,0.2,0.6,0.6])
    plt.title(title)
    plt.axis('scaled')

    x_pos = xrange(vspace)
    plt.xlim(0,hspace)
    plt.ylim(-1,vspace)

    plt.barh(x_pos, size, height=1, align='center', alpha=0.5)
    plt.yticks(x_pos, bars)

    plt.savefig(title+'.png',bbox_inches='tight')
    plt.clf()

barchart(["1x1","A","B","C"],[1,3,5,2],"many short bars")

barchart(["1x1","A"],[1,17],"few long bars")

But I would like to do this with a different aspect-ratio, so that e.g. each 1x1 element appears as 60x30 pixel on the image. Is there a replacement for .axis('scaled') which does this? I have tried to scale the width in figsize, xlim and both, as well as in .add_axes() and several key-words in .axis(). They all seem to affect the final scale and aspect ratio of the images in different ways.

The exact pixel-size does not matter, whether it is 60x30 or 66x33 or otherwise, as long as it is consistent throughout all images.

Bhargav Rao
  • 50,140
  • 28
  • 121
  • 140
DanSch
  • 1
  • 2
  • scaling the figure size proportionally to the number of bars is quite imprecise because the margins are not taken into account. – ImportanceOfBeingErnest Oct 29 '18 at 15:13
  • But the result of `add_axes()` is supposed to be proportional to the figure size (in this case 0.6 on both axes). How can it be anything else? – DanSch Oct 29 '18 at 23:02
  • You're right, this would hold for true proportionality. But here you have a `+1`. – ImportanceOfBeingErnest Oct 30 '18 at 14:37
  • I've figured it out from there, thanks! Some of the +/- 1 are there to have controllable margins in this particular example (bar-chart with bars centered at the tick). Is this too specific to be considered "helpful"? – DanSch Oct 30 '18 at 18:38

1 Answers1

0

Finally figured out the answer with the hints in the comment above and some more trial-and-error:

import matplotlib.pyplot as plt

def barchart(bars,size,title):
    hspace,vspace = (max(size)+1,len(size))

    AR = 0.5 # x-axis will be scaled to 50%
    fig = plt.figure(figsize=(AR*hspace,vspace+1)) 
    fig.add_axes([0.2,0.2,0.6,0.6])
    plt.xlim(0,hspace)
    plt.ylim(-1,vspace)

    plt.title(title)

    x_pos = xrange(vspace)

    plt.barh(x_pos, size, height=1, align='center', alpha=0.5)
    plt.yticks(x_pos, bars)

    plt.savefig(title+'.png',bbox_inches='tight')
    plt.clf()

barchart(["1x1","A","B","C"],[1,3,5,2],"many short bars")

barchart(["1x1","A"],[1,17],"few long bars")

The solution was to fix both the figure size and the axis limits to the same proportions and to simply leave out the .axis('scaled'). Then scale only the fig-width by the desired factor.

DanSch
  • 1
  • 2