Skip to content

Use of matplotlib's OOP API #113

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
mocquin opened this issue Apr 13, 2025 · 1 comment
Open

Use of matplotlib's OOP API #113

mocquin opened this issue Apr 13, 2025 · 1 comment

Comments

@mocquin
Copy link

mocquin commented Apr 13, 2025

Most use of matplotlib througout the package rely on the implicit pyplot API.

I'd advocate for using the more explicit OOP-oriented API since it is considered cleaner, more pythonic, and allows for more control over plots.

The difference between the 2 APIs is described here : https://matplotlib.org/stable/users/explain/figure/api_interfaces.html

Example for distributions

For example, in the base class for distributions, the view method :

def view(self):

uses plt for accessing matplotlib. This code could be replace with, for example :

Before

    def view(self):
        """Visualize the distribution.

        This method plots the distribution points and a unit circle for
            reference.
        """
        plt.plot(self.x, self.y, "k*")
        t = np.linspace(0, 2 * np.pi, 256)
        x, y = np.cos(t), np.sin(t)
        plt.plot(x, y, "r")
        plt.xlabel("Normalized Pupil Coordinate X")
        plt.ylabel("Normalized Pupil Coordinate Y")
        plt.axis("equal")
        plt.show()

After

    def view(self, ax=None):
        """Visualize the distribution.
    
        This method plots the distribution points and a unit circle for
            reference.
        """
        if ax is None:
            fig, ax = plt.subplots()
        
        ax.plot(self.x, self.y, "k*")
        t = np.linspace(0, 2 * np.pi, 256)
        x, y = np.cos(t), np.sin(t)
        ax.plot(x, y, "r")
        ax.set_xlabel("Normalized Pupil Coordinate X")
        ax.set_ylabel("Normalized Pupil Coordinate Y")
        ax.set_aspect("equal")
        return ax

This way :

  • the user can either provide the Axes or let the method just create a new one with signature (self, ax=None):
  • and return the Axes for further customization with return ax
  • and delay the actual displaying by removing plt.show()

Generalization

This approach could (and should?) be applied throughout all plottings with matplotlib in the package:

Note on circles

I believe using

circle = plt.Circle((xc, yc), R, fill=False)
ax.add_patch(circle)

is considered a cleaner approach to plot a circle on an ax (removes the need for sampling `t = np.linspace(0, 2 * np.pi, 256)' )

@HarrisonKramer
Copy link
Owner

Hi @mocquin,

Thanks for opening this! Please keep the recommendations coming.

I completely agree with your logic and think it makes sense to update this across the package. I have run into a few issues during the development when using the implicit pyplot API. I will plan to update this. I don't expect the refactor will cause any huge issues, so should be a straightforward update.

Regards,
Kramer

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
Status: Ready
Development

No branches or pull requests

2 participants