最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

python - How to remove padding coming from tick labels from other subplots - Stack Overflow

programmeradmin6浏览0评论

I have created a set of heatmaps in a 6x3 subplot grid.

As you can see, I added a shared color bar in the last row that spans over all columns (I used a grid spec to realize this).
While iterating through the rows and columns, I made sure that the Y tick labels are only rendered for the first column and the X tick labels only for the last row.

I am already pretty happy with the results, except for one thing:
Due to the Y tick labels from subplots of the first column, the color bar also starts where the Y tick labels start.
While this is great for the subplots that contain the actual heatmaps, it is unnecessary for the color bar.
I would like to remove this padding on the left side and if possible center the color bar in the middle of the last row.

I tried various approaches like disabling the ticks for the color bar axis using cax.set_yticks(ticks=[]) or removing the margin with cax.set_xmargin(0) but nothing worked so far.

fig = plt.figure(figsize=(12, 13), layout="constrained")

def render_heatmaps():
    # Create a figure with multiple subplots
    nrows = (
        len(domain_knowledge["DS1: Automotive"]) + 2
    )  # usually 4 + 2 for infobox and color bar
    ncols = len(domain_knowledge.keys())  # usually 3
    gs = fig.add_gridspec(nrows, ncols)  # Adjust the width ratios
    gs.set_height_ratios([0.7, 1, 1, 1, 1, 0.1])

    gs.hspace = 0.07
    gs.wspace = 0.02

    # Render infobox
    ax_text: Axes = fig.add_subplot(gs[0, :])
    render_infobox(ax_text)

    # Create a colorbar based on the min/max values that are in all datasets (contingency matrices for each study object)
    norm = mcolors.Normalize(vmin=np.min(datasets), vmax=np.max(datasets))
    colors = ScalarMappable(norm, cmap="GnBu")

    for row in range(nrows - 2):
        for col in range(ncols):
            axis = fig.add_subplot(gs[row + 1, col])

            # Render the combined heatmap
            sns.heatmap(
                datasets[col * 4 + row],
                annot=True,
                cmap=colors.cmap,
                # only show the y labels on the first column
                yticklabels=(categories_necessity[:nec_range] if col == 0 else False),
                # only show the x labels on the last row
                xticklabels=(
                    categories_temporality[:temp_range]
                    if row + 1 == nrows - 2
                    else False
                ),
                ax=axis,
                cbar=False,  # uses a combined colorbar
            )

            axis.tick_params(axis="y", labelrotation=0)
            axis.set_title(label=f"S{col*4+row}")

    # create a combined colorbar
    cax = fig.add_subplot(gs[nrows - 1, :])  # Use all columns for the colorbar
    cax.set_xmargin(0)
    fig.colorbar(
        colors,
        cax=cax,
        orientation="horizontal",
    )
    cax.set_title("Number of Study Responses")

I have created a set of heatmaps in a 6x3 subplot grid.

As you can see, I added a shared color bar in the last row that spans over all columns (I used a grid spec to realize this).
While iterating through the rows and columns, I made sure that the Y tick labels are only rendered for the first column and the X tick labels only for the last row.

I am already pretty happy with the results, except for one thing:
Due to the Y tick labels from subplots of the first column, the color bar also starts where the Y tick labels start.
While this is great for the subplots that contain the actual heatmaps, it is unnecessary for the color bar.
I would like to remove this padding on the left side and if possible center the color bar in the middle of the last row.

I tried various approaches like disabling the ticks for the color bar axis using cax.set_yticks(ticks=[]) or removing the margin with cax.set_xmargin(0) but nothing worked so far.

fig = plt.figure(figsize=(12, 13), layout="constrained")

def render_heatmaps():
    # Create a figure with multiple subplots
    nrows = (
        len(domain_knowledge["DS1: Automotive"]) + 2
    )  # usually 4 + 2 for infobox and color bar
    ncols = len(domain_knowledge.keys())  # usually 3
    gs = fig.add_gridspec(nrows, ncols)  # Adjust the width ratios
    gs.set_height_ratios([0.7, 1, 1, 1, 1, 0.1])

    gs.hspace = 0.07
    gs.wspace = 0.02

    # Render infobox
    ax_text: Axes = fig.add_subplot(gs[0, :])
    render_infobox(ax_text)

    # Create a colorbar based on the min/max values that are in all datasets (contingency matrices for each study object)
    norm = mcolors.Normalize(vmin=np.min(datasets), vmax=np.max(datasets))
    colors = ScalarMappable(norm, cmap="GnBu")

    for row in range(nrows - 2):
        for col in range(ncols):
            axis = fig.add_subplot(gs[row + 1, col])

            # Render the combined heatmap
            sns.heatmap(
                datasets[col * 4 + row],
                annot=True,
                cmap=colors.cmap,
                # only show the y labels on the first column
                yticklabels=(categories_necessity[:nec_range] if col == 0 else False),
                # only show the x labels on the last row
                xticklabels=(
                    categories_temporality[:temp_range]
                    if row + 1 == nrows - 2
                    else False
                ),
                ax=axis,
                cbar=False,  # uses a combined colorbar
            )

            axis.tick_params(axis="y", labelrotation=0)
            axis.set_title(label=f"S{col*4+row}")

    # create a combined colorbar
    cax = fig.add_subplot(gs[nrows - 1, :])  # Use all columns for the colorbar
    cax.set_xmargin(0)
    fig.colorbar(
        colors,
        cax=cax,
        orientation="horizontal",
    )
    cax.set_title("Number of Study Responses")

Share Improve this question edited Mar 21 at 9:51 jonrsharpe 122k30 gold badges268 silver badges476 bronze badges asked Mar 21 at 9:49 Mayor MayerMayor Mayer 3624 silver badges15 bronze badges 1
  • How to decrease colorbar WIDTH is a possible approach. It adss an independent ax for the color bar. – JohanC Commented Mar 21 at 11:21
Add a comment  | 

3 Answers 3

Reset to default 1

I think the best way is to either just add an explicit (independent) axes for the colorbar with fig.add_axes(...) or to explicitly use GridSpec (and GridSpecFromSubplotSpec) for this.

This way you can adjust all margins independently:

For example:

import matplotlib.pyplot as plt
from matplotlib.gridspec import GridSpec, GridSpecFromSubplotSpec

# create the figure
f = plt.figure()

# setup the base-grid (2 areas, one for the subplots and one for the colorbar)
gs = GridSpec(2, 1, height_ratios=(1, .05), figure=f, hspace=.2)

# setup the sub-grid for the plot axes
ax_gs = GridSpecFromSubplotSpec(4, 4, gs[0,0], hspace=1, wspace=1)
axes = ax_gs.subplots()

# add the colorbar axes
cb_ax = f.add_subplot(gs[1, 0])

Which will create a grid like this:

Almost all of this is available just using Matplotlib as it is meant to be used. https://matplotlib./stable/users/explain/axes/colorbar_placement.html

import matplotlib.pyplot as plt
import numpy as np

fig, axs = plt.subplots(4, 4, layout='constrained')
for ax in axs.flat:
    pc = ax.pcolormesh(np.random.rand(10, 10))
    ax.label_outer()
fig.colorbar(pc, ax=axs, orientation='horizontal', shrink=0.4)
plt.show()

Based on the approach from @raphael, I adjusted my code that is makes use of a sub-grid.


def render_heatmaps():
    # Create a figure with multiple subplots
    nrows = len(domain_knowledge["DS1: Automotive"])
    ncols = len(domain_knowledge.keys())
    gs = fig.add_gridspec(3, 1)  # Adjust the width ratios
    gs.set_height_ratios([0.7, 4, 0.1])

    # setup the sub-grid for the plot axes
    heatmaps_gs = GridSpecFromSubplotSpec(
        nrows, ncols, gs[1, 0], hspace=0.07, wspace=0.02
    )

    # Render infobox
    ax_text: Axes = fig.add_subplot(gs[0, :])
    render_infobox(ax_text)

    # Create a colorbar based on the min/max values that are in all datasets (contingency matrices for each study object)
    norm = mcolors.Normalize(vmin=np.min(datasets), vmax=np.max(datasets))
    colors = ScalarMappable(norm, cmap="GnBu")

    for row in range(nrows):
        for col in range(ncols):
            ax = fig.add_subplot(heatmaps_gs[row, col])
           [... same code...]

    # create a combined colorbar
    cax = fig.add_subplot(gs[2, :])  # Use all columns for the colorbar
    fig.colorbar(colors, cax=cax, orientation="horizontal")
    cax.set_title("Number of Study Responses")

Looks pretty much like I wanted it:

But since i've seen the result from @Jody's solution, I wonder if it's possible to add a bit padding around the colorbar subplot/ axis (cax) as well?
If somebody knows how to do that, would be great if you could comment and I will adjust the final solution.

Edit: I found a way to achieve this by increasing the number of columns from the outer grid to 3 and adjusting the width using gs.set_width_ration(...).
The inner sub-grid spans over all three columns, so it is not affected by the margins (unlike using plt.subplots_adjust(...)for example).



def render_heatmaps():
    # Create a figure with multiple subplots
    nrows = len(domain_knowledge["DS1: Automotive"])
    ncols = len(domain_knowledge.keys())
    gs = fig.add_gridspec(3, 3, hspace=0.1)  # Adjust the width ratios
    gs.set_height_ratios([0.7, 4, 0.1])
    gs.set_width_ratios([0.5, 9, 0.5])

    # setup the sub-grid for the plot axes (spans over all columns from the outer grid)
    heatmaps_gs = GridSpecFromSubplotSpec(
        nrows, ncols, gs[1, :], hspace=0.07, wspace=0.02
    )
    
    [...]

    # Create a colorbar based on the min/max values that are in all datasets (contingency matrices for each study object)
    norm = mcolors.Normalize(vmin=np.min(datasets), vmax=np.max(datasets))
    colors = ScalarMappable(norm, cmap="GnBu")

    for row in range(nrows):
        for col in range(ncols):
            ax = fig.add_subplot(heatmaps_gs[row, col])
            
        [...]

    # create a combined colorbar
    cax = fig.add_subplot(gs[2, 1])  # Use only the middle column for the colorbar
    fig.colorbar(colors, cax=cax, orientation="horizontal")
    cax.set_title("Number of Study Responses")
发布评论

评论列表(0)

  1. 暂无评论