简体   繁体   English

使用不同大小的 y 轴制作相同的 matplotlib 图

[英]Make identical matplotlib plots with y-axes of different sizes

I am trying to make a series of matplotlib plots that plot timespans for different classes of objects.我正在尝试制作一系列 matplotlib 图,为不同类别的对象绘制时间跨度。 Each plot has an identical x-axis and plot elements like a title and a legend.每个绘图都有一个相同的 x 轴和绘图元素,如标题和图例。 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.对于每个绘图,y 轴上每个条目之间的距离应该相等。

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 (eg, 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.似乎我的困难在于我可以使用plt.figure(figsize=(w,h))设置图形的绝对大小(以英寸为单位plt.figure(figsize=(w,h)) ,但我只能设置具有相对尺寸的轴的大小(例如, fig.add_axes([0.3,0.05,0.6,0.85])这会导致我的 x 轴标签在类数量很少时被切断。

Here is an MSPaint version of what I'd like to get vs. what I'm getting.这是我想要得到的东西与我得到的东西的 MSPaint 版本。 所需输出与当前输出

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

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 .然后除以英寸余量由图中高度给出的数字大小为单位的相对容限,这可以使用被提供给数字subplots_adjust ,鉴于副区已被添加与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()

在此处输入图片说明

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM