2025, Dec 24 19:00

How to Scale Bubble Sizes and Loosen Axis Limits in Plotly to Improve Readability and Label Spacing

Learn how to scale Plotly bubble chart sizes by percentage, align size_max, and expand axis ranges to reduce clutter and improve label spacing and readability.

When bubble markers in a Plotly scatter chart sit too close to each other, labels compete for space and the plot becomes hard to read. The challenge splits into two parts: enlarging the bubbles by a defined percentage and creating visual breathing room so nearby points don’t feel cramped. Below is a compact walkthrough that keeps the original plotting approach intact while showing how to scale sizes and adjust the coordinate range to open up the layout.

Repro case: the tight bubble layout

The following example renders a bubble chart with fixed axes and colored quadrants. It illustrates how tightly packed bubbles can feel when values cluster near each other.

import plotly.express as px
import pandas as pd
payload = {
    "tag_label": ["test 98lop1", "test9665 opp1", "test QSDFR1", "test ABBE1", "testtest21", "test23"],
    "x_val": [12.6, 10.8, -1, -15.2, -10.4, 1.6],
    "y_val": [15, 5, 44, -11, -35, -19],
    "bubble_size": [375, 112.5, 60, 210, 202.5, 195],
    "tone": ["green", "green", "green", "red", "red", "red"]
}
frame = pd.DataFrame(payload)
chart = px.scatter(
    frame,
    x="x_val",
    y="y_val",
    color="tone",
    size="bubble_size",
    text="tag_label",
    hover_name="tag_label",
    color_discrete_map={"red": "red", "green": "green"},
    title="chart"
)
chart.update_traces(
    textposition="middle right",
    textfont_size=14,
    textfont_color="black",
    textfont_family="Inter",
    hoverinfo="skip"
)
alias_map = {"red": "red title", "green": "green title"}
chart.update_layout(
    {
        "yaxis": {
            "range": [-200, 200],
            "zerolinewidth": 2,
            "zerolinecolor": "red",
            "tick0": -200,
            "dtick": 45,
        },
        "xaxis": {
            "range": [-200, 200],
            "zerolinewidth": 2,
            "zerolinecolor": "gray",
            "tick0": -200,
            "dtick": 45,
        },
        "height": 800,
    }
)
chart.add_scatter(
    x=[0, 0, -200, -200],
    y=[0, 200, 200, 0],
    fill="toself",
    fillcolor="gray",
    zorder=-1,
    mode="markers",
    marker_color="rgba(0,0,0,0)",
    showlegend=False,
    hoverinfo="skip"
)
chart.add_scatter(
    x=[0, 0, 200, 200],
    y=[0, -200, -200, 0],
    fill="toself",
    fillcolor="yellow",
    zorder=-1,
    mode="markers",
    marker_color="rgba(0,0,0,0)",
    showlegend=False,
    hoverinfo="skip"
)
chart.update_layout(paper_bgcolor="#F1F2F6")
chart.show()

What actually causes the clutter

Two things drive the crowding. First, the axes are hard-limited to a fixed range, so any cluster near the origin won’t get extra breathing room. Second, size scaling can make markers visually dominate if the maximum bubble mapping isn’t aligned with the data’s largest size value. Without adapting both, the plot either compresses points or inflates circles in a way that reduces legibility.

The fix: scale sizes by percentage and relax the plot range

To scale each bubble by 4%, multiply the size column by 1.04. Then align Plotly’s bubble mapping by setting size_max to the largest size in the data. Finally, compute the axis range from the furthest point and expand it a bit to create space around the outermost markers, which also helps separate nearby points.

import plotly.express as px
import pandas as pd
payload = {
    "tag_label": ["test 98lop1", "test9665 opp1", "test QSDFR1", "test ABBE1", "testtest21", "test23"],
    "x_val": [12.6, 10.8, -1, -15.2, -10.4, 1.6],
    "y_val": [15, 5, 44, -11, -35, -19],
    "bubble_size": [375, 112.5, 60, 210, 202.5, 195],
    "tone": ["green", "green", "green", "red", "red", "red"]
}
frame = pd.DataFrame(payload)
# Increase every bubble by 4%
grow_factor = 1.04
frame["bubble_size"] = frame["bubble_size"] * grow_factor
# Use the largest bubble as Plotly's size_max reference
largest_size = max(frame["bubble_size"])
chart = px.scatter(
    frame,
    x="x_val",
    y="y_val",
    color="tone",
    size="bubble_size",
    size_max=largest_size,
    text="tag_label",
    hover_name="tag_label",
    color_discrete_map={"red": "red", "green": "green"},
    title="chart"
)
chart.update_traces(
    textposition="middle right",
    textfont_size=14,
    textfont_color="black",
    textfont_family="Inter",
    hoverinfo="skip"
)
# Compute a symmetric range from the outermost point and add padding
span_max = max(frame["x_val"].max(), frame["y_val"].max())
span_max = span_max * 1.2  # extra space around the furthest point
grid_steps = 10
chart.update_layout(
    {
        "yaxis": {
            "range": [-span_max, span_max],
            "zerolinewidth": 2,
            "zerolinecolor": "red",
            "tick0": -span_max,
            "dtick": span_max / grid_steps,
        },
        "xaxis": {
            "range": [-span_max, span_max],
            "zerolinewidth": 2,
            "zerolinecolor": "gray",
            "tick0": -span_max,
            "dtick": span_max / grid_steps,
        },
        "height": 800,
    }
)
chart.add_scatter(
    x=[0, 0, -span_max, -span_max],
    y=[0, span_max, span_max, 0],
    fill="toself",
    fillcolor="gray",
    zorder=-1,
    mode="markers",
    marker_color="rgba(0,0,0,0)",
    showlegend=False,
    hoverinfo="skip"
)
chart.add_scatter(
    x=[0, 0, span_max, span_max],
    y=[0, -span_max, -span_max, 0],
    fill="toself",
    fillcolor="yellow",
    zorder=-1,
    mode="markers",
    marker_color="rgba(0,0,0,0)",
    showlegend=False,
    hoverinfo="skip"
)
chart.update_layout(paper_bgcolor="#F1F2F6")
chart.show()

If the bubbles feel oversized after changing size_max, switch to a smaller factor instead of 1.04. For example, using grow_factor = 0.1 will noticeably reduce the visual weight of the markers.

grow_factor = 0.1
frame["bubble_size"] = frame["bubble_size"] * grow_factor

Why this matters for readability and interaction

Scaling the underlying size data and matching size_max keeps visual proportions predictable. Adjusting the axes based on the outermost point avoids over-constraining the plot, which helps neighboring points separate on the canvas. If you choose to enlarge markers directly via update_traces marker.size, the hover will grow accordingly; to decouple what’s displayed on hover from the marker’s visual size, use customdata to supply independent values.

Conclusion

For bubble charts built with Plotly, the most reliable way to increase each bubble by a fixed percentage is to scale the size column and set size_max to the largest resulting value. To open up space between tight points, compute the axis limits from the data’s furthest point and pad the range. Start with a modest size factor, review how labels and hover behave, and tune the range padding and grid density to balance legibility with density.