Plotnine: Grammar of Graphics for Python (2019)
datascienceworkshops.com
datascienceworkshops.com
I've noticed that a lot of bootcamp grads can use matplotlib to do very simple plots, but when it comes to iterating on a data analysis (trying different plots, facetting on variables, etc..), they get tripped up quickly.
I'm trying to use a port of dplyr I'm working on (siuba) and plotnine to show what on-the-fly analyses might look like. Can't speak highly enough of plotnine!
Plotly really does look like ggplot, but Altair doesn't look nearly as friendly:
alt.Chart(source).mark_bar().encode(
alt.X("IMDB_Rating:Q", bin=True),
y='count()',
)
I guess I can see how it does the same thing, but the ergonomics don't really appear to be in the same league.What specifically do you find unergonomic about the design of Altair?
But I’ve also been able to just preprocess some of the aggregation and calculation before the visualization and that’s worked out ok.
I’ve used it over plotly a few times because there’s no offline/online issue with plotly, and I think the Altair method of saving interactive javascript versions more straightforward than plotly.
Use "named datasets", specify data via URL, etc. Do not ever specify data inline with the spec. It might get loaded into a JSON object under the hood regardless, IDK, but it's remarkably faster and simple plots of ~100000 points will work on the average computer/vm
ggplot is an interpretation of grammar of graphics. Altair is another, separate interpretation.
As far as I can understand, plotnine is an attempt at replicating ggplot.
Also, an unfortunate namespace wart is that there's a python package called ggpy that seems to have been abandoned since 2016: https://github.com/yhat/ggpy
"plotnine" being the de facto python equivalent of ggplot2 is not obvious at all, but I'll take it :)
Some are slightly more excusable, like aes instead of aesthetic, but given today's editors auto-completion, I find this kind of choice annoying by making the code less readable for new-comers.
But others are more gratuitous to my sense. For example, the colors gradients. 'Blues' for blue gradient, but GnBl for a gradient going from green to blue? How hard is it to type Greens-Blues? It also makes it harder to remember which are abbreviated and which are not.
[1] https://colorbrewer2.org/#type=sequential&scheme=GnBu&n=3
The author builds up plots step by step, showing the changes to the plot along the way. It's really great at showing what each element contributes to the final plot.
Is there a sort of Turing completeness among plotting programs, i.e. can everything ultimately be done on any popular plotting program, or are there things achievable only in some and not others?
For strengths of plotnine, ggplot, and Altair over Gnuplot (or matplotlib), see section 3.3 of the article, particularly the example
ggplot(data=mpg) + geom_point(mapping=aes(x="displ", y="hwy", color="class"))
You can easily replace 'color' with 'shape', or even faceting (getting a separate plot for each class).
I do not know Gnuplot very well -- what's the easiest Guplot way to do this? [1] I do know that when I was looking at data sets, switching from plain matplotlib (where you'd have to plot in a loop over each class) to the grammar of graphics style was a breath of fresh air for me.
Separately, and less interestingly, if you are already using Python or Jupyter, Gnuplot isn't as smoothly integrated into that ecosystem.
[1]: The first thing I found on the website is http://gnuplot.sourceforge.net/demo_5.4/varcolor.html, but perhaps there's a better or more minimal example?
ggplot(data=mpg) +\
geom_point(mapping=aes(x="displ", y="hwy", alpha="manufacturer"))
How much easier is that from g = ggplot(data=mpg)
g.geom_point(mapping=aes(x="displ", y="hwy", alpha="manufacturer"))
Or, for instance: ggplot(mpg, aes("displ", "hwy")) +\
geom_point(aes(color="class")) +\
geom_smooth(se=False) +\
labs(title="Fuel efficiency generally decreases with engine size")
VS: g = ggplot(mpg, aes("displ", "hwy"))
g.geom_point(aes(color="class"))
g.geom_smooth(se=False)
g.title = "Fuel efficiency generally decreases with engine size"
What am I missing?Using the "+" operator to denote composing parts of visualizations is not the greatest syntax but I think we're basically stuck with it for a bit due to historical baggage. See this note from the creator of ggplot, Hadley Wickham: https://community.rstudio.com/t/why-cant-ggplot2-use/4372/7
```
df = cudf.read_csv('1GB.csv').drop_duplicates(['user_ip', 'click'])
g1 = graphistry.edges(df, 'user_ip', 'click')
g1.plot()
g2 = g1.encode_point_color('risk', ['blue','yellow','red])
g2.plot()
g2.edges(cudf.read_csv('file2.csv')).plot() # reuse g2's color settings
g1.edges(cudf.read_csv('file2.csv')).plot() # ... or just g1's graph shape
```
Being able to 'fork' plots and interactively swap in different data / encodings is super great over the course of a session. You can always go back to an earlier one as you make progress. Likewise, you can rerun notebook cells and read them top-to-bottom without worrying too much.
So while we're looking at some V2 additions, maybe supporting R, and updating some of the core (more automatic GPU goodness!)... we're definitely keeping the compositional style.
Interesting nit: Libraries copying the original grammar of graphics can likely benefit from friendlier functional DSL presentation styles. As is, I think they make it much harder to read + write, undercutting much of the productivity potential. I love the academic concept of making everything a composable value, but doing naked composition over a massive namespace of diverse types.. is super confusing to read + write.
Learning from pandas & jquery, we ended up instead steering users to chaining for the typical case: `g.bind(...).edges(...).nodes(...).encode(...).plot()`. It's functional so you can always do `g_intermediate = g...` and likewise still do first-class GoG-syntax-style things with them of you really want `f(g._bindings)`. However, those are the minor case, and people doing them make code harder to read + write:
-- Reading GoG code is confusing: In `x + f(y)`, often unclear what x, y, and f(y) are, and more so in dynamic languages like Python + R that they're used in. In `g.bind(..).encode(...).plot(...)`, each composition is pretty obvious in the typical case, and you can always read back or do first-class in the atypical case.
-- GoG plot authoring is jarring: When doing `x + ...`, tab complete doesn't get you far. If tab complete does somehow kick in, you are dealing with a big namespace dump. Instead, I see people turn to google for almost every step! In contrast, table complete on `g.nodes(df)...` will pull up the most likely next settings to add, and then again for the arguments to fill into whatever command you pick.
GoG defaults to those for the typical case, vs atypical one, so a 2nd-class imperative API may be easier. But with chaining, we get functional composition without losing straight-line reading and tab-complete. Best of both worlds!
But if you use python already, it wouldn't matter.
With method chaining the gganimate author would have to mutate some class, and users would have to load all methods (vs importing what you need).
some here say "i haven't gone back to mpl". oh i have. every single time i tried any of these alternatives.
Keeping a bookmark for when I need it and I will use this sometime in future I am sure.
I tried to find something like ggplot in Python but failed to find one. Good to know that there is a python version of it.