From: Damon M. <dam...@gm...> - 2013-09-02 01:21:55
|
On Sat, Aug 31, 2013 at 10:21 AM, Chris Beaumont <bea...@ha...>wrote: > Pandas has some nice tools to make faceted plots -- small multiples of > plots where data is grouped by category ( > http://pandas.pydata.org/pandas-docs/stable/rplot.html). However, I think > there would be value in having this functionality built into matplotlib. > Mainly: > > 1. Not every dataset lives in a dataframe > 2. The pandas library mimics the ggplot interface, and some people would > prefer an interface closer to matplotlib > 3. Properly implemented, I think a matplotlib facet system would enable a > wider variety of faceted plots than the pandas tools. > > I've taken a stab at this, and came up with an interface that I think has > potential. This currently exists as a separate repository at > https://github.com/ChrisBeaumont/mplfacet, and an example notebook at > http://bit.ly/17u1JzP > > There two basic ways to use a facet object: > > Facet(key, data).method() > > will group one or more data arrays by key, and build a subplot for each > group by calling method (which is any axes plot method). Alternatively, > > for item in Facet(key, data): > x, y = item.data > item.axes.scatter(x, y) > > sets up the subplots and groups the data for you, but gives you more > freedom to populate each subplot however you like. > > Is there interest in building this into matplotlib? If so, I would like to > polish it up and submit a PR. > > Cheers, > Chris > Chris, This is lovely work. Thanks for taking the time to put this together, I think it has a lot of potential. I'd like to get a discussion going regarding the current implementation of the API you've rolled together for faceted plots. Overall, I like the flexibility you have provided. However, I have some reservations and I'd like to outline those now. The current workflow is: Organise your data and create a Facet object. Then call one of the Facet's plotting methods. You have designed the Facet object to respond to calls to matplotlib's plotting methods. That's pretty cool. My reservation here is that I'm not sure it aligns with the design of matplotlib. At present, the Axes object implements the plotting methods, and each method will have its own way of plotting the various types of matplotlib objects. These are Collections, PolyCollections, LineCollections, Line2D, etc... What would feel more natural is if I could do the following: f = Facet(...) ax.facet(f, 'scatter') Granted, this isn't as flexible, but it aligns with the current design philosophy. That is, the user plots objects to the axes, not the other way around. In short, this is the matplotlib workflow: Organise your data, and pass it to one of the axes object's plotting methods. The way you have implemented Facet reminds of a discussion Mike, Phil, Ben and myself were having over beers at the SciPy conference in Austin. We were talking about how matplotlib's plotting API should move forward. Admittedly, functions like plot() are a total disaster, they take a plethora of different argument orders and types and try to conform to many calling signatures at once. Specifically, the way the data is passed to the plotting method varies wildly. ax.plot(x1, y1, x2, y3, ...) ax.plot((x1, x2, x3), (y1, y2, y3), ...) This goes for the ax.tri* methods too. In addition to this, I tried to extend this in a pull request< https://github.com/matplotlib/matplotlib/pull/1143> by allowing the user to pass in a callable object, to ax.fplot(), and have matplotlib decide how to plot it. Fernando then asked the killer question, "So are you going to write an fcontour, fcontourf, ftriplot, ftricontour, etc?" Obviously, no. You have to draw the line somewhere. This led to the following line of thinking: What if matplotlib's plotting methods just acted on an object of type Plottable? That is, it doesn't matter whether your data is an array, a function, or in your case, a Facet object. The Plottable class will carve out an interface that each of matplotlib's plotting methods can utilise that interface to do the drawing. This is the new workflow: The user organises their data into a Plottable object. Pass that Plottable object to any one of matplotlib's plotting methods. I think your Faceted plotting API supports exactly what I'm hoping to see matplotlib will move towards: class Facet(matplotlib.Plottable) def __init__(self, ...) ... f = Facet(...) ax.scatter(f) Thoughts? Thanks for the hard work. Best wishes, Damon -- Damon McDougall http://www.damon-is-a-geek.com Institute for Computational Engineering Sciences 201 E. 24th St. Stop C0200 The University of Texas at Austin Austin, TX 78712-1229 |