Make identical matplotlib plots with y-axes of different sizes

梦想的初衷 提交于 2020-12-13 05:42:50

问题


I am trying to make a series of matplotlib plots that plot timespans for different classes of objects. Each plot has an identical x-axis and plot elements like a title and a legend. However, which classes appear in each plot differs; each plot represents a different sampling unit, each of which only contains only a subset of all the possible classes.

I am having a lot of trouble determining how to set the figure and axis dimensions. The horizontal size should always remain the same, but the vertical dimensions need to be scaled to the number of classes represented in that sampling unit. The distance between each entry on the y-axis should be equal for every plot.

It seems that my difficulties lie in the fact that I can set the absolute size (in inches) of the figure with plt.figure(figsize=(w,h)), but I can only set the size of the axis with relative dimensions (e.g., fig.add_axes([0.3,0.05,0.6,0.85]) which leads to my x-axis labels getting cut off when the number of classes is small.

Here is an MSPaint version of what I'd like to get vs. what I'm getting.

Here is a simplified version of the code I have used. Hopefully it is enough to identify the problem/solution.

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 = 1
end = 6
interval = 1 # 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)

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

    ############################################################
    # I think the problem lies here                            
    fig = plt.figure(figsize=(11, len(line_subset.index)*0.25))
    ax = fig.add_axes([0.3,0.05,0.6,0.85])
    ############################################################

    ax.add_collection(lc)
    ax.set_xlim(left=start, right=end)
    ax.set_xticks(xticks)
    ax.xaxis.set_ticks_position('bottom')

    ax.margins(0.05)
    sns.despine(left=True)

    ax.set_yticks(line_subset['order_y'])
    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]
    leg = ax.legend(proxies, ['0-10%', '10-25%', '25-50%', '50-75%', '75-90%', '90-100%'], bbox_to_anchor=(1.0, 0.9), 
              loc='best', ncol=1, labelspacing=3.0, handlelength=4.0, handletextpad=0.5, markerfirst=True, 
              columnspacing=1.0)

    for txt in leg.get_texts():
        txt.set_ha("center") # horizontal alignment of text item
        txt.set_x(-23) # x-position
        txt.set_y(15) # y-position

回答1:


You can start by defining the margins on top and bottom in units of inches. Having a fixed unit of one data unit in inches allows to calculate how large the final figure should be. Then dividing the margin in inches by the figure height gives the relative margin in units of figure size, this can be supplied to the figure using subplots_adjust, given the subplots has been added with add_subplot.

A minimal example:

import numpy as np
import matplotlib.pyplot as plt

data = [np.random.rand(i,2) for i in [2,5,8,4,3]]

height_unit = 0.25 #inch
t = 0.15; b = 0.4  #inch

for d in data:
    height = height_unit*(len(d)+1)+t+b
    fig = plt.figure(figsize=(5, height))
    ax = fig.add_subplot(111)
    ax.set_ylim(-1, len(d))
    fig.subplots_adjust(bottom=b/height, top=1-t/height, left=0.2, right=0.9)

    ax.barh(range(len(d)),d[:,1], left=d[:,0], ec="k")
    ax.set_yticks(range(len(d)))

plt.show()



来源:https://stackoverflow.com/questions/55347378/how-to-fix-overlapping-matplotlib-y-axis-tick-labels-or-autoscale-the-plot

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!