deppen8
deppen8

Reputation: 153

Make x-axes of all subplots same length on the page

I am new to matplotlib and trying to create and save plots from pandas dataframes via a loop. Each plot should have an identical x-axis, but different y-axis lengths and labels. I have no problem creating and saving the plots with different y-axis lengths and labels, but when I create the plots, matplotlib rescales the x-axis depending on how much space is needed for the y-axis labels on the left side of the figure.

These figures are for a technical report. I plan to place one on each page of the report and I would like to have all of the x-axes take up the same amount of space on the page.

Here is an MSPaint version of what I'm getting and what I'd like to get. MSPaint-drawn plot examples of the problem and desired solution

Hopefully this is enough code to help. I'm sure there are lots of non-optimal parts of this.

import pandas as pd
import matplotlib.pyplot as plt
import pylab as pl
from matplotlib import collections as mc
from matplotlib.lines import Line2D
import seaborn as sns

# elements for x-axis
start = -1600
end = 2001
interval = 200 # x-axis tick interval
xticks = [x for x in range(start, end, interval)] # create x ticks

# items needed for legend construction
lw_bins = [0,10,25,50,75,90,100] # bins for line width
lw_labels = [3,6,9,12,15,18] # line widths
def make_proxy(zvalue, scalar_mappable, **kwargs):
    color = 'black'
    return Line2D([0, 1], [0, 1], color=color, solid_capstyle='butt', **kwargs)

# generic image ID
img_path = r'C:\\Users\\user\\chart'
img_ID = 0

for line_subset in data:
    # create line collection for this run through loop
    lc = mc.LineCollection(line_subset)

    # create plot and set properties
    sns.set(style="ticks")
    sns.set_context("notebook")
    fig, ax = pl.subplots(figsize=(16, len(line_subset)*0.5)) # I want the height of the figure to change based on number of labels on y-axis
                                                              # Figure width should stay the same
    ax.add_collection(lc)
    ax.set_xlim(left=start, right=end)
    ax.set_xticks(xticks)
    ax.set_ylim(0, len(line_subset)+1)
    ax.margins(0.05)
    sns.despine(left=True)
    ax.xaxis.set_ticks_position('bottom')
    ax.set_yticks(line_subset['order'])
    ax.set_yticklabels(line_subset['ylabel'])
    ax.tick_params(axis='y', length=0)

    # legend
    proxies = [make_proxy(item, lc, linewidth=item) for item in lw_labels]
    ax.legend(proxies, ['0-10%', '10-25%', '25-50%', '50-75%', '75-90%', '90-100%'], bbox_to_anchor=(1.05, 1.0), 
              loc=2, ncol=2, labelspacing=1.25, handlelength=4.0, handletextpad=0.5, markerfirst=False, 
              columnspacing=1.0)

    # title
    ax.text(0, len(line_subset)+2, s=str(img_ID), fontsize=20)

    # save as .png images
    plt.savefig(r'C:\\Users\\user\\Desktop\\chart' + str(img_ID) + '.png', dpi=300, bbox_inches='tight')

Upvotes: 1

Views: 2519

Answers (2)

ImportanceOfBeingErnest
ImportanceOfBeingErnest

Reputation: 339745

Unless you use an axes of specifically defined aspect ratio (like in an imshow plot or by calling .set_aspect("equal")), the space taken by the axes should only depend on the figure size along that direction and the spacings set to the figure.

You are therefore pretty much asking for the default behaviour and the only thing that prevents you from obtaining that is that you use bbox_inches='tight' in the savefig command.

bbox_inches='tight' will change the figure size! So don't use it and the axes will remain constant in size. `

Your figure size, defined like figsize=(16, len(line_subset)*0.5) seems to make sense according to what I understand from the question. So what remains is to make sure the axes inside the figure are the size you want them to be. You can do that by manually placing it using fig.add_axes

fig.add_axes([left, bottom, width, height])

where left, bottom, width, height are in figure coordinates ranging from 0 to 1. Or, you can adjust the spacings outside the subplot using subplots_adjust

plt.subplots_adjust(left, bottom, right, top)

Upvotes: 2

plasmon360
plasmon360

Reputation: 4199

To get matching x axis for the subplots (same x axis length for each subplot) , you need to share the x axis between subplots.

See the example here https://matplotlib.org/examples/pylab_examples/shared_axis_demo.html

Upvotes: 0

Related Questions