diff --git a/Examples.ipynb b/Examples.ipynb index ee51a95..9b3948f 100644 --- a/Examples.ipynb +++ b/Examples.ipynb @@ -485,6 +485,41 @@ " title=\"Number of Cars by Make\"))" ] }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "tags": [ + "ex", + "name:bar-counts", + "package:matplotlib" + ] + }, + "outputs": [], + "source": [ + "counts = mpg['manufacturer'].value_counts(\n", + " sort=False)\n", + "fig, ax = pyplot.subplots()\n", + "ax.barh(counts.index, counts.values)\n", + "ax.set_title('Number of Cars by Make');" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "tags": [ + "ex", + "name:bar-counts", + "package:seaborn" + ] + }, + "outputs": [], + "source": [ + "ax = sns.countplot(mpg, y='manufacturer')\n", + "ax.set_title('Number of Cars by Make');" + ] + }, { "cell_type": "markdown", "metadata": {}, @@ -648,6 +683,21 @@ "mpg.hvplot.hist(\"cty\", bins=12)" ] }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "tags": [ + "ex", + "name:simple-histogram", + "package:seaborn" + ] + }, + "outputs": [], + "source": [ + "sns.histplot(mpg, x='cty', binwidth=2);" + ] + }, { "cell_type": "markdown", "metadata": {}, @@ -823,6 +873,46 @@ " \"vs Highway MPG\")" ] }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "tags": [ + "ex", + "name:scatter-plot", + "package:matplotlib" + ] + }, + "outputs": [], + "source": [ + "fig, ax = pyplot.subplots()\n", + "ax.scatter(mpg['displ'], mpg['hwy'])\n", + "ax.set_title('Engine Displacement in Liters '\n", + " 'vs Highway MPG')\n", + "ax.set_xlabel('Engine Displacement in Liters')\n", + "ax.set_ylabel('Highway MPG');" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "tags": [ + "ex", + "name:scatter-plot", + "package:seaborn" + ] + }, + "outputs": [], + "source": [ + "ax = sns.scatterplot(mpg, x='displ', y='hwy')\n", + "ax.set(\n", + " title='Engine Displacement in Liters '\n", + " 'vs Highway MPG',\n", + " xlabel='Engine Displacement in Liters',\n", + " ylabel='Highway MPG');" + ] + }, { "cell_type": "markdown", "metadata": {}, @@ -1355,6 +1445,45 @@ " s=\"cyl\", scale=4, alpha=0.5)" ] }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "tags": [ + "ex", + "name:scatter-plot-with-size", + "package:matplotlib" + ] + }, + "outputs": [], + "source": [ + "fig, ax = pyplot.subplots()\n", + "ax.scatter(mpg['cty'], mpg['hwy'],\n", + " s=10 * mpg['cyl'], alpha=.5)\n", + "ax.set_title('City MPG vs Highway MPG')\n", + "ax.set_xlabel('City MPG')\n", + "ax.set_ylabel('Highway MPG');" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "tags": [ + "ex", + "name:scatter-plot-with-size", + "package:seaborn" + ] + }, + "outputs": [], + "source": [ + "ax = sns.scatterplot(mpg, x='cty', y='hwy',\n", + " size='cyl', alpha=.5)\n", + "ax.set(title='City MPG vs Highway MPG',\n", + " xlabel='City MPG',\n", + " ylabel='Highway MPG');" + ] + }, { "cell_type": "markdown", "metadata": {}, @@ -1513,6 +1642,34 @@ " .cols(4))" ] }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "tags": [ + "ex", + "name:scatter-plot-with-facet", + "package:matplotlib" + ] + }, + "outputs": [], + "source": [ + "\"\"\"Matplotlib has no faceting. `subplots` makes\n", + "the grid; splitting the data, titling each panel\n", + "and hiding the leftover axes is manual.\n", + "\"\"\"\n", + "classes = sorted(mpg['class'].unique())\n", + "fig, axes = pyplot.subplots(\n", + " 2, 4, sharex=True, sharey=True)\n", + "for ax, c in zip(axes.flat, classes):\n", + " d = mpg[mpg['class'] == c]\n", + " ax.scatter(d['displ'], d['hwy'], s=20)\n", + " ax.set_title(c, fontsize=16)\n", + " ax.tick_params(labelsize=12)\n", + "for ax in axes.flat[len(classes):]:\n", + " ax.remove()" + ] + }, { "cell_type": "markdown", "metadata": {}, @@ -1678,6 +1835,42 @@ " fontscale=0.8, width=230, height=180)" ] }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "tags": [ + "ex", + "name:scatter-plot-with-facets", + "package:matplotlib" + ] + }, + "outputs": [], + "source": [ + "\"\"\"The `drv` by `cyl` grid is indexed by hand.\n", + "The strip labels are axis titles on the top row\n", + "and the right column.\n", + "\"\"\"\n", + "drvs = sorted(mpg['drv'].unique())\n", + "cyls = sorted(mpg['cyl'].unique())\n", + "fig, axes = pyplot.subplots(\n", + " len(drvs), len(cyls),\n", + " sharex=True, sharey=True)\n", + "for i, drv in enumerate(drvs):\n", + " for j, cyl in enumerate(cyls):\n", + " d = mpg[(mpg['drv'] == drv)\n", + " & (mpg['cyl'] == cyl)]\n", + " ax = axes[i, j]\n", + " ax.scatter(d['displ'], d['hwy'], s=20)\n", + " ax.tick_params(labelsize=12)\n", + "for j, cyl in enumerate(cyls):\n", + " axes[0, j].set_title(cyl, fontsize=16)\n", + "for i, drv in enumerate(drvs):\n", + " ax = axes[i, -1]\n", + " ax.set_ylabel(drv, fontsize=16)\n", + " ax.yaxis.set_label_position('right')" + ] + }, { "cell_type": "markdown", "metadata": {}, @@ -1848,6 +2041,55 @@ "Image(fig.to_image(format=\"png\", width=750, height=750))" ] }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "tags": [ + "ex", + "name:stacked-smooth-line-and-scatter", + "package:matplotlib" + ] + }, + "outputs": [], + "source": [ + "\"\"\"Matplotlib has no smoother, so the loess fit\n", + "comes from statsmodels.\n", + "\"\"\"\n", + "import statsmodels.api as sm\n", + "\n", + "fig, ax = pyplot.subplots()\n", + "for c, d in mpg.groupby('class'):\n", + " ax.scatter(d['displ'], d['hwy'], label=c)\n", + "sub = mpg[mpg['class'] == 'subcompact']\n", + "fit = sm.nonparametric.lowess(sub['hwy'],\n", + " sub['displ'])\n", + "ax.plot(fit[:, 0], fit[:, 1], color='black')\n", + "ax.legend();" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "tags": [ + "ex", + "name:stacked-smooth-line-and-scatter", + "package:seaborn" + ] + }, + "outputs": [], + "source": [ + "\"\"\"`lowess=True` fits the loess curve. Seaborn\n", + "draws no confidence band around it.\n", + "\"\"\"\n", + "ax = sns.scatterplot(mpg, x='displ', y='hwy',\n", + " hue='class')\n", + "sns.regplot(mpg[mpg['class'] == 'subcompact'],\n", + " x='displ', y='hwy', lowess=True,\n", + " scatter=False, ax=ax);" + ] + }, { "cell_type": "code", "execution_count": null, @@ -1994,6 +2236,55 @@ " legend=\"top_left\"))" ] }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "tags": [ + "ex", + "name:stacked-bar-chart", + "package:matplotlib" + ] + }, + "outputs": [], + "source": [ + "\"\"\"Matplotlib stacks bars by carrying the running\n", + "total of each series in `bottom`.\n", + "\"\"\"\n", + "counts = (diamonds\n", + " .groupby(['cut', 'clarity'])\n", + " .size()\n", + " .unstack())\n", + "fig, ax = pyplot.subplots()\n", + "bottom = np.zeros(len(counts))\n", + "for clarity in counts.columns:\n", + " ax.bar(counts.index, counts[clarity],\n", + " bottom=bottom, label=clarity)\n", + " bottom += counts[clarity].values\n", + "ax.legend();" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "tags": [ + "ex", + "name:stacked-bar-chart", + "package:seaborn" + ] + }, + "outputs": [], + "source": [ + "\"\"\"Classic seaborn has no bar-stacking function.\n", + "`histplot` counts the categories and stacks the\n", + "hue levels.\n", + "\"\"\"\n", + "sns.histplot(diamonds, x='cut',\n", + " hue='clarity',\n", + " multiple='stack', shrink=.8);" + ] + }, { "cell_type": "code", "execution_count": null, @@ -2143,6 +2434,53 @@ " fontscale=0.9))" ] }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "tags": [ + "ex", + "name:dodged-bar-chart", + "package:matplotlib" + ] + }, + "outputs": [], + "source": [ + "\"\"\"Dodging is manual: shift each series by its\n", + "own offset and move the ticks back to the group\n", + "centers.\n", + "\"\"\"\n", + "counts = (diamonds\n", + " .groupby(['cut', 'clarity'])\n", + " .size()\n", + " .unstack())\n", + "x = np.arange(len(counts))\n", + "width = .8 / len(counts.columns)\n", + "fig, ax = pyplot.subplots()\n", + "for i, clarity in enumerate(counts.columns):\n", + " ax.bar(x + i * width, counts[clarity],\n", + " width=width, label=clarity)\n", + "ax.set_xticks(x + .4 - width / 2)\n", + "ax.set_xticklabels(counts.index, rotation=45)\n", + "ax.legend();" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "tags": [ + "ex", + "name:dodged-bar-chart", + "package:seaborn" + ] + }, + "outputs": [], + "source": [ + "sns.countplot(diamonds, x='cut',\n", + " hue='clarity');" + ] + }, { "cell_type": "code", "execution_count": null, @@ -2329,6 +2667,34 @@ " alpha=0.1, xlim=(55, 70))" ] }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "tags": [ + "ex", + "name:stacked-kde", + "package:matplotlib" + ] + }, + "outputs": [], + "source": [ + "\"\"\"Matplotlib has no density estimator, so the\n", + "KDE comes from scipy. `set_xlim` clips the axis;\n", + "ggplot2's `xlim()` drops rows first.\n", + "\"\"\"\n", + "from scipy.stats import gaussian_kde\n", + "\n", + "grid = np.linspace(55, 70, 200)\n", + "fig, ax = pyplot.subplots()\n", + "for cut, d in diamonds.groupby('cut')['depth']:\n", + " density = gaussian_kde(d)(grid)\n", + " ax.fill_between(grid, density, alpha=.1)\n", + " ax.plot(grid, density, label=cut)\n", + "ax.set_xlim(55, 70)\n", + "ax.legend();" + ] + }, { "cell_type": "code", "execution_count": null, @@ -2458,6 +2824,37 @@ "source": [ "ts.hvplot.line(x=\"date\", y=\"value\")" ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "tags": [ + "ex", + "name:timeseries", + "package:matplotlib" + ] + }, + "outputs": [], + "source": [ + "fig, ax = pyplot.subplots()\n", + "ax.plot(ts['date'], ts['value']);" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "tags": [ + "ex", + "name:timeseries", + "package:seaborn" + ] + }, + "outputs": [], + "source": [ + "sns.lineplot(ts, x='date', y='value');" + ] } ], "metadata": { diff --git a/INTRO.md b/INTRO.md index 8798f06..46cc8ea 100644 --- a/INTRO.md +++ b/INTRO.md @@ -1,72 +1,68 @@ ## Introduction -Plotting is an essential component of data analysis. As a data scientist, -I spend a significant amount of my time making simple plots to understand complex data sets (exploratory data analysis) and help others understand them (presentations). +As a data scientist, I spend much of my time making simple plots to understand complex data sets (exploratory data analysis) and help others understand them (presentations). In particular, I make a lot of bar charts (including histograms), line plots (including time series), scatter plots, and density plots from data in [Pandas data frames](https://pandas.pydata.org/pandas-docs/stable/generated/pandas.DataFrame.html "pandas.DataFrame documentation"). I often want to facet these on various categorical variables and layer them on a common grid. ### Python Plotting Options -Python plotting libraries are manifold. Most well known is Matplotlib. +Python has many plotting libraries. Matplotlib is the best known, and several others build on it. -"[Matplotlib](https://matplotlib.org/ "Matplotlib: Python plotting") is a Python 2D plotting library which produces publication quality figures in a variety of hardcopy formats and interactive environments across platforms." Native Matplotlib is the cause of [frustration](https://stackoverflow.com/questions/tagged/matplotlib) to many data analysts due to the complex syntax. Much of that frustration would be alleviated if it were recognized as a library of lower level plotting primitives on which other tools can be built. (If you are frustrated by Matplotlib and haven't read [Effectively Using Matplotlib](http://pbpython.com/effective-matplotlib.html) by [Chris Moffitt](https://twitter.com/chris1610), go read it.) +"[Matplotlib](https://matplotlib.org/ "Matplotlib: Visualization with Python") makes easy things easy and hard things possible." It hands you figures, axes, and drawing primitives. You assemble everything above that level yourself: faceting, stacking, density estimation, smoothing. That assembly is what sends analysts to [Stack Overflow](https://stackoverflow.com/questions/tagged/matplotlib). -#### Matplotlib-Based Libraries +Put the Matplotlib and ggplot2 versions of the two-variable faceted scatter plot below side by side: eighteen lines of subplot bookkeeping against four lines of grammar. If Matplotlib annoys you and you haven't read [Effectively Using Matplotlib](http://pbpython.com/effective-matplotlib.html) by [Chris Moffitt](https://twitter.com/chris1610), go read it. -Many excellent plotting tools are built on top of Matplotlib. +#### Matplotlib-Based Libraries -[Pandas plots](https://pandas.pydata.org/pandas-docs/stable/visualization.html "pandas documentation") provides the "basics to easily create decent looking plots" from data frames. It provides about 70% of what I want to do day-to-day. Importantly, it lacks robust faceting capabilities. +[Pandas plotting](https://pandas.pydata.org/docs/user_guide/visualization.html "pandas user guide: Chart Visualization") provides "the basics ... to easily create decent looking plots" from data frames. That is about 70% of what I do day-to-day. It has no faceting, no categorical color mapping, and no smoothing, so five of the examples below have no pandas column. -"[plotnine](https://plotnine.readthedocs.io/en/stable/) is an implementation of a grammar of graphics in Python, it is based on ggplot2." plotnine is a attempt to directly translate ggplot2 to Python; despite some quirks and bugs, it works very well for a young product. +Seaborn calls itself "[statistical data visualization](https://seaborn.pydata.org/ "seaborn: statistical data visualization")." Its classic interface is a set of named functions (`histplot`, `scatterplot`, `countplot`, `lmplot`, `kdeplot`) plus [FacetGrid](http://seaborn.pydata.org/tutorial/axis_grids.html), which I use for faceting more than anything else in the library. It covers every plot below, once you know which function to reach for. -"[Seaborn](https://seaborn.pydata.org/ "Seaborn: statistical data visualization") is a Python visualization library based on matplotlib. It provides a high-level interface for drawing attractive statistical graphics." Seaborn makes beautiful plots but is geared toward specific statistical plots, not general purpose plotting. It does have a powerful [faceting utility function](http://seaborn.pydata.org/tutorial/axis_grids.html) that I use regularly. +Seaborn 0.12 added [seaborn.objects](https://seaborn.pydata.org/tutorial/objects_interface.html), a second interface built on the grammar of graphics. It composes a plot from marks and statistical transforms instead of dispatching to a named plotting function. The interface has no loess smoother and no regression confidence band, so those two examples are missing. -Seaborn 0.12 added [seaborn.objects](https://seaborn.pydata.org/tutorial/objects_interface.html), a second interface built on the grammar of graphics. It composes a plot from marks and statistical transforms instead of dispatching to a named plotting function, so it covers far more of the examples below than the classic interface does. It has no loess smoother and no regression confidence band, so those two examples are missing. +"[plotnine](https://plotnine.org/) is a data visualization package for Python based on the grammar of graphics." It tracks ggplot2 closely enough that most R code translates line for line, down to the `+` for layering. I reach for it when I want ggplot2 semantics without leaving Python. #### Interactive Plotting Libraries -There are several tools that can make the kinds of plots described here. At present, I have little experience with them. If anyone would like to help add examples, please [get in touch](https://github.com/tdhopper/pythonplot.com). - -"[Altair](https://altair-viz.github.io/ "Declarative Visualization in Python") is a declarative statistical visualization library for Python, based on [Vega-Lite](https://vega.github.io/vega-lite/ "Vega-Lite: A High-Level Visualization Grammar for Interactive Graphics")." According to [Jake Vanderplas](https://speakerdeck.com/jakevdp/visualization-in-python-with-altair), "Declarative visualization lets you think about data and relationships, rather than incidental details." I provide Altair examples rendered as static images. - +These libraries draw in the browser. The examples here are static PNGs, so their tooltips, panning, and linked selection are gone. -"[plotly](https://plot.ly/ "Plotly - Make charts and dashboards online")'s Python graphing library makes interactive, publication-quality graphs online. Examples of how to make line plots, scatter plots, area charts, bar charts, error bars, box plots, histograms, heatmaps, subplots, multiple-axes, polar charts, and bubble charts." I provide plotly examples rendered as static images. +"[Vega-Altair](https://altair-viz.github.io/ "Vega-Altair: Declarative Visualization in Python") is a declarative visualization library for Python," built on [Vega-Lite](https://vega.github.io/vega-lite/ "Vega-Lite: A High-Level Visualization Grammar for Interactive Graphics"). According to [Jake Vanderplas](https://speakerdeck.com/jakevdp/visualization-in-python-with-altair), "Declarative visualization lets you think about data and relationships, rather than incidental details." You describe the encoding and Altair chooses the marks, scales, and legend. -"[Lets-Plot](https://lets-plot.org/ "Lets-Plot: an open-source plotting library for statistical data") is an open-source plotting library for statistical data," written by JetBrains and modeled on the grammar of graphics. Its Python API tracks ggplot2 closely enough that most of the examples below translate line for line. I provide Lets-Plot examples rendered as static images. +"[plotly](https://plotly.com/python/ "Plotly Open Source Graphing Library for Python")'s Python graphing library makes interactive, publication-quality graphs." The examples here use [Plotly Express](https://plotly.com/python/plotly-express/), which the project calls "the recommended starting point for creating most common figures." Express covers most of these plots in one call; the regression and smoothing examples fall back to `graph_objects` and statsmodels. -"[Bokeh](http://bokeh.pydata.org/en/latest/ "Python interactive visualization library") is a Python interactive visualization library that targets modern web browsers for presentation." The Bokeh examples below go through [hvPlot](https://hvplot.holoviz.org/), which adds an `.hvplot` accessor to data frames that deliberately echoes the pandas `.plot` API, so most of these plots are one call with a few keyword arguments. hvPlot has no regression line or loess smoother, so those two examples are missing. I provide hvPlot examples rendered as static images. +JetBrains writes [Lets-Plot](https://lets-plot.org/ "Lets-Plot: multiplatform plotting library built on the principles of the Grammar of Graphics"), which it calls "a faithful port of R's ggplot2 to Python and Kotlin." The claim holds up: most of the examples below are the ggplot2 column with `lp.` prefixes. Like Altair, it renders to HTML in the notebook. -"[bqplot](https://github.com/bloomberg/bqplot) is a Grammar of Graphics-based interactive plotting framework for the Jupyter notebook." +"[Bokeh](https://docs.bokeh.org/en/latest/ "Bokeh documentation") is a Python library for creating interactive visualizations for modern web browsers." The Bokeh examples below go through [hvPlot](https://hvplot.holoviz.org/), which adds an `.hvplot` accessor to data frames. The accessor echoes the pandas `.plot` API, so most of these plots are one call plus a few keyword arguments. hvPlot has no regression line or loess smoother, so it is absent from those two examples. -### The Python Plotting Landscape +### Further Reading -If you're interested in the breadth of plotting tools available for Python, I commend Jake Vanderplas's Pycon 2017 talk called the [The Python Visualization Landscape](https://www.youtube.com/watch?v=FytuB8nFHPQ). Similarly, the blogpost [A Dramatic Tour through Python's Data Visualization Landscape (including ggplot and Altair)](https://dsaber.com/2016/10/02/a-dramatic-tour-through-pythons-data-visualization-landscape-including-ggplot-and-altair/) by Dan Saber is worth your time. +Jake Vanderplas's PyCon 2017 talk [The Python Visualization Landscape](https://www.youtube.com/watch?v=FytuB8nFHPQ) still explains how these libraries relate to one another, as does Dan Saber's [A Dramatic Tour through Python's Data Visualization Landscape (including ggplot and Altair)](https://dsaber.com/2016/10/02/a-dramatic-tour-through-pythons-data-visualization-landscape-including-ggplot-and-altair/), though both predate several of the libraries here. ### Hearty Thank You -Much Python plotting development is done by open source developers who have an (almost) thankless task. I am extremely grateful for the countless hours of many who have helped me do my job. Please keep it up! +Open source developers do most Python plotting development, an (almost) thankless job. I am grateful for the hours they have spent helping me do mine. Please keep it up! ### Why all the talk about ggplot? -The word "ggplot" comes up a lot in discussions of plotting. Before I started using Python, I did most of my data analysis work in [R](https://cran.r-project.org/ "The Comprehensive R Archive Network"). I, with many Pythonistas, remain a big fan of Hadley Wickham's [ggplot2](http://ggplot2.org/ "ggplot2"), a "[grammar of graphics](https://www.amazon.com/Grammar-Graphics-Statistics-Computing/dp/0387245448 "The Grammar of Graphics (Statistics and Computing): Leland Wilkinson, D. Wills, D. Rope, A. Norton, R. Dubbs: 9780387245447: Amazon.com: Books")" implementation in R, for exploratory data analysis. +Before I started using Python, I did most of my data analysis work in [R](https://cran.r-project.org/ "The Comprehensive R Archive Network"). Like many Pythonistas, I remain a fan of Hadley Wickham's [ggplot2](https://ggplot2.tidyverse.org/ "ggplot2"), a "[grammar of graphics](https://www.amazon.com/Grammar-Graphics-Statistics-Computing/dp/0387245448 "The Grammar of Graphics")" implementation in R, for exploratory data analysis. -Like [scikit-learn](http://scikit-learn.org/ "scikit-learn: machine learning in Python") for machine learning in Python, ggplot2 provides a consistent API with sane defaults. The consistent interface makes it easier to iterate rapidly with low cognitive overhead. The sane defaults makes it easy to drop plots right into an email or presentation. +Like [scikit-learn](http://scikit-learn.org/ "scikit-learn: machine learning in Python") for machine learning in Python, ggplot2 has a consistent API and sane defaults. The consistent interface lets me iterate without stopping to think. The sane defaults make it easy to drop plots right into an email or presentation. -Particularly, ggplot2 allows the user to make basic plots (bar, histogram, line, scatter, density, violin) from data frames _with_ [faceting](http://ggplot2.tidyverse.org/reference/facet_grid.html) and [layering](https://rpubs.com/hadley/ggplot2-layers) by discrete values. +ggplot2 makes basic plots (bar, histogram, line, scatter, density, violin) from data frames _with_ [faceting](http://ggplot2.tidyverse.org/reference/facet_grid.html) and [layering](https://rpubs.com/hadley/ggplot2-layers) by discrete values. -An excellent introduction to the power of ggplot2 is in Hadley Wickham and Garrett Grolemund's book [R for Data Science](http://r4ds.had.co.nz/data-visualisation.html). +Hadley Wickham and Garrett Grolemund's [R for Data Science](http://r4ds.had.co.nz/data-visualisation.html) teaches ggplot2 well. ### Humble Rosetta Stone for Visualization in Exploratory Data Analysis -Below I have begun compiling a list of basic plots for exploratory data analysis. I have generated the plots with as many different libraries as time (and library) permits. +Below is a list of basic plots for exploratory data analysis, each made with as many libraries as time (and library) permit. -My hope is that this will (1) help you in your daily practice to work with what is available and (2) help inspire future development of Python plotting libraries. +I hope it helps you work with what exists today and inspires what gets built next. -Some rudimentary instructions on how you can contribute plots are [here](https://github.com/tdhopper/pythonplot.com#contributing). [General feedback or other plot suggestions](https://github.com/tdhopper/pythonplot.com/issues) are welcome. +[Contributing instructions](https://github.com/tdhopper/pythonplot.com#contributing) are on GitHub. [General feedback or other plot suggestions](https://github.com/tdhopper/pythonplot.com/issues) are welcome. #### Data -The datasets used below are included with ggplot2. One is the [Prices of 50,000 round cut diamonds](http://ggplot2.tidyverse.org/reference/diamonds.html) and the other is [Fuel economy data from 1999 and 2008 for 38 popular models of car](http://ggplot2.tidyverse.org/reference/mpg.html). +ggplot2 ships the datasets used below: the [Prices of 50,000 round cut diamonds](http://ggplot2.tidyverse.org/reference/diamonds.html) and [Fuel economy data from 1999 and 2008 for 38 popular models of car](http://ggplot2.tidyverse.org/reference/mpg.html). The time series example is a random walk I generate with a quick Python script. -Here's what a few rows of the datasets looks like: +A few rows of each: diff --git a/tests/test_plots.py b/tests/test_plots.py index 745bfa4..4c067ca 100644 --- a/tests/test_plots.py +++ b/tests/test_plots.py @@ -2,76 +2,150 @@ from collections import defaultdict defined_plots = { - "bar-counts": [ - "pandas", "seaborn-objects", "plotnine", "lets-plot", "ggplot", "plotly", "altair", - "hvplot", + 'bar-counts': [ + 'pandas', + 'matplotlib', + 'seaborn', + 'seaborn-objects', + 'plotnine', + 'lets-plot', + 'plotly', + 'hvplot', + 'altair', + 'ggplot', ], - "dodged-bar-chart": [ - "pandas", "seaborn-objects", "plotnine", "lets-plot", "ggplot", "plotly", "altair", - "hvplot", + 'dodged-bar-chart': [ + 'pandas', + 'matplotlib', + 'seaborn', + 'seaborn-objects', + 'plotnine', + 'lets-plot', + 'plotly', + 'hvplot', + 'altair', + 'ggplot', ], - "scatter-plot": [ - "pandas", "seaborn-objects", "plotnine", "lets-plot", "ggplot", "plotly", "altair", - "hvplot", + 'scatter-plot': [ + 'pandas', + 'matplotlib', + 'seaborn', + 'seaborn-objects', + 'plotnine', + 'lets-plot', + 'plotly', + 'hvplot', + 'altair', + 'ggplot', ], - "scatter-plot-with-colors": [ - "matplotlib", - "seaborn", - "seaborn-objects", - "plotnine", - "lets-plot", - "ggplot", - "plotly", - "altair", - "hvplot", + 'scatter-plot-with-colors': [ + 'matplotlib', + 'seaborn', + 'seaborn-objects', + 'plotnine', + 'lets-plot', + 'plotly', + 'hvplot', + 'altair', + 'ggplot', ], - "scatter-plot-with-facet": [ - "seaborn", "seaborn-objects", "plotnine", "lets-plot", "ggplot", "plotly", "altair", - "hvplot", + 'scatter-plot-with-facet': [ + 'matplotlib', + 'seaborn', + 'seaborn-objects', + 'plotnine', + 'lets-plot', + 'plotly', + 'hvplot', + 'altair', + 'ggplot', ], - "scatter-plot-with-facets": [ - "seaborn", "seaborn-objects", "plotnine", "lets-plot", "ggplot", "plotly", "altair", - "hvplot", + 'scatter-plot-with-facets': [ + 'matplotlib', + 'seaborn', + 'seaborn-objects', + 'plotnine', + 'lets-plot', + 'plotly', + 'hvplot', + 'altair', + 'ggplot', ], - "scatter-plot-with-size": [ - "pandas", "seaborn-objects", "plotnine", "lets-plot", "ggplot", "plotly", "altair", - "hvplot", + 'scatter-plot-with-size': [ + 'pandas', + 'matplotlib', + 'seaborn', + 'seaborn-objects', + 'plotnine', + 'lets-plot', + 'plotly', + 'hvplot', + 'altair', + 'ggplot', ], - "scatter-with-regression": [ - "seaborn", "plotnine", "lets-plot", "ggplot", "plotly", + 'scatter-with-regression': [ + 'seaborn', + 'plotnine', + 'lets-plot', + 'plotly', + 'ggplot', ], - "simple-histogram": [ - "pandas", - "matplotlib", - "seaborn-objects", - "plotnine", - "lets-plot", - "ggplot", - "plotly", - "altair", - "hvplot", + 'simple-histogram': [ + 'pandas', + 'matplotlib', + 'seaborn', + 'seaborn-objects', + 'plotnine', + 'lets-plot', + 'plotly', + 'hvplot', + 'altair', + 'ggplot', ], - "stacked-bar-chart": [ - "pandas", "seaborn-objects", "plotnine", "lets-plot", "ggplot", "plotly", "altair", - "hvplot", + 'stacked-bar-chart': [ + 'pandas', + 'matplotlib', + 'seaborn', + 'seaborn-objects', + 'plotnine', + 'lets-plot', + 'plotly', + 'hvplot', + 'altair', + 'ggplot', ], - "stacked-kde": [ - "pandas", - "seaborn", - "seaborn-objects", - "plotnine", - "lets-plot", - "ggplot", - "plotly", - "altair", - "hvplot", + 'stacked-kde': [ + 'pandas', + 'matplotlib', + 'seaborn', + 'seaborn-objects', + 'plotnine', + 'lets-plot', + 'plotly', + 'hvplot', + 'altair', + 'ggplot', ], - "stacked-smooth-line-and-scatter": [ - "plotnine", "lets-plot", "ggplot", "plotly", "altair", + 'stacked-smooth-line-and-scatter': [ + 'matplotlib', + 'seaborn', + 'plotnine', + 'lets-plot', + 'plotly', + 'altair', + 'ggplot', ], - "timeseries": [ - "pandas", "seaborn-objects", "plotnine", "lets-plot", "ggplot", "plotly", "altair", - "hvplot", + 'timeseries': [ + 'pandas', + 'matplotlib', + 'seaborn', + 'seaborn-objects', + 'plotnine', + 'lets-plot', + 'plotly', + 'hvplot', + 'altair', + 'ggplot', ], }