Skip to content

[MNT]: Inconsistency in log-scale handling: bbox_inches='tight' creates massive canvas for negative text coordinates #31054

@leakyH

Description

@leakyH

Summary

Problem Description

When using ax.set_yscale('log'), Matplotlib correctly ignores/masks negative values in data series, and also Artists like text placed at a negative coordinate. However, when combined with savefig(..., bbox_inches='tight'), the layout engine attempts to include these "invisible" texts, resulting in a figure with a massive white canvas and a microscopic plot.

Minimal Reproducible Example

1. The Normal Case (Data Masking)

This script shows that Matplotlib handles negative data gracefully. It simply doesn't draw the parts of the line that go below zero on a log scale.

import matplotlib.pyplot as plt
import numpy as np

# Data goes from -10 to 10
x = np.linspace(0, 10, 100)
y = np.sin(x) * 10 

fig, ax = plt.subplots()
ax.plot(x, y, label='Sine Wave')
ax.set_yscale('log')
ax.set_title("1. Data with negatives is masked correctly")

plt.savefig("1_normal_data.png")

2. The "Hidden Danger" (Negative Text)

Here, we add text at negative places. Everything looks fine because the window is usually sized to the axes, not the artists. The text is simply "off-screen" in a mathematical void.

import matplotlib.pyplot as plt
import numpy as np

x = np.linspace(0, 10, 100)
y = np.sin(x) * 10 

fig, ax = plt.subplots()
ax.plot(x, y)
ax.set_yscale('log')

# Adding text at a negative Y-coordinate
ax.text(5, -5, "I am at y = -5", color='red', fontweight='bold')
ax.set_title("2. Text at y=-5 (Looks okay in viewer)")

plt.savefig("2_text_no_tight.png")

3. The "Explosion" (bbox_inches='tight')

This is the script that triggers the issue. By using bbox_inches='tight', you tell Matplotlib to "shrink-wrap" the image around every artist. Because it results in an extreme coordinate, the "shrink-wrap" stretches across thousands of virtual pixels.

import matplotlib.pyplot as plt
import numpy as np

x = np.linspace(0, 10, 100)
y = np.sin(x) * 10 

fig, ax = plt.subplots()
ax.plot(x, y)
ax.set_yscale('log')

# This text coordinate will 'hallucinate' a massive distance in log-space
ax.text(5, -5, "I am at y = -5", color='red')
ax.set_title("3. The Explosion (Check the saved file!)")

# This specific command triggers the canvas stretching
plt.savefig("3_canvas_explosion.png", bbox_inches='tight')

Actual Behavior

The log transform of a negative number (e.g., ) results in a non-finite or extreme proxy value (maybe). The tight_layout / bbox_inches logic includes this coordinate in the bounding box calculation, stretching the figure canvas to extreme proportions.

Expected Behavior

Ideally, Artists with mathematically undefined coordinates on the current scale should be treated similarly to data points:

  1. They should be excluded from the bounding box calculation for bbox_inches='tight'.
  2. Or, at least they should trigger a warning that an Artist is located at an undefined coordinate for the chosen scale, which may result in an unexpected figure.

Suggested Mitigation

I know matplotlib provides tools like set_in_layout to control whether an artist is to be included in layout calculations, but bbox_inches='tight' originally tries to figure out the tight bbox of the figure [says the documentation], so I would suggest not considering those items when deciding bbox, or at least raise some warning in the early versions.

Proposed fix

For the modification, I would recommend adding some filters in get_tightbbox of artist.py for situations of scale=='log'.

[A part of expressions is refined by GPT but manually checked]

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions