Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ This file provides guidance to Claude Code (claude.ai/code) when working with co

## Project Overview

pythonplot.com is a static website that provides a visual comparison of different Python plotting libraries (pandas, matplotlib, seaborn, plotnine, plotly, altair) and R's ggplot2 for exploratory data analysis. It serves as a "Rosetta Stone" showing how to create the same plots across different libraries.
pythonplot.com is a static website that provides a visual comparison of different Python plotting libraries (pandas, matplotlib, seaborn, plotnine, lets-plot, plotly, altair) and R's ggplot2 for exploratory data analysis. It serves as a "Rosetta Stone" showing how to create the same plots across different libraries.

## Architecture

Expand Down Expand Up @@ -115,7 +115,7 @@ All plots are rendered to static PNG images inside the executed notebook:

## Dependencies

Python dependencies are declared in `pyproject.toml` and locked in `uv.lock` (committed). Key libraries: pandas, matplotlib, seaborn, plotnine, plotly (+kaleido), altair (+vl-convert-python), statsmodels, rpy2, Jinja2 with jinja2-highlight.
Python dependencies are declared in `pyproject.toml` and locked in `uv.lock` (committed). Key libraries: pandas, matplotlib, seaborn, plotnine, lets-plot, plotly (+kaleido), altair (+vl-convert-python), statsmodels, rpy2, Jinja2 with jinja2-highlight.

R (system install) with ggplot2 and mgcv, installed by `setup_r.sh`.

Expand Down
243 changes: 241 additions & 2 deletions Examples.ipynb
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
"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)"
"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)"
},
{
"cell_type": "code",
Expand Down Expand Up @@ -227,6 +227,28 @@
")"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"tags": [
"ex",
"name:bar-counts",
"package:lets-plot"
]
},
"outputs": [],
"source": [
"\"\"\"Lets-Plot mirrors the ggplot2 grammar. It is imported as `lp` here\n",
"so its names don't collide with plotnine's.\n",
"\"\"\"\n",
"(lp.ggplot(mpg) +\n",
" lp.aes(x=\"manufacturer\") +\n",
" lp.geom_bar() +\n",
" lp.coord_flip() +\n",
" lp.ggtitle(\"Number of Cars by Make\"))"
]
},
{
"cell_type": "code",
"execution_count": null,
Expand Down Expand Up @@ -344,6 +366,23 @@
" geom_histogram(binwidth=2))"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"tags": [
"ex",
"name:simple-histogram",
"package:lets-plot"
]
},
"outputs": [],
"source": [
"(lp.ggplot(mpg) +\n",
" lp.aes(x=\"cty\") +\n",
" lp.geom_histogram(binwidth=2))"
]
},
{
"cell_type": "code",
"execution_count": null,
Expand Down Expand Up @@ -452,6 +491,26 @@
" ylab(\"Highway MPG\"))"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"tags": [
"ex",
"name:scatter-plot",
"package:lets-plot"
]
},
"outputs": [],
"source": [
"(lp.ggplot(mpg) +\n",
" lp.aes(x=\"displ\", y=\"hwy\") +\n",
" lp.geom_point() +\n",
" lp.ggtitle(\"Engine Displacement in Liters vs Highway MPG\") +\n",
" lp.xlab(\"Engine Displacement in Liters\") +\n",
" lp.ylab(\"Highway MPG\"))"
]
},
{
"cell_type": "code",
"execution_count": null,
Expand Down Expand Up @@ -543,6 +602,24 @@
" geom_smooth(method=\"lm\"))"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"tags": [
"ex",
"name:scatter-with-regression",
"package:lets-plot"
]
},
"outputs": [],
"source": [
"(lp.ggplot(mpg) +\n",
" lp.aes(\"displ\", \"hwy\") +\n",
" lp.geom_point() +\n",
" lp.geom_smooth(method=\"lm\"))"
]
},
{
"cell_type": "code",
"execution_count": null,
Expand Down Expand Up @@ -762,6 +839,26 @@
" ylab(\"Highway MPG\"))"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"tags": [
"ex",
"name:scatter-plot-with-colors",
"package:lets-plot"
]
},
"outputs": [],
"source": [
"(lp.ggplot(mpg) +\n",
" lp.aes(x=\"displ\", y=\"hwy\", color=\"class\") +\n",
" lp.geom_point() +\n",
" lp.ggtitle(\"Engine Displacement in Liters vs Highway MPG\") +\n",
" lp.xlab(\"Engine Displacement in Liters\") +\n",
" lp.ylab(\"Highway MPG\"))"
]
},
{
"cell_type": "code",
"execution_count": null,
Expand Down Expand Up @@ -883,6 +980,23 @@
" geom_point(alpha=.5))"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"tags": [
"ex",
"name:scatter-plot-with-size",
"package:lets-plot"
]
},
"outputs": [],
"source": [
"(lp.ggplot(mpg) +\n",
" lp.aes(x=\"cty\", y=\"hwy\", size=\"cyl\") +\n",
" lp.geom_point(alpha=.5))"
]
},
{
"cell_type": "code",
"execution_count": null,
Expand Down Expand Up @@ -989,6 +1103,24 @@
" facet_wrap(\" ~ c\", nrow = 2))"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"tags": [
"ex",
"name:scatter-plot-with-facet",
"package:lets-plot"
]
},
"outputs": [],
"source": [
"(lp.ggplot(mpg) +\n",
" lp.aes(x=\"displ\", y=\"hwy\") +\n",
" lp.geom_point() +\n",
" lp.facet_wrap(facets=\"class\", nrow=2))"
]
},
{
"cell_type": "code",
"execution_count": null,
Expand Down Expand Up @@ -1097,6 +1229,24 @@
" facet_grid(\"drv ~ cyl\"))"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"tags": [
"ex",
"name:scatter-plot-with-facets",
"package:lets-plot"
]
},
"outputs": [],
"source": [
"(lp.ggplot(mpg) +\n",
" lp.aes(x=\"displ\", y=\"hwy\") +\n",
" lp.geom_point() +\n",
" lp.facet_grid(x=\"cyl\", y=\"drv\"))"
]
},
{
"cell_type": "code",
"execution_count": null,
Expand Down Expand Up @@ -1214,6 +1364,26 @@
" ))"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"tags": [
"ex",
"name:stacked-smooth-line-and-scatter",
"package:lets-plot"
]
},
"outputs": [],
"source": [
"(lp.ggplot(mpg) +\n",
" lp.aes(x=\"displ\", y=\"hwy\") +\n",
" lp.geom_point(lp.aes(color=\"class\")) +\n",
" lp.geom_smooth(data=mpg[mpg[\"class\"] == \"subcompact\"],\n",
" se=False,\n",
" method=\"loess\"))"
]
},
{
"cell_type": "code",
"execution_count": null,
Expand Down Expand Up @@ -1301,6 +1471,23 @@
" geom_bar())"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"tags": [
"ex",
"name:stacked-bar-chart",
"package:lets-plot"
]
},
"outputs": [],
"source": [
"(lp.ggplot(diamonds) +\n",
" lp.aes(x=\"cut\", fill=\"clarity\") +\n",
" lp.geom_bar())"
]
},
{
"cell_type": "code",
"execution_count": null,
Expand Down Expand Up @@ -1415,6 +1602,23 @@
" geom_bar(position = \"dodge\"))\n"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"tags": [
"ex",
"name:dodged-bar-chart",
"package:lets-plot"
]
},
"outputs": [],
"source": [
"(lp.ggplot(diamonds) +\n",
" lp.aes(x=\"cut\", fill=\"clarity\") +\n",
" lp.geom_bar(position=\"dodge\"))"
]
},
{
"cell_type": "code",
"execution_count": null,
Expand Down Expand Up @@ -1530,6 +1734,24 @@
" geom_density(alpha=0.1))"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"tags": [
"ex",
"name:stacked-kde",
"package:lets-plot"
]
},
"outputs": [],
"source": [
"(lp.ggplot(diamonds) +\n",
" lp.aes(\"depth\", fill=\"cut\", color=\"cut\") +\n",
" lp.geom_density(alpha=0.1) +\n",
" lp.xlim(55, 70))"
]
},
{
"cell_type": "code",
"execution_count": null,
Expand Down Expand Up @@ -1614,6 +1836,23 @@
" + geom_line())"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"tags": [
"ex",
"name:timeseries",
"package:lets-plot"
]
},
"outputs": [],
"source": [
"(lp.ggplot(ts)\n",
" + lp.aes(\"date\", \"value\")\n",
" + lp.geom_line())"
]
},
{
"cell_type": "code",
"execution_count": null,
Expand Down Expand Up @@ -1686,4 +1925,4 @@
},
"nbformat": 4,
"nbformat_minor": 1
}
}
2 changes: 2 additions & 0 deletions INTRO.md
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,8 @@ There are several tools that can make the kinds of plots described here. At pres

"[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.

"[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.

"[Bokeh](http://bokeh.pydata.org/en/latest/ "Python interactive visualization library") is a Python interactive visualization library that targets modern web browsers for presentation."

"[bqplot](https://github.com/bloomberg/bqplot) is a Grammar of Graphics-based interactive plotting framework for the Jupyter notebook."
Expand Down
1 change: 1 addition & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ dependencies = [
"matplotlib>=3.7",
"seaborn>=0.13",
"plotnine>=0.13",
"lets-plot>=4.11",
"plotly>=5.24",
"altair>=5.0",
"vl-convert-python>=1.0", # Required for Altair PNG export
Expand Down
1 change: 1 addition & 0 deletions render.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
"matplotlib": "Matplotlib",
"seaborn": "Seaborn",
"plotnine": "plotnine",
"lets-plot": "lets-plot",
"plotly": "plotly",
"altair": "Altair",
"ggplot": "ggplot2 (R)",
Expand Down
Loading
Loading