has2k1 / has2k1/plotnine

Allow to use a non fixed axes formatter

Open
#1,081 3 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

Enhancement
Dominant language
Python
Stars
4.8k
Forks
254
Avg merge
10h 35m
Merged PRs (30d)
9

Description

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:

https://github.com/has2k1/plotnine/blob/06653090d05f6cf9ce6fc9f554794e0b106ed274/plotnine/_mpl/ticker.py#L6-L16

Which is used here:

https://github.com/has2k1/plotnine/blob/06653090d05f6cf9ce6fc9f554794e0b106ed274/plotnine/facets/facet.py#L351-L352

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.

Contributor guide

No contributing guide indexed for this repository

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start by reading the fixed formatter in plotnine/_mpl/ticker.py and how it is applied from plotnine/facets/facet.py. Trace ggplot's draw flow and the interaction between plot.draw(), plot.show(), and the axes exposed by the figure. Done should allow users to apply Matplotlib locators and formatters such as AutoLocator and EngFormatter so ticks update appropriately when zooming, without monkey-patching ggplot.

Written by the indexing model from the issue text.

Assessment

Tech stack
python
Domain
data-visualization
Issue type
Feature
Difficulty
4/5
Estimated time
3-5 days
Activity status
Quiet
Clarity
Mostly clear
Newbie friendliness
52/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.