diff --git a/Examples.ipynb b/Examples.ipynb index 1e7db06..bc5e3e9 100644 --- a/Examples.ipynb +++ b/Examples.ipynb @@ -5,7 +5,93 @@ "execution_count": null, "metadata": {}, "outputs": [], - "source": "%matplotlib inline\n\nimport readline\nimport altair as alt\nimport pandas as pd\nimport seaborn as sns\nfrom matplotlib import pyplot\nfrom plotnine import *\nimport numpy as np\n\nfrom plotly import figure_factory\nfrom plotly import graph_objects\nimport plotly.express as px\nfrom IPython.core.magic import Magics, magics_class, cell_magic\n\nfrom IPython.display import Image\n\nfrom pylab import rcParams\n\nsize = 20\nparams = {\n \"legend.fontsize\": size,\n \"figure.figsize\": (15, 5),\n \"axes.labelsize\": size,\n \"axes.titlesize\": size,\n \"xtick.labelsize\": size,\n \"ytick.labelsize\": size,\n \"axes.titlesize\": 1.5 * size,\n \"figure.figsize\": (12, 12),\n}\nrcParams.update(params)\ntheme_update(\n figure_size=(9, 9),\n title=element_text(size=size),\n text=element_text(size=0.6 * size),\n) # for plotnine\n\n\nimport plotly.io as pio\npio.renderers.default = \"png\"\npio.renderers[\"png\"].width = 750\npio.renderers[\"png\"].height = 750\n\n# Render Altair charts as PNG via vl-convert\nalt.renderers.enable(\"png\", scale_factor=2.0)\n\nimport tempfile\n\nimport lets_plot as lp\nfrom lets_plot.export import ggsave\n\nlp.LetsPlot.setup_html()\nlp.LetsPlot.set_theme(\n lp.theme(text=lp.element_text(size=16), title=lp.element_text(size=20))\n)\n\n_LETS_PLOT_DIR = tempfile.mkdtemp(prefix=\"lets-plot-\")\n\n\ndef _lets_plot_png(plot):\n \"\"\"Render a Lets-Plot spec to PNG so the cell output carries an image.\"\"\"\n path = ggsave(plot + lp.ggsize(750, 750), \"plot.png\", path=_LETS_PLOT_DIR)\n with open(path, \"rb\") as f:\n return f.read()\n\n\nget_ipython().display_formatter.formatters[\"image/png\"].for_type(\n lp.plot.core.PlotSpec, _lets_plot_png\n)" + "source": [ + "%matplotlib inline\n", + "\n", + "import readline\n", + "import altair as alt\n", + "import pandas as pd\n", + "import seaborn as sns\n", + "from matplotlib import pyplot\n", + "from plotnine import *\n", + "import numpy as np\n", + "\n", + "from plotly import figure_factory\n", + "from plotly import graph_objects\n", + "import plotly.express as px\n", + "from IPython.core.magic import Magics, magics_class, cell_magic\n", + "\n", + "from IPython.display import Image\n", + "\n", + "from pylab import rcParams\n", + "\n", + "size = 20\n", + "params = {\n", + " \"legend.fontsize\": size,\n", + " \"figure.figsize\": (15, 5),\n", + " \"axes.labelsize\": size,\n", + " \"axes.titlesize\": size,\n", + " \"xtick.labelsize\": size,\n", + " \"ytick.labelsize\": size,\n", + " \"axes.titlesize\": 1.5 * size,\n", + " \"figure.figsize\": (12, 12),\n", + "}\n", + "rcParams.update(params)\n", + "theme_update(\n", + " figure_size=(9, 9),\n", + " title=element_text(size=size),\n", + " text=element_text(size=0.6 * size),\n", + ") # for plotnine\n", + "\n", + "\n", + "import seaborn.objects as so\n", + "\n", + "# Match the other libraries: ~750x750 px output with scaled-up text\n", + "so.Plot.config.display[\"scaling\"] = 1\n", + "so.Plot.config.theme.update(\n", + " {\n", + " \"figure.figsize\": (8, 8),\n", + " \"axes.labelsize\": size,\n", + " \"axes.titlesize\": 1.5 * size,\n", + " \"xtick.labelsize\": size,\n", + " \"ytick.labelsize\": size,\n", + " \"legend.fontsize\": size,\n", + " \"legend.title_fontsize\": size,\n", + " }\n", + ")\n", + "\n", + "import plotly.io as pio\n", + "pio.renderers.default = \"png\"\n", + "pio.renderers[\"png\"].width = 750\n", + "pio.renderers[\"png\"].height = 750\n", + "\n", + "# Render Altair charts as PNG via vl-convert\n", + "alt.renderers.enable(\"png\", scale_factor=2.0)\n", + "\n", + "import tempfile\n", + "\n", + "import lets_plot as lp\n", + "from lets_plot.export import ggsave\n", + "\n", + "lp.LetsPlot.setup_html()\n", + "lp.LetsPlot.set_theme(\n", + " lp.theme(text=lp.element_text(size=16), title=lp.element_text(size=20))\n", + ")\n", + "\n", + "_LETS_PLOT_DIR = tempfile.mkdtemp(prefix=\"lets-plot-\")\n", + "\n", + "\n", + "def _lets_plot_png(plot):\n", + " \"\"\"Render a Lets-Plot spec to PNG so the cell output carries an image.\"\"\"\n", + " path = ggsave(plot + lp.ggsize(750, 750), \"plot.png\", path=_LETS_PLOT_DIR)\n", + " with open(path, \"rb\") as f:\n", + " return f.read()\n", + "\n", + "\n", + "get_ipython().display_formatter.formatters[\"image/png\"].for_type(\n", + " lp.plot.core.PlotSpec, _lets_plot_png\n", + ")" + ] }, { "cell_type": "code", @@ -291,6 +377,26 @@ ")" ] }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "tags": [ + "ex", + "name:bar-counts", + "package:seaborn-objects" + ] + }, + "outputs": [], + "source": [ + "\"\"\"seaborn.objects has no `coord_flip`, so the\n", + "categorical variable is mapped to `y` instead.\n", + "\"\"\"\n", + "(so.Plot(mpg, y=\"manufacturer\")\n", + " .add(so.Bar(), so.Count())\n", + " .label(title=\"Number of Cars by Make\"))" + ] + }, { "cell_type": "markdown", "metadata": {}, @@ -423,6 +529,22 @@ ")" ] }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "tags": [ + "ex", + "name:simple-histogram", + "package:seaborn-objects" + ] + }, + "outputs": [], + "source": [ + "(so.Plot(mpg, x=\"cty\")\n", + " .add(so.Bars(), so.Hist(binwidth=2)))" + ] + }, { "cell_type": "markdown", "metadata": {}, @@ -558,6 +680,26 @@ ")" ] }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "tags": [ + "ex", + "name:scatter-plot", + "package:seaborn-objects" + ] + }, + "outputs": [], + "source": [ + "(so.Plot(mpg, x=\"displ\", y=\"hwy\")\n", + " .add(so.Dot())\n", + " .label(\n", + " x=\"Engine Displacement in Liters\",\n", + " y=\"Highway MPG\",\n", + " title=\"Engine Displacement in Liters vs Highway MPG\"))" + ] + }, { "cell_type": "markdown", "metadata": {}, @@ -819,6 +961,27 @@ "));" ] }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "tags": [ + "ex", + "name:scatter-plot-with-colors", + "package:seaborn-objects" + ] + }, + "outputs": [], + "source": [ + "(so.Plot(mpg, x=\"displ\", y=\"hwy\",\n", + " color=\"class\")\n", + " .add(so.Dot())\n", + " .label(\n", + " x=\"Engine Displacement in Liters\",\n", + " y=\"Highway MPG\",\n", + " title=\"Engine Displacement in Liters vs Highway MPG\"))" + ] + }, { "cell_type": "code", "execution_count": null, @@ -1017,6 +1180,25 @@ ")" ] }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "tags": [ + "ex", + "name:scatter-plot-with-size", + "package:seaborn-objects" + ] + }, + "outputs": [], + "source": [ + "(so.Plot(mpg, x=\"cty\", y=\"hwy\",\n", + " pointsize=\"cyl\")\n", + " .add(so.Dots(alpha=.5))\n", + " .scale(pointsize=(4, 12))\n", + " .label(x=\"City MPG\", y=\"Highway MPG\"))" + ] + }, { "cell_type": "markdown", "metadata": {}, @@ -1085,6 +1267,23 @@ ");" ] }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "tags": [ + "ex", + "name:scatter-plot-with-facet", + "package:seaborn-objects" + ] + }, + "outputs": [], + "source": [ + "(so.Plot(mpg, x=\"displ\", y=\"hwy\")\n", + " .add(so.Dot())\n", + " .facet(\"class\", wrap=4))" + ] + }, { "cell_type": "code", "execution_count": null, @@ -1211,6 +1410,23 @@ ");" ] }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "tags": [ + "ex", + "name:scatter-plot-with-facets", + "package:seaborn-objects" + ] + }, + "outputs": [], + "source": [ + "(so.Plot(mpg, x=\"displ\", y=\"hwy\")\n", + " .add(so.Dot())\n", + " .facet(col=\"cyl\", row=\"drv\"))" + ] + }, { "cell_type": "code", "execution_count": null, @@ -1546,6 +1762,22 @@ ")" ] }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "tags": [ + "ex", + "name:stacked-bar-chart", + "package:seaborn-objects" + ] + }, + "outputs": [], + "source": [ + "(so.Plot(diamonds, x=\"cut\", color=\"clarity\")\n", + " .add(so.Bar(), so.Count(), so.Stack()))" + ] + }, { "cell_type": "code", "execution_count": null, @@ -1659,6 +1891,22 @@ ")" ] }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "tags": [ + "ex", + "name:dodged-bar-chart", + "package:seaborn-objects" + ] + }, + "outputs": [], + "source": [ + "(so.Plot(diamonds, x=\"cut\", color=\"clarity\")\n", + " .add(so.Bar(), so.Count(), so.Dodge()))" + ] + }, { "cell_type": "code", "execution_count": null, @@ -1715,6 +1963,27 @@ "outputs": [], "source": "(sns\n .FacetGrid(diamonds, \n hue=\"cut\", \n height=10, \n xlim=(55, 70))\n .map(sns.kdeplot, 'depth', fill=True)\n .add_legend()\n);" }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "tags": [ + "ex", + "name:stacked-kde", + "package:seaborn-objects" + ] + }, + "outputs": [], + "source": [ + "\"\"\"`.limit()` only clips the axis; unlike ggplot2's\n", + "`xlim()` it does not drop rows before the density is\n", + "estimated.\n", + "\"\"\"\n", + "(so.Plot(diamonds, x=\"depth\", color=\"cut\")\n", + " .add(so.Area(alpha=.1), so.KDE(gridsize=500))\n", + " .limit(x=(55, 70)))" + ] + }, { "cell_type": "code", "execution_count": null, @@ -1901,6 +2170,22 @@ " x=\"date\", y=\"value\"\n", ")" ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "tags": [ + "ex", + "name:timeseries", + "package:seaborn-objects" + ] + }, + "outputs": [], + "source": [ + "(so.Plot(ts, x=\"date\", y=\"value\")\n", + " .add(so.Line()))" + ] } ], "metadata": { diff --git a/INTRO.md b/INTRO.md index 9398ad4..333bf38 100644 --- a/INTRO.md +++ b/INTRO.md @@ -20,6 +20,8 @@ Many excellent plotting tools are built on top of Matplotlib. "[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, 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. + #### 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). diff --git a/render.py b/render.py index 7173fc2..a3164ff 100644 --- a/render.py +++ b/render.py @@ -20,6 +20,7 @@ "pandas": "Pandas", "matplotlib": "Matplotlib", "seaborn": "Seaborn", + "seaborn-objects": "seaborn.objects", "plotnine": "plotnine", "lets-plot": "lets-plot", "plotly": "plotly", diff --git a/tests/test_plots.py b/tests/test_plots.py index 4bd99c4..3a4af40 100644 --- a/tests/test_plots.py +++ b/tests/test_plots.py @@ -3,17 +3,18 @@ defined_plots = { "bar-counts": [ - "pandas", "plotnine", "lets-plot", "ggplot", "plotly", "altair", + "pandas", "seaborn-objects", "plotnine", "lets-plot", "ggplot", "plotly", "altair", ], "dodged-bar-chart": [ - "pandas", "plotnine", "lets-plot", "ggplot", "plotly", "altair", + "pandas", "seaborn-objects", "plotnine", "lets-plot", "ggplot", "plotly", "altair", ], "scatter-plot": [ - "pandas", "plotnine", "lets-plot", "ggplot", "plotly", "altair", + "pandas", "seaborn-objects", "plotnine", "lets-plot", "ggplot", "plotly", "altair", ], "scatter-plot-with-colors": [ "matplotlib", "seaborn", + "seaborn-objects", "plotnine", "lets-plot", "ggplot", @@ -21,13 +22,13 @@ "altair", ], "scatter-plot-with-facet": [ - "seaborn", "plotnine", "lets-plot", "ggplot", "plotly", "altair", + "seaborn", "seaborn-objects", "plotnine", "lets-plot", "ggplot", "plotly", "altair", ], "scatter-plot-with-facets": [ - "seaborn", "plotnine", "lets-plot", "ggplot", "plotly", "altair", + "seaborn", "seaborn-objects", "plotnine", "lets-plot", "ggplot", "plotly", "altair", ], "scatter-plot-with-size": [ - "pandas", "plotnine", "lets-plot", "ggplot", "plotly", "altair", + "pandas", "seaborn-objects", "plotnine", "lets-plot", "ggplot", "plotly", "altair", ], "scatter-with-regression": [ "seaborn", "plotnine", "lets-plot", "ggplot", "plotly", @@ -35,6 +36,7 @@ "simple-histogram": [ "pandas", "matplotlib", + "seaborn-objects", "plotnine", "lets-plot", "ggplot", @@ -42,16 +44,23 @@ "altair", ], "stacked-bar-chart": [ - "pandas", "plotnine", "lets-plot", "ggplot", "plotly", "altair", + "pandas", "seaborn-objects", "plotnine", "lets-plot", "ggplot", "plotly", "altair", ], "stacked-kde": [ - "pandas", "seaborn", "plotnine", "lets-plot", "ggplot", "plotly", "altair", + "pandas", + "seaborn", + "seaborn-objects", + "plotnine", + "lets-plot", + "ggplot", + "plotly", + "altair", ], "stacked-smooth-line-and-scatter": [ "plotnine", "lets-plot", "ggplot", "plotly", "altair", ], "timeseries": [ - "pandas", "plotnine", "lets-plot", "ggplot", "plotly", "altair", + "pandas", "seaborn-objects", "plotnine", "lets-plot", "ggplot", "plotly", "altair", ], }