diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml
index 8b33947de1..58c0157a6f 100644
--- a/.github/workflows/ci.yaml
+++ b/.github/workflows/ci.yaml
@@ -12,6 +12,7 @@ on:
env:
NB_KERNEL: python
MPLBACKEND: Agg
+ SEABORN_DATA: ${{ github.workspace }}/seaborn-data
jobs:
build-docs:
@@ -34,9 +35,13 @@ jobs:
sudo apt-get install pandoc
- name: Cache datasets
- run: python ci/cache_datasets.py
+ run: |
+ git clone https://github.com/mwaskom/seaborn-data.git
+ ls $SEABORN_DATA
- name: Build docs
+ env:
+ SPHINXOPTS: -j `nproc`
run: |
cd doc
make -j `nproc` notebooks
@@ -49,17 +54,14 @@ jobs:
strategy:
matrix:
python: ["3.7", "3.8", "3.9", "3.10"]
- target: [test]
install: [full]
deps: [latest]
include:
- python: "3.7"
- target: unittests
install: full
deps: pinned
- python: "3.10"
- target: unittests
install: light
deps: latest
@@ -78,11 +80,8 @@ jobs:
if [[ ${{matrix.deps }} == 'pinned' ]]; then DEPS='-r ci/deps_pinned.txt'; fi
pip install .[dev$EXTRAS] $DEPS
- - name: Cache datasets
- run: python ci/cache_datasets.py
-
- name: Run tests
- run: make ${{ matrix.target }}
+ run: make test
- name: Upload coverage
uses: codecov/codecov-action@v2
diff --git a/Makefile b/Makefile
index 073785f22b..0d2b7247a1 100644
--- a/Makefile
+++ b/Makefile
@@ -1,9 +1,6 @@
export SHELL := /bin/bash
test:
- pytest -n auto --doctest-modules --cov=seaborn --cov=tests --cov-config=.coveragerc seaborn tests
-
-unittests:
pytest -n auto --cov=seaborn --cov=tests --cov-config=.coveragerc tests
lint:
diff --git a/README.md b/README.md
index fc2d963023..8b40b5f669 100644
--- a/README.md
+++ b/README.md
@@ -60,11 +60,9 @@ Testing
Testing seaborn requires installing additional dependencies; they can be installed with the `dev` extra (e.g., `pip install .[dev]`).
-To test the code, run `make test` in the source directory. This will exercise both the unit tests and docstring examples (using [pytest](https://docs.pytest.org/)) and generate a coverage report.
+To test the code, run `make test` in the source directory. This will exercise the unit tests (using [pytest](https://docs.pytest.org/)) and generate a coverage report.
-The doctests require a network connection (unless all example datasets are cached), but the unit tests can be run offline with `make unittests`.
-
-Code style is enforced with `flake8` using the settings in the [`setup.cfg`](./setup.cfg) file. Run `make lint` to check. Alternately, you can use `pre-commit` to automatically run lint checks on any files you are committing – just run `pre-commit install` to set it up, and then commit as usual going forward.
+Code style is enforced with `flake8` using the settings in the [`setup.cfg`](./setup.cfg) file. Run `make lint` to check. Alternately, you can use `pre-commit` to automatically run lint checks on any files you are committing: just run `pre-commit install` to set it up, and then commit as usual going forward.
Development
-----------
diff --git a/doc/README.md b/doc/README.md
index 05c99c5670..78cfc1ef64 100644
--- a/doc/README.md
+++ b/doc/README.md
@@ -1,7 +1,7 @@
Building the seaborn docs
=========================
-Building the docs requires additional dependencies; they can be installed with `pip install seaborn[docs]`.
+Building the docs requires additional dependencies; they can be installed with `pip install seaborn[stats,docs]`.
The build process involves conversion of Jupyter notebooks to `rst` files. To facilitate this, you may need to set `NB_KERNEL` environment variable to the name of a kernel on your machine (e.g. `export NB_KERNEL="python3"`). To get a list of available Python kernels, run `jupyter kernelspec list`.
diff --git a/doc/_docstrings/FacetGrid.ipynb b/doc/_docstrings/FacetGrid.ipynb
index 7fc9b8146d..eeb329f2ce 100644
--- a/doc/_docstrings/FacetGrid.ipynb
+++ b/doc/_docstrings/FacetGrid.ipynb
@@ -280,9 +280,9 @@
],
"metadata": {
"kernelspec": {
- "display_name": "seaborn-py39-latest",
+ "display_name": "py310",
"language": "python",
- "name": "seaborn-py39-latest"
+ "name": "py310"
},
"language_info": {
"codemirror_mode": {
@@ -294,7 +294,7 @@
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
- "version": "3.9.13"
+ "version": "3.10.0"
}
},
"nbformat": 4,
diff --git a/doc/_docstrings/JointGrid.ipynb b/doc/_docstrings/JointGrid.ipynb
index 608529cfdf..ef8014aa0a 100644
--- a/doc/_docstrings/JointGrid.ipynb
+++ b/doc/_docstrings/JointGrid.ipynb
@@ -222,9 +222,9 @@
],
"metadata": {
"kernelspec": {
- "display_name": "seaborn-py39-latest",
+ "display_name": "py310",
"language": "python",
- "name": "seaborn-py39-latest"
+ "name": "py310"
},
"language_info": {
"codemirror_mode": {
@@ -236,7 +236,7 @@
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
- "version": "3.9.13"
+ "version": "3.10.0"
}
},
"nbformat": 4,
diff --git a/doc/_docstrings/PairGrid.ipynb b/doc/_docstrings/PairGrid.ipynb
index a5c54c8eab..c39af330e9 100644
--- a/doc/_docstrings/PairGrid.ipynb
+++ b/doc/_docstrings/PairGrid.ipynb
@@ -249,9 +249,9 @@
],
"metadata": {
"kernelspec": {
- "display_name": "seaborn-py39-latest",
+ "display_name": "py310",
"language": "python",
- "name": "seaborn-py39-latest"
+ "name": "py310"
},
"language_info": {
"codemirror_mode": {
@@ -263,7 +263,7 @@
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
- "version": "3.9.13"
+ "version": "3.10.0"
}
},
"nbformat": 4,
diff --git a/doc/_docstrings/axes_style.ipynb b/doc/_docstrings/axes_style.ipynb
index aedc546911..2dca0e87cf 100644
--- a/doc/_docstrings/axes_style.ipynb
+++ b/doc/_docstrings/axes_style.ipynb
@@ -80,9 +80,9 @@
],
"metadata": {
"kernelspec": {
- "display_name": "seaborn-py39-latest",
+ "display_name": "py310",
"language": "python",
- "name": "seaborn-py39-latest"
+ "name": "py310"
},
"language_info": {
"codemirror_mode": {
@@ -94,7 +94,7 @@
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
- "version": "3.9.13"
+ "version": "3.10.0"
}
},
"nbformat": 4,
diff --git a/doc/_docstrings/barplot.ipynb b/doc/_docstrings/barplot.ipynb
index 3a128a39c6..7b1264448d 100644
--- a/doc/_docstrings/barplot.ipynb
+++ b/doc/_docstrings/barplot.ipynb
@@ -103,9 +103,9 @@
],
"metadata": {
"kernelspec": {
- "display_name": "seaborn-py39-latest",
+ "display_name": "py310",
"language": "python",
- "name": "seaborn-py39-latest"
+ "name": "py310"
},
"language_info": {
"codemirror_mode": {
@@ -117,7 +117,7 @@
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
- "version": "3.9.13"
+ "version": "3.10.0"
}
},
"nbformat": 4,
diff --git a/doc/_docstrings/blend_palette.ipynb b/doc/_docstrings/blend_palette.ipynb
new file mode 100644
index 0000000000..85f8755a1c
--- /dev/null
+++ b/doc/_docstrings/blend_palette.ipynb
@@ -0,0 +1,103 @@
+{
+ "cells": [
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "id": "8f97280e-cec8-42b2-a968-4fd4364594f8",
+ "metadata": {
+ "tags": [
+ "hide"
+ ]
+ },
+ "outputs": [],
+ "source": [
+ "import seaborn as sns\n",
+ "sns.set_theme()\n",
+ "sns.palettes._patch_colormap_display()"
+ ]
+ },
+ {
+ "cell_type": "raw",
+ "id": "972edede-df1a-4010-9674-00b864d020e2",
+ "metadata": {},
+ "source": [
+ "Pass a list of two colors to interpolate between them:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "id": "e6ae2547-1042-4ac0-84ea-6f37a0229871",
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "sns.blend_palette([\"b\", \"r\"])"
+ ]
+ },
+ {
+ "cell_type": "raw",
+ "id": "1d983eac-2dd5-4746-b27f-4dfa19b5e091",
+ "metadata": {},
+ "source": [
+ "The color list can be arbitrarily long, and any color format can be used:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "id": "846b78fd-30ce-4507-93f4-4274122c1987",
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "sns.blend_palette([\"#45a872\", \".8\", \"xkcd:golden\"])"
+ ]
+ },
+ {
+ "cell_type": "raw",
+ "id": "318fef32-1f83-44d9-9ff9-21fa0231b7c6",
+ "metadata": {},
+ "source": [
+ "Return a continuous colormap instead of a discrete palette:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "id": "f0a05bc3-c60b-47a1-b276-d2e28a4a8226",
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "sns.blend_palette([\"#bdc\", \"#7b9\", \"#47a\"], as_cmap=True)"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "id": "0473a402-0ec2-4877-81d2-ed6c57aefc77",
+ "metadata": {},
+ "outputs": [],
+ "source": []
+ }
+ ],
+ "metadata": {
+ "kernelspec": {
+ "display_name": "py310",
+ "language": "python",
+ "name": "py310"
+ },
+ "language_info": {
+ "codemirror_mode": {
+ "name": "ipython",
+ "version": 3
+ },
+ "file_extension": ".py",
+ "mimetype": "text/x-python",
+ "name": "python",
+ "nbconvert_exporter": "python",
+ "pygments_lexer": "ipython3",
+ "version": "3.10.0"
+ }
+ },
+ "nbformat": 4,
+ "nbformat_minor": 5
+}
diff --git a/doc/_docstrings/boxenplot.ipynb b/doc/_docstrings/boxenplot.ipynb
index 00176eb646..1b2f863b63 100644
--- a/doc/_docstrings/boxenplot.ipynb
+++ b/doc/_docstrings/boxenplot.ipynb
@@ -108,9 +108,9 @@
],
"metadata": {
"kernelspec": {
- "display_name": "seaborn-py39-latest",
+ "display_name": "py310",
"language": "python",
- "name": "seaborn-py39-latest"
+ "name": "py310"
},
"language_info": {
"codemirror_mode": {
@@ -122,7 +122,7 @@
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
- "version": "3.9.13"
+ "version": "3.10.0"
}
},
"nbformat": 4,
diff --git a/doc/_docstrings/boxplot.ipynb b/doc/_docstrings/boxplot.ipynb
index 1c6f5daf91..098cda1ac4 100644
--- a/doc/_docstrings/boxplot.ipynb
+++ b/doc/_docstrings/boxplot.ipynb
@@ -151,9 +151,9 @@
],
"metadata": {
"kernelspec": {
- "display_name": "seaborn-py39-latest",
+ "display_name": "py310",
"language": "python",
- "name": "seaborn-py39-latest"
+ "name": "py310"
},
"language_info": {
"codemirror_mode": {
@@ -165,7 +165,7 @@
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
- "version": "3.9.13"
+ "version": "3.10.0"
}
},
"nbformat": 4,
diff --git a/doc/_docstrings/catplot.ipynb b/doc/_docstrings/catplot.ipynb
index d4f01b6a67..ba956e6ab5 100644
--- a/doc/_docstrings/catplot.ipynb
+++ b/doc/_docstrings/catplot.ipynb
@@ -168,9 +168,9 @@
],
"metadata": {
"kernelspec": {
- "display_name": "seaborn-py39-latest",
+ "display_name": "py310",
"language": "python",
- "name": "seaborn-py39-latest"
+ "name": "py310"
},
"language_info": {
"codemirror_mode": {
@@ -182,7 +182,7 @@
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
- "version": "3.9.13"
+ "version": "3.10.0"
}
},
"nbformat": 4,
diff --git a/doc/_docstrings/clustermap.ipynb b/doc/_docstrings/clustermap.ipynb
new file mode 100644
index 0000000000..6937145881
--- /dev/null
+++ b/doc/_docstrings/clustermap.ipynb
@@ -0,0 +1,184 @@
+{
+ "cells": [
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "id": "ffc1e1d9-fa74-4121-aa87-e1a8665e4c2b",
+ "metadata": {
+ "tags": [
+ "hide"
+ ]
+ },
+ "outputs": [],
+ "source": [
+ "import seaborn as sns\n",
+ "sns.set_theme()"
+ ]
+ },
+ {
+ "cell_type": "raw",
+ "id": "41b4f602-32af-44f8-bf1a-0f1695c9abbb",
+ "metadata": {},
+ "source": [
+ "Plot a heatmap with row and column clustering:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "id": "c715bd8f-cf5d-4caa-9244-336b3d0248a8",
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "iris = sns.load_dataset(\"iris\")\n",
+ "species = iris.pop(\"species\")\n",
+ "sns.clustermap(iris)"
+ ]
+ },
+ {
+ "cell_type": "raw",
+ "id": "1cc3134c-579a-442a-97d8-a878651ce90a",
+ "metadata": {},
+ "source": [
+ "Change the size and layout of the figure:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "id": "fd33cf4b-9589-4b9a-a246-0b95bad28c51",
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "sns.clustermap(\n",
+ " iris,\n",
+ " figsize=(7, 5),\n",
+ " row_cluster=False,\n",
+ " dendrogram_ratio=(.1, .2),\n",
+ " cbar_pos=(0, .2, .03, .4)\n",
+ ")"
+ ]
+ },
+ {
+ "cell_type": "raw",
+ "id": "c5d3408d-f5d6-4045-9d61-15573a981587",
+ "metadata": {},
+ "source": [
+ "Add colored labels to identify observations:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "id": "79d3fe52-6146-4f33-a39a-1d4a47243ea5",
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "lut = dict(zip(species.unique(), \"rbg\"))\n",
+ "row_colors = species.map(lut)\n",
+ "sns.clustermap(iris, row_colors=row_colors)"
+ ]
+ },
+ {
+ "cell_type": "raw",
+ "id": "f2f944e2-36cd-4653-86b4-6d2affec13d6",
+ "metadata": {},
+ "source": [
+ "Use a different colormap and adjust the limits of the color range:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "id": "6137c7ad-db92-47b8-9d00-3228c4e1f7df",
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "sns.clustermap(iris, cmap=\"mako\", vmin=0, vmax=10)"
+ ]
+ },
+ {
+ "cell_type": "raw",
+ "id": "93f96d1c-9d04-464f-93c9-4319caa8504a",
+ "metadata": {},
+ "source": [
+ "Use differente clustering parameters:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "id": "f9e76bde-a222-4eca-971f-54f56ad53281",
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "sns.clustermap(iris, metric=\"correlation\", method=\"single\")"
+ ]
+ },
+ {
+ "cell_type": "raw",
+ "id": "ea6ed3fd-188d-4244-adac-ec0169c02205",
+ "metadata": {},
+ "source": [
+ "Standardize the data within the columns:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "id": "e5f744c4-b959-4ed1-b2cf-6046c9214568",
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "sns.clustermap(iris, standard_scale=1)"
+ ]
+ },
+ {
+ "cell_type": "raw",
+ "id": "7ca72242-4eb0-4f8e-b0c0-d1ef7166b738",
+ "metadata": {},
+ "source": [
+ "Normalize the data within rows:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "id": "33815c4c-9bae-4226-bd11-3dfdb7ecab2b",
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "sns.clustermap(iris, z_score=0, cmap=\"vlag\", center=0)"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "id": "0f37d57a-b049-4665-9c24-4d5fbbca00ba",
+ "metadata": {},
+ "outputs": [],
+ "source": []
+ }
+ ],
+ "metadata": {
+ "kernelspec": {
+ "display_name": "py310",
+ "language": "python",
+ "name": "py310"
+ },
+ "language_info": {
+ "codemirror_mode": {
+ "name": "ipython",
+ "version": 3
+ },
+ "file_extension": ".py",
+ "mimetype": "text/x-python",
+ "name": "python",
+ "nbconvert_exporter": "python",
+ "pygments_lexer": "ipython3",
+ "version": "3.10.0"
+ }
+ },
+ "nbformat": 4,
+ "nbformat_minor": 5
+}
diff --git a/doc/_docstrings/color_palette.ipynb b/doc/_docstrings/color_palette.ipynb
index 2dcfc8f93f..a0408b429a 100644
--- a/doc/_docstrings/color_palette.ipynb
+++ b/doc/_docstrings/color_palette.ipynb
@@ -10,47 +10,9 @@
},
"outputs": [],
"source": [
- "import seaborn as sns; sns.set_theme()"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "metadata": {
- "tags": [
- "hide"
- ]
- },
- "outputs": [],
- "source": [
- "# Add colormap display methods to matplotlib colormaps.\n",
- "# These are forthcoming in matplotlib 3.4, but, the matplotlib display\n",
- "# method includes the colormap name, which is redundant.\n",
- "def _repr_png_(self):\n",
- " \"\"\"Generate a PNG representation of the Colormap.\"\"\"\n",
- " import io\n",
- " from PIL import Image\n",
- " import numpy as np\n",
- " IMAGE_SIZE = (400, 50)\n",
- " X = np.tile(np.linspace(0, 1, IMAGE_SIZE[0]), (IMAGE_SIZE[1], 1))\n",
- " pixels = self(X, bytes=True)\n",
- " png_bytes = io.BytesIO()\n",
- " Image.fromarray(pixels).save(png_bytes, format='png')\n",
- " return png_bytes.getvalue()\n",
- " \n",
- "def _repr_html_(self):\n",
- " \"\"\"Generate an HTML representation of the Colormap.\"\"\"\n",
- " import base64\n",
- " png_bytes = self._repr_png_()\n",
- " png_base64 = base64.b64encode(png_bytes).decode('ascii')\n",
- " return ('')\n",
- " \n",
- "import matplotlib as mpl\n",
- "mpl.colors.Colormap._repr_png_ = _repr_png_\n",
- "mpl.colors.Colormap._repr_html_ = _repr_html_"
+ "import seaborn as sns\n",
+ "sns.set_theme()\n",
+ "sns.palettes._patch_colormap_display()"
]
},
{
@@ -122,7 +84,39 @@
"cell_type": "raw",
"metadata": {},
"source": [
- "Return one of the perceptually-uniform colormaps included in seaborn:"
+ "Return a diverging Color Brewer palette as a continuous colormap:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "sns.color_palette(\"Spectral\", as_cmap=True)"
+ ]
+ },
+ {
+ "cell_type": "raw",
+ "metadata": {},
+ "source": [
+ "Return one of the perceptually-uniform palettes included in seaborn as a discrete palette:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "sns.color_palette(\"flare\")"
+ ]
+ },
+ {
+ "cell_type": "raw",
+ "metadata": {},
+ "source": [
+ "Return one of the perceptually-uniform palettes included in seaborn as a continuous colormap:"
]
},
{
@@ -154,7 +148,7 @@
"cell_type": "raw",
"metadata": {},
"source": [
- "Return a light-themed sequential colormap to a seed color:"
+ "Return a light sequential gradient:"
]
},
{
@@ -166,6 +160,91 @@
"sns.color_palette(\"light:#5A9\", as_cmap=True)"
]
},
+ {
+ "cell_type": "raw",
+ "metadata": {},
+ "source": [
+ "Return a reversed dark sequential gradient:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "sns.color_palette(\"dark:#5A9_r\", as_cmap=True)"
+ ]
+ },
+ {
+ "cell_type": "raw",
+ "metadata": {},
+ "source": [
+ "Return a blend gradient between two endpoints:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "sns.color_palette(\"blend:#7AB,#EDA\", as_cmap=True)"
+ ]
+ },
+ {
+ "cell_type": "raw",
+ "metadata": {},
+ "source": [
+ "Use as a context manager to change the default qualitative color palette:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {
+ "tags": [
+ "hide"
+ ]
+ },
+ "outputs": [],
+ "source": [
+ "x, y = list(range(10)), [0] * 10\n",
+ "hue = list(map(str, x))"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "with sns.color_palette(\"Set3\"):\n",
+ " sns.relplot(x=x, y=y, hue=hue, s=500, legend=False, height=1.3, aspect=4)\n",
+ "\n",
+ "sns.relplot(x=x, y=y, hue=hue, s=500, legend=False, height=1.3, aspect=4)"
+ ]
+ },
+ {
+ "cell_type": "raw",
+ "metadata": {},
+ "source": [
+ "See the underlying color values as hex codes:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {
+ "tags": [
+ "show-output"
+ ]
+ },
+ "outputs": [],
+ "source": [
+ "print(sns.color_palette(\"pastel6\").as_hex())"
+ ]
+ },
{
"cell_type": "code",
"execution_count": null,
@@ -176,9 +255,9 @@
],
"metadata": {
"kernelspec": {
- "display_name": "seaborn-py39-latest",
+ "display_name": "py310",
"language": "python",
- "name": "seaborn-py39-latest"
+ "name": "py310"
},
"language_info": {
"codemirror_mode": {
@@ -190,7 +269,7 @@
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
- "version": "3.9.13"
+ "version": "3.10.0"
}
},
"nbformat": 4,
diff --git a/doc/_docstrings/countplot.ipynb b/doc/_docstrings/countplot.ipynb
index c0cdf8abdb..6205ac15c1 100644
--- a/doc/_docstrings/countplot.ipynb
+++ b/doc/_docstrings/countplot.ipynb
@@ -77,9 +77,9 @@
],
"metadata": {
"kernelspec": {
- "display_name": "seaborn-py39-latest",
+ "display_name": "py310",
"language": "python",
- "name": "seaborn-py39-latest"
+ "name": "py310"
},
"language_info": {
"codemirror_mode": {
@@ -91,7 +91,7 @@
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
- "version": "3.9.13"
+ "version": "3.10.0"
}
},
"nbformat": 4,
diff --git a/doc/_docstrings/cubehelix_palette.ipynb b/doc/_docstrings/cubehelix_palette.ipynb
new file mode 100644
index 0000000000..a48aab5aed
--- /dev/null
+++ b/doc/_docstrings/cubehelix_palette.ipynb
@@ -0,0 +1,229 @@
+{
+ "cells": [
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "id": "60aebc68-2c7c-4af5-a159-8421e1f94ba6",
+ "metadata": {
+ "tags": [
+ "hide"
+ ]
+ },
+ "outputs": [],
+ "source": [
+ "import seaborn as sns\n",
+ "sns.set_theme()\n",
+ "sns.palettes._patch_colormap_display()"
+ ]
+ },
+ {
+ "cell_type": "raw",
+ "id": "242b3d42-1f10-4da2-9ef9-af06f7fbd724",
+ "metadata": {},
+ "source": [
+ "Return a discrete palette with default parameters:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "id": "6526accb-9930-4e39-9f58-1ca2941c1c9d",
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "sns.cubehelix_palette()"
+ ]
+ },
+ {
+ "cell_type": "raw",
+ "id": "887a40f0-d949-41fa-9a43-0ee246c9a077",
+ "metadata": {},
+ "source": [
+ "Increase the number of colors:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "id": "02833290-b1ee-46df-a2a0-8268fba94628",
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "sns.cubehelix_palette(8)"
+ ]
+ },
+ {
+ "cell_type": "raw",
+ "id": "a9eb86c7-f92e-4422-ae62-a2ef136e7e35",
+ "metadata": {},
+ "source": [
+ "Return a continuous colormap rather than a discrete palette:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "id": "a460efc2-cf0a-46bf-a12f-12870afce8a5",
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "sns.cubehelix_palette(as_cmap=True)"
+ ]
+ },
+ {
+ "cell_type": "raw",
+ "id": "5b84aa6c-ad79-45b1-a7d2-44b7ecba5f7d",
+ "metadata": {},
+ "source": [
+ "Change the starting point of the helix:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "id": "70ee079a-e760-4d43-8447-648fd236ab15",
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "sns.cubehelix_palette(start=2)"
+ ]
+ },
+ {
+ "cell_type": "raw",
+ "id": "5e21fa22-9ac3-4354-8694-967f2447b286",
+ "metadata": {},
+ "source": [
+ "Change the amount of rotation in the helix:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "id": "ddb1b8c7-8933-4317-827f-4f10d2b4cecc",
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "sns.cubehelix_palette(rot=.2)"
+ ]
+ },
+ {
+ "cell_type": "raw",
+ "id": "fa91aff7-54e7-4754-a13c-b629dfc33e8f",
+ "metadata": {},
+ "source": [
+ "Rotate in the reverse direction:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "id": "548a3942-48ae-40d2-abb7-acc2ffd71601",
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "sns.cubehelix_palette(rot=-.2)"
+ ]
+ },
+ {
+ "cell_type": "raw",
+ "id": "e7188a1b-183f-4b04-93a0-975c27fe408e",
+ "metadata": {},
+ "source": [
+ "Apply a nonlinearity to the luminance ramp:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "id": "9ced54ff-a396-451e-b17f-2366b56f920b",
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "sns.cubehelix_palette(gamma=.5)"
+ ]
+ },
+ {
+ "cell_type": "raw",
+ "id": "bc82ce48-2df3-464e-b70e-a1d73d0432c6",
+ "metadata": {},
+ "source": [
+ "Increase the saturation of the colors:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "id": "a38b91a8-3fdc-4293-a3ea-71b4006cd2a1",
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "sns.cubehelix_palette(hue=1)"
+ ]
+ },
+ {
+ "cell_type": "raw",
+ "id": "f8d23ba1-013a-489f-94c4-f2080bfdae87",
+ "metadata": {},
+ "source": [
+ "Change the luminance at the start and end points:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "id": "a4f05a16-18f0-4c14-99a4-57a0734aad02",
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "sns.cubehelix_palette(dark=.25, light=.75)"
+ ]
+ },
+ {
+ "cell_type": "raw",
+ "id": "0bfcc5d9-05ba-4715-94ac-8d430d9416c2",
+ "metadata": {},
+ "source": [
+ "Reverse the direction of the luminance ramp:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "id": "74563491-5448-42c3-86c5-f5d55ce6924c",
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "sns.cubehelix_palette(reverse=True)"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "id": "94a83211-8b8e-4e60-8365-9600e71ddc5d",
+ "metadata": {},
+ "outputs": [],
+ "source": []
+ }
+ ],
+ "metadata": {
+ "kernelspec": {
+ "display_name": "py310",
+ "language": "python",
+ "name": "py310"
+ },
+ "language_info": {
+ "codemirror_mode": {
+ "name": "ipython",
+ "version": 3
+ },
+ "file_extension": ".py",
+ "mimetype": "text/x-python",
+ "name": "python",
+ "nbconvert_exporter": "python",
+ "pygments_lexer": "ipython3",
+ "version": "3.10.0"
+ }
+ },
+ "nbformat": 4,
+ "nbformat_minor": 5
+}
diff --git a/doc/_docstrings/dark_palette.ipynb b/doc/_docstrings/dark_palette.ipynb
new file mode 100644
index 0000000000..a4ed7adf43
--- /dev/null
+++ b/doc/_docstrings/dark_palette.ipynb
@@ -0,0 +1,139 @@
+{
+ "cells": [
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "id": "5cd1cbb8-ba1a-460b-8e3a-bc285867f1d1",
+ "metadata": {
+ "tags": [
+ "hide"
+ ]
+ },
+ "outputs": [],
+ "source": [
+ "import seaborn as sns\n",
+ "sns.set_theme()\n",
+ "sns.palettes._patch_colormap_display()"
+ ]
+ },
+ {
+ "cell_type": "raw",
+ "id": "b157eb25-015f-4dd6-9785-83ba19cf4f94",
+ "metadata": {},
+ "source": [
+ "Define a sequential ramp from a dark gray to a specified color:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "id": "5b655d28-9855-4528-8b8e-a6c50288fd1b",
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "sns.dark_palette(\"seagreen\")"
+ ]
+ },
+ {
+ "cell_type": "raw",
+ "id": "50053b26-112a-4378-8ef0-9be0fb565ec7",
+ "metadata": {},
+ "source": [
+ "Specify the color with a hex code:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "id": "74ae0d17-f65b-4bcf-ae66-d97d46964d5c",
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "sns.dark_palette(\"#79C\")"
+ ]
+ },
+ {
+ "cell_type": "raw",
+ "id": "eea376a2-fdf5-40e4-a187-3a28af529072",
+ "metadata": {},
+ "source": [
+ "Specify the color from the husl system:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "id": "66e451ee-869a-41ea-8dc5-4240b11e7be5",
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "sns.dark_palette((20, 60, 50), input=\"husl\")"
+ ]
+ },
+ {
+ "cell_type": "raw",
+ "id": "e4f44dcd-cf49-4920-ac05-b4db67870363",
+ "metadata": {},
+ "source": [
+ "Increase the number of colors:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "id": "75985f07-de92-4d8b-89d5-caf445b9375e",
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "sns.dark_palette(\"xkcd:golden\", 8)"
+ ]
+ },
+ {
+ "cell_type": "raw",
+ "id": "34687ae8-fd6d-427a-a639-208f19e61122",
+ "metadata": {},
+ "source": [
+ "Return a continuous colormap rather than a discrete palette:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "id": "2c342db4-7f97-40f5-934e-9a82201890d1",
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "sns.dark_palette(\"#b285bc\", as_cmap=True)"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "id": "e7ebe64b-25fa-4c52-9ebe-fdcbba0ee51e",
+ "metadata": {},
+ "outputs": [],
+ "source": []
+ }
+ ],
+ "metadata": {
+ "kernelspec": {
+ "display_name": "py310",
+ "language": "python",
+ "name": "py310"
+ },
+ "language_info": {
+ "codemirror_mode": {
+ "name": "ipython",
+ "version": 3
+ },
+ "file_extension": ".py",
+ "mimetype": "text/x-python",
+ "name": "python",
+ "nbconvert_exporter": "python",
+ "pygments_lexer": "ipython3",
+ "version": "3.10.0"
+ }
+ },
+ "nbformat": 4,
+ "nbformat_minor": 5
+}
diff --git a/doc/_docstrings/displot.ipynb b/doc/_docstrings/displot.ipynb
index 543aa7fb8c..1b2011fec0 100644
--- a/doc/_docstrings/displot.ipynb
+++ b/doc/_docstrings/displot.ipynb
@@ -217,9 +217,9 @@
],
"metadata": {
"kernelspec": {
- "display_name": "seaborn-py39-latest",
+ "display_name": "py310",
"language": "python",
- "name": "seaborn-py39-latest"
+ "name": "py310"
},
"language_info": {
"codemirror_mode": {
@@ -231,7 +231,7 @@
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
- "version": "3.9.13"
+ "version": "3.10.0"
}
},
"nbformat": 4,
diff --git a/doc/_docstrings/diverging_palette.ipynb b/doc/_docstrings/diverging_palette.ipynb
new file mode 100644
index 0000000000..c38196be98
--- /dev/null
+++ b/doc/_docstrings/diverging_palette.ipynb
@@ -0,0 +1,183 @@
+{
+ "cells": [
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "id": "01295cb6-cc7a-4c6d-94cf-9b0e6cde9fa7",
+ "metadata": {
+ "tags": [
+ "hide"
+ ]
+ },
+ "outputs": [],
+ "source": [
+ "import seaborn as sns\n",
+ "sns.set_theme()\n",
+ "sns.palettes._patch_colormap_display()"
+ ]
+ },
+ {
+ "cell_type": "raw",
+ "id": "84880848-0805-4c41-999a-50808b397275",
+ "metadata": {},
+ "source": [
+ "Generate diverging ramps from blue to red through white:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "id": "643b3e07-8365-46e3-b033-af7a2fdcd158",
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "sns.diverging_palette(240, 20)"
+ ]
+ },
+ {
+ "cell_type": "raw",
+ "id": "5ae53941-d9d9-4b5a-8abc-173911ebee74",
+ "metadata": {},
+ "source": [
+ "Change the center color to be dark:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "id": "41f03771-8fb2-46f6-93c5-5a0e28be625c",
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "sns.diverging_palette(240, 20, center=\"dark\")"
+ ]
+ },
+ {
+ "cell_type": "raw",
+ "id": "0aeb2402-2cbe-4546-a354-f1f501f762ae",
+ "metadata": {},
+ "source": [
+ "Return a continuous colormap rather than a discrete palette:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "id": "64d335a5-f8b2-433f-a83f-5aeff7db583a",
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "sns.diverging_palette(240, 20, as_cmap=True)"
+ ]
+ },
+ {
+ "cell_type": "raw",
+ "id": "77223a07-8492-4056-a0f7-14e133e3ce2c",
+ "metadata": {},
+ "source": [
+ "Increase the amount of separation around the center value:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "id": "82472c1e-4b16-40eb-be1d-480bbd2aa702",
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "sns.diverging_palette(240, 20, sep=30, as_cmap=True)"
+ ]
+ },
+ {
+ "cell_type": "raw",
+ "id": "966e8594-b458-414c-a7b0-3e804ce407bf",
+ "metadata": {},
+ "source": [
+ "Use a magenta-to-green palette instead:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "id": "a03f8ede-b424-4e06-beb6-cf63c94bcd9e",
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "sns.diverging_palette(280, 150)"
+ ]
+ },
+ {
+ "cell_type": "raw",
+ "id": "b3b17689-58e2-4065-9d52-1cf5ebcd4e89",
+ "metadata": {},
+ "source": [
+ "Decrease the saturation of the endpoints:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "id": "02aaa009-f257-4fc7-a2de-40fbb1464490",
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "sns.diverging_palette(280, 150, s=50)"
+ ]
+ },
+ {
+ "cell_type": "raw",
+ "id": "db75ca48-ba72-4ca2-8480-bc72c20a70cc",
+ "metadata": {},
+ "source": [
+ "Decrease the lightness of the endpoints:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "id": "89e3bcb1-a17c-4465-830f-46043cb6c322",
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "sns.diverging_palette(280, 150, l=35)"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "id": "4e42452a-a485-43e7-bbc3-338db58e4637",
+ "metadata": {},
+ "outputs": [],
+ "source": []
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "id": "e19f523f-c2f7-489a-ba00-326810e31a67",
+ "metadata": {},
+ "outputs": [],
+ "source": []
+ }
+ ],
+ "metadata": {
+ "kernelspec": {
+ "display_name": "py310",
+ "language": "python",
+ "name": "py310"
+ },
+ "language_info": {
+ "codemirror_mode": {
+ "name": "ipython",
+ "version": 3
+ },
+ "file_extension": ".py",
+ "mimetype": "text/x-python",
+ "name": "python",
+ "nbconvert_exporter": "python",
+ "pygments_lexer": "ipython3",
+ "version": "3.10.0"
+ }
+ },
+ "nbformat": 4,
+ "nbformat_minor": 5
+}
diff --git a/doc/_docstrings/ecdfplot.ipynb b/doc/_docstrings/ecdfplot.ipynb
index 9eff8bd735..d95e517a1d 100644
--- a/doc/_docstrings/ecdfplot.ipynb
+++ b/doc/_docstrings/ecdfplot.ipynb
@@ -120,9 +120,9 @@
],
"metadata": {
"kernelspec": {
- "display_name": "seaborn-py39-latest",
+ "display_name": "py310",
"language": "python",
- "name": "seaborn-py39-latest"
+ "name": "py310"
},
"language_info": {
"codemirror_mode": {
@@ -134,7 +134,7 @@
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
- "version": "3.9.13"
+ "version": "3.10.0"
}
},
"nbformat": 4,
diff --git a/doc/_docstrings/heatmap.ipynb b/doc/_docstrings/heatmap.ipynb
new file mode 100644
index 0000000000..b4563a880e
--- /dev/null
+++ b/doc/_docstrings/heatmap.ipynb
@@ -0,0 +1,213 @@
+{
+ "cells": [
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "id": "987b9549-532e-4091-a6cf-007d1b23e825",
+ "metadata": {
+ "tags": [
+ "hide"
+ ]
+ },
+ "outputs": [],
+ "source": [
+ "import seaborn as sns\n",
+ "sns.set_theme()"
+ ]
+ },
+ {
+ "cell_type": "raw",
+ "id": "2c78ca60-e232-44f6-956b-b86b472b1c28",
+ "metadata": {},
+ "source": [
+ "Pass a :class:`DataFrame` to plot with indices as row/column labels:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "id": "fad17798-c2e3-4334-abf0-0d46153971fa",
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "glue = sns.load_dataset(\"glue\").pivot(\"Model\", \"Task\", \"Score\")\n",
+ "sns.heatmap(glue)"
+ ]
+ },
+ {
+ "cell_type": "raw",
+ "id": "f3255c5f-2477-4d13-b4c2-7e56380e9cc2",
+ "metadata": {},
+ "source": [
+ "Use `annot` to represent the cell values with text:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "id": "3c9f3c73-c8bc-426e-bc67-dec8f807082e",
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "sns.heatmap(glue, annot=True)"
+ ]
+ },
+ {
+ "cell_type": "raw",
+ "id": "bc412da8-866a-49b7-8496-01fbf06dd908",
+ "metadata": {},
+ "source": [
+ "Control the annotations with a formatting string:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "id": "ac952d0d-9187-4dff-a560-88430076851a",
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "sns.heatmap(glue, annot=True, fmt=\".1f\")"
+ ]
+ },
+ {
+ "cell_type": "raw",
+ "id": "5eb12725-e9ee-4df0-9708-243d7e0a77b5",
+ "metadata": {},
+ "source": [
+ "Use a separate dataframe for the annotations:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "id": "1189a37f-9f74-455a-a09a-c22e056d8ba7",
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "sns.heatmap(glue, annot=glue.rank(axis=\"columns\"))"
+ ]
+ },
+ {
+ "cell_type": "raw",
+ "id": "253dfb7f-aa12-4716-adc2-3a38b003b2c3",
+ "metadata": {},
+ "source": [
+ "Add lines between cells:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "id": "5cac673e-9b86-490b-9e67-ec0cf865bede",
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "sns.heatmap(glue, annot=True, linewidth=.5)"
+ ]
+ },
+ {
+ "cell_type": "raw",
+ "id": "b7d3659c-f996-4af3-a612-430d97799c72",
+ "metadata": {},
+ "source": [
+ "Select a different colormap by name:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "id": "86806d72-e784-430e-8320-48f2c91115bb",
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "sns.heatmap(glue, cmap=\"crest\")"
+ ]
+ },
+ {
+ "cell_type": "raw",
+ "id": "8336fd53-3841-458f-b26c-411efff54d45",
+ "metadata": {},
+ "source": [
+ "Or pass a colormap object:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "id": "9944ff33-991f-4138-a951-e3015c0326f1",
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "sns.heatmap(glue, cmap=sns.cubehelix_palette(as_cmap=True))"
+ ]
+ },
+ {
+ "cell_type": "raw",
+ "id": "52cc4dba-b86a-4da8-9cbd-3f8aa06b43b4",
+ "metadata": {},
+ "source": [
+ "Set the colormap norm (data values corresponding to minimum and maximum points):"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "id": "b4ddb41e-c075-41a5-8afe-422ad6d105bf",
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "sns.heatmap(glue, vmin=50, vmax=100)"
+ ]
+ },
+ {
+ "cell_type": "raw",
+ "id": "6e828517-a532-49b1-be11-eda47c50cc37",
+ "metadata": {},
+ "source": [
+ "Use methods on the :class:`matplotlib.axes.Axes` object to tweak the plot:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "id": "1aab26fc-2de4-4d4f-ad08-487809573deb",
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "ax = sns.heatmap(glue, annot=True)\n",
+ "ax.set(xlabel=\"\", ylabel=\"\")\n",
+ "ax.xaxis.tick_top()"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "id": "1d8e738c-388a-453a-b9c7-4c71a674b69c",
+ "metadata": {},
+ "outputs": [],
+ "source": []
+ }
+ ],
+ "metadata": {
+ "kernelspec": {
+ "display_name": "py310",
+ "language": "python",
+ "name": "py310"
+ },
+ "language_info": {
+ "codemirror_mode": {
+ "name": "ipython",
+ "version": 3
+ },
+ "file_extension": ".py",
+ "mimetype": "text/x-python",
+ "name": "python",
+ "nbconvert_exporter": "python",
+ "pygments_lexer": "ipython3",
+ "version": "3.10.0"
+ }
+ },
+ "nbformat": 4,
+ "nbformat_minor": 5
+}
diff --git a/doc/_docstrings/histplot.ipynb b/doc/_docstrings/histplot.ipynb
index f3ed72e328..308d0db15e 100644
--- a/doc/_docstrings/histplot.ipynb
+++ b/doc/_docstrings/histplot.ipynb
@@ -461,9 +461,9 @@
],
"metadata": {
"kernelspec": {
- "display_name": "seaborn-py39-latest",
+ "display_name": "py310",
"language": "python",
- "name": "seaborn-py39-latest"
+ "name": "py310"
},
"language_info": {
"codemirror_mode": {
@@ -475,7 +475,7 @@
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
- "version": "3.9.13"
+ "version": "3.10.0"
}
},
"nbformat": 4,
diff --git a/doc/_docstrings/hls_palette.ipynb b/doc/_docstrings/hls_palette.ipynb
new file mode 100644
index 0000000000..03c95a248d
--- /dev/null
+++ b/doc/_docstrings/hls_palette.ipynb
@@ -0,0 +1,157 @@
+{
+ "cells": [
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "id": "158cd1cf-6b30-4054-b32f-a166fcb883be",
+ "metadata": {
+ "tags": [
+ "hide"
+ ]
+ },
+ "outputs": [],
+ "source": [
+ "import seaborn as sns\n",
+ "sns.set_theme()\n",
+ "sns.palettes._patch_colormap_display()"
+ ]
+ },
+ {
+ "cell_type": "raw",
+ "id": "c81b86cb-fb4e-418b-8d2f-6cd10601ac5a",
+ "metadata": {},
+ "source": [
+ "By default, return 6 colors with identical lightness and saturation and evenly-sampled hues:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "id": "6c3eaeaf-88eb-4012-96ea-41b328fa98b9",
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "sns.hls_palette()"
+ ]
+ },
+ {
+ "cell_type": "raw",
+ "id": "f7624b0b-2311-45de-b6a5-fc07132ce455",
+ "metadata": {},
+ "source": [
+ "Increase the number of colors:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "id": "555c29d1-6972-4a19-ad32-957fb7545634",
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "sns.hls_palette(8)"
+ ]
+ },
+ {
+ "cell_type": "raw",
+ "id": "24713fa6-e485-4358-9ffc-d40bd9543caa",
+ "metadata": {},
+ "source": [
+ "Decrease the lightness:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "id": "b6f80b4c-f7b4-4deb-a119-cdf6cfe1f7b5",
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "sns.hls_palette(l=.3)"
+ ]
+ },
+ {
+ "cell_type": "raw",
+ "id": "e521b514-5572-43e8-95ae-a20cc30169b8",
+ "metadata": {},
+ "source": [
+ "Decrease the saturation:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "id": "f88bd038-0c9c-48b1-92b0-d272a9c199f4",
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "sns.hls_palette(s=.3)"
+ ]
+ },
+ {
+ "cell_type": "raw",
+ "id": "92a2212c-2177-4c82-8a5e-9dd788e9f87c",
+ "metadata": {},
+ "source": [
+ "Change the start-point for hue sampling:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "id": "f8da8fbc-551c-4896-b1b8-04203e740d78",
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "sns.hls_palette(h=.5)"
+ ]
+ },
+ {
+ "cell_type": "raw",
+ "id": "87780608-1f5a-409f-b31f-6a31a599f122",
+ "metadata": {},
+ "source": [
+ "Return a continuous colormap. Notice the perceptual discontinuities, especially around yellow, cyan, and magenta: "
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "id": "4c622b3b-70d7-4139-8389-f3d0d4addd66",
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "sns.hls_palette(as_cmap=True)"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "id": "3a83c1de-88c5-4327-abd2-19e8f3642052",
+ "metadata": {},
+ "outputs": [],
+ "source": []
+ }
+ ],
+ "metadata": {
+ "kernelspec": {
+ "display_name": "py310",
+ "language": "python",
+ "name": "py310"
+ },
+ "language_info": {
+ "codemirror_mode": {
+ "name": "ipython",
+ "version": 3
+ },
+ "file_extension": ".py",
+ "mimetype": "text/x-python",
+ "name": "python",
+ "nbconvert_exporter": "python",
+ "pygments_lexer": "ipython3",
+ "version": "3.10.0"
+ }
+ },
+ "nbformat": 4,
+ "nbformat_minor": 5
+}
diff --git a/doc/_docstrings/husl_palette.ipynb b/doc/_docstrings/husl_palette.ipynb
new file mode 100644
index 0000000000..a933bb0496
--- /dev/null
+++ b/doc/_docstrings/husl_palette.ipynb
@@ -0,0 +1,157 @@
+{
+ "cells": [
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "id": "a6794650-f28f-40eb-95a7-3f0e5c4b332d",
+ "metadata": {
+ "tags": [
+ "hide"
+ ]
+ },
+ "outputs": [],
+ "source": [
+ "import seaborn as sns\n",
+ "sns.set_theme()\n",
+ "sns.palettes._patch_colormap_display()"
+ ]
+ },
+ {
+ "cell_type": "raw",
+ "id": "fab2f86e-45d4-4982-ade7-0a5ea6d762d1",
+ "metadata": {},
+ "source": [
+ "By default, return 6 colors with identical lightness and saturation and evenly-sampled hues:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "id": "b220950e-0ca2-4101-b56a-14eebe8ee8d0",
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "sns.husl_palette()"
+ ]
+ },
+ {
+ "cell_type": "raw",
+ "id": "c5e4a2e3-e6b8-42bf-be19-348ff7ae2798",
+ "metadata": {},
+ "source": [
+ "Increase the number of colors:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "id": "7d0af740-cfca-49fb-a472-1daa4ccb3f3a",
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "sns.husl_palette(8)"
+ ]
+ },
+ {
+ "cell_type": "raw",
+ "id": "1a7189f2-2a26-446a-90e7-cf41dcac4f25",
+ "metadata": {},
+ "source": [
+ "Decrease the lightness:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "id": "43af79c7-f497-41e5-874a-83eed99500f3",
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "sns.husl_palette(l=.4)"
+ ]
+ },
+ {
+ "cell_type": "raw",
+ "id": "6d4099b7-5115-4365-b120-33a345581f5d",
+ "metadata": {},
+ "source": [
+ "Decrease the saturation:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "id": "52c1afc7-d982-4199-b218-222aa94563c5",
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "sns.husl_palette(s=.4)"
+ ]
+ },
+ {
+ "cell_type": "raw",
+ "id": "d26131ac-0d11-48c5-88b1-4e5cf9383000",
+ "metadata": {},
+ "source": [
+ "Change the start-point for hue sampling:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "id": "d72f06a0-13e0-47f7-bc70-4c5935eaa130",
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "sns.husl_palette(h=.5)"
+ ]
+ },
+ {
+ "cell_type": "raw",
+ "id": "7e6c3c19-41d3-4315-b03e-909d201d0e76",
+ "metadata": {},
+ "source": [
+ "Return a continuous colormap:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "id": "49c18838-0589-496f-9a61-635195c07f61",
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "sns.husl_palette(as_cmap=True)"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "id": "c710a557-8e84-44cb-ab4c-baabcc4fd328",
+ "metadata": {},
+ "outputs": [],
+ "source": []
+ }
+ ],
+ "metadata": {
+ "kernelspec": {
+ "display_name": "py310",
+ "language": "python",
+ "name": "py310"
+ },
+ "language_info": {
+ "codemirror_mode": {
+ "name": "ipython",
+ "version": 3
+ },
+ "file_extension": ".py",
+ "mimetype": "text/x-python",
+ "name": "python",
+ "nbconvert_exporter": "python",
+ "pygments_lexer": "ipython3",
+ "version": "3.10.0"
+ }
+ },
+ "nbformat": 4,
+ "nbformat_minor": 5
+}
diff --git a/doc/_docstrings/jointplot.ipynb b/doc/_docstrings/jointplot.ipynb
index 32801c3a3b..379b5307c8 100644
--- a/doc/_docstrings/jointplot.ipynb
+++ b/doc/_docstrings/jointplot.ipynb
@@ -172,9 +172,9 @@
],
"metadata": {
"kernelspec": {
- "display_name": "seaborn-py39-latest",
+ "display_name": "py310",
"language": "python",
- "name": "seaborn-py39-latest"
+ "name": "py310"
},
"language_info": {
"codemirror_mode": {
@@ -186,7 +186,7 @@
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
- "version": "3.9.13"
+ "version": "3.10.0"
}
},
"nbformat": 4,
diff --git a/doc/_docstrings/kdeplot.ipynb b/doc/_docstrings/kdeplot.ipynb
index 81d375a7b3..40693d05b4 100644
--- a/doc/_docstrings/kdeplot.ipynb
+++ b/doc/_docstrings/kdeplot.ipynb
@@ -327,9 +327,9 @@
],
"metadata": {
"kernelspec": {
- "display_name": "seaborn-py39-latest",
+ "display_name": "py310",
"language": "python",
- "name": "seaborn-py39-latest"
+ "name": "py310"
},
"language_info": {
"codemirror_mode": {
@@ -341,7 +341,7 @@
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
- "version": "3.9.13"
+ "version": "3.10.0"
}
},
"nbformat": 4,
diff --git a/doc/_docstrings/light_palette.ipynb b/doc/_docstrings/light_palette.ipynb
new file mode 100644
index 0000000000..a1a830a3d9
--- /dev/null
+++ b/doc/_docstrings/light_palette.ipynb
@@ -0,0 +1,139 @@
+{
+ "cells": [
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "id": "5cd1cbb8-ba1a-460b-8e3a-bc285867f1d1",
+ "metadata": {
+ "tags": [
+ "hide"
+ ]
+ },
+ "outputs": [],
+ "source": [
+ "import seaborn as sns\n",
+ "sns.set_theme()\n",
+ "sns.palettes._patch_colormap_display()"
+ ]
+ },
+ {
+ "cell_type": "raw",
+ "id": "b157eb25-015f-4dd6-9785-83ba19cf4f94",
+ "metadata": {},
+ "source": [
+ "Define a sequential ramp from a light gray to a specified color:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "id": "851a4742-6276-4383-b17e-480beb896877",
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "sns.light_palette(\"seagreen\")"
+ ]
+ },
+ {
+ "cell_type": "raw",
+ "id": "50053b26-112a-4378-8ef0-9be0fb565ec7",
+ "metadata": {},
+ "source": [
+ "Specify the color with a hex code:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "id": "74ae0d17-f65b-4bcf-ae66-d97d46964d5c",
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "sns.light_palette(\"#79C\")"
+ ]
+ },
+ {
+ "cell_type": "raw",
+ "id": "eea376a2-fdf5-40e4-a187-3a28af529072",
+ "metadata": {},
+ "source": [
+ "Specify the color from the husl system:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "id": "66e451ee-869a-41ea-8dc5-4240b11e7be5",
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "sns.light_palette((20, 60, 50), input=\"husl\")"
+ ]
+ },
+ {
+ "cell_type": "raw",
+ "id": "e4f44dcd-cf49-4920-ac05-b4db67870363",
+ "metadata": {},
+ "source": [
+ "Increase the number of colors:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "id": "75985f07-de92-4d8b-89d5-caf445b9375e",
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "sns.light_palette(\"xkcd:copper\", 8)"
+ ]
+ },
+ {
+ "cell_type": "raw",
+ "id": "34687ae8-fd6d-427a-a639-208f19e61122",
+ "metadata": {},
+ "source": [
+ "Return a continuous colormap rather than a discrete palette:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "id": "2c342db4-7f97-40f5-934e-9a82201890d1",
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "sns.light_palette(\"#a275ac\", as_cmap=True)"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "id": "e7ebe64b-25fa-4c52-9ebe-fdcbba0ee51e",
+ "metadata": {},
+ "outputs": [],
+ "source": []
+ }
+ ],
+ "metadata": {
+ "kernelspec": {
+ "display_name": "py310",
+ "language": "python",
+ "name": "py310"
+ },
+ "language_info": {
+ "codemirror_mode": {
+ "name": "ipython",
+ "version": 3
+ },
+ "file_extension": ".py",
+ "mimetype": "text/x-python",
+ "name": "python",
+ "nbconvert_exporter": "python",
+ "pygments_lexer": "ipython3",
+ "version": "3.10.0"
+ }
+ },
+ "nbformat": 4,
+ "nbformat_minor": 5
+}
diff --git a/doc/_docstrings/lineplot.ipynb b/doc/_docstrings/lineplot.ipynb
index 02b20c73d2..2d73607415 100644
--- a/doc/_docstrings/lineplot.ipynb
+++ b/doc/_docstrings/lineplot.ipynb
@@ -431,9 +431,9 @@
"hash": "8bdfc9d9da1e36addfcfc8a3409187c45d33387af0f87d0d91e99e8d6403f1c3"
},
"kernelspec": {
- "display_name": "seaborn-py39-latest",
+ "display_name": "py310",
"language": "python",
- "name": "seaborn-py39-latest"
+ "name": "py310"
},
"language_info": {
"codemirror_mode": {
@@ -445,7 +445,7 @@
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
- "version": "3.9.13"
+ "version": "3.10.0"
}
},
"nbformat": 4,
diff --git a/doc/_docstrings/lmplot.ipynb b/doc/_docstrings/lmplot.ipynb
new file mode 100644
index 0000000000..c080373d40
--- /dev/null
+++ b/doc/_docstrings/lmplot.ipynb
@@ -0,0 +1,157 @@
+{
+ "cells": [
+ {
+ "cell_type": "raw",
+ "id": "034a9a5b-91ff-4ccc-932d-0f314e2cd6d2",
+ "metadata": {},
+ "source": [
+ "See the :func:`regplot` docs for demonstrations of various options for specifying the regression model, which are also accepted here."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "id": "76c91243-3bd8-49a1-b8c8-b7272f09a3f1",
+ "metadata": {
+ "tags": [
+ "hide"
+ ]
+ },
+ "outputs": [],
+ "source": [
+ "import seaborn as sns\n",
+ "sns.set_theme(style=\"ticks\")\n",
+ "penguins = sns.load_dataset(\"penguins\")"
+ ]
+ },
+ {
+ "cell_type": "raw",
+ "id": "0ba9f55d-17ea-4084-a74f-852d51771380",
+ "metadata": {},
+ "source": [
+ "Plot a regression fit over a scatter plot:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "id": "2f789265-93c0-4867-b666-798713e4e7e5",
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "sns.lmplot(data=penguins, x=\"bill_length_mm\", y=\"bill_depth_mm\")"
+ ]
+ },
+ {
+ "cell_type": "raw",
+ "id": "7e4b0ad4-446c-4109-9393-961f76132e34",
+ "metadata": {},
+ "source": [
+ "Condition the regression fit on another variable and represent it using color:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "id": "61347189-34e5-42ea-b77b-4acdef843326",
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "sns.lmplot(data=penguins, x=\"bill_length_mm\", y=\"bill_depth_mm\", hue=\"species\")"
+ ]
+ },
+ {
+ "cell_type": "raw",
+ "id": "c9b6d059-49dc-46a7-869b-86baa3a7ed65",
+ "metadata": {},
+ "source": [
+ "Condition the regression fit on another variable and split across subplots:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "id": "d8ec2955-ccc9-493c-b9ec-c78648ce9f53",
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "sns.lmplot(\n",
+ " data=penguins, x=\"bill_length_mm\", y=\"bill_depth_mm\",\n",
+ " hue=\"species\", col=\"sex\", height=4,\n",
+ ")"
+ ]
+ },
+ {
+ "cell_type": "raw",
+ "id": "de01dee1-b2ce-445c-8d0d-d054ca0dfedb",
+ "metadata": {},
+ "source": [
+ "Condition across two variables using both columns and rows:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "id": "6f1264aa-829c-416a-805a-b989e5f11a17",
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "sns.lmplot(\n",
+ " data=penguins, x=\"bill_length_mm\", y=\"bill_depth_mm\",\n",
+ " col=\"species\", row=\"sex\", height=3,\n",
+ ")"
+ ]
+ },
+ {
+ "cell_type": "raw",
+ "id": "b3888f04-b22f-4205-8acc-24ce5b59568e",
+ "metadata": {},
+ "source": [
+ "Allow axis limits to vary across subplots:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "id": "67ed5af1-d228-4b81-b4f8-21937c513a10",
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "sns.lmplot(\n",
+ " data=penguins, x=\"bill_length_mm\", y=\"bill_depth_mm\",\n",
+ " col=\"species\", row=\"sex\", height=3,\n",
+ " facet_kws=dict(sharex=False, sharey=False),\n",
+ ")"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "id": "46e9cf18-c847-4c40-8e38-6c20cdde2be5",
+ "metadata": {},
+ "outputs": [],
+ "source": []
+ }
+ ],
+ "metadata": {
+ "kernelspec": {
+ "display_name": "py310",
+ "language": "python",
+ "name": "py310"
+ },
+ "language_info": {
+ "codemirror_mode": {
+ "name": "ipython",
+ "version": 3
+ },
+ "file_extension": ".py",
+ "mimetype": "text/x-python",
+ "name": "python",
+ "nbconvert_exporter": "python",
+ "pygments_lexer": "ipython3",
+ "version": "3.10.0"
+ }
+ },
+ "nbformat": 4,
+ "nbformat_minor": 5
+}
diff --git a/doc/_docstrings/move_legend.ipynb b/doc/_docstrings/move_legend.ipynb
index 604eda0b3a..f36848cf54 100644
--- a/doc/_docstrings/move_legend.ipynb
+++ b/doc/_docstrings/move_legend.ipynb
@@ -134,9 +134,9 @@
],
"metadata": {
"kernelspec": {
- "display_name": "seaborn-py39-latest",
+ "display_name": "py310",
"language": "python",
- "name": "seaborn-py39-latest"
+ "name": "py310"
},
"language_info": {
"codemirror_mode": {
@@ -148,7 +148,7 @@
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
- "version": "3.9.13"
+ "version": "3.10.0"
}
},
"nbformat": 4,
diff --git a/doc/_docstrings/mpl_palette.ipynb b/doc/_docstrings/mpl_palette.ipynb
new file mode 100644
index 0000000000..d878fcc9ec
--- /dev/null
+++ b/doc/_docstrings/mpl_palette.ipynb
@@ -0,0 +1,139 @@
+{
+ "cells": [
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "id": "1d0d41d3-463c-4c6f-aa65-38131bdf3ddb",
+ "metadata": {
+ "tags": [
+ "hide"
+ ]
+ },
+ "outputs": [],
+ "source": [
+ "import seaborn as sns\n",
+ "sns.set_theme()\n",
+ "sns.palettes._patch_colormap_display()"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "d2a0ae1e-a01e-49b3-a677-2b05a195990a",
+ "metadata": {},
+ "source": [
+ "Return discrete samples from a continuous matplotlib colormap:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "id": "2b6a4ce9-6e4e-4b59-ada8-14ef8aef21d7",
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "sns.mpl_palette(\"viridis\")"
+ ]
+ },
+ {
+ "cell_type": "raw",
+ "id": "0ccc47b1-c969-46e2-93bb-b9eb5a2e2141",
+ "metadata": {},
+ "source": [
+ "Return the continuous colormap instead; note how the extreme values are more intense:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "id": "a8a1bc5d-1d62-45c6-a53b-9fadb58f11c0",
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "sns.mpl_palette(\"viridis\", as_cmap=True)"
+ ]
+ },
+ {
+ "cell_type": "raw",
+ "id": "ff0d1a3b-8641-40c0-bb4b-c22b83ec9432",
+ "metadata": {},
+ "source": [
+ "Return more colors:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "id": "8faef1d8-a1eb-4060-be10-377342c9bd1d",
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "sns.mpl_palette(\"viridis\", 8)"
+ ]
+ },
+ {
+ "cell_type": "raw",
+ "id": "612bf052-e888-411d-a2ea-6a742a78bc63",
+ "metadata": {},
+ "source": [
+ "Return values from a qualitative colormap:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "id": "74db95a8-4898-4f6c-a57d-c751af1dc7bf",
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "sns.mpl_palette(\"Set2\")"
+ ]
+ },
+ {
+ "cell_type": "raw",
+ "id": "918494bf-1b8e-4b00-8950-1bd73032dee1",
+ "metadata": {},
+ "source": [
+ "Notice how the palette will only contain distinct colors and can be shorter than requested:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "id": "d97efa25-9050-4e28-b758-da6f43c9f963",
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "sns.mpl_palette(\"Set2\", 10)"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "id": "f64ad118-e213-43cc-a714-98ed13cc3824",
+ "metadata": {},
+ "outputs": [],
+ "source": []
+ }
+ ],
+ "metadata": {
+ "kernelspec": {
+ "display_name": "py310",
+ "language": "python",
+ "name": "py310"
+ },
+ "language_info": {
+ "codemirror_mode": {
+ "name": "ipython",
+ "version": 3
+ },
+ "file_extension": ".py",
+ "mimetype": "text/x-python",
+ "name": "python",
+ "nbconvert_exporter": "python",
+ "pygments_lexer": "ipython3",
+ "version": "3.10.0"
+ }
+ },
+ "nbformat": 4,
+ "nbformat_minor": 5
+}
diff --git a/doc/_docstrings/objects.Area.ipynb b/doc/_docstrings/objects.Area.ipynb
index f3ddc6aefd..256b46a93f 100644
--- a/doc/_docstrings/objects.Area.ipynb
+++ b/doc/_docstrings/objects.Area.ipynb
@@ -139,9 +139,9 @@
],
"metadata": {
"kernelspec": {
- "display_name": "seaborn-py39-latest",
+ "display_name": "py310",
"language": "python",
- "name": "seaborn-py39-latest"
+ "name": "py310"
},
"language_info": {
"codemirror_mode": {
@@ -153,7 +153,7 @@
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
- "version": "3.9.13"
+ "version": "3.10.0"
}
},
"nbformat": 4,
diff --git a/doc/_docstrings/objects.Band.ipynb b/doc/_docstrings/objects.Band.ipynb
index 902e33a3aa..bcd1975847 100644
--- a/doc/_docstrings/objects.Band.ipynb
+++ b/doc/_docstrings/objects.Band.ipynb
@@ -13,7 +13,7 @@
"source": [
"import seaborn.objects as so\n",
"from seaborn import load_dataset\n",
- "fmri = load_dataset(\"fmri\")\n",
+ "fmri = load_dataset(\"fmri\").query(\"region == 'parietal'\")\n",
"seaice = (\n",
" load_dataset(\"seaice\")\n",
" .assign(\n",
@@ -22,7 +22,7 @@
" )\n",
" .query(\"Year >= 1980\")\n",
" .astype({\"Year\": str})\n",
- " .pivot(\"Day\", \"Year\", \"Extent\")\n",
+ " .pivot(index=\"Day\", columns=\"Year\", values=\"Extent\")\n",
" .filter([\"1980\", \"2019\"])\n",
" .dropna()\n",
" .reset_index()\n",
@@ -90,16 +90,40 @@
},
{
"cell_type": "raw",
- "id": "4e817cdd-09a3-4cf6-8602-e9665607bfe1",
+ "id": "9f0c82bf-3457-4ac5-ba48-8930bac03d75",
"metadata": {},
+ "source": [
+ "When min/max values are not explicitly assigned or added in a transform, the band will cover the full extent of the data:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "id": "309f578e-da3d-4dc5-b6ac-a354321334c8",
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "(\n",
+ " so.Plot(fmri, x=\"timepoint\", y=\"signal\", color=\"event\")\n",
+ " .add(so.Line(linewidth=.5), group=\"subject\")\n",
+ " .add(so.Band())\n",
+ ")"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "id": "4330a3cd-63fe-470a-8e83-09e9606643b5",
+ "metadata": {},
+ "outputs": [],
"source": []
}
],
"metadata": {
"kernelspec": {
- "display_name": "seaborn-py39-latest",
+ "display_name": "py310",
"language": "python",
- "name": "seaborn-py39-latest"
+ "name": "py310"
},
"language_info": {
"codemirror_mode": {
@@ -111,7 +135,7 @@
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
- "version": "3.9.13"
+ "version": "3.10.0"
}
},
"nbformat": 4,
diff --git a/doc/_docstrings/objects.Bar.ipynb b/doc/_docstrings/objects.Bar.ipynb
index 30ac8ece4b..1ca0851975 100644
--- a/doc/_docstrings/objects.Bar.ipynb
+++ b/doc/_docstrings/objects.Bar.ipynb
@@ -164,9 +164,9 @@
],
"metadata": {
"kernelspec": {
- "display_name": "seaborn-py39-latest",
+ "display_name": "py310",
"language": "python",
- "name": "seaborn-py39-latest"
+ "name": "py310"
},
"language_info": {
"codemirror_mode": {
@@ -178,7 +178,7 @@
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
- "version": "3.9.13"
+ "version": "3.10.0"
}
},
"nbformat": 4,
diff --git a/doc/_docstrings/objects.Bars.ipynb b/doc/_docstrings/objects.Bars.ipynb
index eb47550ab6..71969df4b3 100644
--- a/doc/_docstrings/objects.Bars.ipynb
+++ b/doc/_docstrings/objects.Bars.ipynb
@@ -143,9 +143,9 @@
],
"metadata": {
"kernelspec": {
- "display_name": "seaborn-py39-latest",
+ "display_name": "py310",
"language": "python",
- "name": "seaborn-py39-latest"
+ "name": "py310"
},
"language_info": {
"codemirror_mode": {
@@ -157,7 +157,7 @@
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
- "version": "3.9.13"
+ "version": "3.10.0"
}
},
"nbformat": 4,
diff --git a/doc/_docstrings/objects.Dot.ipynb b/doc/_docstrings/objects.Dot.ipynb
index 34a262bcef..d133c04127 100644
--- a/doc/_docstrings/objects.Dot.ipynb
+++ b/doc/_docstrings/objects.Dot.ipynb
@@ -168,9 +168,9 @@
],
"metadata": {
"kernelspec": {
- "display_name": "seaborn-py39-latest",
+ "display_name": "py310",
"language": "python",
- "name": "seaborn-py39-latest"
+ "name": "py310"
},
"language_info": {
"codemirror_mode": {
@@ -182,7 +182,7 @@
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
- "version": "3.9.13"
+ "version": "3.10.0"
}
},
"nbformat": 4,
diff --git a/doc/_docstrings/objects.Dots.ipynb b/doc/_docstrings/objects.Dots.ipynb
index a7f920fa43..f1b3a53d2c 100644
--- a/doc/_docstrings/objects.Dots.ipynb
+++ b/doc/_docstrings/objects.Dots.ipynb
@@ -124,9 +124,9 @@
],
"metadata": {
"kernelspec": {
- "display_name": "seaborn-py39-latest",
+ "display_name": "py310",
"language": "python",
- "name": "seaborn-py39-latest"
+ "name": "py310"
},
"language_info": {
"codemirror_mode": {
@@ -138,7 +138,7 @@
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
- "version": "3.9.13"
+ "version": "3.10.0"
}
},
"nbformat": 4,
diff --git a/doc/_docstrings/objects.Hist.ipynb b/doc/_docstrings/objects.Hist.ipynb
new file mode 100644
index 0000000000..778e0def95
--- /dev/null
+++ b/doc/_docstrings/objects.Hist.ipynb
@@ -0,0 +1,231 @@
+{
+ "cells": [
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "id": "59690096-a0ad-4ff3-b82c-0258d724035a",
+ "metadata": {
+ "tags": [
+ "hide"
+ ]
+ },
+ "outputs": [],
+ "source": [
+ "import seaborn.objects as so\n",
+ "from seaborn import load_dataset\n",
+ "penguins = load_dataset(\"penguins\")"
+ ]
+ },
+ {
+ "cell_type": "raw",
+ "id": "c345a35c-bac8-4163-ba40-e7c208df1033",
+ "metadata": {},
+ "source": [
+ "For discrete or categorical variables, this stat is commonly combined with a :class:`Bar` mark:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "id": "6a96ac9b-1240-496d-9385-840205945208",
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "so.Plot(penguins, \"island\").add(so.Bar(), so.Hist())"
+ ]
+ },
+ {
+ "cell_type": "raw",
+ "id": "1e5ff9d5-c6a9-4adc-a9be-0f155b1575be",
+ "metadata": {},
+ "source": [
+ "When used to estimate a univariate distribution, it is better to use the :class:`Bars` mark:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "id": "7f3e3144-752a-4d71-9528-85eb1ed0a9a4",
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "p = so.Plot(penguins, \"flipper_length_mm\")\n",
+ "p.add(so.Bars(), so.Hist())"
+ ]
+ },
+ {
+ "cell_type": "raw",
+ "id": "008b9ffe-da74-4406-9756-4f70e333f33b",
+ "metadata": {},
+ "source": [
+ "The granularity of the bins will influence whether the underlying distribution is accurately represented. Adjust it by setting the total number:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "id": "27d221d5-add5-40a8-85d2-05102384dad1",
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "p.add(so.Bars(), so.Hist(bins=20))"
+ ]
+ },
+ {
+ "cell_type": "raw",
+ "id": "fffebb54-0299-45c5-b7fb-6fcad6427239",
+ "metadata": {},
+ "source": [
+ "Alternatively, specify the *width* of the bins:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "id": "d036ca65-7dcf-45ac-a2d1-caafb9f922a7",
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "p.add(so.Bars(), so.Hist(binwidth=5))"
+ ]
+ },
+ {
+ "cell_type": "raw",
+ "id": "bc1e4bd3-2a16-42bd-9c13-a660dd381f66",
+ "metadata": {},
+ "source": [
+ "By default, the transform returns the count of observations in each bin. The counts can be normalized, e.g. to show a proportion:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "id": "dbf23712-2231-4226-8265-0e2a5299c4bb",
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "p.add(so.Bars(), so.Hist(stat=\"proportion\"))"
+ ]
+ },
+ {
+ "cell_type": "raw",
+ "id": "6c6fb23e-78c5-4630-a958-62cb4dee4ec8",
+ "metadata": {},
+ "source": [
+ "When additional variables define groups, the default behavior is to normalize across all groups:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "id": "ac3fe4ef-56e3-4ec7-b580-596d2a3d924b",
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "p = p.facet(\"island\")\n",
+ "p.add(so.Bars(), so.Hist(stat=\"proportion\"))"
+ ]
+ },
+ {
+ "cell_type": "raw",
+ "id": "f7afc403-26cc-4325-a28a-913c2291aa35",
+ "metadata": {},
+ "source": [
+ "Pass `common_norm=False` to normalize each distribution independently:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "id": "b2029324-069f-4261-a178-1efad2fd0e88",
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "p.add(so.Bars(), so.Hist(stat=\"proportion\", common_norm=False))"
+ ]
+ },
+ {
+ "cell_type": "raw",
+ "id": "0f83401a-e456-4a14-af69-f1483c6c03c4",
+ "metadata": {},
+ "source": [
+ "Or, with more than one grouping varible, specify a subset to normalize within:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "id": "5c092262-8a8f-4a3e-8cae-9e0f23dd94ba",
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "p.add(so.Bars(), so.Hist(stat=\"proportion\", common_norm=[\"col\"]), color=\"sex\")"
+ ]
+ },
+ {
+ "cell_type": "raw",
+ "id": "86532133-bf33-4674-9614-86ae3408aa51",
+ "metadata": {},
+ "source": [
+ "When distributions overlap it may be easier to discern their shapes with an :class:`Area` mark:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "id": "00b18ad8-52d4-460a-a012-d87c66b3e71e",
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "p.add(so.Area(), so.Hist(), color=\"sex\")"
+ ]
+ },
+ {
+ "cell_type": "raw",
+ "id": "2b34d435-abbf-41aa-b219-91883d7d29f3",
+ "metadata": {},
+ "source": [
+ "Or add :class:`Stack` move to represent a part-whole relationship:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "id": "3a7a0c05-d774-4f99-950f-5dc9865027c4",
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "p.add(so.Bars(), so.Hist(), so.Stack(), color=\"sex\")"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "id": "e247e74b-2c09-40f0-8f45-9fa5f8264d78",
+ "metadata": {},
+ "outputs": [],
+ "source": []
+ }
+ ],
+ "metadata": {
+ "kernelspec": {
+ "display_name": "py310",
+ "language": "python",
+ "name": "py310"
+ },
+ "language_info": {
+ "codemirror_mode": {
+ "name": "ipython",
+ "version": 3
+ },
+ "file_extension": ".py",
+ "mimetype": "text/x-python",
+ "name": "python",
+ "nbconvert_exporter": "python",
+ "pygments_lexer": "ipython3",
+ "version": "3.10.0"
+ }
+ },
+ "nbformat": 4,
+ "nbformat_minor": 5
+}
diff --git a/doc/_docstrings/objects.Jitter.ipynb b/doc/_docstrings/objects.Jitter.ipynb
new file mode 100644
index 0000000000..6aa600cddd
--- /dev/null
+++ b/doc/_docstrings/objects.Jitter.ipynb
@@ -0,0 +1,178 @@
+{
+ "cells": [
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "id": "f2e5a85d-c710-492b-a4fc-09b45ae26471",
+ "metadata": {
+ "tags": [
+ "hide"
+ ]
+ },
+ "outputs": [],
+ "source": [
+ "import seaborn.objects as so\n",
+ "from seaborn import load_dataset\n",
+ "penguins = load_dataset(\"penguins\")"
+ ]
+ },
+ {
+ "cell_type": "raw",
+ "id": "14b5927c-42f1-4934-adee-3d380b8b3228",
+ "metadata": {},
+ "source": [
+ "When used without any arguments, a small amount of jitter will be applied along the orientation axis:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "id": "bc1b4941-bbe6-4afc-b51a-0ac67cbe417d",
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "(\n",
+ " so.Plot(penguins, \"species\", \"body_mass_g\")\n",
+ " .add(so.Dots(), so.Jitter())\n",
+ ")"
+ ]
+ },
+ {
+ "cell_type": "raw",
+ "id": "1101690e-6c19-4219-aa4e-180798454df1",
+ "metadata": {},
+ "source": [
+ "The `width` parameter controls the amount of jitter relative to the spacing between the marks:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "id": "c4251b9d-8b11-4c2c-905c-2f3b523dee70",
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "(\n",
+ " so.Plot(penguins, \"species\", \"body_mass_g\")\n",
+ " .add(so.Dots(), so.Jitter(.5))\n",
+ ")"
+ ]
+ },
+ {
+ "cell_type": "raw",
+ "id": "38aa639a-356e-4674-970b-53d55379b2b7",
+ "metadata": {},
+ "source": [
+ "The `width` parameter always applies to the orientation axis, so the direction of jitter will adapt along with the orientation:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "id": "1cfe1c07-7e81-45a0-a989-240503046133",
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "(\n",
+ " so.Plot(penguins, \"body_mass_g\", \"species\")\n",
+ " .add(so.Dots(), so.Jitter(.5))\n",
+ ")"
+ ]
+ },
+ {
+ "cell_type": "raw",
+ "id": "0f5de4cc-3383-4503-8b59-9c48230a12a5",
+ "metadata": {},
+ "source": [
+ "Because the `width` jitter is relative, it can be used when the orientation axis is numeric without further tweaking:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "id": "c94c41e8-29c4-4439-a5d1-0b8ffb244890",
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "(\n",
+ " so.Plot(penguins[\"body_mass_g\"].round(-3), penguins[\"flipper_length_mm\"])\n",
+ " .add(so.Dots(), so.Jitter())\n",
+ ")"
+ ]
+ },
+ {
+ "cell_type": "raw",
+ "id": "dd982dfa-fd9f-4edc-8190-18f0e101ae1a",
+ "metadata": {},
+ "source": [
+ "In contrast to `width`, the `x` and `y` parameters always refer to specific axes and control the jitter in data units:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "id": "b0f2e5ca-68ad-4439-a4ee-f32f65682e95",
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "(\n",
+ " so.Plot(penguins[\"body_mass_g\"].round(-3), penguins[\"flipper_length_mm\"])\n",
+ " .add(so.Dots(), so.Jitter(x=100))\n",
+ ")"
+ ]
+ },
+ {
+ "cell_type": "raw",
+ "id": "a90ba526-8043-42ed-8f57-36445c163c0d",
+ "metadata": {},
+ "source": [
+ "Both `x` and `y` can be used in a single transform:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "id": "6c07ed1d-ac77-4b30-90a8-e1b8760f9fad",
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "(\n",
+ " so.Plot(\n",
+ " penguins[\"body_mass_g\"].round(-3),\n",
+ " penguins[\"flipper_length_mm\"].round(-1),\n",
+ " )\n",
+ " .add(so.Dots(), so.Jitter(x=200, y=5))\n",
+ ")"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "id": "bb04c7a2-93f0-44cf-aacf-0eb436d0f14b",
+ "metadata": {},
+ "outputs": [],
+ "source": []
+ }
+ ],
+ "metadata": {
+ "kernelspec": {
+ "display_name": "py310",
+ "language": "python",
+ "name": "py310"
+ },
+ "language_info": {
+ "codemirror_mode": {
+ "name": "ipython",
+ "version": 3
+ },
+ "file_extension": ".py",
+ "mimetype": "text/x-python",
+ "name": "python",
+ "nbconvert_exporter": "python",
+ "pygments_lexer": "ipython3",
+ "version": "3.10.0"
+ }
+ },
+ "nbformat": 4,
+ "nbformat_minor": 5
+}
diff --git a/doc/_docstrings/objects.Line.ipynb b/doc/_docstrings/objects.Line.ipynb
index 27c140913a..bc8b8b5ec3 100644
--- a/doc/_docstrings/objects.Line.ipynb
+++ b/doc/_docstrings/objects.Line.ipynb
@@ -146,9 +146,9 @@
],
"metadata": {
"kernelspec": {
- "display_name": "seaborn-py39-latest",
+ "display_name": "py310",
"language": "python",
- "name": "seaborn-py39-latest"
+ "name": "py310"
},
"language_info": {
"codemirror_mode": {
@@ -160,7 +160,7 @@
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
- "version": "3.9.13"
+ "version": "3.10.0"
}
},
"nbformat": 4,
diff --git a/doc/_docstrings/objects.Lines.ipynb b/doc/_docstrings/objects.Lines.ipynb
index ccce6c28ae..375715636d 100644
--- a/doc/_docstrings/objects.Lines.ipynb
+++ b/doc/_docstrings/objects.Lines.ipynb
@@ -75,9 +75,9 @@
],
"metadata": {
"kernelspec": {
- "display_name": "seaborn-py39-latest",
+ "display_name": "py310",
"language": "python",
- "name": "seaborn-py39-latest"
+ "name": "py310"
},
"language_info": {
"codemirror_mode": {
@@ -89,7 +89,7 @@
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
- "version": "3.9.13"
+ "version": "3.10.0"
}
},
"nbformat": 4,
diff --git a/doc/_docstrings/objects.Path.ipynb b/doc/_docstrings/objects.Path.ipynb
index 1b9a440013..39b4a2b78a 100644
--- a/doc/_docstrings/objects.Path.ipynb
+++ b/doc/_docstrings/objects.Path.ipynb
@@ -64,9 +64,9 @@
],
"metadata": {
"kernelspec": {
- "display_name": "seaborn-py39-latest",
+ "display_name": "py310",
"language": "python",
- "name": "seaborn-py39-latest"
+ "name": "py310"
},
"language_info": {
"codemirror_mode": {
@@ -78,7 +78,7 @@
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
- "version": "3.9.13"
+ "version": "3.10.0"
}
},
"nbformat": 4,
diff --git a/doc/_docstrings/objects.Paths.ipynb b/doc/_docstrings/objects.Paths.ipynb
index 0f67ab75ee..5d9d33990e 100644
--- a/doc/_docstrings/objects.Paths.ipynb
+++ b/doc/_docstrings/objects.Paths.ipynb
@@ -81,9 +81,9 @@
],
"metadata": {
"kernelspec": {
- "display_name": "seaborn-py39-latest",
+ "display_name": "py310",
"language": "python",
- "name": "seaborn-py39-latest"
+ "name": "py310"
},
"language_info": {
"codemirror_mode": {
@@ -95,7 +95,7 @@
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
- "version": "3.9.13"
+ "version": "3.10.0"
}
},
"nbformat": 4,
diff --git a/doc/_docstrings/objects.Perc.ipynb b/doc/_docstrings/objects.Perc.ipynb
new file mode 100644
index 0000000000..b97c87cc1f
--- /dev/null
+++ b/doc/_docstrings/objects.Perc.ipynb
@@ -0,0 +1,130 @@
+{
+ "cells": [
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "id": "2d44a326-029b-47ff-b560-5f4b6a4bb73f",
+ "metadata": {
+ "tags": [
+ "hide"
+ ]
+ },
+ "outputs": [],
+ "source": [
+ "import seaborn.objects as so\n",
+ "from seaborn import load_dataset\n",
+ "diamonds = load_dataset(\"diamonds\")"
+ ]
+ },
+ {
+ "cell_type": "raw",
+ "id": "65e975a2-2559-4bf1-8851-8bbbf52bf22d",
+ "metadata": {},
+ "source": [
+ "The default behavior computes the quartiles and min/max of the input data:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "id": "36f927f5-3b64-4871-a355-adadc4da769b",
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "p = (\n",
+ " so.Plot(diamonds, \"cut\", \"price\")\n",
+ " .scale(y=\"log\")\n",
+ ")\n",
+ "p.add(so.Dot(), so.Perc())"
+ ]
+ },
+ {
+ "cell_type": "raw",
+ "id": "feba1b99-0f71-4b18-8e7e-bd5470cc2d0c",
+ "metadata": {},
+ "source": [
+ "Passing an integer will compute that many evenly-spaced percentiles:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "id": "f030dd39-1223-475a-93e1-1759a8971a6c",
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "p.add(so.Dot(), so.Perc(20))"
+ ]
+ },
+ {
+ "cell_type": "raw",
+ "id": "85bd754b-122e-4475-8727-2d584a90a38e",
+ "metadata": {},
+ "source": [
+ "Passing a list will compute exactly those percentiles:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "id": "2fde7549-45b5-411a-afba-eb0da754d9e9",
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "p.add(so.Dot(), so.Perc([10, 25, 50, 75, 90]))"
+ ]
+ },
+ {
+ "cell_type": "raw",
+ "id": "7be16a13-dfc8-4595-a904-42f9be10f4f6",
+ "metadata": {},
+ "source": [
+ "Combine with a range mark to show a percentile interval:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "id": "05c561c6-0449-4a61-96d1-390611a1b694",
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "(\n",
+ " so.Plot(diamonds, \"price\", \"cut\")\n",
+ " .add(so.Dots(pointsize=1, alpha=.2), so.Jitter(.3))\n",
+ " .add(so.Range(color=\"k\"), so.Perc([25, 75]), so.Shift(y=.2))\n",
+ " .scale(x=\"log\")\n",
+ ")"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "id": "d464157c-3187-49c1-9cd8-71f284ce4c50",
+ "metadata": {},
+ "outputs": [],
+ "source": []
+ }
+ ],
+ "metadata": {
+ "kernelspec": {
+ "display_name": "py310",
+ "language": "python",
+ "name": "py310"
+ },
+ "language_info": {
+ "codemirror_mode": {
+ "name": "ipython",
+ "version": 3
+ },
+ "file_extension": ".py",
+ "mimetype": "text/x-python",
+ "name": "python",
+ "nbconvert_exporter": "python",
+ "pygments_lexer": "ipython3",
+ "version": "3.10.0"
+ }
+ },
+ "nbformat": 4,
+ "nbformat_minor": 5
+}
diff --git a/doc/_docstrings/objects.Plot.add.ipynb b/doc/_docstrings/objects.Plot.add.ipynb
index 3f9089982e..e997aca980 100644
--- a/doc/_docstrings/objects.Plot.add.ipynb
+++ b/doc/_docstrings/objects.Plot.add.ipynb
@@ -196,9 +196,9 @@
],
"metadata": {
"kernelspec": {
- "display_name": "seaborn-py39-latest",
+ "display_name": "py310",
"language": "python",
- "name": "seaborn-py39-latest"
+ "name": "py310"
},
"language_info": {
"codemirror_mode": {
@@ -210,7 +210,7 @@
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
- "version": "3.9.13"
+ "version": "3.10.0"
}
},
"nbformat": 4,
diff --git a/doc/_docstrings/objects.Plot.facet.ipynb b/doc/_docstrings/objects.Plot.facet.ipynb
index dbded39a7c..2155dfb5ec 100644
--- a/doc/_docstrings/objects.Plot.facet.ipynb
+++ b/doc/_docstrings/objects.Plot.facet.ipynb
@@ -200,9 +200,9 @@
],
"metadata": {
"kernelspec": {
- "display_name": "seaborn-py39-latest",
+ "display_name": "py310",
"language": "python",
- "name": "seaborn-py39-latest"
+ "name": "py310"
},
"language_info": {
"codemirror_mode": {
@@ -214,7 +214,7 @@
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
- "version": "3.9.13"
+ "version": "3.10.0"
}
},
"nbformat": 4,
diff --git a/doc/_docstrings/objects.Plot.label.ipynb b/doc/_docstrings/objects.Plot.label.ipynb
index 5d9d0b6be8..3a4300b3e3 100644
--- a/doc/_docstrings/objects.Plot.label.ipynb
+++ b/doc/_docstrings/objects.Plot.label.ipynb
@@ -139,9 +139,9 @@
],
"metadata": {
"kernelspec": {
- "display_name": "seaborn-py39-latest",
+ "display_name": "py310",
"language": "python",
- "name": "seaborn-py39-latest"
+ "name": "py310"
},
"language_info": {
"codemirror_mode": {
@@ -153,7 +153,7 @@
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
- "version": "3.9.13"
+ "version": "3.10.0"
}
},
"nbformat": 4,
diff --git a/doc/_docstrings/objects.Plot.layout.ipynb b/doc/_docstrings/objects.Plot.layout.ipynb
index 53e60c0e58..1198766365 100644
--- a/doc/_docstrings/objects.Plot.layout.ipynb
+++ b/doc/_docstrings/objects.Plot.layout.ipynb
@@ -80,9 +80,9 @@
],
"metadata": {
"kernelspec": {
- "display_name": "seaborn-py39-latest",
+ "display_name": "py310",
"language": "python",
- "name": "seaborn-py39-latest"
+ "name": "py310"
},
"language_info": {
"codemirror_mode": {
@@ -94,7 +94,7 @@
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
- "version": "3.9.13"
+ "version": "3.10.0"
}
},
"nbformat": 4,
diff --git a/doc/_docstrings/objects.Plot.limit.ipynb b/doc/_docstrings/objects.Plot.limit.ipynb
index cf7ec4a1b0..9d8f33cfa7 100644
--- a/doc/_docstrings/objects.Plot.limit.ipynb
+++ b/doc/_docstrings/objects.Plot.limit.ipynb
@@ -98,9 +98,9 @@
],
"metadata": {
"kernelspec": {
- "display_name": "seaborn-py39-latest",
+ "display_name": "py310",
"language": "python",
- "name": "seaborn-py39-latest"
+ "name": "py310"
},
"language_info": {
"codemirror_mode": {
@@ -112,7 +112,7 @@
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
- "version": "3.9.13"
+ "version": "3.10.0"
}
},
"nbformat": 4,
diff --git a/doc/_docstrings/objects.Plot.on.ipynb b/doc/_docstrings/objects.Plot.on.ipynb
index 5596c7b107..7e14557bc0 100644
--- a/doc/_docstrings/objects.Plot.on.ipynb
+++ b/doc/_docstrings/objects.Plot.on.ipynb
@@ -160,9 +160,9 @@
],
"metadata": {
"kernelspec": {
- "display_name": "seaborn-py39-latest",
+ "display_name": "py310",
"language": "python",
- "name": "seaborn-py39-latest"
+ "name": "py310"
},
"language_info": {
"codemirror_mode": {
@@ -174,7 +174,7 @@
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
- "version": "3.9.13"
+ "version": "3.10.0"
}
},
"nbformat": 4,
diff --git a/doc/_docstrings/objects.Plot.pair.ipynb b/doc/_docstrings/objects.Plot.pair.ipynb
index b917a90fcd..fc78cbf175 100644
--- a/doc/_docstrings/objects.Plot.pair.ipynb
+++ b/doc/_docstrings/objects.Plot.pair.ipynb
@@ -195,9 +195,9 @@
],
"metadata": {
"kernelspec": {
- "display_name": "seaborn-py39-latest",
+ "display_name": "py310",
"language": "python",
- "name": "seaborn-py39-latest"
+ "name": "py310"
},
"language_info": {
"codemirror_mode": {
@@ -209,7 +209,7 @@
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
- "version": "3.9.13"
+ "version": "3.10.0"
}
},
"nbformat": 4,
diff --git a/doc/_docstrings/objects.Plot.scale.ipynb b/doc/_docstrings/objects.Plot.scale.ipynb
index 9bf784263e..d2d679f429 100644
--- a/doc/_docstrings/objects.Plot.scale.ipynb
+++ b/doc/_docstrings/objects.Plot.scale.ipynb
@@ -294,9 +294,9 @@
],
"metadata": {
"kernelspec": {
- "display_name": "seaborn-py39-latest",
+ "display_name": "py310",
"language": "python",
- "name": "seaborn-py39-latest"
+ "name": "py310"
},
"language_info": {
"codemirror_mode": {
@@ -308,7 +308,7 @@
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
- "version": "3.9.13"
+ "version": "3.10.0"
}
},
"nbformat": 4,
diff --git a/doc/_docstrings/objects.Plot.share.ipynb b/doc/_docstrings/objects.Plot.share.ipynb
index d26ecd0862..d0b1ef5cb1 100644
--- a/doc/_docstrings/objects.Plot.share.ipynb
+++ b/doc/_docstrings/objects.Plot.share.ipynb
@@ -109,9 +109,9 @@
],
"metadata": {
"kernelspec": {
- "display_name": "seaborn-py39-latest",
+ "display_name": "py310",
"language": "python",
- "name": "seaborn-py39-latest"
+ "name": "py310"
},
"language_info": {
"codemirror_mode": {
@@ -123,7 +123,7 @@
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
- "version": "3.9.13"
+ "version": "3.10.0"
}
},
"nbformat": 4,
diff --git a/doc/_docstrings/objects.Plot.theme.ipynb b/doc/_docstrings/objects.Plot.theme.ipynb
index 01eae3eca7..bb459a5620 100644
--- a/doc/_docstrings/objects.Plot.theme.ipynb
+++ b/doc/_docstrings/objects.Plot.theme.ipynb
@@ -125,9 +125,9 @@
],
"metadata": {
"kernelspec": {
- "display_name": "seaborn-py39-latest",
+ "display_name": "py310",
"language": "python",
- "name": "seaborn-py39-latest"
+ "name": "py310"
},
"language_info": {
"codemirror_mode": {
@@ -139,7 +139,7 @@
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
- "version": "3.9.13"
+ "version": "3.10.0"
}
},
"nbformat": 4,
diff --git a/doc/_docstrings/objects.Range.ipynb b/doc/_docstrings/objects.Range.ipynb
index 40725f3825..cccb2296ed 100644
--- a/doc/_docstrings/objects.Range.ipynb
+++ b/doc/_docstrings/objects.Range.ipynb
@@ -57,7 +57,7 @@
" so.Plot(penguins, x=\"sex\", y=\"body_mass_g\", linestyle=\"species\")\n",
" .facet(\"species\")\n",
" .add(so.Line(marker=\"o\"), so.Agg())\n",
- " .add(so.Range(), so.Est(errorbar=\"pi\"))\n",
+ " .add(so.Range(), so.Est(errorbar=\"sd\"))\n",
")"
]
},
@@ -78,21 +78,39 @@
"source": [
"(\n",
" penguins\n",
- " .rename_axis(\"penguin\")\n",
- " .pipe(so.Plot, ymin=\"bill_depth_mm\", ymax=\"bill_length_mm\", x=\"penguin\")\n",
- " .add(so.Range(), color=\"island\", linewidth=\"body_mass_g\")\n",
- " .scale(x=so.Continuous().tick(count=0), linewidth=(.5, 1.5))\n",
- " .facet(row=\"species\", col=\"sex\")\n",
- " .layout(size=(8, 4))\n",
- " .share(x=False)\n",
- " .label(x=\"\", y=\"Size (mm)\")\n",
+ " .rename_axis(index=\"penguin\")\n",
+ " .pipe(so.Plot, x=\"penguin\", ymin=\"bill_depth_mm\", ymax=\"bill_length_mm\")\n",
+ " .add(so.Range(), color=\"island\")\n",
+ ")"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "2191bec6-a02e-48e0-b92c-69c38826049d",
+ "metadata": {},
+ "source": [
+ "When `min`/`max` variables are neither computed as part of a transform or explicitly assigned, the range will cover the full extent of the data at each unique observation on the orient axis:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "id": "63c6352e-4ef5-4cff-940e-35fa5804b2c7",
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "(\n",
+ " so.Plot(penguins, x=\"sex\", y=\"body_mass_g\")\n",
+ " .facet(\"species\")\n",
+ " .add(so.Dots(pointsize=6))\n",
+ " .add(so.Range(linewidth=2))\n",
")"
]
},
{
"cell_type": "code",
"execution_count": null,
- "id": "08751ee7-d0a0-4e70-92b4-c1b38ea28890",
+ "id": "c215deb1-e510-4631-b999-737f5f41cae2",
"metadata": {},
"outputs": [],
"source": []
@@ -100,9 +118,9 @@
],
"metadata": {
"kernelspec": {
- "display_name": "seaborn-py39-latest",
+ "display_name": "py310",
"language": "python",
- "name": "seaborn-py39-latest"
+ "name": "py310"
},
"language_info": {
"codemirror_mode": {
@@ -114,7 +132,7 @@
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
- "version": "3.9.13"
+ "version": "3.10.0"
}
},
"nbformat": 4,
diff --git a/doc/_docstrings/objects.Text.ipynb b/doc/_docstrings/objects.Text.ipynb
new file mode 100644
index 0000000000..ce0455fc6d
--- /dev/null
+++ b/doc/_docstrings/objects.Text.ipynb
@@ -0,0 +1,188 @@
+{
+ "cells": [
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "id": "cd1cdefe-b8c1-40b9-be31-006d52ec9f18",
+ "metadata": {
+ "tags": [
+ "hide"
+ ]
+ },
+ "outputs": [],
+ "source": [
+ "import seaborn.objects as so\n",
+ "from seaborn import load_dataset\n",
+ "glue = (\n",
+ " load_dataset(\"glue\")\n",
+ " .pivot(index=[\"Model\", \"Encoder\"], columns=\"Task\", values=\"Score\")\n",
+ " .assign(Average=lambda x: x.mean(axis=1).round(1))\n",
+ " .sort_values(\"Average\")\n",
+ ")"
+ ]
+ },
+ {
+ "cell_type": "raw",
+ "id": "3e49ffb1-8778-4cd5-80d6-9d7e1438bc9c",
+ "metadata": {},
+ "source": [
+ "Add text at x/y locations on the plot:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "id": "3bf21068-d39e-436c-8deb-aa1b15aeb2b3",
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "(\n",
+ " so.Plot(glue, x=\"SST-2\", y=\"MRPC\", text=\"Model\")\n",
+ " .add(so.Text())\n",
+ ")"
+ ]
+ },
+ {
+ "cell_type": "raw",
+ "id": "a4b9a8b2-6603-46db-9ede-3b3fb45e0e64",
+ "metadata": {},
+ "source": [
+ "Add bar annotations, horizontally-aligned with `halign`:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "id": "f68501f0-c868-439e-9485-d71cca86ea47",
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "(\n",
+ " so.Plot(glue, x=\"Average\", y=\"Model\", text=\"Average\")\n",
+ " .add(so.Bar())\n",
+ " .add(so.Text(color=\"w\", halign=\"right\"))\n",
+ ")"
+ ]
+ },
+ {
+ "cell_type": "raw",
+ "id": "a9d39479-0afa-477b-8403-fe92a54643c9",
+ "metadata": {},
+ "source": [
+ "Fine-tune the alignment using `offset`:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "id": "b5da4a9d-79f3-4c11-bab3-f89da8512ce4",
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "(\n",
+ " so.Plot(glue, x=\"Average\", y=\"Model\", text=\"Average\")\n",
+ " .add(so.Bar())\n",
+ " .add(so.Text(color=\"w\", halign=\"right\", offset=6))\n",
+ ")"
+ ]
+ },
+ {
+ "cell_type": "raw",
+ "id": "e9c43798-70d5-42b5-bd91-b85684d1b671",
+ "metadata": {},
+ "source": [
+ "Add text above dots, mapping the text color with a third variable:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "id": "b2d26ebc-24ac-4531-9ba2-fa03720c58bc",
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "(\n",
+ " so.Plot(glue, x=\"SST-2\", y=\"MRPC\", color=\"Encoder\", text=\"Model\")\n",
+ " .add(so.Dot())\n",
+ " .add(so.Text(valign=\"bottom\"))\n",
+ "\n",
+ ")"
+ ]
+ },
+ {
+ "cell_type": "raw",
+ "id": "f31aaa38-6728-4299-8422-8762c52c9857",
+ "metadata": {},
+ "source": [
+ "Map the text alignment for better use of space:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "id": "cf4bbf0c-0c5f-4c31-b971-720ea8910918",
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "(\n",
+ " so.Plot(glue, x=\"RTE\", y=\"MRPC\", color=\"Encoder\", text=\"Model\")\n",
+ " .add(so.Dot())\n",
+ " .add(so.Text(), halign=\"Encoder\")\n",
+ " .scale(halign={\"LSTM\": \"left\", \"Transformer\": \"right\"})\n",
+ ")"
+ ]
+ },
+ {
+ "cell_type": "raw",
+ "id": "a5de35a6-1ccf-4958-8013-edd9ed1cd4b0",
+ "metadata": {},
+ "source": [
+ "Use additional matplotlib parameters to control the appearance of the text:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "id": "9c4be188-1614-4c19-9bd7-b07e986f6a23",
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "(\n",
+ " so.Plot(glue, x=\"RTE\", y=\"MRPC\", color=\"Encoder\", text=\"Model\")\n",
+ " .add(so.Dot())\n",
+ " .add(so.Text({\"fontweight\": \"bold\"}), halign=\"Encoder\")\n",
+ " .scale(halign={\"LSTM\": \"left\", \"Transformer\": \"right\"})\n",
+ ")"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "id": "95fb7aee-090a-4415-917c-b5258d2b298b",
+ "metadata": {},
+ "outputs": [],
+ "source": []
+ }
+ ],
+ "metadata": {
+ "kernelspec": {
+ "display_name": "py310",
+ "language": "python",
+ "name": "py310"
+ },
+ "language_info": {
+ "codemirror_mode": {
+ "name": "ipython",
+ "version": 3
+ },
+ "file_extension": ".py",
+ "mimetype": "text/x-python",
+ "name": "python",
+ "nbconvert_exporter": "python",
+ "pygments_lexer": "ipython3",
+ "version": "3.10.0"
+ }
+ },
+ "nbformat": 4,
+ "nbformat_minor": 5
+}
diff --git a/doc/_docstrings/pairplot.ipynb b/doc/_docstrings/pairplot.ipynb
index dd91f3638b..67948e4f9b 100644
--- a/doc/_docstrings/pairplot.ipynb
+++ b/doc/_docstrings/pairplot.ipynb
@@ -203,9 +203,9 @@
],
"metadata": {
"kernelspec": {
- "display_name": "seaborn-py39-latest",
+ "display_name": "py310",
"language": "python",
- "name": "seaborn-py39-latest"
+ "name": "py310"
},
"language_info": {
"codemirror_mode": {
@@ -217,7 +217,7 @@
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
- "version": "3.9.13"
+ "version": "3.10.0"
}
},
"nbformat": 4,
diff --git a/doc/_docstrings/plotting_context.ipynb b/doc/_docstrings/plotting_context.ipynb
index 48f65bc23a..4f757331a8 100644
--- a/doc/_docstrings/plotting_context.ipynb
+++ b/doc/_docstrings/plotting_context.ipynb
@@ -88,9 +88,9 @@
],
"metadata": {
"kernelspec": {
- "display_name": "seaborn-py39-latest",
+ "display_name": "py310",
"language": "python",
- "name": "seaborn-py39-latest"
+ "name": "py310"
},
"language_info": {
"codemirror_mode": {
@@ -102,7 +102,7 @@
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
- "version": "3.9.13"
+ "version": "3.10.0"
}
},
"nbformat": 4,
diff --git a/doc/_docstrings/pointplot.ipynb b/doc/_docstrings/pointplot.ipynb
index a4541bbd32..e58aeec19a 100644
--- a/doc/_docstrings/pointplot.ipynb
+++ b/doc/_docstrings/pointplot.ipynb
@@ -120,9 +120,9 @@
],
"metadata": {
"kernelspec": {
- "display_name": "seaborn-py39-latest",
+ "display_name": "py310",
"language": "python",
- "name": "seaborn-py39-latest"
+ "name": "py310"
},
"language_info": {
"codemirror_mode": {
@@ -134,7 +134,7 @@
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
- "version": "3.9.13"
+ "version": "3.10.0"
}
},
"nbformat": 4,
diff --git a/doc/_docstrings/regplot.ipynb b/doc/_docstrings/regplot.ipynb
new file mode 100644
index 0000000000..2a8f102091
--- /dev/null
+++ b/doc/_docstrings/regplot.ipynb
@@ -0,0 +1,251 @@
+{
+ "cells": [
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "id": "611aed40-d120-4fbf-b1e6-9712ed8167fc",
+ "metadata": {
+ "tags": [
+ "hide"
+ ]
+ },
+ "outputs": [],
+ "source": [
+ "import numpy as np\n",
+ "import seaborn as sns\n",
+ "sns.set_theme()\n",
+ "mpg = sns.load_dataset(\"mpg\")"
+ ]
+ },
+ {
+ "cell_type": "raw",
+ "id": "61bebade-0c45-4e99-9567-dfe0bc2dc6e1",
+ "metadata": {},
+ "source": [
+ "Plot the relationship between two variables in a DataFrame:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "id": "2f4107db-d89b-46ad-a4c6-9ba1181b2122",
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "sns.regplot(data=mpg, x=\"weight\", y=\"acceleration\")"
+ ]
+ },
+ {
+ "cell_type": "raw",
+ "id": "146225d0-2e38-4b92-8e64-6d7f78311f40",
+ "metadata": {},
+ "source": [
+ "Fit a higher-order polynomial regression to capture nonlinear trends:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "id": "ba29488c-8a45-4387-bfb1-71a584fa1b3d",
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "sns.regplot(data=mpg, x=\"weight\", y=\"mpg\", order=2)"
+ ]
+ },
+ {
+ "cell_type": "raw",
+ "id": "0ad71f54-b362-465e-8780-1d8b99ff2d51",
+ "metadata": {},
+ "source": [
+ "Alternatively, fit a log-linear regression:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "id": "aae2acaa-ed07-4568-97d2-8665603eb7eb",
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "sns.regplot(data=mpg, x=\"displacement\", y=\"mpg\", logx=True)"
+ ]
+ },
+ {
+ "cell_type": "raw",
+ "id": "eef37c8a-7190-465c-b963-076ec17e1b3a",
+ "metadata": {},
+ "source": [
+ "Or use a locally-weighted (LOWESS) smoother:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "id": "9276c469-72ea-4c36-9b7c-19ecba564376",
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "sns.regplot(data=mpg, x=\"horsepower\", y=\"mpg\", lowess=True)"
+ ]
+ },
+ {
+ "cell_type": "raw",
+ "id": "d18f1534-598e-4f08-91dd-0c4020f30b00",
+ "metadata": {},
+ "source": [
+ "Fit a logistic regression when the response variable is binary:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "id": "79ec9180-10c9-4910-9713-dcd1fdd266be",
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "sns.regplot(x=mpg[\"weight\"], y=mpg[\"origin\"].eq(\"usa\").rename(\"from_usa\"), logistic=True)"
+ ]
+ },
+ {
+ "cell_type": "raw",
+ "id": "2e165783-d505-4acb-a20a-d22a49965c2b",
+ "metadata": {},
+ "source": [
+ "Fit a robust regression to downweight the influence of outliers:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "id": "fd5cf940-de8f-4230-8b04-5c650418f3c4",
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "sns.regplot(data=mpg, x=\"horsepower\", y=\"weight\", robust=True)"
+ ]
+ },
+ {
+ "cell_type": "raw",
+ "id": "e7d43c4e-e819-4634-8269-cbf5de4a2f24",
+ "metadata": {},
+ "source": [
+ "Disable the confidence interval for faster plotting:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "id": "b21384ff-6395-4fa9-b7da-63e8a951d8a5",
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "sns.regplot(data=mpg, x=\"weight\", y=\"horsepower\", ci=None)"
+ ]
+ },
+ {
+ "cell_type": "raw",
+ "id": "06e979ac-f418-4ead-bde1-ec684d0545ff",
+ "metadata": {},
+ "source": [
+ "Jitter the scatterplot when the `x` variable is discrete:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "id": "543a8ace-a89e-4af9-bf6d-a8722ebdfac5",
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "sns.regplot(data=mpg, x=\"cylinders\", y=\"weight\", x_jitter=.15)"
+ ]
+ },
+ {
+ "cell_type": "raw",
+ "id": "c3042eb2-0933-4886-9bff-88c276371516",
+ "metadata": {},
+ "source": [
+ "Or aggregate over the distinct `x` values:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "id": "158c6e36-8858-415b-b78c-7d8d79879ee5",
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "sns.regplot(data=mpg, x=\"cylinders\", y=\"acceleration\", x_estimator=np.mean, order=2)"
+ ]
+ },
+ {
+ "cell_type": "raw",
+ "id": "d9cefe7a-7f86-4353-95da-d7e72e65d4fc",
+ "metadata": {},
+ "source": [
+ "With a continuous `x` variable, bin and then aggregate:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "id": "1c48829b-2e3b-4e6b-9b1d-5ba69f713617",
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "sns.regplot(data=mpg, x=\"weight\", y=\"mpg\", x_bins=np.arange(2000, 5500, 250), order=2)"
+ ]
+ },
+ {
+ "cell_type": "raw",
+ "id": "dfe5a36a-20b0-4e69-b986-fede8e1506cc",
+ "metadata": {},
+ "source": [
+ "Customize the appearance of various elements:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "id": "df689a39-c5e1-4f7b-a8f9-8ffb09b95238",
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "sns.regplot(\n",
+ " data=mpg, x=\"weight\", y=\"horsepower\",\n",
+ " ci=99, marker=\"x\", color=\".3\", line_kws=dict(color=\"r\"),\n",
+ ")"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "id": "d625745b-3706-447b-9224-88e6cb1eb7f9",
+ "metadata": {},
+ "outputs": [],
+ "source": []
+ }
+ ],
+ "metadata": {
+ "kernelspec": {
+ "display_name": "py310",
+ "language": "python",
+ "name": "py310"
+ },
+ "language_info": {
+ "codemirror_mode": {
+ "name": "ipython",
+ "version": 3
+ },
+ "file_extension": ".py",
+ "mimetype": "text/x-python",
+ "name": "python",
+ "nbconvert_exporter": "python",
+ "pygments_lexer": "ipython3",
+ "version": "3.10.0"
+ }
+ },
+ "nbformat": 4,
+ "nbformat_minor": 5
+}
diff --git a/doc/_docstrings/relplot.ipynb b/doc/_docstrings/relplot.ipynb
index 9e782b756c..a7b36f1a44 100644
--- a/doc/_docstrings/relplot.ipynb
+++ b/doc/_docstrings/relplot.ipynb
@@ -240,9 +240,9 @@
],
"metadata": {
"kernelspec": {
- "display_name": "seaborn-py39-latest",
+ "display_name": "py310",
"language": "python",
- "name": "seaborn-py39-latest"
+ "name": "py310"
},
"language_info": {
"codemirror_mode": {
@@ -254,7 +254,7 @@
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
- "version": "3.9.13"
+ "version": "3.10.0"
}
},
"nbformat": 4,
diff --git a/doc/_docstrings/residplot.ipynb b/doc/_docstrings/residplot.ipynb
new file mode 100644
index 0000000000..3dd26d0168
--- /dev/null
+++ b/doc/_docstrings/residplot.ipynb
@@ -0,0 +1,113 @@
+{
+ "cells": [
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "id": "776f8271-21ed-4707-a1ad-09d8c63ae95a",
+ "metadata": {
+ "tags": [
+ "hide"
+ ]
+ },
+ "outputs": [],
+ "source": [
+ "import seaborn as sns\n",
+ "sns.set_theme()\n",
+ "mpg = sns.load_dataset(\"mpg\")"
+ ]
+ },
+ {
+ "cell_type": "raw",
+ "id": "85717971-adc9-45b0-9c4b-3f022d96179c",
+ "metadata": {},
+ "source": [
+ "Pass `x` and `y` to see a scatter plot of the residuals after fitting a simple regression model:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "id": "5aea4655-fb51-4b51-b41d-4769de50e956",
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "sns.residplot(data=mpg, x=\"weight\", y=\"displacement\")"
+ ]
+ },
+ {
+ "cell_type": "raw",
+ "id": "175b6287-9240-493f-94bc-9d18258e952b",
+ "metadata": {},
+ "source": [
+ "Structure in the residual plot can reveal a violation of linear regression assumptions:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "id": "39aa84c2-d623-44be-9b0b-746f52b55fd4",
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "sns.residplot(data=mpg, x=\"horsepower\", y=\"mpg\")"
+ ]
+ },
+ {
+ "cell_type": "raw",
+ "id": "bd9641e4-8df5-4751-b261-6443888fbbfe",
+ "metadata": {},
+ "source": [
+ "Remove higher-order trends to test whether that stabilizes the residuals:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "id": "03a68199-1272-464b-8b85-7a309c22a4a6",
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "sns.residplot(data=mpg, x=\"horsepower\", y=\"mpg\", order=2)"
+ ]
+ },
+ {
+ "cell_type": "raw",
+ "id": "b17750af-0393-4c53-8057-bf95d0de821a",
+ "metadata": {},
+ "source": [
+ "Adding a LOWESS curve can help reveal or emphasize structure:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "id": "494359bd-47b2-426e-9c35-14b5351eec93",
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "sns.residplot(data=mpg, x=\"horsepower\", y=\"mpg\", lowess=True, line_kws=dict(color=\"r\"))"
+ ]
+ }
+ ],
+ "metadata": {
+ "kernelspec": {
+ "display_name": "py310",
+ "language": "python",
+ "name": "py310"
+ },
+ "language_info": {
+ "codemirror_mode": {
+ "name": "ipython",
+ "version": 3
+ },
+ "file_extension": ".py",
+ "mimetype": "text/x-python",
+ "name": "python",
+ "nbconvert_exporter": "python",
+ "pygments_lexer": "ipython3",
+ "version": "3.10.0"
+ }
+ },
+ "nbformat": 4,
+ "nbformat_minor": 5
+}
diff --git a/doc/_docstrings/rugplot.ipynb b/doc/_docstrings/rugplot.ipynb
index 0819e2061c..4092dab06b 100644
--- a/doc/_docstrings/rugplot.ipynb
+++ b/doc/_docstrings/rugplot.ipynb
@@ -115,9 +115,9 @@
],
"metadata": {
"kernelspec": {
- "display_name": "seaborn-py39-latest",
+ "display_name": "py310",
"language": "python",
- "name": "seaborn-py39-latest"
+ "name": "py310"
},
"language_info": {
"codemirror_mode": {
@@ -129,7 +129,7 @@
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
- "version": "3.9.13"
+ "version": "3.10.0"
}
},
"nbformat": 4,
diff --git a/doc/_docstrings/scatterplot.ipynb b/doc/_docstrings/scatterplot.ipynb
index 4b51829f44..315a54845b 100644
--- a/doc/_docstrings/scatterplot.ipynb
+++ b/doc/_docstrings/scatterplot.ipynb
@@ -285,9 +285,9 @@
],
"metadata": {
"kernelspec": {
- "display_name": "seaborn-py39-latest",
+ "display_name": "py310",
"language": "python",
- "name": "seaborn-py39-latest"
+ "name": "py310"
},
"language_info": {
"codemirror_mode": {
@@ -299,7 +299,7 @@
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
- "version": "3.9.13"
+ "version": "3.10.0"
}
},
"nbformat": 4,
diff --git a/doc/_docstrings/set_context.ipynb b/doc/_docstrings/set_context.ipynb
index 882d577bab..07a5c091d4 100644
--- a/doc/_docstrings/set_context.ipynb
+++ b/doc/_docstrings/set_context.ipynb
@@ -82,9 +82,9 @@
],
"metadata": {
"kernelspec": {
- "display_name": "seaborn-py39-latest",
+ "display_name": "py310",
"language": "python",
- "name": "seaborn-py39-latest"
+ "name": "py310"
},
"language_info": {
"codemirror_mode": {
@@ -96,7 +96,7 @@
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
- "version": "3.9.13"
+ "version": "3.10.0"
}
},
"nbformat": 4,
diff --git a/doc/_docstrings/set_style.ipynb b/doc/_docstrings/set_style.ipynb
index 8f7f7b545d..25cdde23d6 100644
--- a/doc/_docstrings/set_style.ipynb
+++ b/doc/_docstrings/set_style.ipynb
@@ -63,9 +63,9 @@
],
"metadata": {
"kernelspec": {
- "display_name": "seaborn-py39-latest",
+ "display_name": "py310",
"language": "python",
- "name": "seaborn-py39-latest"
+ "name": "py310"
},
"language_info": {
"codemirror_mode": {
@@ -77,7 +77,7 @@
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
- "version": "3.9.13"
+ "version": "3.10.0"
}
},
"nbformat": 4,
diff --git a/doc/_docstrings/set_theme.ipynb b/doc/_docstrings/set_theme.ipynb
index 8b09f43936..add6eb2886 100644
--- a/doc/_docstrings/set_theme.ipynb
+++ b/doc/_docstrings/set_theme.ipynb
@@ -139,9 +139,9 @@
],
"metadata": {
"kernelspec": {
- "display_name": "seaborn-py39-latest",
+ "display_name": "py310",
"language": "python",
- "name": "seaborn-py39-latest"
+ "name": "py310"
},
"language_info": {
"codemirror_mode": {
@@ -153,7 +153,7 @@
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
- "version": "3.9.13"
+ "version": "3.10.0"
}
},
"nbformat": 4,
diff --git a/doc/_docstrings/stripplot.ipynb b/doc/_docstrings/stripplot.ipynb
index 34264355a7..d33034b5ba 100644
--- a/doc/_docstrings/stripplot.ipynb
+++ b/doc/_docstrings/stripplot.ipynb
@@ -291,9 +291,9 @@
],
"metadata": {
"kernelspec": {
- "display_name": "seaborn-py39-latest",
+ "display_name": "py310",
"language": "python",
- "name": "seaborn-py39-latest"
+ "name": "py310"
},
"language_info": {
"codemirror_mode": {
@@ -305,7 +305,7 @@
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
- "version": "3.9.13"
+ "version": "3.10.0"
}
},
"nbformat": 4,
diff --git a/doc/_docstrings/swarmplot.ipynb b/doc/_docstrings/swarmplot.ipynb
index 87b1eb653a..e90ee52115 100644
--- a/doc/_docstrings/swarmplot.ipynb
+++ b/doc/_docstrings/swarmplot.ipynb
@@ -263,9 +263,9 @@
],
"metadata": {
"kernelspec": {
- "display_name": "seaborn-py39-latest",
+ "display_name": "py310",
"language": "python",
- "name": "seaborn-py39-latest"
+ "name": "py310"
},
"language_info": {
"codemirror_mode": {
@@ -277,7 +277,7 @@
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
- "version": "3.9.13"
+ "version": "3.10.0"
}
},
"nbformat": 4,
diff --git a/doc/_docstrings/violinplot.ipynb b/doc/_docstrings/violinplot.ipynb
index cbb4a8b10f..ebf5c4d963 100644
--- a/doc/_docstrings/violinplot.ipynb
+++ b/doc/_docstrings/violinplot.ipynb
@@ -171,9 +171,9 @@
],
"metadata": {
"kernelspec": {
- "display_name": "seaborn-py39-latest",
+ "display_name": "py310",
"language": "python",
- "name": "seaborn-py39-latest"
+ "name": "py310"
},
"language_info": {
"codemirror_mode": {
@@ -185,7 +185,7 @@
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
- "version": "3.9.13"
+ "version": "3.10.0"
}
},
"nbformat": 4,
diff --git a/doc/_tutorial/aesthetics.ipynb b/doc/_tutorial/aesthetics.ipynb
index ecd78c0763..46f55d83ac 100644
--- a/doc/_tutorial/aesthetics.ipynb
+++ b/doc/_tutorial/aesthetics.ipynb
@@ -404,9 +404,9 @@
"metadata": {
"celltoolbar": "Tags",
"kernelspec": {
- "display_name": "seaborn-py39-latest",
+ "display_name": "py310",
"language": "python",
- "name": "seaborn-py39-latest"
+ "name": "py310"
},
"language_info": {
"codemirror_mode": {
@@ -418,7 +418,7 @@
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
- "version": "3.9.13"
+ "version": "3.10.0"
}
},
"nbformat": 4,
diff --git a/doc/_tutorial/axis_grids.ipynb b/doc/_tutorial/axis_grids.ipynb
index 051e4efcb6..2c3aacdc24 100644
--- a/doc/_tutorial/axis_grids.ipynb
+++ b/doc/_tutorial/axis_grids.ipynb
@@ -531,9 +531,9 @@
"metadata": {
"celltoolbar": "Tags",
"kernelspec": {
- "display_name": "seaborn-py39-latest",
+ "display_name": "py310",
"language": "python",
- "name": "seaborn-py39-latest"
+ "name": "py310"
},
"language_info": {
"codemirror_mode": {
@@ -545,7 +545,7 @@
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
- "version": "3.9.13"
+ "version": "3.10.0"
}
},
"nbformat": 4,
diff --git a/doc/_tutorial/categorical.ipynb b/doc/_tutorial/categorical.ipynb
index 7af75e398c..a1faa86a4f 100644
--- a/doc/_tutorial/categorical.ipynb
+++ b/doc/_tutorial/categorical.ipynb
@@ -520,9 +520,9 @@
"metadata": {
"celltoolbar": "Tags",
"kernelspec": {
- "display_name": "seaborn-py39-latest",
+ "display_name": "py310",
"language": "python",
- "name": "seaborn-py39-latest"
+ "name": "py310"
},
"language_info": {
"codemirror_mode": {
@@ -534,7 +534,7 @@
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
- "version": "3.9.13"
+ "version": "3.10.0"
}
},
"nbformat": 4,
diff --git a/doc/_tutorial/color_palettes.ipynb b/doc/_tutorial/color_palettes.ipynb
index 70afb7bf18..48cb84640f 100644
--- a/doc/_tutorial/color_palettes.ipynb
+++ b/doc/_tutorial/color_palettes.ipynb
@@ -982,9 +982,9 @@
"metadata": {
"celltoolbar": "Tags",
"kernelspec": {
- "display_name": "seaborn-py39-latest",
+ "display_name": "py310",
"language": "python",
- "name": "seaborn-py39-latest"
+ "name": "py310"
},
"language_info": {
"codemirror_mode": {
@@ -996,7 +996,7 @@
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
- "version": "3.9.13"
+ "version": "3.10.0"
}
},
"nbformat": 4,
diff --git a/doc/_tutorial/data_structure.ipynb b/doc/_tutorial/data_structure.ipynb
index 754460aebd..be7d55a026 100644
--- a/doc/_tutorial/data_structure.ipynb
+++ b/doc/_tutorial/data_structure.ipynb
@@ -475,9 +475,9 @@
],
"metadata": {
"kernelspec": {
- "display_name": "seaborn-py39-latest",
+ "display_name": "py310",
"language": "python",
- "name": "seaborn-py39-latest"
+ "name": "py310"
},
"language_info": {
"codemirror_mode": {
@@ -489,7 +489,7 @@
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
- "version": "3.9.13"
+ "version": "3.10.0"
}
},
"nbformat": 4,
diff --git a/doc/_tutorial/distributions.ipynb b/doc/_tutorial/distributions.ipynb
index 5c1dca9ed8..7e47442b49 100644
--- a/doc/_tutorial/distributions.ipynb
+++ b/doc/_tutorial/distributions.ipynb
@@ -836,9 +836,9 @@
"metadata": {
"celltoolbar": "Tags",
"kernelspec": {
- "display_name": "seaborn-py39-latest",
+ "display_name": "py310",
"language": "python",
- "name": "seaborn-py39-latest"
+ "name": "py310"
},
"language_info": {
"codemirror_mode": {
@@ -850,7 +850,7 @@
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
- "version": "3.9.13"
+ "version": "3.10.0"
}
},
"nbformat": 4,
diff --git a/doc/_tutorial/error_bars.ipynb b/doc/_tutorial/error_bars.ipynb
index d8525ea842..1d34b35c75 100644
--- a/doc/_tutorial/error_bars.ipynb
+++ b/doc/_tutorial/error_bars.ipynb
@@ -347,9 +347,9 @@
],
"metadata": {
"kernelspec": {
- "display_name": "seaborn-py39-latest",
+ "display_name": "py310",
"language": "python",
- "name": "seaborn-py39-latest"
+ "name": "py310"
},
"language_info": {
"codemirror_mode": {
@@ -361,7 +361,7 @@
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
- "version": "3.9.13"
+ "version": "3.10.0"
}
},
"nbformat": 4,
diff --git a/doc/_tutorial/function_overview.ipynb b/doc/_tutorial/function_overview.ipynb
index 08d69cea24..5096ca02e4 100644
--- a/doc/_tutorial/function_overview.ipynb
+++ b/doc/_tutorial/function_overview.ipynb
@@ -474,9 +474,9 @@
],
"metadata": {
"kernelspec": {
- "display_name": "seaborn-py39-latest",
+ "display_name": "py310",
"language": "python",
- "name": "seaborn-py39-latest"
+ "name": "py310"
},
"language_info": {
"codemirror_mode": {
@@ -488,7 +488,7 @@
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
- "version": "3.9.13"
+ "version": "3.10.0"
}
},
"nbformat": 4,
diff --git a/doc/_tutorial/introduction.ipynb b/doc/_tutorial/introduction.ipynb
index 0955c375d5..c3f74f0fcf 100644
--- a/doc/_tutorial/introduction.ipynb
+++ b/doc/_tutorial/introduction.ipynb
@@ -447,9 +447,9 @@
"metadata": {
"celltoolbar": "Tags",
"kernelspec": {
- "display_name": "seaborn-py39-latest",
+ "display_name": "py310",
"language": "python",
- "name": "seaborn-py39-latest"
+ "name": "py310"
},
"language_info": {
"codemirror_mode": {
@@ -461,7 +461,7 @@
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
- "version": "3.9.13"
+ "version": "3.10.0"
}
},
"nbformat": 4,
diff --git a/doc/_tutorial/objects_interface.ipynb b/doc/_tutorial/objects_interface.ipynb
index 14bcd9c8f4..d5a0700ef1 100644
--- a/doc/_tutorial/objects_interface.ipynb
+++ b/doc/_tutorial/objects_interface.ipynb
@@ -1057,9 +1057,9 @@
],
"metadata": {
"kernelspec": {
- "display_name": "seaborn-py39-latest",
+ "display_name": "py310",
"language": "python",
- "name": "seaborn-py39-latest"
+ "name": "py310"
},
"language_info": {
"codemirror_mode": {
@@ -1071,7 +1071,7 @@
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
- "version": "3.9.13"
+ "version": "3.10.0"
}
},
"nbformat": 4,
diff --git a/doc/_tutorial/properties.ipynb b/doc/_tutorial/properties.ipynb
index 36501bf819..acfd7fad61 100644
--- a/doc/_tutorial/properties.ipynb
+++ b/doc/_tutorial/properties.ipynb
@@ -916,6 +916,157 @@
")"
]
},
+ {
+ "cell_type": "raw",
+ "id": "c2ca33db-df52-4958-889a-320b4833a0d7",
+ "metadata": {},
+ "source": [
+ "Text properties\n",
+ "---------------"
+ ]
+ },
+ {
+ "cell_type": "raw",
+ "id": "b75af2fe-4d81-407c-9858-23362710f25f",
+ "metadata": {},
+ "source": [
+ ".. _horizontalalignment_property:\n",
+ "\n",
+ ".. _verticalalignment_property:\n",
+ "\n",
+ "halign, valign\n",
+ "~~~~~~~~~~~~~~\n",
+ "\n",
+ "The `halign` and `valign` properties control the *horizontal* and *vertical* alignment of text marks. The options for horizontal alignment are `'left'`, `'right'`, and `'center'`, while the options for vertical alignment are `'top'`, `'bottom'`, `'center'`, `'baseline'`, and `'center_baseline'`."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "id": "e9588309-bee4-4b97-b428-eb91ea582105",
+ "metadata": {
+ "tags": [
+ "hide-input"
+ ]
+ },
+ "outputs": [],
+ "source": [
+ "x = [\"left\", \"right\", \"top\", \"bottom\", \"baseline\", \"center\"]\n",
+ "ha = x[:2] + [\"center\"] * 4\n",
+ "va = [\"center_baseline\"] * 2 + x[2:]\n",
+ "y = np.zeros(len(x))\n",
+ "(\n",
+ " so.Plot(x=[f\"'{_x_}'\" for _x_ in x], y=y, halign=ha, valign=va)\n",
+ " .add(so.Dot(marker=\"+\", color=\"r\", alpha=.5, stroke=1, pointsize=24))\n",
+ " .add(so.Text(text=\"XyZ\", fontsize=14, offset=0))\n",
+ " .scale(y=so.Continuous().tick(at=[]), halign=None, valign=None)\n",
+ " .limit(x=(-.25, len(x) - .75))\n",
+ " .layout(size=(9, .6), engine=None)\n",
+ " .theme({\n",
+ " **axes_style(\"ticks\"),\n",
+ " **{f\"axes.spines.{side}\": False for side in [\"left\", \"right\", \"top\"]},\n",
+ " \"xtick.labelsize\": 12,\n",
+ " \"axes.xmargin\": .015,\n",
+ " \"ytick.labelsize\": 12,\n",
+ " })\n",
+ " .plot()\n",
+ ")"
+ ]
+ },
+ {
+ "cell_type": "raw",
+ "id": "ea74c7e5-798b-47bc-bc18-9086902fb5c6",
+ "metadata": {},
+ "source": [
+ ".. _fontsize_property:\n",
+ "\n",
+ "fontsize\n",
+ "~~~~~~~~\n",
+ "\n",
+ "The `fontsize` property controls the size of textual marks. The value has point units:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "id": "c515b790-385d-4521-b14a-0769c1902928",
+ "metadata": {
+ "tags": [
+ "hide-input"
+ ]
+ },
+ "outputs": [],
+ "source": [
+ "from string import ascii_uppercase\n",
+ "n = 26\n",
+ "s = np.arange(n) + 1\n",
+ "y = np.zeros(n)\n",
+ "t = list(ascii_uppercase[:n])\n",
+ "(\n",
+ " so.Plot(x=s, y=y, text=t, fontsize=s)\n",
+ " .add(so.Text())\n",
+ " .scale(x=so.Nominal(), y=so.Continuous().tick(at=[]))\n",
+ " .layout(size=(9, .5), engine=None)\n",
+ " .theme({\n",
+ " **axes_style(\"ticks\"),\n",
+ " **{f\"axes.spines.{side}\": False for side in [\"left\", \"right\", \"top\"]},\n",
+ " \"xtick.labelsize\": 12,\n",
+ " \"axes.xmargin\": .015,\n",
+ " \"ytick.labelsize\": 12,\n",
+ " })\n",
+ " .plot()\n",
+ ")"
+ ]
+ },
+ {
+ "cell_type": "raw",
+ "id": "4b367f36-fb96-44fa-83a3-1cc66c7a3279",
+ "metadata": {},
+ "source": [
+ ".. _offset_property:\n",
+ "\n",
+ "offset\n",
+ "~~~~~~\n",
+ "\n",
+ "The `offset` property controls the spacing between a text mark and its anchor position. It applies when *not* using `center` alignment (i.e., when using left/right or top/bottom). The value has point units. "
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "id": "25a49331-9580-4578-8bdb-d0d1829dde71",
+ "metadata": {
+ "tags": [
+ "hide-input"
+ ]
+ },
+ "outputs": [],
+ "source": [
+ "n = 17\n",
+ "x = np.linspace(0, 8, n)\n",
+ "y = np.full(n, .5)\n",
+ "(\n",
+ " so.Plot(x=x, y=y, offset=x)\n",
+ " .add(so.Bar(color=\".6\", edgecolor=\"k\"))\n",
+ " .add(so.Text(text=\"abc\", valign=\"bottom\"))\n",
+ " .scale(\n",
+ " x=so.Continuous().tick(every=1, minor=1),\n",
+ " y=so.Continuous().tick(at=[]),\n",
+ " offset=None,\n",
+ " )\n",
+ " .limit(y=(0, 1.5))\n",
+ " .layout(size=(9, .5), engine=None)\n",
+ " .theme({\n",
+ " **axes_style(\"ticks\"),\n",
+ " **{f\"axes.spines.{side}\": False for side in [\"left\", \"right\", \"top\"]},\n",
+ " \"axes.xmargin\": .015,\n",
+ " \"xtick.labelsize\": 12,\n",
+ " \"ytick.labelsize\": 12,\n",
+ " })\n",
+ " .plot()\n",
+ ")"
+ ]
+ },
{
"cell_type": "raw",
"id": "77723ffd-2da3-4ece-a97a-3c00e864c743",
@@ -932,6 +1083,11 @@
"source": [
".. _property_property:\n",
"\n",
+ "text\n",
+ "~~~~\n",
+ "\n",
+ "The `text` property is used to set the content of a textual mark. It is always used literally (not mapped), and cast to string when necessary.\n",
+ "\n",
"group\n",
"~~~~~\n",
"\n",
@@ -949,9 +1105,9 @@
],
"metadata": {
"kernelspec": {
- "display_name": "seaborn-py39-latest",
+ "display_name": "py310",
"language": "python",
- "name": "seaborn-py39-latest"
+ "name": "py310"
},
"language_info": {
"codemirror_mode": {
@@ -963,7 +1119,7 @@
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
- "version": "3.9.13"
+ "version": "3.10.0"
}
},
"nbformat": 4,
diff --git a/doc/_tutorial/regression.ipynb b/doc/_tutorial/regression.ipynb
index d08fdbec76..72e7c68667 100644
--- a/doc/_tutorial/regression.ipynb
+++ b/doc/_tutorial/regression.ipynb
@@ -432,9 +432,9 @@
"metadata": {
"celltoolbar": "Tags",
"kernelspec": {
- "display_name": "seaborn-py39-latest",
+ "display_name": "py310",
"language": "python",
- "name": "seaborn-py39-latest"
+ "name": "py310"
},
"language_info": {
"codemirror_mode": {
@@ -446,7 +446,7 @@
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
- "version": "3.9.13"
+ "version": "3.10.0"
}
},
"nbformat": 4,
diff --git a/doc/_tutorial/relational.ipynb b/doc/_tutorial/relational.ipynb
index face989a7f..4d8fb5f6b1 100644
--- a/doc/_tutorial/relational.ipynb
+++ b/doc/_tutorial/relational.ipynb
@@ -663,9 +663,9 @@
"metadata": {
"celltoolbar": "Tags",
"kernelspec": {
- "display_name": "seaborn-py39-latest",
+ "display_name": "py310",
"language": "python",
- "name": "seaborn-py39-latest"
+ "name": "py310"
},
"language_info": {
"codemirror_mode": {
@@ -677,7 +677,7 @@
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
- "version": "3.9.13"
+ "version": "3.10.0"
}
},
"nbformat": 4,
diff --git a/doc/api.rst b/doc/api.rst
index 5ca95bf8c6..357d1e7f14 100644
--- a/doc/api.rst
+++ b/doc/api.rst
@@ -66,6 +66,15 @@ Mark objects
Area
Band
+.. rubric:: Text marks
+
+.. autosummary::
+ :toctree: generated/
+ :template: object
+ :nosignatures:
+
+ Text
+
Stat objects
~~~~~~~~~~~~
@@ -77,6 +86,7 @@ Stat objects
Agg
Est
Hist
+ Perc
PolyFit
Move objects
diff --git a/doc/conf.py b/doc/conf.py
index f3a36eb936..81d2c1b9ff 100644
--- a/doc/conf.py
+++ b/doc/conf.py
@@ -139,6 +139,12 @@
"icon": "fab fa-github",
"type": "fontawesome",
},
+ {
+ "name": "StackOverflow",
+ "url": "https://stackoverflow.com/tags/seaborn",
+ "icon": "fab fa-stack-overflow",
+ "type": "fontawesome",
+ },
{
"name": "Twitter",
"url": "https://twitter.com/michaelwaskom",
diff --git a/doc/whatsnew/v0.12.1.rst b/doc/whatsnew/v0.12.1.rst
index 283cf05d32..f1ed1bb85a 100644
--- a/doc/whatsnew/v0.12.1.rst
+++ b/doc/whatsnew/v0.12.1.rst
@@ -2,6 +2,22 @@
v0.12.1 (Unreleased)
--------------------
+- |Feature| Added the :class:`objects.Text` mark (:pr:`3051`).
+
+- |Feature| Added the :class:`objects.Perc` stat (:pr:`3063`).
+
+- |Feature| The :class:`Band` and :class:`Range` marks will now cover the full extent of the data if `min` / `max` variables are not explicitly assigned or added in a transform (:pr:`3056`).
+
+- |Enhancement| The :class:`Jitter` move now applies a small amount of jitter by default (:pr:`3066`).
+
+- |Enhancement| Marks that sort along the orient axis (e.g. :class:`Line`) now use a stable algorithm (:pr:`3064`).
+
- |Enhancement| |Fix| Add a `label` parameter to :func:`pointplot`, which addresses a regression in 0.12.0 when :func:`pointplot` is passed to :class:`FacetGrid` (:pr:`3016`).
-- |Fix| Make :class:`objects.PolyFit` robust to missing data (:pr:`3010`).
\ No newline at end of file
+- |Fix| Fixed a bug that caused an exception when more than two layers with the same mappings were added (:pr:`3055`).
+
+- |Fix| Make :class:`objects.PolyFit` robust to missing data (:pr:`3010`).
+
+- |Fix| Fixed a regression in :func:`kdeplot` where passing `cmap` for an unfilled bivariate plot would raise an exception (:pr:`3065`).
+
+- |Build| Seaborn no longer contains doctest-style examples, simplifying the testing infrastructure (:pr:`3034`).
diff --git a/pyproject.toml b/pyproject.toml
index f53063cb40..3a1a207406 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -25,7 +25,7 @@ requires-python = ">=3.7"
dependencies = [
"numpy>=1.17",
"pandas>=0.25",
- "matplotlib>=3.1",
+ "matplotlib>=3.1,!=3.6.1",
"typing_extensions; python_version < '3.8'",
]
@@ -40,6 +40,7 @@ dev = [
"pytest-xdist",
"flake8",
"mypy",
+ "pandas-stubs",
"pre-commit",
]
docs = [
diff --git a/seaborn/_core/data.py b/seaborn/_core/data.py
index 9de8be5fb8..535fafe83f 100644
--- a/seaborn/_core/data.py
+++ b/seaborn/_core/data.py
@@ -3,16 +3,14 @@
"""
from __future__ import annotations
-from collections import abc
-import pandas as pd
+from collections.abc import Mapping, Sized
+from typing import cast
-from typing import TYPE_CHECKING
-if TYPE_CHECKING:
- from pandas import DataFrame
- from seaborn._core.typing import DataSource, VariableSpec
+import pandas as pd
+from pandas import DataFrame
+from seaborn._core.typing import DataSource, VariableSpec, ColumnName
-# TODO Repetition in the docstrings should be reduced with interpolation tools
class PlotData:
"""
@@ -154,7 +152,7 @@ def _assign_variables(
non-indexed vector datatypes that have a different length from `data`.
"""
- source_data: dict | DataFrame
+ source_data: Mapping | DataFrame
frame: DataFrame
names: dict[str, str | None]
ids: dict[str, str | int]
@@ -164,7 +162,7 @@ def _assign_variables(
ids = {}
given_data = data is not None
- if given_data:
+ if data is not None:
source_data = data
else:
# Data is optional; all variables can be defined as vectors
@@ -208,7 +206,7 @@ def _assign_variables(
)
if val_as_data_key:
-
+ val = cast(ColumnName, val)
if val in source_data:
plot_data[key] = source_data[val]
elif val in index:
@@ -231,12 +229,12 @@ def _assign_variables(
# Otherwise, assume the value somehow represents data
# Ignore empty data structures
- if isinstance(val, abc.Sized) and len(val) == 0:
+ if isinstance(val, Sized) and len(val) == 0:
continue
# If vector has no index, it must match length of data table
if isinstance(data, pd.DataFrame) and not isinstance(val, pd.Series):
- if isinstance(val, abc.Sized) and len(data) != len(val):
+ if isinstance(val, Sized) and len(data) != len(val):
val_cls = val.__class__.__name__
err = (
f"Length of {val_cls} vectors must match length of `data`"
diff --git a/seaborn/_core/groupby.py b/seaborn/_core/groupby.py
index 3809a530f5..cc41566cde 100644
--- a/seaborn/_core/groupby.py
+++ b/seaborn/_core/groupby.py
@@ -1,6 +1,8 @@
"""Simplified split-apply-combine paradigm on dataframes for internal use."""
from __future__ import annotations
+from typing import cast, Iterable
+
import pandas as pd
from seaborn._core.rules import categorical_order
@@ -44,7 +46,9 @@ def __init__(self, order: list[str] | dict[str, list | None]):
order = {k: None for k in order}
self.order = order
- def _get_groups(self, data: DataFrame) -> MultiIndex:
+ def _get_groups(
+ self, data: DataFrame
+ ) -> tuple[str | list[str], Index | MultiIndex]:
"""Return index with Cartesian product of ordered grouping variable levels."""
levels = {}
for var, order in self.order.items():
@@ -54,10 +58,10 @@ def _get_groups(self, data: DataFrame) -> MultiIndex:
levels[var] = order
grouper: str | list[str]
- groups: Index | MultiIndex | None
+ groups: Index | MultiIndex
if not levels:
grouper = []
- groups = None
+ groups = pd.Index([])
elif len(levels) > 1:
grouper = list(levels)
groups = pd.MultiIndex.from_product(levels.values(), names=grouper)
@@ -115,7 +119,8 @@ def apply(
for key in groups:
if key in parts:
if isinstance(grouper, list):
- group_ids = dict(zip(grouper, key))
+ # Implies that we had a MultiIndex so key is iterable
+ group_ids = dict(zip(grouper, cast(Iterable, key)))
else:
group_ids = {grouper: key}
stack.append(parts[key].assign(**group_ids))
diff --git a/seaborn/_core/moves.py b/seaborn/_core/moves.py
index 2c814be87d..1efba41e63 100644
--- a/seaborn/_core/moves.py
+++ b/seaborn/_core/moves.py
@@ -1,12 +1,15 @@
from __future__ import annotations
from dataclasses import dataclass
-from typing import ClassVar, Callable, Optional, Union
+from typing import ClassVar, Callable, Optional, Union, cast
import numpy as np
from pandas import DataFrame
from seaborn._core.groupby import GroupBy
from seaborn._core.scales import Scale
+from seaborn._core.typing import Default
+
+default = Default()
@dataclass
@@ -24,26 +27,34 @@ def __call__(
@dataclass
class Jitter(Move):
"""
- Random displacement of marks along either or both axes to reduce overplotting.
+ Random displacement along one or both axes to reduce overplotting.
+
+ Parameters
+ ----------
+ width : float
+ Magnitude of jitter, relative to mark width, along the orientation axis.
+ If not provided, the default value will be 0 when `x` or `y` are set, otherwise
+ there will be a small amount of jitter applied by default.
+ x : float
+ Magnitude of jitter, in data units, along the x axis.
+ y : float
+ Magnitude of jitter, in data units, along the y axis.
+
+ Examples
+ --------
+ .. include:: ../docstrings/objects.Jitter.rst
+
"""
- width: float = 0
+ width: float | Default = default
x: float = 0
y: float = 0
-
- seed: Optional[int] = None
-
- # TODO what is the best way to have a reasonable default?
- # The problem is that "reasonable" seems dependent on the mark
+ seed: int | None = None
def __call__(
self, data: DataFrame, groupby: GroupBy, orient: str, scales: dict[str, Scale],
) -> DataFrame:
- # TODO is it a problem that GroupBy is not used for anything here?
- # Should we type it as optional?
-
data = data.copy()
-
rng = np.random.default_rng(self.seed)
def jitter(data, col, scale):
@@ -51,8 +62,13 @@ def jitter(data, col, scale):
offsets = noise * scale
return data[col] + offsets
+ if self.width is default:
+ width = 0.0 if self.x or self.y else 0.2
+ else:
+ width = cast(float, self.width)
+
if self.width:
- data[orient] = jitter(data, orient, self.width * data["width"])
+ data[orient] = jitter(data, orient, width * data["width"])
if self.x:
data["x"] = jitter(data, "x", self.x)
if self.y:
diff --git a/seaborn/_core/plot.py b/seaborn/_core/plot.py
index 2ac220bc7b..4f0290a494 100644
--- a/seaborn/_core/plot.py
+++ b/seaborn/_core/plot.py
@@ -15,7 +15,7 @@
from cycler import cycler
import pandas as pd
-from pandas import DataFrame, Series
+from pandas import DataFrame, Series, Index
import matplotlib as mpl
from matplotlib.axes import Axes
from matplotlib.artist import Artist
@@ -29,7 +29,13 @@
from seaborn._core.subplots import Subplots
from seaborn._core.groupby import GroupBy
from seaborn._core.properties import PROPERTIES, Property
-from seaborn._core.typing import DataSource, VariableSpec, VariableSpecList, OrderSpec
+from seaborn._core.typing import (
+ DataSource,
+ VariableSpec,
+ VariableSpecList,
+ OrderSpec,
+ Default,
+)
from seaborn._core.rules import categorical_order
from seaborn._compat import set_scale_obj, set_layout_engine
from seaborn.rcmod import axes_style, plotting_context
@@ -47,6 +53,9 @@
from typing_extensions import TypedDict
+default = Default()
+
+
# ---- Definitions for internal specs --------------------------------- #
@@ -79,13 +88,6 @@ class PairSpec(TypedDict, total=False):
# --- Local helpers ----------------------------------------------------------------
-class Default:
- def __repr__(self):
- return ""
-
-
-default = Default()
-
@contextmanager
def theme_context(params: dict[str, Any]) -> Generator:
@@ -258,7 +260,8 @@ def _resolve_positionals(
if name in variables:
raise TypeError(f"`{name}` given by both name and position.")
# Keep coordinates at the front of the variables dict
- variables = {name: var, **variables}
+ # Cast type because we know this isn't a DataSource at this point
+ variables = {name: cast(VariableSpec, var), **variables}
return data, variables
@@ -331,8 +334,11 @@ def _variables(self) -> list[str]:
+ list(self._facet_spec.get("variables", []))
)
for layer in self._layers:
- variables.extend(c for c in layer["vars"] if c not in variables)
- return variables
+ variables.extend(v for v in layer["vars"] if v not in variables)
+
+ # Coerce to str in return to appease mypy; we know these will only
+ # ever be strings but I don't think we can type a DataFrame that way yet
+ return [str(v) for v in variables]
def on(self, target: Axes | SubFigure | Figure) -> Plot:
"""
@@ -558,7 +564,7 @@ def facet(
.. include:: ../docstrings/objects.Plot.facet.rst
"""
- variables = {}
+ variables: dict[str, VariableSpec] = {}
if col is not None:
variables["col"] = col
if row is not None:
@@ -1106,7 +1112,7 @@ def _compute_stats(self, spec: Plot, layers: list[Layer]) -> None:
for axis, var in zip(*pairings):
if axis != var:
df = df.rename(columns={var: axis})
- drop_cols = [x for x in df if re.match(rf"{axis}\d+", x)]
+ drop_cols = [x for x in df if re.match(rf"{axis}\d+", str(x))]
df = df.drop(drop_cols, axis=1)
scales[axis] = scales[var]
@@ -1176,7 +1182,7 @@ def _setup_scales(
for layer in layers:
variables.extend(layer["data"].frame.columns)
for df in layer["data"].frames.values():
- variables.extend(v for v in df if v not in variables)
+ variables.extend(str(v) for v in df if v not in variables)
variables = [v for v in variables if v not in self._scales]
for var in variables:
@@ -1307,7 +1313,7 @@ def get_order(var):
if "width" in mark._mappable_props:
width = mark._resolve(df, "width", None)
else:
- width = df.get("width", 0.8) # TODO what default
+ width = 0.8 if "width" not in df else df["width"] # TODO what default?
if orient in df:
df["width"] = width * scales[orient]._spacing(df[orient])
@@ -1321,7 +1327,7 @@ def get_order(var):
# TODO unlike width, we might not want to add baseline to data
# if the mark doesn't use it. Practically, there is a concern about
# Mark abstraction like Area / Ribbon
- baseline = df.get("baseline", 0)
+ baseline = 0 if "baseline" not in df else df["baseline"]
df["baseline"] = baseline
if move is not None:
@@ -1351,33 +1357,11 @@ def get_order(var):
if layer["legend"]:
self._update_legend_contents(p, mark, data, scales)
- def _scale_coords(self, subplots: list[dict], df: DataFrame) -> DataFrame:
- # TODO stricter type on subplots
-
- coord_cols = [c for c in df if re.match(r"^[xy]\D*$", c)]
- out_df = (
- df
- .copy(deep=False)
- .drop(coord_cols, axis=1)
- .reindex(df.columns, axis=1) # So unscaled columns retain their place
- )
-
- for view in subplots:
- view_df = self._filter_subplot_data(df, view)
- axes_df = view_df[coord_cols]
- with pd.option_context("mode.use_inf_as_null", True):
- axes_df = axes_df.dropna()
- for var, values in axes_df.items():
- scale = view[f"{var[0]}scale"]
- out_df.loc[values.index, var] = scale(values)
-
- return out_df
-
def _unscale_coords(
self, subplots: list[dict], df: DataFrame, orient: str,
) -> DataFrame:
# TODO do we still have numbers in the variable name at this point?
- coord_cols = [c for c in df if re.match(r"^[xy]\D*$", c)]
+ coord_cols = [c for c in df if re.match(r"^[xy]\D*$", str(c))]
drop_cols = [*coord_cols, "width"] if "width" in df else coord_cols
out_df = (
df
@@ -1391,11 +1375,11 @@ def _unscale_coords(
axes_df = view_df[coord_cols]
for var, values in axes_df.items():
- axis = getattr(view["ax"], f"{var[0]}axis")
+ axis = getattr(view["ax"], f"{str(var)[0]}axis")
# TODO see https://github.com/matplotlib/matplotlib/issues/22713
transform = axis.get_transform().inverted().transform
inverted = transform(values)
- out_df.loc[values.index, var] = inverted
+ out_df.loc[values.index, str(var)] = inverted
if var == orient and "width" in view_df:
width = view_df["width"]
@@ -1442,12 +1426,12 @@ def _generate_pairings(
for axis, var in zip("xy", (x, y)):
if axis != var:
out_df = out_df.rename(columns={var: axis})
- cols = [col for col in out_df if re.match(rf"{axis}\d+", col)]
+ cols = [col for col in out_df if re.match(rf"{axis}\d+", str(col))]
out_df = out_df.drop(cols, axis=1)
yield subplots, out_df, scales
- def _get_subplot_index(self, df: DataFrame, subplot: dict) -> DataFrame:
+ def _get_subplot_index(self, df: DataFrame, subplot: dict) -> Index:
dims = df.columns.intersection(["col", "row"])
if dims.empty:
@@ -1553,11 +1537,12 @@ def _update_legend_contents(
) -> None:
"""Add legend artists / labels for one layer in the plot."""
if data.frame.empty and data.frames:
- legend_vars = set()
+ legend_vars: list[str] = []
for frame in data.frames.values():
- legend_vars.update(frame.columns.intersection(scales))
+ frame_vars = frame.columns.intersection(list(scales))
+ legend_vars.extend(v for v in frame_vars if v not in legend_vars)
else:
- legend_vars = data.frame.columns.intersection(scales)
+ legend_vars = list(data.frame.columns.intersection(list(scales)))
# First pass: Identify the values that will be shown for each variable
schema: list[tuple[
@@ -1579,12 +1564,15 @@ def _update_legend_contents(
schema.append(entry)
# Second pass, generate an artist corresponding to each value
- contents = []
+ contents: list[tuple[tuple[str, str | int], Any, list[str]]] = []
for key, variables, (values, labels) in schema:
artists = []
for val in values:
- artists.append(mark._legend_artist(variables, val, scales))
- contents.append((key, artists, labels))
+ artist = mark._legend_artist(variables, val, scales)
+ if artist is not None:
+ artists.append(artist)
+ if artists:
+ contents.append((key, artists, labels))
self._legend_contents.extend(contents)
@@ -1596,7 +1584,7 @@ def _make_legend(self, p: Plot) -> None:
merged_contents: dict[
tuple[str, str | int], tuple[list[Artist], list[str]],
] = {}
- for key, artists, labels in self._legend_contents:
+ for key, new_artists, labels in self._legend_contents:
# Key is (name, id); we need the id to resolve variable uniqueness,
# but will need the name in the next step to title the legend
if key in merged_contents:
@@ -1605,11 +1593,11 @@ def _make_legend(self, p: Plot) -> None:
for i, artist in enumerate(existing_artists):
# Matplotlib accepts a tuple of artists and will overlay them
if isinstance(artist, tuple):
- artist += artist[i],
+ artist += new_artists[i],
else:
- existing_artists[i] = artist, artists[i]
+ existing_artists[i] = artist, new_artists[i]
else:
- merged_contents[key] = artists.copy(), labels
+ merged_contents[key] = new_artists.copy(), labels
# TODO explain
loc = "center right" if self._pyplot else "center left"
diff --git a/seaborn/_core/properties.py b/seaborn/_core/properties.py
index 173fd45efb..cd10e260ef 100644
--- a/seaborn/_core/properties.py
+++ b/seaborn/_core/properties.py
@@ -298,6 +298,23 @@ class Alpha(IntervalProperty):
# TODO validate / enforce that output is in [0, 1]
+class Offset(IntervalProperty):
+ """Offset for edge-aligned text, in point units."""
+ _default_range = 0, 5
+ _legend = False
+
+
+class FontSize(IntervalProperty):
+ """Font size for textual marks, in points."""
+ _legend = False
+
+ @property
+ def default_range(self) -> tuple[float, float]:
+ """Min and max values used by default for semantic mapping."""
+ base = mpl.rcParams["font.size"]
+ return base * .5, base * 2
+
+
# =================================================================================== #
# Properties defined by arbitrary objects with inherently nominal scaling
# =================================================================================== #
@@ -496,6 +513,24 @@ def _get_dash_pattern(style: str | DashPattern) -> DashPatternWithOffset:
return offset, dashes
+class TextAlignment(ObjectProperty):
+ legend = False
+
+
+class HorizontalAlignment(TextAlignment):
+
+ def _default_values(self, n: int) -> list:
+ vals = itertools.cycle(["left", "right"])
+ return [next(vals) for _ in range(n)]
+
+
+class VerticalAlignment(TextAlignment):
+
+ def _default_values(self, n: int) -> list:
+ vals = itertools.cycle(["top", "bottom"])
+ return [next(vals) for _ in range(n)]
+
+
# =================================================================================== #
# Properties with RGB(A) color values
# =================================================================================== #
@@ -751,6 +786,11 @@ def mapping(x):
"edgestyle": LineStyle,
"edgecolor": Color,
"edgealpha": Alpha,
+ "text": Property,
+ "halign": HorizontalAlignment,
+ "valign": VerticalAlignment,
+ "offset": Offset,
+ "fontsize": FontSize,
"xmin": Coordinate,
"xmax": Coordinate,
"ymin": Coordinate,
diff --git a/seaborn/_core/rules.py b/seaborn/_core/rules.py
index d378fb2dc2..fea910342b 100644
--- a/seaborn/_core/rules.py
+++ b/seaborn/_core/rules.py
@@ -147,7 +147,7 @@ def categorical_order(vector: Series, order: list | None = None) -> list:
order = list(vector.cat.categories)
else:
order = list(filter(pd.notnull, vector.unique()))
- if variable_type(order) == "numeric":
+ if variable_type(pd.Series(order)) == "numeric":
order.sort()
return order
diff --git a/seaborn/_core/scales.py b/seaborn/_core/scales.py
index 368d17a143..bbd71ec1b5 100644
--- a/seaborn/_core/scales.py
+++ b/seaborn/_core/scales.py
@@ -4,7 +4,7 @@
from collections.abc import Sequence
from dataclasses import dataclass
from functools import partial
-from typing import Any, Callable, Tuple, Optional, Union, ClassVar
+from typing import Any, Callable, Tuple, Optional, ClassVar
import numpy as np
import matplotlib as mpl
@@ -39,13 +39,15 @@
from typing import TYPE_CHECKING
if TYPE_CHECKING:
from seaborn._core.properties import Property
- from numpy.typing import ArrayLike
+ from numpy.typing import ArrayLike, NDArray
- Transforms = Tuple[
+ TransFuncs = Tuple[
Callable[[ArrayLike], ArrayLike], Callable[[ArrayLike], ArrayLike]
]
- Pipeline = Sequence[Optional[Callable[[Union[Series, ArrayLike]], ArrayLike]]]
+ # TODO Reverting typing to Any as it was proving too complicated to
+ # work out the right way to communicate the types to mypy. Revisit!
+ Pipeline = Sequence[Optional[Callable[[Any], Any]]]
class Scale:
@@ -101,20 +103,24 @@ def _setup(
def __call__(self, data: Series) -> ArrayLike:
+ trans_data: Series | NDArray | list
+
# TODO sometimes we need to handle scalars (e.g. for Line)
# but what is the best way to do that?
scalar_data = np.isscalar(data)
if scalar_data:
- data = np.array([data])
+ trans_data = np.array([data])
+ else:
+ trans_data = data
for func in self._pipeline:
if func is not None:
- data = func(data)
+ trans_data = func(trans_data)
if scalar_data:
- data = data[0]
-
- return data
+ return trans_data[0]
+ else:
+ return trans_data
@staticmethod
def _identity():
@@ -319,7 +325,7 @@ def _setup(
forward, inverse = new._get_transform()
- mpl_scale = new._get_scale(data.name, forward, inverse)
+ mpl_scale = new._get_scale(str(data.name), forward, inverse)
if axis is None:
axis = PseudoAxis(mpl_scale)
@@ -411,7 +417,7 @@ class Continuous(ContinuousBase):
A numeric scale supporting norms and functional transforms.
"""
values: tuple | str | None = None
- trans: str | Transforms | None = None
+ trans: str | TransFuncs | None = None
# TODO Add this to deal with outliers?
# outside: Literal["keep", "drop", "clip"] = "keep"
@@ -530,7 +536,7 @@ def label(
return new
def _parse_for_log_params(
- self, trans: str | Transforms | None
+ self, trans: str | TransFuncs | None
) -> tuple[float | None, float | None]:
log_base = symlog_thresh = None
@@ -877,7 +883,7 @@ def get_majorticklocs(self):
# Transform function creation
-def _make_identity_transforms() -> Transforms:
+def _make_identity_transforms() -> TransFuncs:
def identity(x):
return x
@@ -885,7 +891,7 @@ def identity(x):
return identity, identity
-def _make_logit_transforms(base: float = None) -> Transforms:
+def _make_logit_transforms(base: float = None) -> TransFuncs:
log, exp = _make_log_transforms(base)
@@ -900,8 +906,9 @@ def expit(x):
return logit, expit
-def _make_log_transforms(base: float | None = None) -> Transforms:
+def _make_log_transforms(base: float | None = None) -> TransFuncs:
+ fs: TransFuncs
if base is None:
fs = np.log, np.exp
elif base == 2:
@@ -913,18 +920,18 @@ def forward(x):
return np.log(x) / np.log(base)
fs = forward, partial(np.power, base)
- def log(x):
+ def log(x: ArrayLike) -> ArrayLike:
with np.errstate(invalid="ignore", divide="ignore"):
return fs[0](x)
- def exp(x):
+ def exp(x: ArrayLike) -> ArrayLike:
with np.errstate(invalid="ignore", divide="ignore"):
return fs[1](x)
return log, exp
-def _make_symlog_transforms(c: float = 1, base: float = 10) -> Transforms:
+def _make_symlog_transforms(c: float = 1, base: float = 10) -> TransFuncs:
# From https://iopscience.iop.org/article/10.1088/0957-0233/24/2/027001
@@ -944,7 +951,7 @@ def symexp(x):
return symlog, symexp
-def _make_sqrt_transforms() -> Transforms:
+def _make_sqrt_transforms() -> TransFuncs:
def sqrt(x):
return np.sign(x) * np.sqrt(np.abs(x))
@@ -955,7 +962,7 @@ def square(x):
return sqrt, square
-def _make_power_transforms(exp: float) -> Transforms:
+def _make_power_transforms(exp: float) -> TransFuncs:
def forward(x):
return np.sign(x) * np.power(np.abs(x), exp)
diff --git a/seaborn/_core/typing.py b/seaborn/_core/typing.py
index 15ac4ec07d..5295b995e4 100644
--- a/seaborn/_core/typing.py
+++ b/seaborn/_core/typing.py
@@ -1,16 +1,22 @@
from __future__ import annotations
+from datetime import date, datetime, timedelta
from typing import Any, Optional, Union, Mapping, Tuple, List, Dict
from collections.abc import Hashable, Iterable
+
from numpy import ndarray # TODO use ArrayLike?
-from pandas import DataFrame, Series, Index
+from pandas import DataFrame, Series, Index, Timestamp, Timedelta
from matplotlib.colors import Colormap, Normalize
+
+ColumnName = Union[
+ str, bytes, date, datetime, timedelta, bool, complex, Timestamp, Timedelta
+]
Vector = Union[Series, Index, ndarray]
-PaletteSpec = Union[str, list, dict, Colormap, None]
-VariableSpec = Union[Hashable, Vector, None]
+
+VariableSpec = Union[ColumnName, Vector, None]
VariableSpecList = Union[List[VariableSpec], Index, None]
-# TODO can we better unify the VarType object and the VariableType alias?
+
DataSource = Union[DataFrame, Mapping[Hashable, Vector], None]
OrderSpec = Union[Iterable, None] # TODO technically str is iterable
@@ -18,7 +24,16 @@
# TODO for discrete mappings, it would be ideal to use a parameterized type
# as the dict values / list entries should be of specific type(s) for each method
+PaletteSpec = Union[str, list, dict, Colormap, None]
DiscreteValueSpec = Union[dict, list, None]
ContinuousValueSpec = Union[
Tuple[float, float], List[float], Dict[Any, float], None,
]
+
+
+class Default:
+ def __repr__(self):
+ return ""
+
+
+default = Default()
diff --git a/seaborn/_marks/area.py b/seaborn/_marks/area.py
index 243a1572f5..7514a6d13b 100644
--- a/seaborn/_marks/area.py
+++ b/seaborn/_marks/area.py
@@ -59,7 +59,7 @@ def _postprocess_artist(self, artist, ax, orient):
def _get_verts(self, data, orient):
dv = {"x": "y", "y": "x"}[orient]
- data = data.sort_values(orient)
+ data = data.sort_values(orient, kind="mergesort")
verts = np.concatenate([
data[[orient, f"{dv}min"]].to_numpy(),
data[[orient, f"{dv}max"]].to_numpy()[::-1],
@@ -162,4 +162,9 @@ class Band(AreaBase, Mark):
def _standardize_coordinate_parameters(self, data, orient):
# dv = {"x": "y", "y": "x"}[orient]
# TODO assert that all(ymax >= ymin)?
+ # TODO what if only one exist?
+ other = {"x": "y", "y": "x"}[orient]
+ if not set(data.columns) & {f"{other}min", f"{other}max"}:
+ agg = {f"{other}min": (other, "min"), f"{other}max": (other, "max")}
+ data = data.groupby(orient).agg(**agg).reset_index()
return data
diff --git a/seaborn/_marks/base.py b/seaborn/_marks/base.py
index 1fc2b9f2ef..87e0216d9d 100644
--- a/seaborn/_marks/base.py
+++ b/seaborn/_marks/base.py
@@ -217,8 +217,8 @@ def _plot(
def _legend_artist(
self, variables: list[str], value: Any, scales: dict[str, Scale],
) -> Artist:
- # TODO return some sensible default?
- raise NotImplementedError
+
+ return None
def resolve_properties(
diff --git a/seaborn/_marks/line.py b/seaborn/_marks/line.py
index ba69acfe99..d1ae76e816 100644
--- a/seaborn/_marks/line.py
+++ b/seaborn/_marks/line.py
@@ -60,7 +60,7 @@ def _plot(self, split_gen, scales, orient):
vals["marker"] = vals["marker"]._marker
if self._sort:
- data = data.sort_values(orient)
+ data = data.sort_values(orient, kind="mergesort")
artist_kws = self.artist_kws.copy()
self._handle_capstyle(artist_kws, vals)
@@ -184,7 +184,7 @@ def _setup_lines(self, split_gen, scales, orient):
vals["color"] = resolve_color(self, keys, scales=scales)
if self._sort:
- data = data.sort_values(orient)
+ data = data.sort_values(orient, kind="mergesort")
# Column stack to avoid block consolidation
xy = np.column_stack([data["x"], data["y"]])
@@ -204,8 +204,9 @@ def _plot(self, split_gen, scales, orient):
# Handle datalim update manually
# https://github.com/matplotlib/matplotlib/issues/23129
ax.add_collection(lines, autolim=False)
- xy = np.concatenate(ax_data["segments"])
- ax.update_datalim(xy)
+ if ax_data["segments"]:
+ xy = np.concatenate(ax_data["segments"])
+ ax.update_datalim(xy)
def _legend_artist(self, variables, value, scales):
@@ -270,9 +271,16 @@ def _setup_lines(self, split_gen, scales, orient):
"linestyles": [],
}
+ # TODO better checks on what variables we have
+
vals = resolve_properties(self, keys, scales)
vals["color"] = resolve_color(self, keys, scales=scales)
+ # TODO what if only one exist?
+ if not set(data.columns) & {f"{other}min", f"{other}max"}:
+ agg = {f"{other}min": (other, "min"), f"{other}max": (other, "max")}
+ data = data.groupby(orient).agg(**agg).reset_index()
+
cols = [orient, f"{other}min", f"{other}max"]
data = data[cols].melt(orient, value_name=other)[["x", "y"]]
segments = [d.to_numpy() for _, d in data.groupby(orient)]
diff --git a/seaborn/_marks/text.py b/seaborn/_marks/text.py
new file mode 100644
index 0000000000..58d757c1ac
--- /dev/null
+++ b/seaborn/_marks/text.py
@@ -0,0 +1,76 @@
+from __future__ import annotations
+from collections import defaultdict
+from dataclasses import dataclass
+
+import numpy as np
+import matplotlib as mpl
+from matplotlib.transforms import ScaledTranslation
+
+from seaborn._marks.base import (
+ Mark,
+ Mappable,
+ MappableFloat,
+ MappableString,
+ MappableColor,
+ resolve_properties,
+ resolve_color,
+ document_properties,
+)
+
+
+@document_properties
+@dataclass
+class Text(Mark):
+ """
+ A textual mark to annotate or represent data values.
+
+ Examples
+ --------
+ .. include:: ../docstrings/objects.Text.rst
+
+ """
+ text: MappableString = Mappable("")
+ color: MappableColor = Mappable("k")
+ alpha: MappableFloat = Mappable(1)
+ fontsize: MappableFloat = Mappable(rc="font.size")
+ halign: MappableString = Mappable("center")
+ valign: MappableString = Mappable("center_baseline")
+ offset: MappableFloat = Mappable(4)
+
+ def _plot(self, split_gen, scales, orient):
+
+ ax_data = defaultdict(list)
+
+ for keys, data, ax in split_gen():
+
+ vals = resolve_properties(self, keys, scales)
+ color = resolve_color(self, keys, "", scales)
+
+ halign = vals["halign"]
+ valign = vals["valign"]
+ fontsize = vals["fontsize"]
+ offset = vals["offset"] / 72
+
+ offset_trans = ScaledTranslation(
+ {"right": -offset, "left": +offset}.get(halign, 0),
+ {"top": -offset, "bottom": +offset, "baseline": +offset}.get(valign, 0),
+ ax.figure.dpi_scale_trans,
+ )
+
+ for row in data.to_dict("records"):
+ artist = mpl.text.Text(
+ x=row["x"],
+ y=row["y"],
+ text=str(row.get("text", vals["text"])),
+ color=color,
+ fontsize=fontsize,
+ horizontalalignment=halign,
+ verticalalignment=valign,
+ transform=ax.transData + offset_trans,
+ **self.artist_kws,
+ )
+ ax.add_artist(artist)
+ ax_data[ax].append([row["x"], row["y"]])
+
+ for ax, ax_vals in ax_data.items():
+ ax.update_datalim(np.array(ax_vals))
diff --git a/seaborn/_statistics.py b/seaborn/_statistics.py
index a19fdea7f1..7fe4fbe7b2 100644
--- a/seaborn/_statistics.py
+++ b/seaborn/_statistics.py
@@ -194,6 +194,8 @@ def __call__(self, x1, x2=None, weights=None):
return self._eval_bivariate(x1, x2, weights)
+# Note: we no longer use this for univariate histograms in histplot,
+# preferring _stats.Hist. We'll deprecate this once we have a bivariate Stat class.
class Histogram:
"""Univariate and bivariate histogram estimator."""
def __init__(
diff --git a/seaborn/_stats/aggregation.py b/seaborn/_stats/aggregation.py
index 73da54bdad..0dffba6455 100644
--- a/seaborn/_stats/aggregation.py
+++ b/seaborn/_stats/aggregation.py
@@ -9,7 +9,6 @@
from seaborn._core.groupby import GroupBy
from seaborn._stats.base import Stat
from seaborn._statistics import EstimateAggregator
-
from seaborn._core.typing import Vector
@@ -83,7 +82,7 @@ def __call__(
boot_kws = {"n_boot": self.n_boot, "seed": self.seed}
engine = EstimateAggregator(self.func, self.errorbar, **boot_kws)
- var = {"x": "y", "y": "x"}.get(orient)
+ var = {"x": "y", "y": "x"}[orient]
res = (
groupby
.apply(data, self._process, var, engine)
diff --git a/seaborn/_stats/base.py b/seaborn/_stats/base.py
index c538812493..6584a8e61a 100644
--- a/seaborn/_stats/base.py
+++ b/seaborn/_stats/base.py
@@ -1,7 +1,8 @@
"""Base module for statistical transformations."""
from __future__ import annotations
+from collections.abc import Iterable
from dataclasses import dataclass
-from typing import ClassVar
+from typing import ClassVar, Any
from typing import TYPE_CHECKING
if TYPE_CHECKING:
@@ -28,6 +29,18 @@ class Stat:
# value on the orient axis, but we would not in the latter case.
group_by_orient: ClassVar[bool] = False
+ def _check_param_one_of(self, param: Any, options: Iterable[Any]) -> None:
+ """Raise when parameter value is not one of a specified set."""
+ value = getattr(self, param)
+ if value not in options:
+ *most, last = options
+ option_str = ", ".join(f"{x!r}" for x in most[:-1]) + f" or {last!r}"
+ err = " ".join([
+ f"The `{param}` parameter for `{self.__class__.__name__}` must be",
+ f"one of {option_str}; not {value!r}.",
+ ])
+ raise ValueError(err)
+
def __call__(
self,
data: DataFrame,
diff --git a/seaborn/_stats/histogram.py b/seaborn/_stats/histogram.py
index 85abed1036..59b7b12f2e 100644
--- a/seaborn/_stats/histogram.py
+++ b/seaborn/_stats/histogram.py
@@ -1,6 +1,6 @@
from __future__ import annotations
from dataclasses import dataclass
-from functools import partial
+from warnings import warn
import numpy as np
import pandas as pd
@@ -17,22 +17,76 @@
class Hist(Stat):
"""
Bin observations, count them, and optionally normalize or cumulate.
- """
- stat: str = "count" # TODO how to do validation on this arg?
+ Parameters
+ ----------
+ stat : str
+ Aggregate statistic to compute in each bin:
+
+ - `count`: the number of observations
+ - `density`: normalize so that the total area of the histogram equals 1
+ - `percent`: normalize so that bar heights sum to 100
+ - `probability` or `proportion`: normalize so that bar heights sum to 1
+ - `frequency`: divide the number of observations by the bin width
+
+ bins : str, int, or ArrayLike
+ Generic parameter that can be the name of a reference rule, the number
+ of bins, or the bin breaks. Passed to :func:`numpy.histogram_bin_edges`.
+ binwidth : float
+ Width of each bin; overrides `bins` but can be used with `binrange`.
+ binrange : (min, max)
+ Lowest and highest value for bin edges; can be used with either
+ `bins` (when a number) or `binwidth`. Defaults to data extremes.
+ common_norm : bool or list of variables
+ When not `False`, the normalization is applied across groups. Use
+ `True` to normalize across all groups, or pass variable name(s) that
+ define normalization groups.
+ common_bins : bool or list of variables
+ When not `False`, the same bins are used for all groups. Use `True` to
+ share bins across all groups, or pass variable name(s) to share within.
+ cumulative : bool
+ If True, cumulate the bin values.
+ discrete : bool
+ If True, set `binwidth` and `binrange` so that bins have unit width and
+ are centered on integer values
+
+ Notes
+ -----
+
+ The choice of bins for computing and plotting a histogram can exert
+ substantial influence on the insights that one is able to draw from the
+ visualization. If the bins are too large, they may erase important features.
+ On the other hand, bins that are too small may be dominated by random
+ variability, obscuring the shape of the true underlying distribution. The
+ default bin size is determined using a reference rule that depends on the
+ sample size and variance. This works well in many cases, (i.e., with
+ "well-behaved" data) but it fails in others. It is always a good to try
+ different bin sizes to be sure that you are not missing something important.
+ This function allows you to specify bins in several different ways, such as
+ by setting the total number of bins to use, the width of each bin, or the
+ specific locations where the bins should break.
+
+
+ Examples
+ --------
+ .. include:: ../docstrings/objects.Hist.rst
+
+ """
+ stat: str = "count"
bins: str | int | ArrayLike = "auto"
binwidth: float | None = None
binrange: tuple[float, float] | None = None
common_norm: bool | list[str] = True
common_bins: bool | list[str] = True
cumulative: bool = False
-
- # TODO Require this to be set here or have interface with scale?
- # Q: would Discrete() scale imply binwidth=1 or bins centered on integers?
discrete: bool = False
- # TODO Note that these methods are mostly copied from _statistics.Histogram,
- # but it only computes univariate histograms. We should reconcile the code.
+ def __post_init__(self):
+
+ stat_options = [
+ "count", "density", "percent", "probability", "proportion", "frequency"
+ ]
+ self._check_param_one_of("stat", stat_options)
def _define_bin_edges(self, vals, weight, bins, binwidth, binrange, discrete):
"""Inner function that takes bin parameters as arguments."""
@@ -58,14 +112,14 @@ def _define_bin_edges(self, vals, weight, bins, binwidth, binrange, discrete):
def _define_bin_params(self, data, orient, scale_type):
"""Given data, return numpy.histogram parameters to define bins."""
vals = data[orient]
- weight = data.get("weight", None)
+ weights = data.get("weight", None)
# TODO We'll want this for ordinal / discrete scales too
# (Do we need discrete as a parameter or just infer from scale?)
discrete = self.discrete or scale_type == "nominal"
bin_edges = self._define_bin_edges(
- vals, weight, self.bins, self.binwidth, self.binrange, discrete,
+ vals, weights, self.bins, self.binwidth, self.binrange, discrete,
)
if isinstance(self.bins, (str, int)):
@@ -85,24 +139,19 @@ def _get_bins_and_eval(self, data, orient, groupby, scale_type):
def _eval(self, data, orient, bin_kws):
vals = data[orient]
- weight = data.get("weight", None)
+ weights = data.get("weight", None)
density = self.stat == "density"
- hist, bin_edges = np.histogram(
- vals, **bin_kws, weights=weight, density=density,
- )
-
- width = np.diff(bin_edges)
- pos = bin_edges[:-1] + width / 2
- other = {"x": "y", "y": "x"}[orient]
+ hist, edges = np.histogram(vals, **bin_kws, weights=weights, density=density)
- return pd.DataFrame({orient: pos, other: hist, "space": width})
+ width = np.diff(edges)
+ center = edges[:-1] + width / 2
- def _normalize(self, data, orient):
+ return pd.DataFrame({orient: center, "count": hist, "space": width})
- other = "y" if orient == "x" else "x"
- hist = data[other]
+ def _normalize(self, data):
+ hist = data["count"]
if self.stat == "probability" or self.stat == "proportion":
hist = hist.astype(float) / hist.sum()
elif self.stat == "percent":
@@ -116,13 +165,10 @@ def _normalize(self, data, orient):
else:
hist = hist.cumsum()
- return data.assign(**{other: hist})
+ return data.assign(**{self.stat: hist})
def __call__(self, data, groupby, orient, scales):
- # TODO better to do this as an isinstance check?
- # We are only asking about Nominal scales now,
- # but presumably would apply to Ordinal too?
scale_type = scales[orient].__class__.__name__.lower()
grouping_vars = [v for v in data if v in groupby.order]
if not grouping_vars or self.common_bins is True:
@@ -133,23 +179,30 @@ def __call__(self, data, groupby, orient, scales):
bin_groupby = GroupBy(grouping_vars)
else:
bin_groupby = GroupBy(self.common_bins)
+ undefined = set(self.common_bins) - set(grouping_vars)
+ if undefined:
+ param = f"{self.__class__.__name__}.common_bins"
+ names = ", ".join(f"{x!r}" for x in undefined)
+ msg = f"Undefined variables(s) passed to `{param}`: {names}."
+ warn(msg)
data = bin_groupby.apply(
data, self._get_bins_and_eval, orient, groupby, scale_type,
)
- # TODO Make this an option?
- # (This needs to be tested if enabled, and maybe should be in _eval)
- # other = {"x": "y", "y": "x"}[orient]
- # data = data[data[other] > 0]
-
if not grouping_vars or self.common_norm is True:
- data = self._normalize(data, orient)
+ data = self._normalize(data)
else:
if self.common_norm is False:
norm_grouper = grouping_vars
else:
norm_grouper = self.common_norm
- normalize = partial(self._normalize, orient=orient)
- data = GroupBy(norm_grouper).apply(data, normalize)
+ undefined = set(self.common_norm) - set(grouping_vars)
+ if undefined:
+ param = f"{self.__class__.__name__}.common_norm"
+ names = ", ".join(f"{x!r}" for x in undefined)
+ msg = f"Undefined variables(s) passed to `{param}`: {names}."
+ warn(msg)
+ data = GroupBy(norm_grouper).apply(data, self._normalize)
- return data
+ other = {"x": "y", "y": "x"}[orient]
+ return data.assign(**{other: data[self.stat]})
diff --git a/seaborn/_stats/order.py b/seaborn/_stats/order.py
new file mode 100644
index 0000000000..a0780c1976
--- /dev/null
+++ b/seaborn/_stats/order.py
@@ -0,0 +1,78 @@
+
+from __future__ import annotations
+from dataclasses import dataclass
+from typing import ClassVar, cast
+try:
+ from typing import Literal
+except ImportError:
+ from typing_extensions import Literal # type: ignore
+
+import numpy as np
+from pandas import DataFrame
+
+from seaborn._core.scales import Scale
+from seaborn._core.groupby import GroupBy
+from seaborn._stats.base import Stat
+from seaborn.external.version import Version
+
+
+# From https://github.com/numpy/numpy/blob/main/numpy/lib/function_base.pyi
+_MethodKind = Literal[
+ "inverted_cdf",
+ "averaged_inverted_cdf",
+ "closest_observation",
+ "interpolated_inverted_cdf",
+ "hazen",
+ "weibull",
+ "linear",
+ "median_unbiased",
+ "normal_unbiased",
+ "lower",
+ "higher",
+ "midpoint",
+ "nearest",
+]
+
+
+@dataclass
+class Perc(Stat):
+ """
+ Replace observations with percentile values.
+
+ Parameters
+ ----------
+ k : list of numbers or int
+ If a list of numbers, this gives the percentiles (in [0, 100]) to compute.
+ If an integer, compute `k` evenly-spaced percentiles between 0 and 100.
+ For example, `k=5` computes the 0, 25, 50, 75, and 100th percentiles.
+ method : str
+ Method for interpolating percentiles between observed datapoints.
+ See :func:`numpy.percentile` for valid options and more information.
+
+ Examples
+ --------
+ .. include:: ../docstrings/objects.Perc.rst
+
+ """
+ k: int | list[float] = 5
+ method: str = "linear"
+
+ group_by_orient: ClassVar[bool] = True
+
+ def _percentile(self, data: DataFrame, var: str) -> DataFrame:
+
+ k = list(np.linspace(0, 100, self.k)) if isinstance(self.k, int) else self.k
+ method = cast(_MethodKind, self.method)
+ values = data[var].dropna()
+ if Version(np.__version__) < Version("1.22.0"):
+ res = np.percentile(values, k, interpolation=method) # type: ignore
+ else:
+ res = np.percentile(data[var].dropna(), k, method=method)
+ return DataFrame({var: res, "percentile": k})
+
+ def __call__(
+ self, data: DataFrame, groupby: GroupBy, orient: str, scales: dict[str, Scale],
+ ) -> DataFrame:
+
+ var = {"x": "y", "y": "x"}[orient]
+ return groupby.apply(data, self._percentile, var)
diff --git a/seaborn/axisgrid.py b/seaborn/axisgrid.py
index 5a80694225..7a56310bde 100644
--- a/seaborn/axisgrid.py
+++ b/seaborn/axisgrid.py
@@ -742,7 +742,7 @@ def map(self, func, *args, **kwargs):
plot_data = data_ijk[list(args)]
if self._dropna:
plot_data = plot_data.dropna()
- plot_args = [v for k, v in plot_data.iteritems()]
+ plot_args = [v for k, v in plot_data.items()]
# Some matplotlib functions don't handle pandas objects correctly
if func_module.startswith("matplotlib"):
diff --git a/seaborn/categorical.py b/seaborn/categorical.py
index 87d5346b4c..09b1bd59b7 100644
--- a/seaborn/categorical.py
+++ b/seaborn/categorical.py
@@ -289,7 +289,7 @@ def plot_strips(
jitter_move = jitterer(size=len(sub_data)) if len(sub_data) > 1 else 0
adjusted_data = sub_data[self.cat_axis] + dodge_move + jitter_move
- sub_data.loc[:, self.cat_axis] = adjusted_data
+ sub_data[self.cat_axis] = adjusted_data
for var in "xy":
if self._log_scaled(var):
@@ -346,7 +346,7 @@ def plot_swarms(
dodge_move = offsets[sub_data["hue"].map(self._hue_map.levels.index)]
if not sub_data.empty:
- sub_data.loc[:, self.cat_axis] = sub_data[self.cat_axis] + dodge_move
+ sub_data[self.cat_axis] = sub_data[self.cat_axis] + dodge_move
for var in "xy":
if self._log_scaled(var):
@@ -466,7 +466,7 @@ def establish_variables(self, x=None, y=None, hue=None, data=None,
group_label = data.columns.name
# Convert to a list of arrays, the common representation
- iter_data = plot_data.iteritems()
+ iter_data = plot_data.items()
plot_data = [np.asarray(s, float) for k, s in iter_data]
# Option 1b:
@@ -2096,10 +2096,10 @@ def plot(self, ax, box_kws, flier_kws, line_kws):
stat_api_params=dedent("""\
estimator : string or callable that maps vector -> scalar, optional
Statistical function to estimate within each categorical bin.
- errorbar : string, (string, number) tuple, or callable
+ errorbar : string, (string, number) tuple, callable or None
Name of errorbar method (either "ci", "pi", "se", or "sd"), or a tuple
with a method name and a level parameter, or a function that maps from a
- vector to a (min, max) interval.
+ vector to a (min, max) interval, or None to hide errorbar.
n_boot : int, optional
Number of bootstrap samples used to compute confidence intervals.
units : name of variable in ``data`` or vector data, optional
diff --git a/seaborn/distributions.py b/seaborn/distributions.py
index a6f6ab5cc3..a4526e0edf 100644
--- a/seaborn/distributions.py
+++ b/seaborn/distributions.py
@@ -16,11 +16,12 @@
from ._oldcore import (
VectorPlotter,
)
-from ._statistics import (
- KDE,
- Histogram,
- ECDF,
-)
+
+# We have moved univariate histogram computation over to the new Hist class,
+# but still use the older Histogram for bivariate computation.
+from ._statistics import ECDF, Histogram, KDE
+from ._stats.histogram import Hist
+
from .axisgrid import (
FacetGrid,
_facet_docs,
@@ -246,30 +247,26 @@ def _resolve_multiple(self, curves, multiple):
# Find column groups that are nested within col/row variables
column_groups = {}
- for i, keyd in enumerate(map(dict, curves.columns.tolist())):
+ for i, keyd in enumerate(map(dict, curves.columns)):
facet_key = keyd.get("col", None), keyd.get("row", None)
column_groups.setdefault(facet_key, [])
column_groups[facet_key].append(i)
baselines = curves.copy()
- for cols in column_groups.values():
+ for col_idxs in column_groups.values():
+ cols = curves.columns[col_idxs]
- norm_constant = curves.iloc[:, cols].sum(axis="columns")
+ norm_constant = curves[cols].sum(axis="columns")
# Take the cumulative sum to stack
- curves.iloc[:, cols] = curves.iloc[:, cols].cumsum(axis="columns")
+ curves[cols] = curves[cols].cumsum(axis="columns")
# Normalize by row sum to fill
if multiple == "fill":
- curves.iloc[:, cols] = (curves
- .iloc[:, cols]
- .div(norm_constant, axis="index"))
+ curves[cols] = curves[cols].div(norm_constant, axis="index")
# Define where each segment starts
- baselines.iloc[:, cols] = (curves
- .iloc[:, cols]
- .shift(1, axis=1)
- .fillna(0))
+ baselines[cols] = curves[cols].shift(1, axis=1).fillna(0)
if multiple == "dodge":
@@ -423,19 +420,20 @@ def plot_univariate_histogram(
if estimate_kws["stat"] == "count":
common_norm = False
+ orient = self.data_variable
+
# Now initialize the Histogram estimator
- estimator = Histogram(**estimate_kws)
+ estimator = Hist(**estimate_kws)
histograms = {}
# Do pre-compute housekeeping related to multiple groups
all_data = self.comp_data.dropna()
all_weights = all_data.get("weights", None)
- if set(self.variables) - {"x", "y"}: # Check if we'll have multiple histograms
+ multiple_histograms = set(self.variables) - {"x", "y"}
+ if multiple_histograms:
if common_bins:
- estimator.define_bin_params(
- all_data[self.data_variable], weights=all_weights
- )
+ bin_kws = estimator._define_bin_params(all_data, orient, None)
else:
common_norm = False
@@ -464,17 +462,26 @@ def plot_univariate_histogram(
# Prepare the relevant data
key = tuple(sub_vars.items())
- observations = sub_data[self.data_variable]
+ orient = self.data_variable
if "weights" in self.variables:
- weights = sub_data["weights"]
- part_weight = weights.sum()
+ sub_data["weight"] = sub_data.pop("weights")
+ part_weight = sub_data["weight"].sum()
else:
- weights = None
part_weight = len(sub_data)
# Do the histogram computation
- heights, edges = estimator(observations, weights=weights)
+ if not (multiple_histograms and common_bins):
+ bin_kws = estimator._define_bin_params(sub_data, orient, None)
+ res = estimator._normalize(estimator._eval(sub_data, orient, bin_kws))
+ heights = res[estimator.stat].to_numpy()
+ widths = res["space"].to_numpy()
+ edges = res[orient].to_numpy() - widths / 2
+
+ # Convert edges back to original units for plotting
+ if self._log_scaled(self.data_variable):
+ widths = np.power(10, edges + widths) - np.power(10, edges)
+ edges = np.power(10, edges)
# Rescale the smoothed curve to match the histogram
if kde and key in densities:
@@ -482,17 +489,12 @@ def plot_univariate_histogram(
if estimator.cumulative:
hist_norm = heights.max()
else:
- hist_norm = (heights * np.diff(edges)).sum()
+ hist_norm = (heights * widths).sum()
densities[key] *= hist_norm
- # Convert edges back to original units for plotting
- if self._log_scaled(self.data_variable):
- edges = np.power(10, edges)
-
# Pack the histogram data and metadata together
- orig_widths = np.diff(edges)
- widths = shrink * orig_widths
- edges = edges[:-1] + (1 - shrink) / 2 * orig_widths
+ edges = edges + (1 - shrink) / 2 * widths
+ widths *= shrink
index = pd.MultiIndex.from_arrays([
pd.Index(edges, name="edges"),
pd.Index(widths, name="widths"),
@@ -1128,14 +1130,6 @@ def plot_bivariate_density(
for k, d in densities.items()
}
- # Get a default single color from the attribute cycle
- if self.ax is None:
- default_color = "C0" if color is None else color
- else:
- scout, = self.ax.plot([], color=color)
- default_color = scout.get_color()
- scout.remove()
-
# Define the coloring of the contours
if "hue" in self.variables:
for param in ["cmap", "colors"]:
@@ -1148,10 +1142,10 @@ def plot_bivariate_density(
# Work out a default coloring of the contours
coloring_given = set(contour_kws) & {"cmap", "colors"}
if fill and not coloring_given:
- cmap = self._cmap_from_color(default_color)
+ cmap = self._cmap_from_color(color)
contour_kws["cmap"] = cmap
if not fill and not coloring_given:
- contour_kws["colors"] = [default_color]
+ contour_kws["colors"] = [color]
# Use our internal colormap lookup
cmap = contour_kws.pop("cmap", None)
diff --git a/seaborn/matrix.py b/seaborn/matrix.py
index 2e60d5839f..76f22b89af 100644
--- a/seaborn/matrix.py
+++ b/seaborn/matrix.py
@@ -439,105 +439,8 @@ def heatmap(
Examples
--------
- Plot a heatmap for a numpy array:
+ .. include:: ../docstrings/heatmap.rst
- .. plot::
- :context: close-figs
-
- >>> import numpy as np; np.random.seed(0)
- >>> import seaborn as sns; sns.set_theme()
- >>> uniform_data = np.random.rand(10, 12)
- >>> ax = sns.heatmap(uniform_data)
-
- Change the limits of the colormap:
-
- .. plot::
- :context: close-figs
-
- >>> ax = sns.heatmap(uniform_data, vmin=0, vmax=1)
-
- Plot a heatmap for data centered on 0 with a diverging colormap:
-
- .. plot::
- :context: close-figs
-
- >>> normal_data = np.random.randn(10, 12)
- >>> ax = sns.heatmap(normal_data, center=0)
-
- Plot a dataframe with meaningful row and column labels:
-
- .. plot::
- :context: close-figs
-
- >>> flights = sns.load_dataset("flights")
- >>> flights = flights.pivot("month", "year", "passengers")
- >>> ax = sns.heatmap(flights)
-
- Annotate each cell with the numeric value using integer formatting:
-
- .. plot::
- :context: close-figs
-
- >>> ax = sns.heatmap(flights, annot=True, fmt="d")
-
- Add lines between each cell:
-
- .. plot::
- :context: close-figs
-
- >>> ax = sns.heatmap(flights, linewidths=.5)
-
- Use a different colormap:
-
- .. plot::
- :context: close-figs
-
- >>> ax = sns.heatmap(flights, cmap="YlGnBu")
-
- Center the colormap at a specific value:
-
- .. plot::
- :context: close-figs
-
- >>> ax = sns.heatmap(flights, center=flights.loc["Jan", 1955])
-
- Plot every other column label and don't plot row labels:
-
- .. plot::
- :context: close-figs
-
- >>> data = np.random.randn(50, 20)
- >>> ax = sns.heatmap(data, xticklabels=2, yticklabels=False)
-
- Don't draw a colorbar:
-
- .. plot::
- :context: close-figs
-
- >>> ax = sns.heatmap(flights, cbar=False)
-
- Use different axes for the colorbar:
-
- .. plot::
- :context: close-figs
-
- >>> grid_kws = {"height_ratios": (.9, .05), "hspace": .3}
- >>> f, (ax, cbar_ax) = plt.subplots(2, gridspec_kw=grid_kws)
- >>> ax = sns.heatmap(flights, ax=ax,
- ... cbar_ax=cbar_ax,
- ... cbar_kws={"orientation": "horizontal"})
-
- Use a mask to plot only part of a matrix
-
- .. plot::
- :context: close-figs
-
- >>> corr = np.corrcoef(np.random.randn(10, 200))
- >>> mask = np.zeros_like(corr)
- >>> mask[np.triu_indices_from(mask)] = True
- >>> with sns.axes_style("white"):
- ... f, ax = plt.subplots(figsize=(7, 5))
- ... ax = sns.heatmap(corr, mask=mask, vmax=.3, square=True)
"""
# Initialize the plotter object
plotter = _HeatMapper(data, vmin, vmax, cmap, center, robust, annot, fmt,
@@ -1340,70 +1243,8 @@ def clustermap(
Examples
--------
- Plot a clustered heatmap:
-
- .. plot::
- :context: close-figs
-
- >>> import seaborn as sns; sns.set_theme(color_codes=True)
- >>> iris = sns.load_dataset("iris")
- >>> species = iris.pop("species")
- >>> g = sns.clustermap(iris)
-
- Change the size and layout of the figure:
-
- .. plot::
- :context: close-figs
-
- >>> g = sns.clustermap(iris,
- ... figsize=(7, 5),
- ... row_cluster=False,
- ... dendrogram_ratio=(.1, .2),
- ... cbar_pos=(0, .2, .03, .4))
-
- Add colored labels to identify observations:
-
- .. plot::
- :context: close-figs
-
- >>> lut = dict(zip(species.unique(), "rbg"))
- >>> row_colors = species.map(lut)
- >>> g = sns.clustermap(iris, row_colors=row_colors)
-
- Use a different colormap and adjust the limits of the color range:
-
- .. plot::
- :context: close-figs
-
- >>> g = sns.clustermap(iris, cmap="mako", vmin=0, vmax=10)
-
- Use a different similarity metric:
-
- .. plot::
- :context: close-figs
-
- >>> g = sns.clustermap(iris, metric="correlation")
-
- Use a different clustering method:
-
- .. plot::
- :context: close-figs
-
- >>> g = sns.clustermap(iris, method="single")
-
- Standardize the data within the columns:
-
- .. plot::
- :context: close-figs
-
- >>> g = sns.clustermap(iris, standard_scale=1)
-
- Normalize the data within the rows:
-
- .. plot::
- :context: close-figs
+ .. include:: ../docstrings/clustermap.rst
- >>> g = sns.clustermap(iris, z_score=0, cmap="vlag")
"""
if _no_scipy:
raise RuntimeError("clustermap requires scipy to be available")
diff --git a/seaborn/objects.py b/seaborn/objects.py
index f2d6520199..90fc6530a5 100644
--- a/seaborn/objects.py
+++ b/seaborn/objects.py
@@ -31,13 +31,15 @@
from seaborn._marks.base import Mark # noqa: F401
from seaborn._marks.area import Area, Band # noqa: F401
from seaborn._marks.bar import Bar, Bars # noqa: F401
-from seaborn._marks.line import Line, Lines, Path, Paths, Range # noqa: F401
from seaborn._marks.dot import Dot, Dots # noqa: F401
+from seaborn._marks.line import Line, Lines, Path, Paths, Range # noqa: F401
+from seaborn._marks.text import Text # noqa: F401
from seaborn._stats.base import Stat # noqa: F401
from seaborn._stats.aggregation import Agg, Est # noqa: F401
-from seaborn._stats.regression import PolyFit # noqa: F401
from seaborn._stats.histogram import Hist # noqa: F401
+from seaborn._stats.order import Perc # noqa: F401
+from seaborn._stats.regression import PolyFit # noqa: F401
from seaborn._core.moves import Dodge, Jitter, Norm, Shift, Stack, Move # noqa: F401
diff --git a/seaborn/palettes.py b/seaborn/palettes.py
index 9b0fd58ce1..3306b0f2e9 100644
--- a/seaborn/palettes.py
+++ b/seaborn/palettes.py
@@ -91,6 +91,34 @@ def _repr_html_(self):
return html
+def _patch_colormap_display():
+ """Simplify the rich display of matplotlib color maps in a notebook."""
+ def _repr_png_(self):
+ """Generate a PNG representation of the Colormap."""
+ import io
+ from PIL import Image
+ import numpy as np
+ IMAGE_SIZE = (400, 50)
+ X = np.tile(np.linspace(0, 1, IMAGE_SIZE[0]), (IMAGE_SIZE[1], 1))
+ pixels = self(X, bytes=True)
+ png_bytes = io.BytesIO()
+ Image.fromarray(pixels).save(png_bytes, format='png')
+ return png_bytes.getvalue()
+
+ def _repr_html_(self):
+ """Generate an HTML representation of the Colormap."""
+ import base64
+ png_bytes = self._repr_png_()
+ png_base64 = base64.b64encode(png_bytes).decode('ascii')
+ return ('')
+
+ mpl.colors.Colormap._repr_png_ = _repr_png_
+ mpl.colors.Colormap._repr_html_ = _repr_html_
+
+
def color_palette(palette=None, n_colors=None, desat=None, as_cmap=False):
"""Return a list of colors or continuous colormap defining a palette.
@@ -125,11 +153,11 @@ def color_palette(palette=None, n_colors=None, desat=None, as_cmap=False):
desat : float, optional
Proportion to desaturate each color by.
as_cmap : bool
- If True, return a :class:`matplotlib.colors.Colormap`.
+ If True, return a :class:`matplotlib.colors.ListedColormap`.
Returns
-------
- list of RGB tuples or :class:`matplotlib.colors.Colormap`
+ list of RGB tuples or :class:`matplotlib.colors.ListedColormap`
See Also
--------
@@ -228,25 +256,36 @@ def color_palette(palette=None, n_colors=None, desat=None, as_cmap=False):
def hls_palette(n_colors=6, h=.01, l=.6, s=.65, as_cmap=False): # noqa
- """Get a set of evenly spaced colors in HLS hue space.
+ """
+ Return hues with constant lightness and saturation in the HLS system.
+
+ The hues are evenly sampled along a circular path. The resulting palette will be
+ appropriate for categorical or cyclical data.
- h, l, and s should be between 0 and 1
+ The `h`, `l`, and `s` values should be between 0 and 1.
+
+ .. note::
+ While the separation of the resulting colors will be mathematically
+ constant, the HLS system does not construct a perceptually-uniform space,
+ so their apparent intensity will vary.
Parameters
----------
-
n_colors : int
- number of colors in the palette
+ Number of colors in the palette.
h : float
- first hue
+ The value of the first hue.
l : float
- lightness
+ The lightness value.
s : float
- saturation
+ The saturation intensity.
+ as_cmap : bool
+ If True, return a matplotlib colormap object.
Returns
-------
- list of RGB tuples or :class:`matplotlib.colors.Colormap`
+ palette
+ list of RGB tuples or :class:`matplotlib.colors.ListedColormap`
See Also
--------
@@ -254,35 +293,7 @@ def hls_palette(n_colors=6, h=.01, l=.6, s=.65, as_cmap=False): # noqa
Examples
--------
-
- Create a palette of 10 colors with the default parameters:
-
- .. plot::
- :context: close-figs
-
- >>> import seaborn as sns; sns.set_theme()
- >>> sns.palplot(sns.hls_palette(10))
-
- Create a palette of 10 colors that begins at a different hue value:
-
- .. plot::
- :context: close-figs
-
- >>> sns.palplot(sns.hls_palette(10, h=.5))
-
- Create a palette of 10 colors that are darker than the default:
-
- .. plot::
- :context: close-figs
-
- >>> sns.palplot(sns.hls_palette(10, l=.4))
-
- Create a palette of 10 colors that are less saturated than the default:
-
- .. plot::
- :context: close-figs
-
- >>> sns.palplot(sns.hls_palette(10, s=.4))
+ .. include:: ../docstrings/hls_palette.rst
"""
if as_cmap:
@@ -299,62 +310,42 @@ def hls_palette(n_colors=6, h=.01, l=.6, s=.65, as_cmap=False): # noqa
def husl_palette(n_colors=6, h=.01, s=.9, l=.65, as_cmap=False): # noqa
- """Get a set of evenly spaced colors in HUSL hue space.
+ """
+ Return hues with constant lightness and saturation in the HUSL system.
- h, s, and l should be between 0 and 1
+ The hues are evenly sampled along a circular path. The resulting palette will be
+ appropriate for categorical or cyclical data.
+
+ The `h`, `l`, and `s` values should be between 0 and 1.
+
+ This function is similar to :func:`hls_palette`, but it uses a nonlinear color
+ space that is more perceptually uniform.
Parameters
----------
-
n_colors : int
- number of colors in the palette
+ Number of colors in the palette.
h : float
- first hue
- s : float
- saturation
+ The value of the first hue.
l : float
- lightness
+ The lightness value.
+ s : float
+ The saturation intensity.
+ as_cmap : bool
+ If True, return a matplotlib colormap object.
Returns
-------
- list of RGB tuples or :class:`matplotlib.colors.Colormap`
+ palette
+ list of RGB tuples or :class:`matplotlib.colors.ListedColormap`
See Also
--------
- hls_palette : Make a palette using evently spaced circular hues in the
- HSL system.
+ hls_palette : Make a palette using evenly spaced hues in the HSL system.
Examples
--------
-
- Create a palette of 10 colors with the default parameters:
-
- .. plot::
- :context: close-figs
-
- >>> import seaborn as sns; sns.set_theme()
- >>> sns.palplot(sns.husl_palette(10))
-
- Create a palette of 10 colors that begins at a different hue value:
-
- .. plot::
- :context: close-figs
-
- >>> sns.palplot(sns.husl_palette(10, h=.5))
-
- Create a palette of 10 colors that are darker than the default:
-
- .. plot::
- :context: close-figs
-
- >>> sns.palplot(sns.husl_palette(10, l=.4))
-
- Create a palette of 10 colors that are less saturated than the default:
-
- .. plot::
- :context: close-figs
-
- >>> sns.palplot(sns.husl_palette(10, s=.4))
+ .. include:: ../docstrings/husl_palette.rst
"""
if as_cmap:
@@ -373,17 +364,16 @@ def husl_palette(n_colors=6, h=.01, s=.9, l=.65, as_cmap=False): # noqa
def mpl_palette(name, n_colors=6, as_cmap=False):
- """Return discrete colors from a matplotlib palette.
+ """
+ Return a palette or colormap from the matplotlib registry.
- Note that this handles the qualitative colorbrewer palettes
- properly, although if you ask for more colors than a particular
- qualitative palette can provide you will get fewer than you are
- expecting. In contrast, asking for qualitative color brewer palettes
- using :func:`color_palette` will return the expected number of colors,
- but they will cycle.
+ For continuous palettes, evenly-spaced discrete samples are chosen while
+ excluding the minimum and maximum value in the colormap to provide better
+ contrast at the extremes.
- If you are using the IPython notebook, you can also use the function
- :func:`choose_colorbrewer_palette` to interactively select palettes.
+ For qualitative palettes (e.g. those from colorbrewer), exact values are
+ indexed (rather than interpolated), but fewer than `n_colors` can be returned
+ if the palette does not define that many.
Parameters
----------
@@ -394,39 +384,11 @@ def mpl_palette(name, n_colors=6, as_cmap=False):
Returns
-------
- list of RGB tuples or :class:`matplotlib.colors.Colormap`
+ list of RGB tuples or :class:`matplotlib.colors.ListedColormap`
Examples
--------
-
- Create a qualitative colorbrewer palette with 8 colors:
-
- .. plot::
- :context: close-figs
-
- >>> import seaborn as sns; sns.set_theme()
- >>> sns.palplot(sns.mpl_palette("Set2", 8))
-
- Create a sequential colorbrewer palette:
-
- .. plot::
- :context: close-figs
-
- >>> sns.palplot(sns.mpl_palette("Blues"))
-
- Create a diverging palette:
-
- .. plot::
- :context: close-figs
-
- >>> sns.palplot(sns.mpl_palette("seismic", 8))
-
- Create a "dark" sequential palette:
-
- .. plot::
- :context: close-figs
-
- >>> sns.palplot(sns.mpl_palette("GnBu_d"))
+ .. include: ../docstrings/mpl_palette.rst
"""
if name.endswith("_d"):
@@ -491,14 +453,15 @@ def dark_palette(color, n_colors=6, reverse=False, as_cmap=False, input="rgb"):
reverse : bool, optional
if True, reverse the direction of the blend
as_cmap : bool, optional
- If True, return a :class:`matplotlib.colors.Colormap`.
+ If True, return a :class:`matplotlib.colors.ListedColormap`.
input : {'rgb', 'hls', 'husl', xkcd'}
Color space to interpret the input color. The first three options
apply to tuple inputs and the latter applies to string inputs.
Returns
-------
- list of RGB tuples or :class:`matplotlib.colors.Colormap`
+ palette
+ list of RGB tuples or :class:`matplotlib.colors.ListedColormap`
See Also
--------
@@ -507,38 +470,7 @@ def dark_palette(color, n_colors=6, reverse=False, as_cmap=False, input="rgb"):
Examples
--------
-
- Generate a palette from an HTML color:
-
- .. plot::
- :context: close-figs
-
- >>> import seaborn as sns; sns.set_theme()
- >>> sns.palplot(sns.dark_palette("purple"))
-
- Generate a palette that decreases in lightness:
-
- .. plot::
- :context: close-figs
-
- >>> sns.palplot(sns.dark_palette("seagreen", reverse=True))
-
- Generate a palette from an HUSL-space seed:
-
- .. plot::
- :context: close-figs
-
- >>> sns.palplot(sns.dark_palette((260, 75, 60), input="husl"))
-
- Generate a colormap object:
-
- .. plot::
- :context: close-figs
-
- >>> from numpy import arange
- >>> x = arange(25).reshape(5, 5)
- >>> cmap = sns.dark_palette("#2ecc71", as_cmap=True)
- >>> ax = sns.heatmap(x, cmap=cmap)
+ .. include:: ../docstrings/dark_palette.rst
"""
rgb = _color_to_rgb(color, input)
@@ -552,34 +484,32 @@ def dark_palette(color, n_colors=6, reverse=False, as_cmap=False, input="rgb"):
def light_palette(color, n_colors=6, reverse=False, as_cmap=False, input="rgb"):
"""Make a sequential palette that blends from light to ``color``.
- This kind of palette is good for data that range between relatively
- uninteresting low values and interesting high values.
-
The ``color`` parameter can be specified in a number of ways, including
all options for defining a color in matplotlib and several additional
color spaces that are handled by seaborn. You can also use the database
of named colors from the XKCD color survey.
- If you are using the IPython notebook, you can also choose this palette
+ If you are using a Jupyter notebook, you can also choose this palette
interactively with the :func:`choose_light_palette` function.
Parameters
----------
color : base color for high values
- hex code, html color name, or tuple in ``input`` space.
+ hex code, html color name, or tuple in `input` space.
n_colors : int, optional
number of colors in the palette
reverse : bool, optional
if True, reverse the direction of the blend
as_cmap : bool, optional
- If True, return a :class:`matplotlib.colors.Colormap`.
+ If True, return a :class:`matplotlib.colors.ListedColormap`.
input : {'rgb', 'hls', 'husl', xkcd'}
Color space to interpret the input color. The first three options
apply to tuple inputs and the latter applies to string inputs.
Returns
-------
- list of RGB tuples or :class:`matplotlib.colors.Colormap`
+ palette
+ list of RGB tuples or :class:`matplotlib.colors.ListedColormap`
See Also
--------
@@ -588,38 +518,7 @@ def light_palette(color, n_colors=6, reverse=False, as_cmap=False, input="rgb"):
Examples
--------
-
- Generate a palette from an HTML color:
-
- .. plot::
- :context: close-figs
-
- >>> import seaborn as sns; sns.set_theme()
- >>> sns.palplot(sns.light_palette("purple"))
-
- Generate a palette that increases in lightness:
-
- .. plot::
- :context: close-figs
-
- >>> sns.palplot(sns.light_palette("seagreen", reverse=True))
-
- Generate a palette from an HUSL-space seed:
-
- .. plot::
- :context: close-figs
-
- >>> sns.palplot(sns.light_palette((260, 75, 60), input="husl"))
-
- Generate a colormap object:
-
- .. plot::
- :context: close-figs
-
- >>> from numpy import arange
- >>> x = arange(25).reshape(5, 5)
- >>> cmap = sns.light_palette("#2ecc71", as_cmap=True)
- >>> ax = sns.heatmap(x, cmap=cmap)
+ .. include:: ../docstrings/light_palette.rst
"""
rgb = _color_to_rgb(color, input)
@@ -652,11 +551,12 @@ def diverging_palette(h_neg, h_pos, s=75, l=50, sep=1, n=6, # noqa
center : {"light", "dark"}, optional
Whether the center of the palette is light or dark
as_cmap : bool, optional
- If True, return a :class:`matplotlib.colors.Colormap`.
+ If True, return a :class:`matplotlib.colors.ListedColormap`.
Returns
-------
- list of RGB tuples or :class:`matplotlib.colors.Colormap`
+ palette
+ list of RGB tuples or :class:`matplotlib.colors.ListedColormap`
See Also
--------
@@ -665,39 +565,7 @@ def diverging_palette(h_neg, h_pos, s=75, l=50, sep=1, n=6, # noqa
Examples
--------
-
- Generate a blue-white-red palette:
-
- .. plot::
- :context: close-figs
-
- >>> import seaborn as sns; sns.set_theme()
- >>> sns.palplot(sns.diverging_palette(240, 10, n=9))
-
- Generate a brighter green-white-purple palette:
-
- .. plot::
- :context: close-figs
-
- >>> sns.palplot(sns.diverging_palette(150, 275, s=80, l=55, n=9))
-
- Generate a blue-black-red palette:
-
- .. plot::
- :context: close-figs
-
- >>> sns.palplot(sns.diverging_palette(250, 15, s=75, l=40,
- ... n=9, center="dark"))
-
- Generate a colormap object:
-
- .. plot::
- :context: close-figs
-
- >>> from numpy import arange
- >>> x = arange(25).reshape(5, 5)
- >>> cmap = sns.diverging_palette(220, 20, as_cmap=True)
- >>> ax = sns.heatmap(x, cmap=cmap)
+ .. include: ../docstrings/diverging_palette.rst
"""
palfunc = dict(dark=dark_palette, light=light_palette)[center]
@@ -715,16 +583,21 @@ def blend_palette(colors, n_colors=6, as_cmap=False, input="rgb"):
Parameters
----------
- colors : sequence of colors in various formats interpreted by ``input``
- hex code, html color name, or tuple in ``input`` space.
+ colors : sequence of colors in various formats interpreted by `input`
+ hex code, html color name, or tuple in `input` space.
n_colors : int, optional
Number of colors in the palette.
as_cmap : bool, optional
- If True, return a :class:`matplotlib.colors.Colormap`.
+ If True, return a :class:`matplotlib.colors.ListedColormap`.
Returns
-------
- list of RGB tuples or :class:`matplotlib.colors.Colormap`
+ palette
+ list of RGB tuples or :class:`matplotlib.colors.ListedColormap`
+
+ Examples
+ --------
+ .. include: ../docstrings/blend_palette.rst
"""
colors = [_color_to_rgb(color, input) for color in colors]
@@ -741,18 +614,17 @@ def xkcd_palette(colors):
See xkcd for the full list of colors: https://xkcd.com/color/rgb/
- This is just a simple wrapper around the ``seaborn.xkcd_rgb`` dictionary.
+ This is just a simple wrapper around the `seaborn.xkcd_rgb` dictionary.
Parameters
----------
colors : list of strings
- List of keys in the ``seaborn.xkcd_rgb`` dictionary.
+ List of keys in the `seaborn.xkcd_rgb` dictionary.
Returns
-------
- palette : seaborn color palette
- Returns the list of colors as RGB tuples in an object that behaves like
- other seaborn color palettes.
+ palette
+ A list of colors as RGB tuples.
See Also
--------
@@ -769,18 +641,17 @@ def crayon_palette(colors):
Colors are taken from here:
https://en.wikipedia.org/wiki/List_of_Crayola_crayon_colors
- This is just a simple wrapper around the ``seaborn.crayons`` dictionary.
+ This is just a simple wrapper around the `seaborn.crayons` dictionary.
Parameters
----------
colors : list of strings
- List of keys in the ``seaborn.crayons`` dictionary.
+ List of keys in the `seaborn.crayons` dictionary.
Returns
-------
- palette : seaborn color palette
- Returns the list of colors as rgb tuples in an object that behaves like
- other seaborn color palettes.
+ palette
+ A list of colors as RGB tuples.
See Also
--------
@@ -803,20 +674,19 @@ def cubehelix_palette(n_colors=6, start=0, rot=.4, gamma=1.0, hue=0.8,
defaults.
In addition to using this function, it is also possible to generate a
- cubehelix palette generally in seaborn using a string-shorthand; see the
- example below.
+ cubehelix palette generally in seaborn using a string starting with
+ `ch:` and containing other parameters (e.g. `"ch:s=.25,r=-.5"`).
Parameters
----------
n_colors : int
Number of colors in the palette.
start : float, 0 <= start <= 3
- The hue at the start of the helix.
+ The hue value at the start of the helix.
rot : float
Rotations around the hue wheel over the range of the palette.
gamma : float 0 <= gamma
- Gamma factor to emphasize darker (gamma < 1) or lighter (gamma > 1)
- colors.
+ Nonlinearity to emphasize dark (gamma < 1) or light (gamma > 1) colors.
hue : float, 0 <= hue <= 1
Saturation of the colors.
dark : float 0 <= dark <= 1
@@ -826,11 +696,12 @@ def cubehelix_palette(n_colors=6, start=0, rot=.4, gamma=1.0, hue=0.8,
reverse : bool
If True, the palette will go from dark to light.
as_cmap : bool
- If True, return a :class:`matplotlib.colors.Colormap`.
+ If True, return a :class:`matplotlib.colors.ListedColormap`.
Returns
-------
- list of RGB tuples or :class:`matplotlib.colors.Colormap`
+ palette
+ list of RGB tuples or :class:`matplotlib.colors.ListedColormap`
See Also
--------
@@ -847,60 +718,7 @@ def cubehelix_palette(n_colors=6, start=0, rot=.4, gamma=1.0, hue=0.8,
Examples
--------
-
- Generate the default palette:
-
- .. plot::
- :context: close-figs
-
- >>> import seaborn as sns; sns.set_theme()
- >>> sns.palplot(sns.cubehelix_palette())
-
- Rotate backwards from the same starting location:
-
- .. plot::
- :context: close-figs
-
- >>> sns.palplot(sns.cubehelix_palette(rot=-.4))
-
- Use a different starting point and shorter rotation:
-
- .. plot::
- :context: close-figs
-
- >>> sns.palplot(sns.cubehelix_palette(start=2.8, rot=.1))
-
- Reverse the direction of the lightness ramp:
-
- .. plot::
- :context: close-figs
-
- >>> sns.palplot(sns.cubehelix_palette(reverse=True))
-
- Generate a colormap object:
-
- .. plot::
- :context: close-figs
-
- >>> from numpy import arange
- >>> x = arange(25).reshape(5, 5)
- >>> cmap = sns.cubehelix_palette(as_cmap=True)
- >>> ax = sns.heatmap(x, cmap=cmap)
-
- Use the full lightness range:
-
- .. plot::
- :context: close-figs
-
- >>> cmap = sns.cubehelix_palette(dark=0, light=1, as_cmap=True)
- >>> ax = sns.heatmap(x, cmap=cmap)
-
- Use through the :func:`color_palette` interface:
-
- .. plot::
- :context: close-figs
-
- >>> sns.palplot(sns.color_palette("ch:2,r=.2,l=.6"))
+ .. include:: ../docstrings/cubehelix_palette.rst
"""
def get_color_function(p0, p1):
@@ -996,32 +814,17 @@ def set_color_codes(palette="deep"):
set_palette : Color codes can also be set through the function that
sets the matplotlib color cycle.
- Examples
- --------
-
- Map matplotlib color codes to the default seaborn palette.
-
- .. plot::
- :context: close-figs
-
- >>> import matplotlib.pyplot as plt
- >>> import seaborn as sns; sns.set_theme()
- >>> sns.set_color_codes()
- >>> _ = plt.plot([0, 1], color="r")
-
- Use a different seaborn palette.
-
- .. plot::
- :context: close-figs
-
- >>> sns.set_color_codes("dark")
- >>> _ = plt.plot([0, 1], color="g")
- >>> _ = plt.plot([0, 2], color="m")
-
"""
if palette == "reset":
- colors = [(0., 0., 1.), (0., .5, 0.), (1., 0., 0.), (.75, 0., .75),
- (.75, .75, 0.), (0., .75, .75), (0., 0., 0.)]
+ colors = [
+ (0., 0., 1.),
+ (0., .5, 0.),
+ (1., 0., 0.),
+ (.75, 0., .75),
+ (.75, .75, 0.),
+ (0., .75, .75),
+ (0., 0., 0.)
+ ]
elif not isinstance(palette, str):
err = "set_color_codes requires a named seaborn palette"
raise TypeError(err)
diff --git a/seaborn/rcmod.py b/seaborn/rcmod.py
index 145698ca69..ca70a44695 100644
--- a/seaborn/rcmod.py
+++ b/seaborn/rcmod.py
@@ -516,12 +516,6 @@ def set_palette(palette, n_colors=None, desat=None, color_codes=False):
If ``True`` and ``palette`` is a seaborn palette, remap the shorthand
color codes (e.g. "b", "g", "r", etc.) to the colors from this palette.
- Examples
- --------
- >>> set_palette("Reds")
-
- >>> set_palette("Set1", 8, .75)
-
See Also
--------
color_palette : build a color palette or set the color cycle temporarily
diff --git a/seaborn/regression.py b/seaborn/regression.py
index a6b1087338..1c7d804e26 100644
--- a/seaborn/regression.py
+++ b/seaborn/regression.py
@@ -728,98 +728,7 @@ def update_datalim(data, x, y, ax, **kws):
Examples
--------
- These examples focus on basic regression model plots to exhibit the
- various faceting options; see the :func:`regplot` docs for demonstrations
- of the other options for plotting the data and models. There are also
- other examples for how to manipulate plot using the returned object on
- the :class:`FacetGrid` docs.
-
- Plot a simple linear relationship between two variables:
-
- .. plot::
- :context: close-figs
-
- >>> import seaborn as sns; sns.set_theme(color_codes=True)
- >>> tips = sns.load_dataset("tips")
- >>> g = sns.lmplot(x="total_bill", y="tip", data=tips)
-
- Condition on a third variable and plot the levels in different colors:
-
- .. plot::
- :context: close-figs
-
- >>> g = sns.lmplot(x="total_bill", y="tip", hue="smoker", data=tips)
-
- Use different markers as well as colors so the plot will reproduce to
- black-and-white more easily:
-
- .. plot::
- :context: close-figs
-
- >>> g = sns.lmplot(x="total_bill", y="tip", hue="smoker", data=tips,
- ... markers=["o", "x"])
-
- Use a different color palette:
-
- .. plot::
- :context: close-figs
-
- >>> g = sns.lmplot(x="total_bill", y="tip", hue="smoker", data=tips,
- ... palette="Set1")
-
- Map ``hue`` levels to colors with a dictionary:
-
- .. plot::
- :context: close-figs
-
- >>> g = sns.lmplot(x="total_bill", y="tip", hue="smoker", data=tips,
- ... palette=dict(Yes="g", No="m"))
-
- Plot the levels of the third variable across different columns:
-
- .. plot::
- :context: close-figs
-
- >>> g = sns.lmplot(x="total_bill", y="tip", col="smoker", data=tips)
-
- Change the height and aspect ratio of the facets:
-
- .. plot::
- :context: close-figs
-
- >>> g = sns.lmplot(x="size", y="total_bill", hue="day", col="day",
- ... data=tips, height=6, aspect=.4, x_jitter=.1)
-
- Wrap the levels of the column variable into multiple rows:
-
- .. plot::
- :context: close-figs
-
- >>> g = sns.lmplot(x="total_bill", y="tip", col="day", hue="day",
- ... data=tips, col_wrap=2, height=3)
-
- Condition on two variables to make a full grid:
-
- .. plot::
- :context: close-figs
-
- >>> g = sns.lmplot(x="total_bill", y="tip", row="sex", col="time",
- ... data=tips, height=3)
-
- Use methods on the returned :class:`FacetGrid` instance to further tweak
- the plot:
-
- .. plot::
- :context: close-figs
-
- >>> g = sns.lmplot(x="total_bill", y="tip", row="sex", col="time",
- ... data=tips, height=3)
- >>> g = (g.set_axis_labels("Total bill (US Dollars)", "Tip")
- ... .set(xlim=(0, 60), ylim=(0, 12),
- ... xticks=[10, 30, 50], yticks=[2, 6, 10])
- ... .fig.subplots_adjust(wspace=.02))
-
-
+ .. include:: ../docstrings/lmplot.rst
""").format(**_regression_docs)
@@ -921,101 +830,7 @@ def regplot(
Examples
--------
- Plot the relationship between two variables in a DataFrame:
-
- .. plot::
- :context: close-figs
-
- >>> import seaborn as sns; sns.set_theme(color_codes=True)
- >>> tips = sns.load_dataset("tips")
- >>> ax = sns.regplot(x="total_bill", y="tip", data=tips)
-
- Plot with two variables defined as numpy arrays; use a different color:
-
- .. plot::
- :context: close-figs
-
- >>> import numpy as np; np.random.seed(8)
- >>> mean, cov = [4, 6], [(1.5, .7), (.7, 1)]
- >>> x, y = np.random.multivariate_normal(mean, cov, 80).T
- >>> ax = sns.regplot(x=x, y=y, color="g")
-
- Plot with two variables defined as pandas Series; use a different marker:
-
- .. plot::
- :context: close-figs
-
- >>> import pandas as pd
- >>> x, y = pd.Series(x, name="x_var"), pd.Series(y, name="y_var")
- >>> ax = sns.regplot(x=x, y=y, marker="+")
-
- Use a 68% confidence interval, which corresponds with the standard error
- of the estimate, and extend the regression line to the axis limits:
-
- .. plot::
- :context: close-figs
-
- >>> ax = sns.regplot(x=x, y=y, ci=68, truncate=False)
-
- Plot with a discrete ``x`` variable and add some jitter:
-
- .. plot::
- :context: close-figs
-
- >>> ax = sns.regplot(x="size", y="total_bill", data=tips, x_jitter=.1)
-
- Plot with a discrete ``x`` variable showing means and confidence intervals
- for unique values:
-
- .. plot::
- :context: close-figs
-
- >>> ax = sns.regplot(x="size", y="total_bill", data=tips,
- ... x_estimator=np.mean)
-
- Plot with a continuous variable divided into discrete bins:
-
- .. plot::
- :context: close-figs
-
- >>> ax = sns.regplot(x=x, y=y, x_bins=4)
-
- Fit a higher-order polynomial regression:
-
- .. plot::
- :context: close-figs
-
- >>> ans = sns.load_dataset("anscombe")
- >>> ax = sns.regplot(x="x", y="y", data=ans.loc[ans.dataset == "II"],
- ... scatter_kws={{"s": 80}},
- ... order=2, ci=None)
-
- Fit a robust regression and don't plot a confidence interval:
-
- .. plot::
- :context: close-figs
-
- >>> ax = sns.regplot(x="x", y="y", data=ans.loc[ans.dataset == "III"],
- ... scatter_kws={{"s": 80}},
- ... robust=True, ci=None)
-
- Fit a logistic regression; jitter the y variable and use fewer bootstrap
- iterations:
-
- .. plot::
- :context: close-figs
-
- >>> tips["big_tip"] = (tips.tip / tips.total_bill) > .175
- >>> ax = sns.regplot(x="total_bill", y="big_tip", data=tips,
- ... logistic=True, n_boot=500, y_jitter=.03)
-
- Fit the regression model using log(x):
-
- .. plot::
- :context: close-figs
-
- >>> ax = sns.regplot(x="size", y="total_bill", data=tips,
- ... x_estimator=np.mean, logx=True)
+ .. include: ../docstrings/regplot.rst
""").format(**_regression_docs)
@@ -1075,6 +890,11 @@ def residplot(
jointplot : Draw a :func:`residplot` with univariate marginal distributions
(when used with ``kind="resid"``).
+ Examples
+ --------
+
+ .. include:: ../docstrings/residplot.rst
+
"""
plotter = _RegressionPlotter(x, y, data, ci=None,
order=order, robust=robust,
diff --git a/seaborn/utils.py b/seaborn/utils.py
index 3cc529a64b..a4887b2c42 100644
--- a/seaborn/utils.py
+++ b/seaborn/utils.py
@@ -100,7 +100,8 @@ def _default_color(method, hue, color, kws):
elif method.__name__ == "plot":
- scout, = method([], [], scalex=False, scaley=False, **kws)
+ color = _normalize_kwargs(kws, mpl.lines.Line2D).get("color")
+ scout, = method([], [], scalex=False, scaley=False, color=color)
color = scout.get_color()
scout.remove()
diff --git a/seaborn/widgets.py b/seaborn/widgets.py
index c75cc66c48..502812af57 100644
--- a/seaborn/widgets.py
+++ b/seaborn/widgets.py
@@ -2,26 +2,12 @@
import matplotlib.pyplot as plt
from matplotlib.colors import LinearSegmentedColormap
-# Lots of different places that widgets could come from...
try:
from ipywidgets import interact, FloatSlider, IntSlider
except ImportError:
- import warnings
- # ignore ShimWarning raised by IPython, see GH #892
- with warnings.catch_warnings():
- warnings.simplefilter("ignore")
- try:
- from IPython.html.widgets import interact, FloatSlider, IntSlider
- except ImportError:
- try:
- from IPython.html.widgets import (interact,
- FloatSliderWidget,
- IntSliderWidget)
- FloatSlider = FloatSliderWidget
- IntSlider = IntSliderWidget
- except ImportError:
- pass
-
+ def interact(f):
+ msg = "Interactive palettes require `ipywidgets`, which is not installed."
+ raise ImportError(msg)
from .miscplot import palplot
from .palettes import (color_palette, dark_palette, light_palette,
diff --git a/tests/_core/test_moves.py b/tests/_core/test_moves.py
index bac86d12e2..6fd88bb5fc 100644
--- a/tests/_core/test_moves.py
+++ b/tests/_core/test_moves.py
@@ -78,6 +78,15 @@ def check_pos(self, res, df, var, limit):
assert (res[var] < df[var] + limit / 2).all()
assert (res[var] > df[var] - limit / 2).all()
+ def test_default(self, df):
+
+ orient = "x"
+ groupby = self.get_groupby(df, orient)
+ res = Jitter()(df, groupby, orient, {})
+ self.check_same(res, df, "y", "grp2", "width")
+ self.check_pos(res, df, "x", 0.2 * df["width"])
+ assert (res["x"] - df["x"]).abs().min() > 0
+
def test_width(self, df):
width = .4
diff --git a/tests/_core/test_plot.py b/tests/_core/test_plot.py
index b612eeec22..03a64917f3 100644
--- a/tests/_core/test_plot.py
+++ b/tests/_core/test_plot.py
@@ -880,7 +880,7 @@ def test_theme_default(self):
def test_theme_params(self):
- color = "r"
+ color = ".888"
p = Plot().theme({"axes.facecolor": color}).plot()
assert mpl.colors.same_color(p._figure.axes[0].get_facecolor(), color)
@@ -1963,6 +1963,20 @@ def _legend_artist(self, variables, value, scales):
assert len(contents.findobj(mpl.lines.Line2D)) == len(names)
assert len(contents.findobj(mpl.patches.Patch)) == len(names)
+ def test_three_layers(self, xy):
+
+ class MockMarkLine(MockMark):
+ def _legend_artist(self, variables, value, scales):
+ return mpl.lines.Line2D([], [])
+
+ s = pd.Series(["a", "b", "a", "c"], name="s")
+ p = Plot(**xy, color=s)
+ for _ in range(3):
+ p = p.add(MockMarkLine())
+ p = p.plot()
+ texts = p._figure.legends[0].get_texts()
+ assert len(texts) == len(s.unique())
+
def test_identity_scale_ignored(self, xy):
s = pd.Series(["r", "g", "b", "g"])
@@ -1981,8 +1995,17 @@ def test_anonymous_title(self, xy):
legend, = p._figure.legends
assert legend.get_title().get_text() == ""
+ def test_legendless_mark(self, xy):
+
+ class NoLegendMark(MockMark):
+ def _legend_artist(self, variables, value, scales):
+ return None
+
+ p = Plot(**xy, color=["a", "b", "c", "d"]).add(NoLegendMark()).plot()
+ assert not p._figure.legends
+
-class TestHelpers:
+class TestDefaultObject:
def test_default_repr(self):
diff --git a/tests/_marks/test_area.py b/tests/_marks/test_area.py
index 1e44314082..d725e154ce 100644
--- a/tests/_marks/test_area.py
+++ b/tests/_marks/test_area.py
@@ -8,7 +8,7 @@
from seaborn._marks.area import Area, Band
-class TestAreaMarks:
+class TestArea:
def test_single_defaults(self):
@@ -17,6 +17,7 @@ def test_single_defaults(self):
ax = p._figure.axes[0]
poly = ax.patches[0]
verts = poly.get_path().vertices.T
+ colors = p._theme["axes.prop_cycle"].by_key()["color"]
expected_x = [1, 2, 3, 3, 2, 1, 1]
assert_array_equal(verts[0], expected_x)
@@ -25,21 +26,21 @@ def test_single_defaults(self):
assert_array_equal(verts[1], expected_y)
fc = poly.get_facecolor()
- assert_array_equal(fc, to_rgba("C0", .2))
+ assert_array_equal(fc, to_rgba(colors[0], .2))
ec = poly.get_edgecolor()
- assert_array_equal(ec, to_rgba("C0", 1))
+ assert_array_equal(ec, to_rgba(colors[0], 1))
lw = poly.get_linewidth()
assert_array_equal(lw, mpl.rcParams["patch.linewidth"] * 2)
- def test_set_parameters(self):
+ def test_set_properties(self):
x, y = [1, 2, 3], [1, 2, 1]
mark = Area(
- color="C2",
+ color=".33",
alpha=.3,
- edgecolor=".3",
+ edgecolor=".88",
edgealpha=.8,
edgewidth=2,
edgestyle=(0, (2, 1)),
@@ -62,11 +63,12 @@ def test_set_parameters(self):
expected = (0, (mark.edgewidth * dash_on / 4, mark.edgewidth * dash_off / 4))
assert ls == expected
- def test_mapped(self):
+ def test_mapped_properties(self):
x, y = [1, 2, 3, 2, 3, 4], [1, 2, 1, 1, 3, 2]
g = ["a", "a", "a", "b", "b", "b"]
- p = Plot(x=x, y=y, color=g, edgewidth=g).add(Area()).plot()
+ cs = [".2", ".8"]
+ p = Plot(x=x, y=y, color=g, edgewidth=g).scale(color=cs).add(Area()).plot()
ax = p._figure.axes[0]
expected_x = [1, 2, 3, 3, 2, 1, 1], [2, 3, 4, 4, 3, 2, 2]
@@ -78,10 +80,10 @@ def test_mapped(self):
assert_array_equal(verts[1], expected_y[i])
fcs = [p.get_facecolor() for p in ax.patches]
- assert_array_equal(fcs, to_rgba_array(["C0", "C1"], .2))
+ assert_array_equal(fcs, to_rgba_array(cs, .2))
ecs = [p.get_edgecolor() for p in ax.patches]
- assert_array_equal(ecs, to_rgba_array(["C0", "C1"], 1))
+ assert_array_equal(ecs, to_rgba_array(cs, 1))
lws = [p.get_linewidth() for p in ax.patches]
assert lws[0] > lws[1]
@@ -89,12 +91,16 @@ def test_mapped(self):
def test_unfilled(self):
x, y = [1, 2, 3], [1, 2, 1]
- p = Plot(x=x, y=y).add(Area(fill=False)).plot()
+ c = ".5"
+ p = Plot(x=x, y=y).add(Area(fill=False, color=c)).plot()
ax = p._figure.axes[0]
poly = ax.patches[0]
- assert poly.get_facecolor() == to_rgba("C0", 0)
+ assert poly.get_facecolor() == to_rgba(c, 0)
- def test_band(self):
+
+class TestBand:
+
+ def test_range(self):
x, ymin, ymax = [1, 2, 4], [2, 1, 4], [3, 3, 5]
p = Plot(x=x, ymin=ymin, ymax=ymax).add(Band()).plot()
@@ -106,3 +112,17 @@ def test_band(self):
expected_y = [2, 1, 4, 5, 3, 3, 2]
assert_array_equal(verts[1], expected_y)
+
+ def test_auto_range(self):
+
+ x = [1, 1, 2, 2, 2]
+ y = [1, 2, 3, 4, 5]
+ p = Plot(x=x, y=y).add(Band()).plot()
+ ax = p._figure.axes[0]
+ verts = ax.patches[0].get_path().vertices.T
+
+ expected_x = [1, 2, 2, 1, 1]
+ assert_array_equal(verts[0], expected_x)
+
+ expected_y = [1, 3, 5, 2, 1]
+ assert_array_equal(verts[1], expected_y)
diff --git a/tests/_marks/test_bar.py b/tests/_marks/test_bar.py
index e68f66ae38..373882e12c 100644
--- a/tests/_marks/test_bar.py
+++ b/tests/_marks/test_bar.py
@@ -67,7 +67,7 @@ def test_set_properties(self):
y = [1, 3, 2]
mark = Bar(
- color="C2",
+ color=".8",
alpha=.5,
edgecolor=".3",
edgealpha=.9,
@@ -92,9 +92,10 @@ def test_mapped_properties(self):
mark = Bar(alpha=.2)
p = Plot(x, y, color=x, edgewidth=y).add(mark).plot()
ax = p._figure.axes[0]
+ colors = p._theme["axes.prop_cycle"].by_key()["color"]
for i, bar in enumerate(ax.patches):
- assert bar.get_facecolor() == to_rgba(f"C{i}", mark.alpha)
- assert bar.get_edgecolor() == to_rgba(f"C{i}", 1)
+ assert bar.get_facecolor() == to_rgba(colors[i], mark.alpha)
+ assert bar.get_edgecolor() == to_rgba(colors[i], 1)
assert ax.patches[0].get_linewidth() < ax.patches[1].get_linewidth()
def test_zero_height_skipped(self):
@@ -166,7 +167,8 @@ def test_mapped_color_direct_alpha(self, x, y, color):
p = Plot(x, y, color=color).add(Bars(alpha=alpha)).plot()
ax = p._figure.axes[0]
fcs = ax.collections[0].get_facecolors()
- expected = to_rgba_array(["C0", "C1", "C2", "C0", "C2"], alpha)
+ C0, C1, C2, *_ = p._theme["axes.prop_cycle"].by_key()["color"]
+ expected = to_rgba_array([C0, C1, C2, C0, C2], alpha)
assert_array_equal(fcs, expected)
def test_mapped_edgewidth(self, x, y):
@@ -195,5 +197,6 @@ def test_unfilled(self, x, y):
ax = p._figure.axes[0]
fcs = ax.collections[0].get_facecolors()
ecs = ax.collections[0].get_edgecolors()
- assert_array_equal(fcs, to_rgba_array(["C0"] * len(x), 0))
- assert_array_equal(ecs, to_rgba_array(["C4"] * len(x), 1))
+ colors = p._theme["axes.prop_cycle"].by_key()["color"]
+ assert_array_equal(fcs, to_rgba_array([colors[0]] * len(x), 0))
+ assert_array_equal(ecs, to_rgba_array([colors[4]] * len(x), 1))
diff --git a/tests/_marks/test_dot.py b/tests/_marks/test_dot.py
index 7e5a6f56de..49b5e8f129 100644
--- a/tests/_marks/test_dot.py
+++ b/tests/_marks/test_dot.py
@@ -39,9 +39,10 @@ def test_simple(self):
p = Plot(x=x, y=y).add(Dot()).plot()
ax = p._figure.axes[0]
points, = ax.collections
+ C0, *_ = p._theme["axes.prop_cycle"].by_key()["color"]
self.check_offsets(points, x, y)
- self.check_colors("face", points, ["C0"] * 3, 1)
- self.check_colors("edge", points, ["C0"] * 3, 1)
+ self.check_colors("face", points, [C0] * 3, 1)
+ self.check_colors("edge", points, [C0] * 3, 1)
def test_filled_unfilled_mix(self):
@@ -54,9 +55,10 @@ def test_filled_unfilled_mix(self):
p = Plot(x=x, y=y).add(mark, marker=marker).scale(marker=shapes).plot()
ax = p._figure.axes[0]
points, = ax.collections
+ C0, *_ = p._theme["axes.prop_cycle"].by_key()["color"]
self.check_offsets(points, x, y)
- self.check_colors("face", points, ["C0", to_rgba("C0", 0)], None)
- self.check_colors("edge", points, ["w", "C0"], 1)
+ self.check_colors("face", points, [C0, to_rgba(C0, 0)], None)
+ self.check_colors("edge", points, ["w", C0], 1)
expected = [mark.edgewidth, mark.stroke]
assert_array_equal(points.get_linewidths(), expected)
@@ -93,22 +95,24 @@ def test_simple(self):
p = Plot(x=x, y=y).add(Dots()).plot()
ax = p._figure.axes[0]
points, = ax.collections
+ C0, *_ = p._theme["axes.prop_cycle"].by_key()["color"]
self.check_offsets(points, x, y)
- self.check_colors("face", points, ["C0"] * 3, .2)
- self.check_colors("edge", points, ["C0"] * 3, 1)
+ self.check_colors("face", points, [C0] * 3, .2)
+ self.check_colors("edge", points, [C0] * 3, 1)
- def test_color_direct(self):
+ def test_set_color(self):
x = [1, 2, 3]
y = [4, 5, 2]
- p = Plot(x=x, y=y).add(Dots(color="g")).plot()
+ m = Dots(color=".25")
+ p = Plot(x=x, y=y).add(m).plot()
ax = p._figure.axes[0]
points, = ax.collections
self.check_offsets(points, x, y)
- self.check_colors("face", points, ["g"] * 3, .2)
- self.check_colors("edge", points, ["g"] * 3, 1)
+ self.check_colors("face", points, [m.color] * 3, .2)
+ self.check_colors("edge", points, [m.color] * 3, 1)
- def test_color_mapped(self):
+ def test_map_color(self):
x = [1, 2, 3]
y = [4, 5, 2]
@@ -116,9 +120,10 @@ def test_color_mapped(self):
p = Plot(x=x, y=y, color=c).add(Dots()).plot()
ax = p._figure.axes[0]
points, = ax.collections
+ C0, C1, *_ = p._theme["axes.prop_cycle"].by_key()["color"]
self.check_offsets(points, x, y)
- self.check_colors("face", points, ["C0", "C1", "C0"], .2)
- self.check_colors("edge", points, ["C0", "C1", "C0"], 1)
+ self.check_colors("face", points, [C0, C1, C0], .2)
+ self.check_colors("edge", points, [C0, C1, C0], 1)
def test_fill(self):
@@ -128,9 +133,10 @@ def test_fill(self):
p = Plot(x=x, y=y, color=c).add(Dots(fill=False)).plot()
ax = p._figure.axes[0]
points, = ax.collections
+ C0, C1, *_ = p._theme["axes.prop_cycle"].by_key()["color"]
self.check_offsets(points, x, y)
- self.check_colors("face", points, ["C0", "C1", "C0"], 0)
- self.check_colors("edge", points, ["C0", "C1", "C0"], 1)
+ self.check_colors("face", points, [C0, C1, C0], 0)
+ self.check_colors("edge", points, [C0, C1, C0], 1)
def test_pointsize(self):
@@ -165,7 +171,8 @@ def test_filled_unfilled_mix(self):
p = Plot(x=x, y=y).add(mark, marker=marker).scale(marker=shapes).plot()
ax = p._figure.axes[0]
points, = ax.collections
+ C0, C1, *_ = p._theme["axes.prop_cycle"].by_key()["color"]
self.check_offsets(points, x, y)
- self.check_colors("face", points, [to_rgba("C0", .2), to_rgba("C0", 0)], None)
- self.check_colors("edge", points, ["C0", "C0"], 1)
+ self.check_colors("face", points, [to_rgba(C0, .2), to_rgba(C0, 0)], None)
+ self.check_colors("edge", points, [C0, C0], 1)
assert_array_equal(points.get_linewidths(), [mark.stroke] * 2)
diff --git a/tests/_marks/test_line.py b/tests/_marks/test_line.py
index 4157f82079..726daea55a 100644
--- a/tests/_marks/test_line.py
+++ b/tests/_marks/test_line.py
@@ -27,18 +27,19 @@ def test_xy_data(self):
def test_shared_colors_direct(self):
x = y = [1, 2, 3]
- m = Path(color="r")
+ color = ".44"
+ m = Path(color=color)
p = Plot(x=x, y=y).add(m).plot()
line, = p._figure.axes[0].get_lines()
- assert same_color(line.get_color(), "r")
- assert same_color(line.get_markeredgecolor(), "r")
- assert same_color(line.get_markerfacecolor(), "r")
+ assert same_color(line.get_color(), color)
+ assert same_color(line.get_markeredgecolor(), color)
+ assert same_color(line.get_markerfacecolor(), color)
def test_separate_colors_direct(self):
x = y = [1, 2, 3]
y = [1, 2, 3]
- m = Path(color="r", edgecolor="g", fillcolor="b")
+ m = Path(color=".22", edgecolor=".55", fillcolor=".77")
p = Plot(x=x, y=y).add(m).plot()
line, = p._figure.axes[0].get_lines()
assert same_color(line.get_color(), m.color)
@@ -52,10 +53,11 @@ def test_shared_colors_mapped(self):
m = Path()
p = Plot(x=x, y=y, color=c).add(m).plot()
ax = p._figure.axes[0]
+ colors = p._theme["axes.prop_cycle"].by_key()["color"]
for i, line in enumerate(ax.get_lines()):
- assert same_color(line.get_color(), f"C{i}")
- assert same_color(line.get_markeredgecolor(), f"C{i}")
- assert same_color(line.get_markerfacecolor(), f"C{i}")
+ assert same_color(line.get_color(), colors[i])
+ assert same_color(line.get_markeredgecolor(), colors[i])
+ assert same_color(line.get_markerfacecolor(), colors[i])
def test_separate_colors_mapped(self):
@@ -65,10 +67,11 @@ def test_separate_colors_mapped(self):
m = Path()
p = Plot(x=x, y=y, color=c, fillcolor=d).add(m).plot()
ax = p._figure.axes[0]
+ colors = p._theme["axes.prop_cycle"].by_key()["color"]
for i, line in enumerate(ax.get_lines()):
- assert same_color(line.get_color(), f"C{i // 2}")
- assert same_color(line.get_markeredgecolor(), f"C{i // 2}")
- assert same_color(line.get_markerfacecolor(), f"C{i % 2}")
+ assert same_color(line.get_color(), colors[i // 2])
+ assert same_color(line.get_markeredgecolor(), colors[i // 2])
+ assert same_color(line.get_markerfacecolor(), colors[i % 2])
def test_color_with_alpha(self):
@@ -168,10 +171,10 @@ def test_xy_data(self):
assert_array_equal(verts[0], [5, 2])
assert_array_equal(verts[1], [4, 3])
- def test_props_direct(self):
+ def test_set_properties(self):
x = y = [1, 2, 3]
- m = Paths(color="r", linewidth=1, linestyle=(3, 1))
+ m = Paths(color=".737", linewidth=1, linestyle=(3, 1))
p = Plot(x=x, y=y).add(m).plot()
lines, = p._figure.axes[0].collections
@@ -179,7 +182,7 @@ def test_props_direct(self):
assert lines.get_linewidth().item() == m.linewidth
assert lines.get_linestyle()[0] == (0, list(m.linestyle))
- def test_props_mapped(self):
+ def test_mapped_properties(self):
x = y = [1, 2, 3, 4]
g = ["a", "a", "b", "b"]
@@ -243,6 +246,15 @@ def test_xy_data(self):
assert_array_equal(verts[0], [2, 5])
assert_array_equal(verts[1], [3, 4])
+ def test_single_orient_value(self):
+
+ x = [1, 1, 1]
+ y = [1, 2, 3]
+ p = Plot(x, y).add(Lines()).plot()
+ lines, = p._figure.axes[0].collections
+ paths, = lines.get_paths()
+ assert paths.vertices.shape == (0, 2)
+
class TestRange:
@@ -260,6 +272,17 @@ def test_xy_data(self):
assert_array_equal(verts[0], [x[i], x[i]])
assert_array_equal(verts[1], [ymin[i], ymax[i]])
+ def test_auto_range(self):
+
+ x = [1, 1, 2, 2, 2]
+ y = [1, 2, 3, 4, 5]
+
+ p = Plot(x=x, y=y).add(Range()).plot()
+ lines, = p._figure.axes[0].collections
+ paths = lines.get_paths()
+ assert_array_equal(paths[0].vertices, [(1, 1), (1, 2)])
+ assert_array_equal(paths[1].vertices, [(2, 3), (2, 5)])
+
def test_mapped_color(self):
x = [1, 2, 1, 2]
@@ -269,12 +292,13 @@ def test_mapped_color(self):
p = Plot(x=x, ymin=ymin, ymax=ymax, color=group).add(Range()).plot()
lines, = p._figure.axes[0].collections
+ colors = p._theme["axes.prop_cycle"].by_key()["color"]
for i, path in enumerate(lines.get_paths()):
verts = path.vertices.T
assert_array_equal(verts[0], [x[i], x[i]])
assert_array_equal(verts[1], [ymin[i], ymax[i]])
- assert same_color(lines.get_colors()[i], f"C{i // 2}")
+ assert same_color(lines.get_colors()[i], colors[i // 2])
def test_direct_properties(self):
@@ -282,7 +306,7 @@ def test_direct_properties(self):
ymin = [1, 4]
ymax = [2, 3]
- m = Range(color="r", linewidth=4)
+ m = Range(color=".654", linewidth=4)
p = Plot(x=x, ymin=ymin, ymax=ymax).add(m).plot()
lines, = p._figure.axes[0].collections
diff --git a/tests/_marks/test_text.py b/tests/_marks/test_text.py
new file mode 100644
index 0000000000..241b1742e0
--- /dev/null
+++ b/tests/_marks/test_text.py
@@ -0,0 +1,129 @@
+
+import numpy as np
+from matplotlib.colors import to_rgba
+from matplotlib.text import Text as MPLText
+
+from numpy.testing import assert_array_almost_equal
+
+from seaborn._core.plot import Plot
+from seaborn._marks.text import Text
+
+
+class TestText:
+
+ def get_texts(self, ax):
+ if ax.texts:
+ return list(ax.texts)
+ else:
+ # Compatibility with matplotlib < 3.5 (I think)
+ return [a for a in ax.artists if isinstance(a, MPLText)]
+
+ def test_simple(self):
+
+ x = y = [1, 2, 3]
+ s = list("abc")
+
+ p = Plot(x, y, text=s).add(Text()).plot()
+ ax = p._figure.axes[0]
+ for i, text in enumerate(self.get_texts(ax)):
+ x_, y_ = text.get_position()
+ assert x_ == x[i]
+ assert y_ == y[i]
+ assert text.get_text() == s[i]
+ assert text.get_horizontalalignment() == "center"
+ assert text.get_verticalalignment() == "center_baseline"
+
+ def test_set_properties(self):
+
+ x = y = [1, 2, 3]
+ s = list("abc")
+ color = "red"
+ alpha = .6
+ fontsize = 6
+ valign = "bottom"
+
+ m = Text(color=color, alpha=alpha, fontsize=fontsize, valign=valign)
+ p = Plot(x, y, text=s).add(m).plot()
+ ax = p._figure.axes[0]
+ for i, text in enumerate(self.get_texts(ax)):
+ assert text.get_text() == s[i]
+ assert text.get_color() == to_rgba(m.color, m.alpha)
+ assert text.get_fontsize() == m.fontsize
+ assert text.get_verticalalignment() == m.valign
+
+ def test_mapped_properties(self):
+
+ x = y = [1, 2, 3]
+ s = list("abc")
+ color = list("aab")
+ fontsize = [1, 2, 4]
+
+ p = Plot(x, y, color=color, fontsize=fontsize, text=s).add(Text()).plot()
+ ax = p._figure.axes[0]
+ texts = self.get_texts(ax)
+ assert texts[0].get_color() == texts[1].get_color()
+ assert texts[0].get_color() != texts[2].get_color()
+ assert (
+ texts[0].get_fontsize()
+ < texts[1].get_fontsize()
+ < texts[2].get_fontsize()
+ )
+
+ def test_mapped_alignment(self):
+
+ x = [1, 2]
+ p = Plot(x=x, y=x, halign=x, valign=x, text=x).add(Text()).plot()
+ ax = p._figure.axes[0]
+ t1, t2 = self.get_texts(ax)
+ assert t1.get_horizontalalignment() == "left"
+ assert t2.get_horizontalalignment() == "right"
+ assert t1.get_verticalalignment() == "top"
+ assert t2.get_verticalalignment() == "bottom"
+
+ def test_identity_fontsize(self):
+
+ x = y = [1, 2, 3]
+ s = list("abc")
+ fs = [5, 8, 12]
+ p = Plot(x, y, text=s, fontsize=fs).add(Text()).scale(fontsize=None).plot()
+ ax = p._figure.axes[0]
+ for i, text in enumerate(self.get_texts(ax)):
+ assert text.get_fontsize() == fs[i]
+
+ def test_offset_centered(self):
+
+ x = y = [1, 2, 3]
+ s = list("abc")
+ p = Plot(x, y, text=s).add(Text()).plot()
+ ax = p._figure.axes[0]
+ ax_trans = ax.transData.get_matrix()
+ for text in self.get_texts(ax):
+ assert_array_almost_equal(text.get_transform().get_matrix(), ax_trans)
+
+ def test_offset_valign(self):
+
+ x = y = [1, 2, 3]
+ s = list("abc")
+ m = Text(valign="bottom", fontsize=5, offset=.1)
+ p = Plot(x, y, text=s).add(m).plot()
+ ax = p._figure.axes[0]
+ expected_shift_matrix = np.zeros((3, 3))
+ expected_shift_matrix[1, -1] = m.offset * ax.figure.dpi / 72
+ ax_trans = ax.transData.get_matrix()
+ for text in self.get_texts(ax):
+ shift_matrix = text.get_transform().get_matrix() - ax_trans
+ assert_array_almost_equal(shift_matrix, expected_shift_matrix)
+
+ def test_offset_halign(self):
+
+ x = y = [1, 2, 3]
+ s = list("abc")
+ m = Text(halign="right", fontsize=10, offset=.5)
+ p = Plot(x, y, text=s).add(m).plot()
+ ax = p._figure.axes[0]
+ expected_shift_matrix = np.zeros((3, 3))
+ expected_shift_matrix[0, -1] = -m.offset * ax.figure.dpi / 72
+ ax_trans = ax.transData.get_matrix()
+ for text in self.get_texts(ax):
+ shift_matrix = text.get_transform().get_matrix() - ax_trans
+ assert_array_almost_equal(shift_matrix, expected_shift_matrix)
diff --git a/tests/_stats/test_histogram.py b/tests/_stats/test_histogram.py
index 123305a33e..f70865f0f2 100644
--- a/tests/_stats/test_histogram.py
+++ b/tests/_stats/test_histogram.py
@@ -122,6 +122,11 @@ def test_frequency_stat(self, long_df, single_args):
out = h(long_df, *single_args)
assert (out["y"] * out["space"]).sum() == len(long_df)
+ def test_invalid_stat(self):
+
+ with pytest.raises(ValueError, match="The `stat` parameter for `Hist`"):
+ Hist(stat="invalid")
+
def test_cumulative_count(self, long_df, single_args):
h = Hist(stat="count", cumulative=True)
@@ -157,9 +162,15 @@ def test_common_norm_subset(self, long_df, triple_args):
h = Hist(stat="percent", common_norm=["a"])
out = h(long_df, *triple_args)
- for _, out_part in out.groupby(["a"]):
+ for _, out_part in out.groupby("a"):
assert out_part["y"].sum() == pytest.approx(100)
+ def test_common_norm_warning(self, long_df, triple_args):
+
+ h = Hist(common_norm=["b"])
+ with pytest.warns(UserWarning, match="Undefined variable(s)"):
+ h(long_df, *triple_args)
+
def test_common_bins_default(self, long_df, triple_args):
h = Hist()
@@ -183,10 +194,16 @@ def test_common_bins_subset(self, long_df, triple_args):
h = Hist(common_bins=False)
out = h(long_df, *triple_args)
bins = []
- for _, out_part in out.groupby(["a"]):
+ for _, out_part in out.groupby("a"):
bins.append(tuple(out_part["x"]))
assert len(set(bins)) == out["a"].nunique()
+ def test_common_bins_warning(self, long_df, triple_args):
+
+ h = Hist(common_bins=["b"])
+ with pytest.warns(UserWarning, match="Undefined variable(s)"):
+ h(long_df, *triple_args)
+
def test_histogram_single(self, long_df, single_args):
h = Hist()
diff --git a/tests/_stats/test_order.py b/tests/_stats/test_order.py
new file mode 100644
index 0000000000..eaacdcab8b
--- /dev/null
+++ b/tests/_stats/test_order.py
@@ -0,0 +1,87 @@
+
+import numpy as np
+import pandas as pd
+
+import pytest
+from numpy.testing import assert_array_equal
+
+from seaborn._core.groupby import GroupBy
+from seaborn._stats.order import Perc
+from seaborn.external.version import Version
+
+
+class Fixtures:
+
+ @pytest.fixture
+ def df(self, rng):
+ return pd.DataFrame(dict(x="", y=rng.normal(size=30)))
+
+ def get_groupby(self, df, orient):
+ # TODO note, copied from aggregation
+ other = {"x": "y", "y": "x"}[orient]
+ cols = [c for c in df if c != other]
+ return GroupBy(cols)
+
+
+class TestPerc(Fixtures):
+
+ def test_int_k(self, df):
+
+ ori = "x"
+ gb = self.get_groupby(df, ori)
+ res = Perc(3)(df, gb, ori, {})
+ percentiles = [0, 50, 100]
+ assert_array_equal(res["percentile"], percentiles)
+ assert_array_equal(res["y"], np.percentile(df["y"], percentiles))
+
+ def test_list_k(self, df):
+
+ ori = "x"
+ gb = self.get_groupby(df, ori)
+ percentiles = [0, 20, 100]
+ res = Perc(k=percentiles)(df, gb, ori, {})
+ assert_array_equal(res["percentile"], percentiles)
+ assert_array_equal(res["y"], np.percentile(df["y"], percentiles))
+
+ def test_orientation(self, df):
+
+ df = df.rename(columns={"x": "y", "y": "x"})
+ ori = "y"
+ gb = self.get_groupby(df, ori)
+ res = Perc(k=3)(df, gb, ori, {})
+ assert_array_equal(res["x"], np.percentile(df["x"], [0, 50, 100]))
+
+ def test_method(self, df):
+
+ ori = "x"
+ gb = self.get_groupby(df, ori)
+ method = "nearest"
+ res = Perc(k=5, method=method)(df, gb, ori, {})
+ percentiles = [0, 25, 50, 75, 100]
+ if Version(np.__version__) < Version("1.22.0"):
+ expected = np.percentile(df["y"], percentiles, interpolation=method)
+ else:
+ expected = np.percentile(df["y"], percentiles, method=method)
+ assert_array_equal(res["y"], expected)
+
+ def test_grouped(self, df, rng):
+
+ ori = "x"
+ df = df.assign(x=rng.choice(["a", "b", "c"], len(df)))
+ gb = self.get_groupby(df, ori)
+ k = [10, 90]
+ res = Perc(k)(df, gb, ori, {})
+ for x, res_x in res.groupby("x"):
+ assert_array_equal(res_x["percentile"], k)
+ expected = np.percentile(df.loc[df["x"] == x, "y"], k)
+ assert_array_equal(res_x["y"], expected)
+
+ def test_with_na(self, df):
+
+ ori = "x"
+ df.loc[:5, "y"] = np.nan
+ gb = self.get_groupby(df, ori)
+ k = [10, 90]
+ res = Perc(k)(df, gb, ori, {})
+ expected = np.percentile(df["y"].dropna(), k)
+ assert_array_equal(res["y"], expected)
diff --git a/tests/conftest.py b/tests/conftest.py
index 89375bcd38..0366d1643e 100644
--- a/tests/conftest.py
+++ b/tests/conftest.py
@@ -1,27 +1,13 @@
import numpy as np
import pandas as pd
-import datetime
-import matplotlib as mpl
-import matplotlib.pyplot as plt
import pytest
-@pytest.fixture(scope="session", autouse=True)
-def remove_pandas_unit_conversion():
- # Prior to pandas 1.0, it registered its own datetime converters,
- # but they are less powerful than what matplotlib added in 2.2,
- # and we rely on that functionality in seaborn.
- # https://github.com/matplotlib/matplotlib/pull/9779
- # https://github.com/pandas-dev/pandas/issues/27036
- mpl.units.registry[np.datetime64] = mpl.dates.DateConverter()
- mpl.units.registry[datetime.date] = mpl.dates.DateConverter()
- mpl.units.registry[datetime.datetime] = mpl.dates.DateConverter()
-
-
@pytest.fixture(autouse=True)
def close_figs():
yield
+ import matplotlib.pyplot as plt
plt.close("all")
diff --git a/tests/test_axisgrid.py b/tests/test_axisgrid.py
index ed1a1cd721..af8bf19da8 100644
--- a/tests/test_axisgrid.py
+++ b/tests/test_axisgrid.py
@@ -5,7 +5,7 @@
import pytest
import numpy.testing as npt
-from numpy.testing import assert_array_equal
+from numpy.testing import assert_array_equal, assert_array_almost_equal
try:
import pandas.testing as tm
except ImportError:
@@ -1685,12 +1685,12 @@ def test_scatter(self):
assert_array_equal(self.x, x)
assert_array_equal(self.y, y)
- assert_array_equal(
+ assert_array_almost_equal(
[b.get_x() for b in g.ax_marg_x.patches],
np.histogram_bin_edges(self.x, "auto")[:-1],
)
- assert_array_equal(
+ assert_array_almost_equal(
[b.get_y() for b in g.ax_marg_y.patches],
np.histogram_bin_edges(self.y, "auto")[:-1],
)
diff --git a/tests/test_categorical.py b/tests/test_categorical.py
index 1bbc67c517..8b84c40801 100644
--- a/tests/test_categorical.py
+++ b/tests/test_categorical.py
@@ -342,7 +342,7 @@ def test_longform_groupby(self):
p1 = cat._CategoricalPlotter()
p1.establish_variables(self.g, self.y, hue=self.h)
p2 = cat._CategoricalPlotter()
- p2.establish_variables(self.g, self.y[::-1], self.h)
+ p2.establish_variables(self.g, self.y.iloc[::-1], self.h)
for i, (d1, d2) in enumerate(zip(p1.plot_data, p2.plot_data)):
assert np.array_equal(d1.sort_index(), d2.sort_index())
@@ -616,7 +616,7 @@ def test_nested_stats(self):
y.groupby([g, h]).mean().unstack())
for ci_g, (_, grp_y) in zip(p.confint, y.groupby(g)):
- for ci, hue_y in zip(ci_g, [grp_y[::2], grp_y[1::2]]):
+ for ci, hue_y in zip(ci_g, [grp_y.iloc[::2], grp_y.iloc[1::2]]):
sem = hue_y.std() / np.sqrt(len(hue_y))
mean = hue_y.mean()
half_ci = _normal_quantile_func(.975) * sem
@@ -734,7 +734,7 @@ def test_nested_sd_error_bars(self):
y.groupby([g, h]).mean().unstack())
for ci_g, (_, grp_y) in zip(p.confint, y.groupby(g)):
- for ci, hue_y in zip(ci_g, [grp_y[::2], grp_y[1::2]]):
+ for ci, hue_y in zip(ci_g, [grp_y.iloc[::2], grp_y.iloc[1::2]]):
mean = hue_y.mean()
half_ci = np.std(hue_y)
ci_want = mean - half_ci, mean + half_ci
diff --git a/tests/test_core.py b/tests/test_core.py
index 9f657f7fd2..798a8d61fa 100644
--- a/tests/test_core.py
+++ b/tests/test_core.py
@@ -1140,12 +1140,12 @@ def test_attach_converters(self, long_df):
p = VectorPlotter(data=long_df, variables={"x": "x", "y": "t"})
p._attach(ax)
assert ax.xaxis.converter is None
- assert isinstance(ax.yaxis.converter, mpl.dates.DateConverter)
+ assert "Date" in ax.yaxis.converter.__class__.__name__
_, ax = plt.subplots()
p = VectorPlotter(data=long_df, variables={"x": "a", "y": "y"})
p._attach(ax)
- assert isinstance(ax.xaxis.converter, mpl.category.StrCategoryConverter)
+ assert "CategoryConverter" in ax.xaxis.converter.__class__.__name__
assert ax.yaxis.converter is None
def test_attach_facets(self, long_df):
diff --git a/tests/test_distributions.py b/tests/test_distributions.py
index 2b53d51a34..c4d62fa340 100644
--- a/tests/test_distributions.py
+++ b/tests/test_distributions.py
@@ -1060,6 +1060,15 @@ def test_contour_line_colors(self, long_df):
for c in ax.collections:
assert_colors_equal(get_contour_color(c), color)
+ def test_contour_line_cmap(self, long_df):
+
+ color_list = color_palette("Blues", 12)
+ cmap = mpl.colors.ListedColormap(color_list)
+ ax = kdeplot(data=long_df, x="x", y="y", cmap=cmap)
+ for c in ax.collections:
+ color = to_rgb(get_contour_color(c).squeeze())
+ assert color in color_list
+
def test_contour_fill_colors(self, long_df):
n = 6
@@ -1421,8 +1430,8 @@ def test_unique_bins(self, wide_df):
bars = bar_groups[i]
start = bars[0].get_x()
stop = bars[-1].get_x() + bars[-1].get_width()
- assert start == wide_df[col].min()
- assert stop == wide_df[col].max()
+ assert_array_almost_equal(start, wide_df[col].min())
+ assert_array_almost_equal(stop, wide_df[col].max())
def test_weights_with_missing(self, missing_df):
@@ -1559,7 +1568,6 @@ def test_kde_line_kws(self, flat_series):
def test_kde_singular_data(self):
-
with warnings.catch_warnings():
warnings.simplefilter("error")
ax = histplot(x=np.ones(10), kde=True)
diff --git a/tests/test_objects.py b/tests/test_objects.py
new file mode 100644
index 0000000000..5f7f5b9f91
--- /dev/null
+++ b/tests/test_objects.py
@@ -0,0 +1,14 @@
+import seaborn.objects
+from seaborn._core.plot import Plot
+from seaborn._core.moves import Move
+from seaborn._core.scales import Scale
+from seaborn._marks.base import Mark
+from seaborn._stats.base import Stat
+
+
+def test_objects_namespace():
+
+ for name in dir(seaborn.objects):
+ if not name.startswith("__"):
+ obj = getattr(seaborn.objects, name)
+ assert issubclass(obj, (Plot, Mark, Stat, Move, Scale))
diff --git a/tests/test_palettes.py b/tests/test_palettes.py
index cda371c089..4d9e9f916e 100644
--- a/tests/test_palettes.py
+++ b/tests/test_palettes.py
@@ -416,9 +416,24 @@ def test_preserved_palette_length(self):
pal_out = palettes.color_palette(pal_in)
assert pal_in == pal_out
- def test_html_rep(self):
+ def test_html_repr(self):
pal = palettes.color_palette()
html = pal._repr_html_()
for color in pal.as_hex():
assert color in html
+
+ def test_colormap_display_patch(self):
+
+ orig_repr_png = getattr(mpl.colors.Colormap, "_repr_png_", None)
+ orig_repr_html = getattr(mpl.colors.Colormap, "_repr_html_", None)
+
+ try:
+ palettes._patch_colormap_display()
+ cmap = mpl.cm.Reds
+ assert cmap._repr_html_().startswith('