Skip to content

Allow to use a non fixed axes formatter #1081

Description

@doronbehar

Hello,

I am a Python user that came from using plain Matplotlib (not an R user switching to Python). I noticed today that if I take e.g this plot:

zoom-out

And zoom in:

zoom-in

The x & y ticks are fixed! I tried to figure out from reading the code whether I can change this, and I encountered this in plotnine's code:

class MyFixedFormatter(FixedFormatter):
"""
Override MPL fixedformatter for better formatting
"""
def format_data(self, value: float) -> str:
"""
Return a formatted string representation of a number.
"""
s = locale.format_string("%1.10e", (value,))
return self.fix_minus(s)

Which is used here:

ax.xaxis.set_major_formatter(MyFixedFormatter(panel_params.x.labels))
ax.yaxis.set_major_formatter(MyFixedFormatter(panel_params.y.labels))

I am not sure whether the graphics grammar allows modifying this behavior, but I won't mind doing it via Matplotlib's native functions. However, that turned out to be hard and frustrating, as plot.draw() returns a figure, but I can't modify the axes of it. Here's something I tried:

from matplotlib.ticker import (
    EngFormatter,
    AutoLocator,
)
for ax in plot.draw(show=False).axes:
    ax.xaxis.set_major_locator(AutoLocator())
    ax.yaxis.set_major_locator(AutoLocator())
    ax.xaxis.set_major_formatter(EngFormatter())
    ax.yaxis.set_major_formatter(EngFormatter())
plot.show() # Also tried `matplotlib.pyplot.show()` here too.

And it doesn't show anything. As a side note, this made me wonder: What is the benefit in returning the figure if it cannot be manipulated afterwards with plain-old Matplotlib? This is related of course to:

Anyway, I managed to workaround this with the following ugly monkey-patching of ggplot class:

from matplotlib.ticker import (
    EngFormatter,
    AutoLocator,
)
ggplot_original__draw_breaks_and_labels = ggplot._draw_breaks_and_labels
def ggplot__draw_breaks_and_labels_with_my_ax_manipulation(self):
    ggplot_original__draw_breaks_and_labels(self)
    for ax in self.axs:
        ax.xaxis.set_major_formatter(EngFormatter(unit="Hz"))
        ax.yaxis.set_major_formatter(EngFormatter(unit="Hz"))
        ax.xaxis.set_major_locator(AutoLocator())
        ax.yaxis.set_major_locator(AutoLocator())
ggplot._draw_breaks_and_labels = ggplot__draw_breaks_and_labels_with_my_ax_manipulation

Not sure why the ticks are located in fixed positions just due to the major_formatter set above, but for sure the AutoLocator is needed too.

Metadata

Metadata

Assignees

No one assigned

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions