diff --git a/2D-Histogram.ipynb b/2D-Histogram.ipynb new file mode 100644 index 000000000..8f4d98891 --- /dev/null +++ b/2D-Histogram.ipynb @@ -0,0 +1,454 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "1eef2039", + "metadata": {}, + "source": [ + "## 2D Histograms or Density Heatmaps\n", + "\n", + "A 2D histogram, also known as a density heatmap, is the 2-dimensional generalization of a [histogram](/python/histograms/) which resembles a [heatmap](/python/heatmaps/) but is computed by grouping a set of points specified by their `x` and `y` coordinates into bins, and applying an aggregation function such as `count` or `sum` (if `z` is provided) to compute the color of the tile representing the bin. This kind of visualization (and the related [2D histogram contour, or density contour](https://plotly.com/python/2d-histogram-contour/)) is often used to manage over-plotting, or situations where showing large data sets as [scatter plots](/python/line-and-scatter/) would result in points overlapping each other and hiding patterns. For data sets of more than a few thousand points, a better approach than the ones listed here would be to [use Plotly with Datashader](/python/datashader/) to precompute the aggregations before displaying the data with Plotly.\n", + "\n", + "## Density Heatmaps with Plotly Express\n", + "\n", + "[Plotly Express](/python/plotly-express/) is the easy-to-use, high-level interface to Plotly, which [operates on a variety of types of data](/python/px-arguments/) and produces [easy-to-style figures](/python/styling-plotly-express/). The Plotly Express function `density_heatmap()` can be used to produce density heatmaps." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "c1a828fe", + "metadata": {}, + "outputs": [], + "source": [ + "import plotly.express as px\n", + "df = px.data.tips()\n", + "\n", + "fig = px.density_heatmap(df, x=\"total_bill\", y=\"tip\")\n", + "fig.show()" + ] + }, + { + "cell_type": "markdown", + "id": "cd127a2f", + "metadata": {}, + "source": [ + "The number of bins can be controlled with `nbinsx` and `nbinsy` and the [color scale](/python/colorscales/) with `color_continuous_scale`." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "439e9b3d", + "metadata": {}, + "outputs": [], + "source": [ + "import plotly.express as px\n", + "df = px.data.tips()\n", + "\n", + "fig = px.density_heatmap(df, x=\"total_bill\", y=\"tip\", nbinsx=20, nbinsy=20, color_continuous_scale=\"Viridis\")\n", + "fig.show()" + ] + }, + { + "cell_type": "markdown", + "id": "474e326c", + "metadata": {}, + "source": [ + "Marginal plots can be added to visualize the 1-dimensional distributions of the two variables. Here we use a marginal [`histogram`](/python/histograms/). Other allowable values are `violin`, `box` and `rug`." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "f68ba4b2", + "metadata": {}, + "outputs": [], + "source": [ + "import plotly.express as px\n", + "df = px.data.tips()\n", + "\n", + "fig = px.density_heatmap(df, x=\"total_bill\", y=\"tip\", marginal_x=\"histogram\", marginal_y=\"histogram\")\n", + "fig.show()" + ] + }, + { + "cell_type": "markdown", + "id": "0a64cdef", + "metadata": {}, + "source": [ + "Density heatmaps can also be [faceted](/python/facet-plots/):" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "4fad0a36", + "metadata": {}, + "outputs": [], + "source": [ + "import plotly.express as px\n", + "df = px.data.tips()\n", + "\n", + "fig = px.density_heatmap(df, x=\"total_bill\", y=\"tip\", facet_row=\"sex\", facet_col=\"smoker\")\n", + "fig.show()" + ] + }, + { + "cell_type": "markdown", + "id": "03d41dab", + "metadata": {}, + "source": [ + "### Displaying Text\n", + "\n", + "*New in v5.5*\n", + "\n", + "You can add the `z` values as text using the `text_auto` argument. Setting it to `True` will display the values on the bars, and setting it to a `d3-format` formatting string will control the output format." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "28f6af46", + "metadata": {}, + "outputs": [], + "source": [ + "import plotly.express as px\n", + "df = px.data.tips()\n", + "\n", + "fig = px.density_heatmap(df, x=\"total_bill\", y=\"tip\", text_auto=True)\n", + "fig.show()" + ] + }, + { + "cell_type": "markdown", + "id": "5c7c46fe", + "metadata": {}, + "source": [ + "### Other aggregation functions than `count`\n", + "\n", + "By passing in a `z` value and a `histfunc`, density heatmaps can perform basic aggregation operations. Here we show average Sepal Length grouped by Petal Length and Petal Width for the Iris dataset." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "7a678676", + "metadata": {}, + "outputs": [], + "source": [ + "import plotly.express as px\n", + "df = px.data.iris()\n", + "\n", + "fig = px.density_heatmap(df, x=\"petal_length\", y=\"petal_width\", z=\"sepal_length\", histfunc=\"avg\")\n", + "fig.show()" + ] + }, + { + "cell_type": "markdown", + "id": "c9f14e88", + "metadata": {}, + "source": [ + "### 2D Histograms with Graph Objects\n", + "\n", + "To build this kind of figure using [graph objects](/python/graph-objects/) without using Plotly Express, we can use the `go.Histogram2d` class." + ] + }, + { + "cell_type": "markdown", + "id": "dfe41deb", + "metadata": {}, + "source": [ + "### 2D Histogram of a Bivariate Normal Distribution ###" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "a6e91ba3", + "metadata": {}, + "outputs": [], + "source": [ + "import plotly.graph_objects as go\n", + "\n", + "import numpy as np\n", + "np.random.seed(1)\n", + "\n", + "x = np.random.randn(500)\n", + "y = np.random.randn(500)+1\n", + "\n", + "fig = go.Figure(go.Histogram2d(\n", + " x=x,\n", + " y=y\n", + " ))\n", + "fig.show()" + ] + }, + { + "cell_type": "markdown", + "id": "1e1866a7", + "metadata": {}, + "source": [ + "### 2D Histogram Binning and Styling Options ###" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "7dfaf077", + "metadata": { + "lines_to_next_cell": 0 + }, + "outputs": [], + "source": [ + "import plotly.graph_objects as go\n", + "\n", + "import numpy as np\n", + "\n", + "x = np.random.randn(500)\n", + "y = np.random.randn(500)+1\n", + "\n", + "fig = go.Figure(go.Histogram2d(x=x, y=y, histnorm='probability',\n", + " autobinx=False,\n", + " xbins=dict(start=-3, end=3, size=0.1),\n", + " autobiny=False,\n", + " ybins=dict(start=-2.5, end=4, size=0.1),\n", + " colorscale=[[0, 'rgb(12,51,131)'], [0.25, 'rgb(10,136,186)'], [0.5, 'rgb(242,211,56)'], [0.75, 'rgb(242,143,56)'], [1, 'rgb(217,30,30)']]\n", + " ))\n", + "fig.show()" + ] + }, + { + "cell_type": "markdown", + "id": "28d202d8", + "metadata": {}, + "source": [ + "### Sharing bin settings between 2D Histograms\n", + "This example shows how to use [bingroup](https://plotly.com/python/reference/histogram/#histogram-bingroup) attribute to have a compatible bin settings for both histograms. To define `start`, `end` and `size` value of x-axis and y-axis separately, set [ybins](https://plotly.com/python/reference/histogram2dcontour/#histogram2dcontour-ybins) and `xbins`." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "7cd12899", + "metadata": {}, + "outputs": [], + "source": [ + "import plotly.graph_objects as go\n", + "from plotly.subplots import make_subplots\n", + "\n", + "fig = make_subplots(2,2)\n", + "fig.add_trace(go.Histogram2d(\n", + " x = [ 1, 2, 2, 3, 4 ],\n", + " y = [ 1, 2, 2, 3, 4 ],\n", + " coloraxis = \"coloraxis\",\n", + " xbins = {'start':1, 'size':1}), 1,1)\n", + "fig.add_trace(go.Histogram2d(\n", + " x = [ 4, 5, 5, 5, 6 ],\n", + " y = [ 4, 5, 5, 5, 6 ],\n", + " coloraxis = \"coloraxis\",\n", + " ybins = {'start': 3, 'size': 1}),1,2)\n", + "fig.add_trace(go.Histogram2d(\n", + " x = [ 1, 2, 2, 3, 4 ],\n", + " y = [ 1, 2, 2, 3, 4 ],\n", + " bingroup = 1,\n", + " coloraxis = \"coloraxis\",\n", + " xbins = {'start':1, 'size':1}), 2,1)\n", + "fig.add_trace(go.Histogram2d(\n", + " x = [ 4, 5, 5, 5, 6 ],\n", + " y = [ 4, 5, 5, 5, 6 ],\n", + " bingroup = 1,\n", + " coloraxis = \"coloraxis\",\n", + " ybins = {'start': 3, 'size': 1}),2,2)\n", + "fig.show()" + ] + }, + { + "cell_type": "markdown", + "id": "46e75035", + "metadata": {}, + "source": [ + "### 2D Histogram Overlaid with a Scatter Chart ###" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "865bbcc9", + "metadata": {}, + "outputs": [], + "source": [ + "import plotly.graph_objects as go\n", + "\n", + "import numpy as np\n", + "\n", + "x0 = np.random.randn(100)/5. + 0.5 # 5. enforces float division\n", + "y0 = np.random.randn(100)/5. + 0.5\n", + "x1 = np.random.rand(50)\n", + "y1 = np.random.rand(50) + 1.0\n", + "\n", + "x = np.concatenate([x0, x1])\n", + "y = np.concatenate([y0, y1])\n", + "\n", + "fig = go.Figure()\n", + "\n", + "fig.add_trace(go.Scatter(\n", + " x=x0,\n", + " y=y0,\n", + " mode='markers',\n", + " showlegend=False,\n", + " marker=dict(\n", + " symbol='x',\n", + " opacity=0.7,\n", + " color='white',\n", + " size=8,\n", + " line=dict(width=1),\n", + " )\n", + "))\n", + "fig.add_trace(go.Scatter(\n", + " x=x1,\n", + " y=y1,\n", + " mode='markers',\n", + " showlegend=False,\n", + " marker=dict(\n", + " symbol='circle',\n", + " opacity=0.7,\n", + " color='white',\n", + " size=8,\n", + " line=dict(width=1),\n", + " )\n", + "))\n", + "fig.add_trace(go.Histogram2d(\n", + " x=x,\n", + " y=y,\n", + " colorscale='YlGnBu',\n", + " zmax=10,\n", + " nbinsx=14,\n", + " nbinsy=14,\n", + " zauto=False,\n", + "))\n", + "\n", + "fig.update_layout(\n", + " xaxis=dict( ticks='', showgrid=False, zeroline=False, nticks=20 ),\n", + " yaxis=dict( ticks='', showgrid=False, zeroline=False, nticks=20 ),\n", + " autosize=False,\n", + " height=550,\n", + " width=550,\n", + " hovermode='closest',\n", + "\n", + ")\n", + "\n", + "fig.show()" + ] + }, + { + "cell_type": "markdown", + "id": "b18e1cc2", + "metadata": {}, + "source": [ + "### Text on 2D Histogram Points\n", + "\n", + "In this example we add text to 2D Histogram points. We use the values from the `z` attribute for the text." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "14816b45", + "metadata": {}, + "outputs": [], + "source": [ + "import plotly.graph_objects as go\n", + "from plotly import data\n", + "\n", + "df = data.tips()\n", + "\n", + "fig = go.Figure(go.Histogram2d(\n", + " x=df.total_bill,\n", + " y=df.tip,\n", + " texttemplate= \"%{z}\"\n", + " ))\n", + "\n", + "fig.show()" + ] + }, + { + "cell_type": "markdown", + "id": "fac86a3a", + "metadata": {}, + "source": [ + "#### Reference\n", + "See https://plotly.com/python/reference/histogram2d/ for more information and chart attribute options!\n" + ] + }, + { + "cell_type": "markdown", + "id": "4aad9065", + "metadata": {}, + "source": [ + "### What About Dash?\n", + "\n", + "[Dash](https://dash.plot.ly/) is an open-source framework for building analytical applications, with no Javascript required, and it is tightly integrated with the Plotly graphing library.\n", + "\n", + "Learn about how to install Dash at https://dash.plot.ly/installation.\n", + "\n", + "Everywhere in this page that you see `fig.show()`, you can display the same figure in a Dash application by passing it to the `figure` argument of the [`Graph` component](https://dash.plot.ly/dash-core-components/graph) from the built-in `dash_core_components` package like this:\n", + "\n", + "```python\n", + "import plotly.graph_objects as go # or plotly.express as px\n", + "fig = go.Figure() # or any Plotly Express function e.g. px.bar(...)\n", + "# fig.add_trace( ... )\n", + "# fig.update_layout( ... )\n", + "\n", + "from dash import Dash, dcc, html\n", + "\n", + "app = Dash()\n", + "app.layout = html.Div([\n", + " dcc.Graph(figure=fig)\n", + "])\n", + "\n", + "app.run_server(debug=True, use_reloader=False) # Turn off reloader if inside Jupyter\n", + "```" + ] + } + ], + "metadata": { + "jupytext": { + "notebook_metadata_filter": "all" + }, + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "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.8.11" + }, + "plotly": { + "description": "How to make 2D Histograms in Python with Plotly.", + "display_as": "statistical", + "language": "python", + "layout": "base", + "name": "2D Histograms", + "order": 5, + "page_type": "u-guide", + "permalink": "python/2D-Histogram/", + "redirect_from": [ + "python/2d-histogram/", + "python/2d-histograms/" + ], + "thumbnail": "thumbnail/histogram2d.jpg" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/2d-histogram-contour.ipynb b/2d-histogram-contour.ipynb new file mode 100644 index 000000000..8f8553a80 --- /dev/null +++ b/2d-histogram-contour.ipynb @@ -0,0 +1,401 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "9330d1bc", + "metadata": {}, + "source": [ + "## 2D Histogram Contours or Density Contours\n", + "\n", + "A 2D histogram contour plot, also known as a density contour plot, is a 2-dimensional generalization of a [histogram](/python/histograms/) which resembles a [contour plot](/python/contour-plots/) but is computed by grouping a set of points specified by their `x` and `y` coordinates into bins, and applying an aggregation function such as `count` or `sum` (if `z` is provided) to compute the value to be used to compute contours. This kind of visualization (and the related [2D histogram, or density heatmap](/python/2d-histogram/)) is often used to manage over-plotting, or situations where showing large data sets as [scatter plots](/python/line-and-scatter/) would result in points overlapping each other and hiding patterns.\n", + "\n", + "## Density Contours with Plotly Express\n", + "\n", + "[Plotly Express](/python/plotly-express/) is the easy-to-use, high-level interface to Plotly, which [operates on a variety of types of data](/python/px-arguments/) and produces [easy-to-style figures](/python/styling-plotly-express/). The Plotly Express function `density_contour()` can be used to produce density contours." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "1055f4a2", + "metadata": {}, + "outputs": [], + "source": [ + "import plotly.express as px\n", + "df = px.data.tips()\n", + "\n", + "fig = px.density_contour(df, x=\"total_bill\", y=\"tip\")\n", + "fig.show()" + ] + }, + { + "cell_type": "markdown", + "id": "e5db23f2", + "metadata": {}, + "source": [ + "Marginal plots can be added to visualize the 1-dimensional distributions of the two variables. Here we use a marginal [`histogram`](/python/histograms/). Other allowable values are `violin`, `box` and `rug`." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "0551df8f", + "metadata": {}, + "outputs": [], + "source": [ + "import plotly.express as px\n", + "df = px.data.tips()\n", + "\n", + "fig = px.density_contour(df, x=\"total_bill\", y=\"tip\", marginal_x=\"histogram\", marginal_y=\"histogram\")\n", + "fig.show()" + ] + }, + { + "cell_type": "markdown", + "id": "4c657367", + "metadata": {}, + "source": [ + "Density contours can also be [faceted](/python/facet-plots/) and [discretely colored](/python/discrete-color/):" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "99e14f6e", + "metadata": {}, + "outputs": [], + "source": [ + "import plotly.express as px\n", + "df = px.data.tips()\n", + "\n", + "fig = px.density_contour(df, x=\"total_bill\", y=\"tip\", facet_col=\"sex\", color=\"smoker\")\n", + "fig.show()" + ] + }, + { + "cell_type": "markdown", + "id": "405f46d4", + "metadata": {}, + "source": [ + "Plotly Express density contours can be [continuously-colored](/python/colorscales/) and labeled:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "ab260026", + "metadata": {}, + "outputs": [], + "source": [ + "import plotly.express as px\n", + "df = px.data.tips()\n", + "\n", + "fig = px.density_contour(df, x=\"total_bill\", y=\"tip\")\n", + "fig.update_traces(contours_coloring=\"fill\", contours_showlabels = True)\n", + "fig.show()" + ] + }, + { + "cell_type": "markdown", + "id": "5d2c5c02", + "metadata": {}, + "source": [ + "### Other aggregation functions than `count`\n", + "\n", + "By passing in a `z` value and a `histfunc`, density contours can perform basic aggregation operations. Here we show average Sepal Length grouped by Petal Length and Petal Width for the Iris dataset." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "c3aaff55", + "metadata": {}, + "outputs": [], + "source": [ + "import plotly.express as px\n", + "df = px.data.iris()\n", + "\n", + "fig = px.density_contour(df, x=\"petal_length\", y=\"petal_width\", z=\"sepal_length\", histfunc=\"avg\")\n", + "fig.show()" + ] + }, + { + "cell_type": "markdown", + "id": "f7df9bb9", + "metadata": {}, + "source": [ + "### 2D Histograms with Graph Objects\n", + "\n", + "To build this kind of figure with [graph objects](/python/graph-objects/) without using Plotly Express, we can use the `go.Histogram2d` class.\n", + "\n", + "#### Basic 2D Histogram Contour" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "a56ae5df", + "metadata": {}, + "outputs": [], + "source": [ + "import plotly.graph_objects as go\n", + "\n", + "import numpy as np\n", + "np.random.seed(1)\n", + "\n", + "x = np.random.uniform(-1, 1, size=500)\n", + "y = np.random.uniform(-1, 1, size=500)\n", + "\n", + "fig = go.Figure(go.Histogram2dContour(\n", + " x = x,\n", + " y = y\n", + "))\n", + "\n", + "fig.show()" + ] + }, + { + "cell_type": "markdown", + "id": "5d40ea2a", + "metadata": {}, + "source": [ + "#### 2D Histogram Contour Colorscale" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "47bde4de", + "metadata": {}, + "outputs": [], + "source": [ + "import plotly.graph_objects as go\n", + "\n", + "import numpy as np\n", + "\n", + "x = np.random.uniform(-1, 1, size=500)\n", + "y = np.random.uniform(-1, 1, size=500)\n", + "\n", + "fig = go.Figure(go.Histogram2dContour(\n", + " x = x,\n", + " y = y,\n", + " colorscale = 'Blues'\n", + "))\n", + "\n", + "fig.show()" + ] + }, + { + "cell_type": "markdown", + "id": "515902ec", + "metadata": {}, + "source": [ + "#### 2D Histogram Contour Styled" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "ead9b5b6", + "metadata": {}, + "outputs": [], + "source": [ + "import plotly.graph_objects as go\n", + "\n", + "import numpy as np\n", + "\n", + "x = np.random.uniform(-1, 1, size=500)\n", + "y = np.random.uniform(-1, 1, size=500)\n", + "\n", + "fig = go.Figure(go.Histogram2dContour(\n", + " x = x,\n", + " y = y,\n", + " colorscale = 'Jet',\n", + " contours = dict(\n", + " showlabels = True,\n", + " labelfont = dict(\n", + " family = 'Raleway',\n", + " color = 'white'\n", + " )\n", + " ),\n", + " hoverlabel = dict(\n", + " bgcolor = 'white',\n", + " bordercolor = 'black',\n", + " font = dict(\n", + " family = 'Raleway',\n", + " color = 'black'\n", + " )\n", + " )\n", + "\n", + "))\n", + "\n", + "fig.show()" + ] + }, + { + "cell_type": "markdown", + "id": "dc6e8520", + "metadata": {}, + "source": [ + "#### 2D Histogram Contour Subplot" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "ab165388", + "metadata": {}, + "outputs": [], + "source": [ + "import plotly.graph_objects as go\n", + "\n", + "import numpy as np\n", + "\n", + "t = np.linspace(-1, 1.2, 2000)\n", + "x = (t**3) + (0.3 * np.random.randn(2000))\n", + "y = (t**6) + (0.3 * np.random.randn(2000))\n", + "\n", + "fig = go.Figure()\n", + "fig.add_trace(go.Histogram2dContour(\n", + " x = x,\n", + " y = y,\n", + " colorscale = 'Blues',\n", + " reversescale = True,\n", + " xaxis = 'x',\n", + " yaxis = 'y'\n", + " ))\n", + "fig.add_trace(go.Scatter(\n", + " x = x,\n", + " y = y,\n", + " xaxis = 'x',\n", + " yaxis = 'y',\n", + " mode = 'markers',\n", + " marker = dict(\n", + " color = 'rgba(0,0,0,0.3)',\n", + " size = 3\n", + " )\n", + " ))\n", + "fig.add_trace(go.Histogram(\n", + " y = y,\n", + " xaxis = 'x2',\n", + " marker = dict(\n", + " color = 'rgba(0,0,0,1)'\n", + " )\n", + " ))\n", + "fig.add_trace(go.Histogram(\n", + " x = x,\n", + " yaxis = 'y2',\n", + " marker = dict(\n", + " color = 'rgba(0,0,0,1)'\n", + " )\n", + " ))\n", + "\n", + "fig.update_layout(\n", + " autosize = False,\n", + " xaxis = dict(\n", + " zeroline = False,\n", + " domain = [0,0.85],\n", + " showgrid = False\n", + " ),\n", + " yaxis = dict(\n", + " zeroline = False,\n", + " domain = [0,0.85],\n", + " showgrid = False\n", + " ),\n", + " xaxis2 = dict(\n", + " zeroline = False,\n", + " domain = [0.85,1],\n", + " showgrid = False\n", + " ),\n", + " yaxis2 = dict(\n", + " zeroline = False,\n", + " domain = [0.85,1],\n", + " showgrid = False\n", + " ),\n", + " height = 600,\n", + " width = 600,\n", + " bargap = 0,\n", + " hovermode = 'closest',\n", + " showlegend = False\n", + ")\n", + "\n", + "fig.show()" + ] + }, + { + "cell_type": "markdown", + "id": "e655f124", + "metadata": {}, + "source": [ + "#### Reference\n", + "See https://plotly.com/python/reference/histogram2dcontour/ for more information and chart attribute options!\n" + ] + }, + { + "cell_type": "markdown", + "id": "455e355f", + "metadata": {}, + "source": [ + "### What About Dash?\n", + "\n", + "[Dash](https://dash.plot.ly/) is an open-source framework for building analytical applications, with no Javascript required, and it is tightly integrated with the Plotly graphing library.\n", + "\n", + "Learn about how to install Dash at https://dash.plot.ly/installation.\n", + "\n", + "Everywhere in this page that you see `fig.show()`, you can display the same figure in a Dash application by passing it to the `figure` argument of the [`Graph` component](https://dash.plot.ly/dash-core-components/graph) from the built-in `dash_core_components` package like this:\n", + "\n", + "```python\n", + "import plotly.graph_objects as go # or plotly.express as px\n", + "fig = go.Figure() # or any Plotly Express function e.g. px.bar(...)\n", + "# fig.add_trace( ... )\n", + "# fig.update_layout( ... )\n", + "\n", + "from dash import Dash, dcc, html\n", + "\n", + "app = Dash()\n", + "app.layout = html.Div([\n", + " dcc.Graph(figure=fig)\n", + "])\n", + "\n", + "app.run_server(debug=True, use_reloader=False) # Turn off reloader if inside Jupyter\n", + "```" + ] + } + ], + "metadata": { + "jupytext": { + "notebook_metadata_filter": "all" + }, + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "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.6.8" + }, + "plotly": { + "description": "How to make 2D Histogram Contour plots in Python with Plotly.", + "display_as": "statistical", + "language": "python", + "layout": "base", + "name": "2D Histogram Contour", + "order": 11, + "page_type": "u-guide", + "permalink": "python/2d-histogram-contour/", + "redirect_from": "python/2d-density-plots/", + "thumbnail": "thumbnail/hist2dcontour.png" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/3d-axes.ipynb b/3d-axes.ipynb new file mode 100644 index 000000000..9f1f213dc --- /dev/null +++ b/3d-axes.ipynb @@ -0,0 +1,395 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "68bb6fe8", + "metadata": {}, + "source": [ + "### Range of axes\n", + "\n", + "3D figures have an attribute in `layout` called `scene`, which contains\n", + "attributes such as `xaxis`, `yaxis` and `zaxis` parameters, in order to\n", + "set the range, title, ticks, color etc. of the axes.\n", + "\n", + "For creating 3D charts, see [this page](https://plotly.com/python/3d-charts/).\n", + "\n", + "Set `range` on an axis to manually configure a range for that axis. If you don't set `range`, it's automatically calculated. In this example, we set a `range` on `xaxis`, `yaxis`, and `zaxis`." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "b6f2d4dc", + "metadata": {}, + "outputs": [], + "source": [ + "import plotly.graph_objects as go\n", + "import numpy as np\n", + "np.random.seed(1)\n", + "\n", + "N = 70\n", + "\n", + "fig = go.Figure(data=[go.Mesh3d(x=(70*np.random.randn(N)),\n", + " y=(55*np.random.randn(N)),\n", + " z=(40*np.random.randn(N)),\n", + " opacity=0.5,\n", + " color='rgba(244,22,100,0.6)'\n", + " )])\n", + "\n", + "fig.update_layout(\n", + " scene = dict(\n", + " xaxis = dict(nticks=4, range=[-100,100],),\n", + " yaxis = dict(nticks=4, range=[-50,100],),\n", + " zaxis = dict(nticks=4, range=[-100,100],),),\n", + " width=700,\n", + " margin=dict(r=20, l=10, b=10, t=10))\n", + "\n", + "fig.show()" + ] + }, + { + "cell_type": "markdown", + "id": "c9e11629", + "metadata": {}, + "source": [ + "### Setting only a Lower or Upper Bound for Range\n", + "\n", + "*New in 5.17*\n", + "\n", + "You can also set just a lower or upper bound for `range`. In this case, autorange is used for the other bound. In this example, we apply autorange to the lower bound of the `yaxis` and the upper bound of `zaxis` by setting them to `None`." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "1d6c29ba", + "metadata": {}, + "outputs": [], + "source": [ + "import plotly.graph_objects as go\n", + "import numpy as np\n", + "np.random.seed(1)\n", + "\n", + "N = 70\n", + "\n", + "fig = go.Figure(data=[go.Mesh3d(x=(70*np.random.randn(N)),\n", + " y=(55*np.random.randn(N)),\n", + " z=(40*np.random.randn(N)),\n", + " opacity=0.5,\n", + " color='rgba(244,22,100,0.6)'\n", + " )])\n", + "\n", + "fig.update_layout(\n", + " scene = dict(\n", + " xaxis = dict(nticks=4, range=[-100,100],),\n", + " yaxis = dict(nticks=4, range=[None, 100],),\n", + " zaxis = dict(nticks=4, range=[-100, None],),),\n", + " width=700,\n", + " margin=dict(r=20, l=10, b=10, t=10))\n", + "\n", + "fig.show()" + ] + }, + { + "cell_type": "markdown", + "id": "fd61d49e", + "metadata": {}, + "source": [ + "### Fixed Ratio Axes" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "4478ccb7", + "metadata": {}, + "outputs": [], + "source": [ + "import plotly.graph_objects as go\n", + "from plotly.subplots import make_subplots\n", + "import numpy as np\n", + "\n", + "N = 50\n", + "\n", + "fig = make_subplots(rows=2, cols=2,\n", + " specs=[[{'is_3d': True}, {'is_3d': True}],\n", + " [{'is_3d': True}, {'is_3d': True}]],\n", + " print_grid=False)\n", + "for i in [1,2]:\n", + " for j in [1,2]:\n", + " fig.add_trace(\n", + " go.Mesh3d(\n", + " x=(60*np.random.randn(N)),\n", + " y=(25*np.random.randn(N)),\n", + " z=(40*np.random.randn(N)),\n", + " opacity=0.5,\n", + " ),\n", + " row=i, col=j)\n", + "\n", + "fig.update_layout(width=700, margin=dict(r=10, l=10, b=10, t=10))\n", + "# fix the ratio in the top left subplot to be a cube\n", + "fig.update_layout(scene_aspectmode='cube')\n", + "# manually force the z-axis to appear twice as big as the other two\n", + "fig.update_layout(scene2_aspectmode='manual',\n", + " scene2_aspectratio=dict(x=1, y=1, z=2))\n", + "# draw axes in proportion to the proportion of their ranges\n", + "fig.update_layout(scene3_aspectmode='data')\n", + "# automatically produce something that is well proportioned using 'data' as the default\n", + "fig.update_layout(scene4_aspectmode='auto')\n", + "fig.show()" + ] + }, + { + "cell_type": "markdown", + "id": "38f8ca0d", + "metadata": {}, + "source": [ + "### Set Axes Title" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "a59f6639", + "metadata": {}, + "outputs": [], + "source": [ + "import plotly.graph_objects as go\n", + "import numpy as np\n", + "\n", + "# Define random surface\n", + "N = 50\n", + "fig = go.Figure()\n", + "fig.add_trace(go.Mesh3d(x=(60*np.random.randn(N)),\n", + " y=(25*np.random.randn(N)),\n", + " z=(40*np.random.randn(N)),\n", + " opacity=0.5,\n", + " color='yellow'\n", + " ))\n", + "fig.add_trace(go.Mesh3d(x=(70*np.random.randn(N)),\n", + " y=(55*np.random.randn(N)),\n", + " z=(30*np.random.randn(N)),\n", + " opacity=0.5,\n", + " color='pink'\n", + " ))\n", + "\n", + "fig.update_layout(scene = dict(\n", + " xaxis=dict(\n", + " title=dict(\n", + " text='X AXIS TITLE'\n", + " )\n", + " ),\n", + " yaxis=dict(\n", + " title=dict(\n", + " text='Y AXIS TITLE'\n", + " )\n", + " ),\n", + " zaxis=dict(\n", + " title=dict(\n", + " text='Z AXIS TITLE'\n", + " )\n", + " ),\n", + " ),\n", + " width=700,\n", + " margin=dict(r=20, b=10, l=10, t=10))\n", + "\n", + "fig.show()" + ] + }, + { + "cell_type": "markdown", + "id": "315c4538", + "metadata": {}, + "source": [ + "### Ticks Formatting" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "421cba75", + "metadata": {}, + "outputs": [], + "source": [ + "import plotly.graph_objects as go\n", + "import numpy as np\n", + "\n", + "# Define random surface\n", + "N = 50\n", + "fig = go.Figure(data=[go.Mesh3d(x=(60*np.random.randn(N)),\n", + " y=(25*np.random.randn(N)),\n", + " z=(40*np.random.randn(N)),\n", + " opacity=0.5,\n", + " color='rgba(100,22,200,0.5)'\n", + " )])\n", + "\n", + "# Different types of customized ticks\n", + "fig.update_layout(scene = dict(\n", + " xaxis = dict(\n", + " ticktext= ['TICKS','MESH','PLOTLY','PYTHON'],\n", + " tickvals= [0,50,75,-50]),\n", + " yaxis = dict(\n", + " nticks=5, tickfont=dict(\n", + " color='green',\n", + " size=12,\n", + " family='Old Standard TT, serif',),\n", + " ticksuffix='#'),\n", + " zaxis = dict(\n", + " nticks=4, ticks='outside',\n", + " tick0=0, tickwidth=4),),\n", + " width=700,\n", + " margin=dict(r=10, l=10, b=10, t=10)\n", + " )\n", + "\n", + "fig.show()" + ] + }, + { + "cell_type": "markdown", + "id": "88e41734", + "metadata": {}, + "source": [ + "### Background and Grid Color" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "5fbfaadb", + "metadata": {}, + "outputs": [], + "source": [ + "import plotly.graph_objects as go\n", + "import numpy as np\n", + "\n", + "N = 50\n", + "fig = go.Figure(data=[go.Mesh3d(x=(30*np.random.randn(N)),\n", + " y=(25*np.random.randn(N)),\n", + " z=(30*np.random.randn(N)),\n", + " opacity=0.5,)])\n", + "\n", + "\n", + "# xaxis.backgroundcolor is used to set background color\n", + "fig.update_layout(scene = dict(\n", + " xaxis = dict(\n", + " backgroundcolor=\"rgb(200, 200, 230)\",\n", + " gridcolor=\"white\",\n", + " showbackground=True,\n", + " zerolinecolor=\"white\",),\n", + " yaxis = dict(\n", + " backgroundcolor=\"rgb(230, 200,230)\",\n", + " gridcolor=\"white\",\n", + " showbackground=True,\n", + " zerolinecolor=\"white\"),\n", + " zaxis = dict(\n", + " backgroundcolor=\"rgb(230, 230,200)\",\n", + " gridcolor=\"white\",\n", + " showbackground=True,\n", + " zerolinecolor=\"white\",),),\n", + " width=700,\n", + " margin=dict(\n", + " r=10, l=10,\n", + " b=10, t=10)\n", + " )\n", + "fig.show()" + ] + }, + { + "cell_type": "markdown", + "id": "29a75416", + "metadata": {}, + "source": [ + "### Disabling tooltip spikes\n", + "\n", + "By default, guidelines originating from the tooltip point are drawn. It is possible to disable this behaviour with the `showspikes` parameter. In this example we only keep the `z` spikes (projection of the tooltip on the `x-y` plane). Hover on the data to show this behaviour." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "cab2807f", + "metadata": { + "lines_to_next_cell": 2 + }, + "outputs": [], + "source": [ + "import plotly.graph_objects as go\n", + "import numpy as np\n", + "\n", + "N = 50\n", + "fig = go.Figure(data=[go.Mesh3d(x=(30*np.random.randn(N)),\n", + " y=(25*np.random.randn(N)),\n", + " z=(30*np.random.randn(N)),\n", + " opacity=0.5,)])\n", + "fig.update_layout(scene=dict(xaxis_showspikes=False,\n", + " yaxis_showspikes=False))\n", + "fig.show()" + ] + }, + { + "cell_type": "markdown", + "id": "9bb6ce1d", + "metadata": {}, + "source": [ + "### What About Dash?\n", + "\n", + "[Dash](https://dash.plot.ly/) is an open-source framework for building analytical applications, with no Javascript required, and it is tightly integrated with the Plotly graphing library.\n", + "\n", + "Learn about how to install Dash at https://dash.plot.ly/installation.\n", + "\n", + "Everywhere in this page that you see `fig.show()`, you can display the same figure in a Dash application by passing it to the `figure` argument of the [`Graph` component](https://dash.plot.ly/dash-core-components/graph) from the built-in `dash_core_components` package like this:\n", + "\n", + "```python\n", + "import plotly.graph_objects as go # or plotly.express as px\n", + "fig = go.Figure() # or any Plotly Express function e.g. px.bar(...)\n", + "# fig.add_trace( ... )\n", + "# fig.update_layout( ... )\n", + "\n", + "from dash import Dash, dcc, html\n", + "\n", + "app = Dash()\n", + "app.layout = html.Div([\n", + " dcc.Graph(figure=fig)\n", + "])\n", + "\n", + "app.run_server(debug=True, use_reloader=False) # Turn off reloader if inside Jupyter\n", + "```" + ] + } + ], + "metadata": { + "jupytext": { + "notebook_metadata_filter": "all" + }, + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "language": "python", + "name": "python3" + }, + "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.4" + }, + "plotly": { + "description": "How to format axes of 3d plots in Python with Plotly.", + "display_as": "3d_charts", + "language": "python", + "layout": "base", + "name": "3D Axes", + "order": 1, + "page_type": "example_index", + "permalink": "python/3d-axes/", + "thumbnail": "thumbnail/3d-axes.png" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/3d-bubble-charts.ipynb b/3d-bubble-charts.ipynb new file mode 100644 index 000000000..45680349a --- /dev/null +++ b/3d-bubble-charts.ipynb @@ -0,0 +1,309 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "a481006f", + "metadata": {}, + "source": [ + "### 3d Bubble chart with Plotly Express" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "94b088f9", + "metadata": {}, + "outputs": [], + "source": [ + "import plotly.express as px\n", + "import numpy as np\n", + "df = px.data.gapminder()\n", + "fig = px.scatter_3d(df, x='year', y='continent', z='pop', size='gdpPercap', color='lifeExp',\n", + " hover_data=['country'])\n", + "fig.update_layout(scene_zaxis_type=\"log\")\n", + "fig.show()" + ] + }, + { + "cell_type": "markdown", + "id": "7e5f2ec5", + "metadata": {}, + "source": [ + "#### Simple Bubble Chart" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "17d6f5d6", + "metadata": {}, + "outputs": [], + "source": [ + "import plotly.graph_objects as go\n", + "\n", + "import pandas as pd\n", + "\n", + "# Get Data: this ex will only use part of it (i.e. rows 750-1500)\n", + "df = pd.read_csv('https://raw.githubusercontent.com/plotly/datasets/master/gapminderDataFiveYear.csv')\n", + "\n", + "start, end = 750, 1500\n", + "\n", + "fig = go.Figure(data=go.Scatter3d(\n", + " x=df['year'][start:end],\n", + " y=df['continent'][start:end],\n", + " z=df['pop'][start:end],\n", + " text=df['country'][start:end],\n", + " mode='markers',\n", + " marker=dict(\n", + " sizemode='diameter',\n", + " sizeref=750,\n", + " size=df['gdpPercap'][start:end],\n", + " color = df['lifeExp'][start:end],\n", + " colorscale = 'Viridis',\n", + " colorbar_title = 'Life
Expectancy',\n", + " line_color='rgb(140, 140, 170)'\n", + " )\n", + "))\n", + "\n", + "\n", + "fig.update_layout(height=800, width=800,\n", + " title=dict(text='Examining Population and Life Expectancy Over Time'))\n", + "\n", + "fig.show()" + ] + }, + { + "cell_type": "markdown", + "id": "d52cc9f6", + "metadata": {}, + "source": [ + "#### Bubble Chart Sized by a Variable\n", + "\n", + "Plot planets' distance from sun, density, and gravity with bubble size based on planet size" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "bc877ab1", + "metadata": {}, + "outputs": [], + "source": [ + "import plotly.graph_objects as go\n", + "\n", + "planets = ['Mercury', 'Venus', 'Earth', 'Mars', 'Jupiter', 'Saturn', 'Uranus', 'Neptune', 'Pluto']\n", + "planet_colors = ['rgb(135, 135, 125)', 'rgb(210, 50, 0)', 'rgb(50, 90, 255)',\n", + " 'rgb(178, 0, 0)', 'rgb(235, 235, 210)', 'rgb(235, 205, 130)',\n", + " 'rgb(55, 255, 217)', 'rgb(38, 0, 171)', 'rgb(255, 255, 255)']\n", + "distance_from_sun = [57.9, 108.2, 149.6, 227.9, 778.6, 1433.5, 2872.5, 4495.1, 5906.4]\n", + "density = [5427, 5243, 5514, 3933, 1326, 687, 1271, 1638, 2095]\n", + "gravity = [3.7, 8.9, 9.8, 3.7, 23.1, 9.0, 8.7, 11.0, 0.7]\n", + "planet_diameter = [4879, 12104, 12756, 6792, 142984, 120536, 51118, 49528, 2370]\n", + "\n", + "# Create trace, sizing bubbles by planet diameter\n", + "fig = go.Figure(data=go.Scatter3d(\n", + " x = distance_from_sun,\n", + " y = density,\n", + " z = gravity,\n", + " text = planets,\n", + " mode = 'markers',\n", + " marker = dict(\n", + " sizemode = 'diameter',\n", + " sizeref = 750, # info on sizeref: https://plotly.com/python/reference/scatter/#scatter-marker-sizeref\n", + " size = planet_diameter,\n", + " color = planet_colors,\n", + " )\n", + "))\n", + "\n", + "fig.update_layout(\n", + " width=800,\n", + " height=800,\n", + " title=dict(text=\"Planets!\"),\n", + " scene=dict(\n", + " xaxis=dict(\n", + " title=dict(\n", + " text=\"Distance from Sun\",\n", + " font=dict(\n", + " color=\"white\"\n", + " )\n", + " )\n", + " ),\n", + " yaxis=dict(\n", + " title=dict(\n", + " text=\"Density\",\n", + " font=dict(\n", + " color=\"white\"\n", + " )\n", + " )\n", + " ),\n", + " zaxis=dict(\n", + " title=dict(\n", + " text=\"Gravity\",\n", + " font=dict(\n", + " color=\"white\"\n", + " )\n", + " )\n", + " ),\n", + " bgcolor=\"rgb(20, 24, 54)\"\n", + " )\n", + ")\n", + "\n", + "fig.show()" + ] + }, + { + "cell_type": "markdown", + "id": "063e7747", + "metadata": {}, + "source": [ + "#### Edit the Colorbar\n", + "\n", + "Plot planets' distance from sun, density, and gravity with bubble size based on planet size" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "b5e22291", + "metadata": {}, + "outputs": [], + "source": [ + "import plotly.graph_objects as go\n", + "\n", + "planets = ['Mercury', 'Venus', 'Earth', 'Mars', 'Jupiter', 'Saturn', 'Uranus', 'Neptune', 'Pluto']\n", + "temperatures = [167, 464, 15, -20, -65, -110, -140, -195, -200, -225]\n", + "distance_from_sun = [57.9, 108.2, 149.6, 227.9, 778.6, 1433.5, 2872.5, 4495.1, 5906.4]\n", + "density = [5427, 5243, 5514, 3933, 1326, 687, 1271, 1638, 2095]\n", + "gravity = [3.7, 8.9, 9.8, 3.7, 23.1, 9.0, 8.7, 11.0, 0.7]\n", + "planet_diameter = [4879, 12104, 12756, 6792, 142984, 120536, 51118, 49528, 2370]\n", + "\n", + "# Create trace, sizing bubbles by planet diameter\n", + "fig = go.Figure(go.Scatter3d(\n", + " x = distance_from_sun,\n", + " y = density,\n", + " z = gravity,\n", + " text = planets,\n", + " mode = 'markers',\n", + " marker = dict(\n", + " sizemode = 'diameter',\n", + " sizeref = 750, # info on sizeref: https://plotly.com/python/reference/scatter/#scatter-marker-sizeref\n", + " size = planet_diameter,\n", + " color = temperatures,\n", + " colorbar_title = 'Mean
Temperature',\n", + " colorscale=[[0, 'rgb(5, 10, 172)'], [.3, 'rgb(255, 255, 255)'], [1, 'rgb(178, 10, 28)']]\n", + " )\n", + "))\n", + "\n", + "fig.update_layout(\n", + " width=800,\n", + " height=800,\n", + " title=dict(text=\"Planets!\"),\n", + " scene=dict(\n", + " xaxis=dict(\n", + " title=dict(\n", + " text=\"Distance from Sun\",\n", + " font=dict(\n", + " color=\"white\"\n", + " )\n", + " )\n", + " ),\n", + " yaxis=dict(\n", + " title=dict(\n", + " text=\"Density\",\n", + " font=dict(\n", + " color=\"white\"\n", + " )\n", + " )\n", + " ),\n", + " zaxis=dict(\n", + " title=dict(\n", + " text=\"Gravity\",\n", + " font=dict(\n", + " color=\"white\"\n", + " )\n", + " )\n", + " ),\n", + " bgcolor=\"rgb(20, 24, 54)\"\n", + " )\n", + ")\n", + "\n", + "fig.show()" + ] + }, + { + "cell_type": "markdown", + "id": "14447a0c", + "metadata": {}, + "source": [ + "#### Reference\n", + "\n", + "See https://plotly.com/python/reference/scatter3d/ and https://plotly.com/python/reference/scatter/#scatter-marker-sizeref
for more information and chart attribute options!\n" + ] + }, + { + "cell_type": "markdown", + "id": "5814eb68", + "metadata": {}, + "source": [ + "### What About Dash?\n", + "\n", + "[Dash](https://dash.plot.ly/) is an open-source framework for building analytical applications, with no Javascript required, and it is tightly integrated with the Plotly graphing library.\n", + "\n", + "Learn about how to install Dash at https://dash.plot.ly/installation.\n", + "\n", + "Everywhere in this page that you see `fig.show()`, you can display the same figure in a Dash application by passing it to the `figure` argument of the [`Graph` component](https://dash.plot.ly/dash-core-components/graph) from the built-in `dash_core_components` package like this:\n", + "\n", + "```python\n", + "import plotly.graph_objects as go # or plotly.express as px\n", + "fig = go.Figure() # or any Plotly Express function e.g. px.bar(...)\n", + "# fig.add_trace( ... )\n", + "# fig.update_layout( ... )\n", + "\n", + "from dash import Dash, dcc, html\n", + "\n", + "app = Dash()\n", + "app.layout = html.Div([\n", + " dcc.Graph(figure=fig)\n", + "])\n", + "\n", + "app.run_server(debug=True, use_reloader=False) # Turn off reloader if inside Jupyter\n", + "```" + ] + } + ], + "metadata": { + "jupytext": { + "notebook_metadata_filter": "all" + }, + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "language": "python", + "name": "python3" + }, + "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.11.10" + }, + "plotly": { + "description": "How to make 3D Bubble Charts in Python with Plotly. Three examples of 3D Bubble Charts.", + "display_as": "3d_charts", + "language": "python", + "layout": "base", + "name": "3D Bubble Charts", + "order": 6, + "page_type": "u-guide", + "permalink": "python/3d-bubble-charts/", + "thumbnail": "thumbnail/3dbubble.jpg" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/3d-camera-controls.ipynb b/3d-camera-controls.ipynb new file mode 100644 index 000000000..f1df22a9c --- /dev/null +++ b/3d-camera-controls.ipynb @@ -0,0 +1,452 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "05128697", + "metadata": {}, + "source": [ + "### How camera controls work\n", + "\n", + "The camera position and direction is determined by three vectors: *up*, *center*, *eye*. Their coordinates refer to the 3-d domain, i.e., `(0, 0, 0)` is always the center of the domain, no matter data values.\n", + "The `eye` vector determines the position of the camera. The default is $(x=1.25, y=1.25, z=1.25)$.\n", + "\n", + "The `up` vector determines the `up` direction on the page. The default is $(x=0, y=0, z=1)$, that is, the z-axis points up.\n", + "\n", + "The projection of the `center` point lies at the center of the view. By default it is $(x=0, y=0, z=0)$." + ] + }, + { + "cell_type": "markdown", + "id": "cd46a1c4", + "metadata": {}, + "source": [ + "### Default parameters" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "c604d207", + "metadata": {}, + "outputs": [], + "source": [ + "import plotly.graph_objects as go\n", + "import pandas as pd\n", + "\n", + "# Read data from a csv\n", + "z_data = pd.read_csv('https://raw.githubusercontent.com/plotly/datasets/master/api_docs/mt_bruno_elevation.csv')\n", + "\n", + "fig = go.Figure(data=go.Surface(z=z_data, showscale=False))\n", + "fig.update_layout(\n", + " title=dict(text='Mt Bruno Elevation'),\n", + " width=400, height=400,\n", + " margin=dict(t=40, r=0, l=20, b=20)\n", + ")\n", + "\n", + "name = 'default'\n", + "# Default parameters which are used when `layout.scene.camera` is not provided\n", + "camera = dict(\n", + " up=dict(x=0, y=0, z=1),\n", + " center=dict(x=0, y=0, z=0),\n", + " eye=dict(x=1.25, y=1.25, z=1.25)\n", + ")\n", + "\n", + "fig.update_layout(scene_camera=camera, title=name)\n", + "fig.show()" + ] + }, + { + "cell_type": "markdown", + "id": "293cb18e", + "metadata": {}, + "source": [ + "### Changing the camera position by setting the eye parameter\n", + "\n", + "#### Lower the View Point\n", + "\n", + "by setting `eye.z` to a smaller value." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "8eec7d90", + "metadata": {}, + "outputs": [], + "source": [ + "import plotly.graph_objects as go\n", + "import pandas as pd\n", + "\n", + "# Read data from a csv\n", + "z_data = pd.read_csv('https://raw.githubusercontent.com/plotly/datasets/master/api_docs/mt_bruno_elevation.csv')\n", + "\n", + "fig = go.Figure(data=go.Surface(z=z_data, showscale=False))\n", + "fig.update_layout(\n", + " title=dict(text='Mt Bruno Elevation'),\n", + " width=400, height=400,\n", + " margin=dict(t=30, r=0, l=20, b=10)\n", + ")\n", + "\n", + "name = 'eye = (x:2, y:2, z:0.1)'\n", + "camera = dict(\n", + " eye=dict(x=2, y=2, z=0.1)\n", + ")\n", + "\n", + "fig.update_layout(scene_camera=camera, title=name)\n", + "fig.show()" + ] + }, + { + "cell_type": "markdown", + "id": "f9202780", + "metadata": {}, + "source": [ + "#### X-Z plane\n", + "\n", + "set `eye.x` and `eye.z` to zero" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "7ec7a384", + "metadata": {}, + "outputs": [], + "source": [ + "import plotly.graph_objects as go\n", + "import pandas as pd\n", + "\n", + "# Read data from a csv\n", + "z_data = pd.read_csv('https://raw.githubusercontent.com/plotly/datasets/master/api_docs/mt_bruno_elevation.csv')\n", + "\n", + "fig = go.Figure(data=go.Surface(z=z_data, showscale=False))\n", + "fig.update_layout(\n", + " title=dict(text='Mt Bruno Elevation'),\n", + " width=400, height=400,\n", + " margin=dict(t=30, r=0, l=20, b=10)\n", + ")\n", + "\n", + "name = 'eye = (x:0., y:2.5, z:0.)'\n", + "camera = dict(\n", + " eye=dict(x=0., y=2.5, z=0.)\n", + ")\n", + "\n", + "\n", + "fig.update_layout(scene_camera=camera, title=name)\n", + "fig.show()" + ] + }, + { + "cell_type": "markdown", + "id": "2e46ab5b", + "metadata": {}, + "source": [ + "#### Y-Z plane" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "2ab246a0", + "metadata": {}, + "outputs": [], + "source": [ + "import plotly.graph_objects as go\n", + "import pandas as pd\n", + "\n", + "# Read data from a csv\n", + "z_data = pd.read_csv('https://raw.githubusercontent.com/plotly/datasets/master/api_docs/mt_bruno_elevation.csv')\n", + "\n", + "fig = go.Figure(data=go.Surface(z=z_data, showscale=False))\n", + "fig.update_layout(\n", + " title=dict(text='Mt Bruno Elevation'),\n", + " width=400, height=400,\n", + " margin=dict(t=30, r=0, l=20, b=10)\n", + ")\n", + "\n", + "name = 'eye = (x:2.5, y:0., z:0.)'\n", + "camera = dict(\n", + " eye=dict(x=2.5, y=0., z=0.)\n", + ")\n", + "\n", + "fig.update_layout(scene_camera=camera, title=name)\n", + "fig.show()" + ] + }, + { + "cell_type": "markdown", + "id": "962f286a", + "metadata": {}, + "source": [ + "#### View from Above (X-Y plane)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "b372c3c8", + "metadata": {}, + "outputs": [], + "source": [ + "import plotly.graph_objects as go\n", + "import pandas as pd\n", + "\n", + "# Read data from a csv\n", + "z_data = pd.read_csv('https://raw.githubusercontent.com/plotly/datasets/master/api_docs/mt_bruno_elevation.csv')\n", + "\n", + "fig = go.Figure(data=go.Surface(z=z_data, showscale=False))\n", + "fig.update_layout(\n", + " title=dict(text='Mt Bruno Elevation'),\n", + " width=400, height=400,\n", + " margin=dict(t=30, r=0, l=20, b=10)\n", + ")\n", + "\n", + "name = 'eye = (x:0., y:0., z:2.5)'\n", + "camera = dict(\n", + " eye=dict(x=0., y=0., z=2.5)\n", + ")\n", + "\n", + "fig.update_layout(scene_camera=camera, title=name)\n", + "fig.show()" + ] + }, + { + "cell_type": "markdown", + "id": "b7f76db5", + "metadata": {}, + "source": [ + "#### Zooming In\n", + "... by placing the camera closer to the origin (`eye` with a smaller norm)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "5a4beb95", + "metadata": {}, + "outputs": [], + "source": [ + "import plotly.graph_objects as go\n", + "import pandas as pd\n", + "\n", + "# Read data from a csv\n", + "z_data = pd.read_csv('https://raw.githubusercontent.com/plotly/datasets/master/api_docs/mt_bruno_elevation.csv')\n", + "\n", + "fig = go.Figure(data=go.Surface(z=z_data, showscale=False))\n", + "fig.update_layout(\n", + " title=dict(text='Mt Bruno Elevation'),\n", + " width=400, height=400,\n", + " margin=dict(t=30, r=0, l=20, b=10)\n", + ")\n", + "\n", + "name = 'eye = (x:0.1, y:0.1, z:1.5)'\n", + "camera = dict(\n", + " eye=dict(x=0.1, y=0.1, z=1.5)\n", + ")\n", + "\n", + "fig.update_layout(scene_camera=camera, title=name)\n", + "fig.show()" + ] + }, + { + "cell_type": "markdown", + "id": "d8a31fda", + "metadata": {}, + "source": [ + "### Tilting the camera vertical by setting the up parameter\n", + "\n", + "Tilt camera by changing the `up` vector: here the vertical of the view points in the `x` direction." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "98b92a24", + "metadata": {}, + "outputs": [], + "source": [ + "import plotly.graph_objects as go\n", + "import pandas as pd\n", + "\n", + "# Read data from a csv\n", + "z_data = pd.read_csv('https://raw.githubusercontent.com/plotly/datasets/master/api_docs/mt_bruno_elevation.csv')\n", + "\n", + "fig = go.Figure(data=go.Surface(z=z_data, showscale=False))\n", + "fig.update_layout(\n", + " title=dict(text='Mt Bruno Elevation'),\n", + " width=400, height=400,\n", + " margin=dict(t=30, r=0, l=20, b=10)\n", + ")\n", + "\n", + "name = 'eye = (x:0., y:2.5, z:0.), point along x'\n", + "camera = dict(\n", + " up=dict(x=1, y=0., z=0),\n", + " eye=dict(x=0., y=2.5, z=0.)\n", + ")\n", + "\n", + "fig.update_layout(scene_camera=camera, title=name)\n", + "fig.show()" + ] + }, + { + "cell_type": "markdown", + "id": "2de56bf0", + "metadata": {}, + "source": [ + "Note when `up` does not correspond to the direction of an axis, you also need to set `layout.scene.dragmode='orbit'`." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "0689c113", + "metadata": {}, + "outputs": [], + "source": [ + "import math\n", + "import plotly.graph_objects as go\n", + "import pandas as pd\n", + "\n", + "# Read data from a csv\n", + "z_data = pd.read_csv('https://raw.githubusercontent.com/plotly/datasets/master/api_docs/mt_bruno_elevation.csv')\n", + "\n", + "fig = go.Figure(data=go.Surface(z=z_data, showscale=False))\n", + "fig.update_layout(\n", + " title=dict(text='Mt Bruno Elevation'),\n", + " width=400, height=400,\n", + " margin=dict(t=30, r=0, l=20, b=10)\n", + ")\n", + "\n", + "angle = math.pi / 4 # 45 degrees\n", + "\n", + "name = 'vertical is along y+z'\n", + "camera = dict(\n", + " up=dict(x=0, y=math.cos(angle), z=math.sin(angle)),\n", + " eye=dict(x=2, y=0, z=0)\n", + ")\n", + "\n", + "fig.update_layout(scene_camera=camera, scene_dragmode='orbit', title=name)\n", + "fig.show()" + ] + }, + { + "cell_type": "markdown", + "id": "c8da7f0f", + "metadata": {}, + "source": [ + "### Changing the focal point by setting center\n", + "\n", + "You can change the focal point (a point which projection lies at the center of the view) by setting the `center` parameter of `camera`. Note how a part of the data is cropped below because the camera is looking up." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "fa6b1137", + "metadata": {}, + "outputs": [], + "source": [ + "import plotly.graph_objects as go\n", + "import pandas as pd\n", + "\n", + "# Read data from a csv\n", + "z_data = pd.read_csv('https://raw.githubusercontent.com/plotly/datasets/master/api_docs/mt_bruno_elevation.csv')\n", + "\n", + "fig = go.Figure(data=go.Surface(z=z_data, showscale=False))\n", + "fig.update_layout(\n", + " title=dict(text='Mt Bruno Elevation'),\n", + " width=400, height=400,\n", + " margin=dict(t=25, r=0, l=20, b=30)\n", + ")\n", + "\n", + "name = 'looking up'\n", + "camera = dict(\n", + " center=dict(x=0, y=0, z=0.7))\n", + "\n", + "\n", + "fig.update_layout(scene_camera=camera, title=name)\n", + "fig.show()" + ] + }, + { + "cell_type": "markdown", + "id": "2183cfd9", + "metadata": {}, + "source": [ + "#### Reference" + ] + }, + { + "cell_type": "markdown", + "id": "347966eb", + "metadata": {}, + "source": [ + "See https://plotly.com/python/reference/layout/scene/#layout-scene-camera for more information and chart attribute options!" + ] + }, + { + "cell_type": "markdown", + "id": "e138cbc8", + "metadata": {}, + "source": [ + "### What About Dash?\n", + "\n", + "[Dash](https://dash.plot.ly/) is an open-source framework for building analytical applications, with no Javascript required, and it is tightly integrated with the Plotly graphing library.\n", + "\n", + "Learn about how to install Dash at https://dash.plot.ly/installation.\n", + "\n", + "Everywhere in this page that you see `fig.show()`, you can display the same figure in a Dash application by passing it to the `figure` argument of the [`Graph` component](https://dash.plot.ly/dash-core-components/graph) from the built-in `dash_core_components` package like this:\n", + "\n", + "```python\n", + "import plotly.graph_objects as go # or plotly.express as px\n", + "fig = go.Figure() # or any Plotly Express function e.g. px.bar(...)\n", + "# fig.add_trace( ... )\n", + "# fig.update_layout( ... )\n", + "\n", + "from dash import Dash, dcc, html\n", + "\n", + "app = Dash()\n", + "app.layout = html.Div([\n", + " dcc.Graph(figure=fig)\n", + "])\n", + "\n", + "app.run_server(debug=True, use_reloader=False) # Turn off reloader if inside Jupyter\n", + "```" + ] + } + ], + "metadata": { + "jupytext": { + "notebook_metadata_filter": "all" + }, + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "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.7.3" + }, + "plotly": { + "description": "How to Control the Camera in your 3D Charts in Python with Plotly.", + "display_as": "3d_charts", + "language": "python", + "layout": "base", + "name": "3D Camera Controls", + "order": 5, + "permalink": "python/3d-camera-controls/", + "thumbnail": "thumbnail/3d-camera-controls.jpg" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/3d-isosurface-plots.ipynb b/3d-isosurface-plots.ipynb new file mode 100644 index 000000000..75c5c8384 --- /dev/null +++ b/3d-isosurface-plots.ipynb @@ -0,0 +1,375 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "ed53c539", + "metadata": {}, + "source": [ + "With ``go.Isosurface``, you can plot [isosurface contours](https://en.wikipedia.org/wiki/Isosurface) of a scalar field ``value``, which is defined on ``x``, ``y`` and ``z`` coordinates.\n", + "\n", + "#### Basic Isosurface\n", + "\n", + "In this first example, we plot the isocontours of values ``isomin=2`` and ``isomax=6``. In addition, portions of the sides of the coordinate domains for which the value is between ``isomin`` and ``isomax`` (named the ``caps``) are colored. Please rotate the figure to visualize both the internal surfaces and the caps surfaces on the sides." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "40e0cc1c", + "metadata": {}, + "outputs": [], + "source": [ + "import plotly.graph_objects as go\n", + "\n", + "fig= go.Figure(data=go.Isosurface(\n", + " x=[0,0,0,0,1,1,1,1],\n", + " y=[1,0,1,0,1,0,1,0],\n", + " z=[1,1,0,0,1,1,0,0],\n", + " value=[1,2,3,4,5,6,7,8],\n", + " isomin=2,\n", + " isomax=6,\n", + "))\n", + "\n", + "fig.show()" + ] + }, + { + "cell_type": "markdown", + "id": "86b4e156", + "metadata": {}, + "source": [ + "### Removing caps when visualizing isosurfaces\n", + "\n", + "For a clearer visualization of internal surfaces, it is possible to remove the caps (color-coded surfaces on the sides of the visualization domain). Caps are visible by default." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "832b4ca3", + "metadata": {}, + "outputs": [], + "source": [ + "import plotly.graph_objects as go\n", + "import numpy as np\n", + "\n", + "X, Y, Z = np.mgrid[-5:5:40j, -5:5:40j, -5:5:40j]\n", + "\n", + "# ellipsoid\n", + "values = X * X * 0.5 + Y * Y + Z * Z * 2\n", + "\n", + "fig = go.Figure(data=go.Isosurface(\n", + " x=X.flatten(),\n", + " y=Y.flatten(),\n", + " z=Z.flatten(),\n", + " value=values.flatten(),\n", + " isomin=10,\n", + " isomax=40,\n", + " caps=dict(x_show=False, y_show=False)\n", + " ))\n", + "fig.show()" + ] + }, + { + "cell_type": "markdown", + "id": "5aed1772", + "metadata": {}, + "source": [ + "### Modifying the number of isosurfaces" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "681f666a", + "metadata": {}, + "outputs": [], + "source": [ + "import plotly.graph_objects as go\n", + "import numpy as np\n", + "\n", + "X, Y, Z = np.mgrid[-5:5:40j, -5:5:40j, -5:5:40j]\n", + "\n", + "# ellipsoid\n", + "values = X * X * 0.5 + Y * Y + Z * Z * 2\n", + "\n", + "fig = go.Figure(data=go.Isosurface(\n", + " x=X.flatten(),\n", + " y=Y.flatten(),\n", + " z=Z.flatten(),\n", + " value=values.flatten(),\n", + " isomin=10,\n", + " isomax=50,\n", + " surface_count=5, # number of isosurfaces, 2 by default: only min and max\n", + " colorbar_nticks=5, # colorbar ticks correspond to isosurface values\n", + " caps=dict(x_show=False, y_show=False)\n", + " ))\n", + "fig.show()" + ] + }, + { + "cell_type": "markdown", + "id": "44494531", + "metadata": {}, + "source": [ + "### Changing the opacity of isosurfaces" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "14fc412a", + "metadata": {}, + "outputs": [], + "source": [ + "import plotly.graph_objects as go\n", + "import numpy as np\n", + "\n", + "X, Y, Z = np.mgrid[-5:5:40j, -5:5:40j, -5:5:40j]\n", + "\n", + "# ellipsoid\n", + "values = X * X * 0.5 + Y * Y + Z * Z * 2\n", + "\n", + "fig = go.Figure(data=go.Isosurface(\n", + " x=X.flatten(),\n", + " y=Y.flatten(),\n", + " z=Z.flatten(),\n", + " value=values.flatten(),\n", + " opacity=0.6,\n", + " isomin=10,\n", + " isomax=50,\n", + " surface_count=3,\n", + " caps=dict(x_show=False, y_show=False)\n", + " ))\n", + "fig.show()" + ] + }, + { + "cell_type": "markdown", + "id": "0127c47f", + "metadata": {}, + "source": [ + "#### Isosurface with Additional Slices\n", + "\n", + "Here we visualize slices parallel to the axes on top of isosurfaces. For a clearer visualization, the `fill` ratio of isosurfaces is decreased below 1 (completely filled)." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "6541dba9", + "metadata": {}, + "outputs": [], + "source": [ + "import plotly.graph_objects as go\n", + "import numpy as np\n", + "\n", + "X, Y, Z = np.mgrid[-5:5:40j, -5:5:40j, -5:5:40j]\n", + "\n", + "# ellipsoid\n", + "values = X * X * 0.5 + Y * Y + Z * Z * 2\n", + "\n", + "fig = go.Figure(data=go.Isosurface(\n", + " x=X.flatten(),\n", + " y=Y.flatten(),\n", + " z=Z.flatten(),\n", + " value=values.flatten(),\n", + " isomin=5,\n", + " isomax=50,\n", + " surface_fill=0.4,\n", + " caps=dict(x_show=False, y_show=False),\n", + " slices_z=dict(show=True, locations=[-1, -3,]),\n", + " slices_y=dict(show=True, locations=[0]),\n", + " ))\n", + "fig.show()" + ] + }, + { + "cell_type": "markdown", + "id": "15037375", + "metadata": {}, + "source": [ + "#### Multiple Isosurfaces with Caps" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "77e979e9", + "metadata": {}, + "outputs": [], + "source": [ + "import plotly.graph_objects as go\n", + "import numpy as np\n", + "\n", + "X, Y, Z = np.mgrid[-5:5:40j, -5:5:40j, 0:5:20j]\n", + "\n", + "values = X * X * 0.5 + Y * Y + Z * Z * 2\n", + "\n", + "fig = go.Figure(data=go.Isosurface(\n", + " x=X.flatten(),\n", + " y=Y.flatten(),\n", + " z=Z.flatten(),\n", + " value=values.flatten(),\n", + " isomin=30,\n", + " isomax=50,\n", + " surface=dict(count=3, fill=0.7, pattern='odd'),\n", + " caps=dict(x_show=True, y_show=True),\n", + " ))\n", + "fig.show()" + ] + }, + { + "cell_type": "markdown", + "id": "2e60868f", + "metadata": {}, + "source": [ + "### Changing the default colorscale of isosurfaces" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "01cb6941", + "metadata": {}, + "outputs": [], + "source": [ + "import plotly.graph_objects as go\n", + "import numpy as np\n", + "\n", + "X, Y, Z = np.mgrid[-5:5:40j, -5:5:40j, -5:5:40j]\n", + "\n", + "# ellipsoid\n", + "values = X * X * 0.5 + Y * Y + Z * Z * 2\n", + "\n", + "fig = go.Figure(data=go.Isosurface(\n", + " x=X.flatten(),\n", + " y=Y.flatten(),\n", + " z=Z.flatten(),\n", + " value=values.flatten(),\n", + " colorscale='BlueRed',\n", + " isomin=10,\n", + " isomax=50,\n", + " surface_count=3,\n", + " caps=dict(x_show=False, y_show=False)\n", + " ))\n", + "fig.show()" + ] + }, + { + "cell_type": "markdown", + "id": "5fb22106", + "metadata": {}, + "source": [ + "### Customizing the layout and appearance of isosurface plots" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "05f9d380", + "metadata": {}, + "outputs": [], + "source": [ + "import plotly.graph_objects as go\n", + "import numpy as np\n", + "\n", + "X, Y, Z = np.mgrid[-5:5:40j, -5:5:40j, 0:5:20j]\n", + "\n", + "values = X * X * 0.5 + Y * Y + Z * Z * 2\n", + "\n", + "fig = go.Figure(data=go.Isosurface(\n", + " x=X.flatten(),\n", + " y=Y.flatten(),\n", + " z=Z.flatten(),\n", + " value=values.flatten(),\n", + " isomin=30,\n", + " isomax=50,\n", + " surface=dict(count=3, fill=0.7, pattern='odd'),\n", + " showscale=False, # remove colorbar\n", + " caps=dict(x_show=True, y_show=True),\n", + " ))\n", + "\n", + "fig.update_layout(\n", + " margin=dict(t=0, l=0, b=0), # tight layout\n", + " scene_camera_eye=dict(x=1.86, y=0.61, z=0.98))\n", + "fig.show()" + ] + }, + { + "cell_type": "markdown", + "id": "5da8d6bc", + "metadata": {}, + "source": [ + "#### Reference\n", + "See https://plotly.com/python/reference/isosurface/ for more information and chart attribute options!\n" + ] + }, + { + "cell_type": "markdown", + "id": "e6a7d4af", + "metadata": {}, + "source": [ + "### What About Dash?\n", + "\n", + "[Dash](https://dash.plot.ly/) is an open-source framework for building analytical applications, with no Javascript required, and it is tightly integrated with the Plotly graphing library.\n", + "\n", + "Learn about how to install Dash at https://dash.plot.ly/installation.\n", + "\n", + "Everywhere in this page that you see `fig.show()`, you can display the same figure in a Dash application by passing it to the `figure` argument of the [`Graph` component](https://dash.plot.ly/dash-core-components/graph) from the built-in `dash_core_components` package like this:\n", + "\n", + "```python\n", + "import plotly.graph_objects as go # or plotly.express as px\n", + "fig = go.Figure() # or any Plotly Express function e.g. px.bar(...)\n", + "# fig.add_trace( ... )\n", + "# fig.update_layout( ... )\n", + "\n", + "from dash import Dash, dcc, html\n", + "\n", + "app = Dash()\n", + "app.layout = html.Div([\n", + " dcc.Graph(figure=fig)\n", + "])\n", + "\n", + "app.run_server(debug=True, use_reloader=False) # Turn off reloader if inside Jupyter\n", + "```" + ] + } + ], + "metadata": { + "jupytext": { + "notebook_metadata_filter": "all" + }, + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "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.7.3" + }, + "plotly": { + "description": "How to make 3D Isosurface Plots in Python with Plotly.", + "display_as": "3d_charts", + "language": "python", + "layout": "base", + "name": "3D Isosurface Plots", + "order": 10, + "page_type": "u-guide", + "permalink": "python/3d-isosurface-plots/", + "redirect_from": "python/isosurfaces-with-marching-cubes/", + "thumbnail": "thumbnail/isosurface.jpg" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/3d-line-plots.ipynb b/3d-line-plots.ipynb new file mode 100644 index 000000000..452033bd5 --- /dev/null +++ b/3d-line-plots.ipynb @@ -0,0 +1,191 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "245e34e9", + "metadata": {}, + "source": [ + "### 3D Line plot with Plotly Express" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "084c05da", + "metadata": {}, + "outputs": [], + "source": [ + "import plotly.express as px\n", + "df = px.data.gapminder().query(\"country=='Brazil'\")\n", + "fig = px.line_3d(df, x=\"gdpPercap\", y=\"pop\", z=\"year\")\n", + "fig.show()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "3757d751", + "metadata": {}, + "outputs": [], + "source": [ + "import plotly.express as px\n", + "df = px.data.gapminder().query(\"continent=='Europe'\")\n", + "fig = px.line_3d(df, x=\"gdpPercap\", y=\"pop\", z=\"year\", color='country')\n", + "fig.show()" + ] + }, + { + "cell_type": "markdown", + "id": "849cc244", + "metadata": {}, + "source": [ + "#### 3D Line Plot of Brownian Motion\n", + "\n", + "Here we represent a trajectory in 3D." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "720fa984", + "metadata": {}, + "outputs": [], + "source": [ + "import plotly.graph_objects as go\n", + "import pandas as pd\n", + "import numpy as np\n", + "\n", + "rs = np.random.RandomState()\n", + "rs.seed(0)\n", + "\n", + "def brownian_motion(T = 1, N = 100, mu = 0.1, sigma = 0.01, S0 = 20):\n", + " dt = float(T)/N\n", + " t = np.linspace(0, T, N)\n", + " W = rs.standard_normal(size = N)\n", + " W = np.cumsum(W)*np.sqrt(dt) # standard brownian motion\n", + " X = (mu-0.5*sigma**2)*t + sigma*W\n", + " S = S0*np.exp(X) # geometric brownian motion\n", + " return S\n", + "\n", + "dates = pd.date_range('2012-01-01', '2013-02-22')\n", + "T = (dates.max()-dates.min()).days / 365\n", + "N = dates.size\n", + "start_price = 100\n", + "y = brownian_motion(T, N, sigma=0.1, S0=start_price)\n", + "z = brownian_motion(T, N, sigma=0.1, S0=start_price)\n", + "\n", + "fig = go.Figure(data=go.Scatter3d(\n", + " x=dates, y=y, z=z,\n", + " marker=dict(\n", + " size=4,\n", + " color=z,\n", + " colorscale='Viridis',\n", + " ),\n", + " line=dict(\n", + " color='darkblue',\n", + " width=2\n", + " )\n", + "))\n", + "\n", + "fig.update_layout(\n", + " width=800,\n", + " height=700,\n", + " autosize=False,\n", + " scene=dict(\n", + " camera=dict(\n", + " up=dict(\n", + " x=0,\n", + " y=0,\n", + " z=1\n", + " ),\n", + " eye=dict(\n", + " x=0,\n", + " y=1.0707,\n", + " z=1,\n", + " )\n", + " ),\n", + " aspectratio = dict( x=1, y=1, z=0.7 ),\n", + " aspectmode = 'manual'\n", + " ),\n", + ")\n", + "\n", + "fig.show()\n" + ] + }, + { + "cell_type": "markdown", + "id": "be3134b2", + "metadata": {}, + "source": [ + "#### Reference\n", + "\n", + "See [function reference for `px.(line_3d)`](https://plotly.com/python-api-reference/generated/plotly.express.line_3d) or https://plotly.com/python/reference/scatter3d/#scatter3d-marker-line for more information and chart attribute options!\n" + ] + }, + { + "cell_type": "markdown", + "id": "05ca706c", + "metadata": {}, + "source": [ + "### What About Dash?\n", + "\n", + "[Dash](https://dash.plot.ly/) is an open-source framework for building analytical applications, with no Javascript required, and it is tightly integrated with the Plotly graphing library.\n", + "\n", + "Learn about how to install Dash at https://dash.plot.ly/installation.\n", + "\n", + "Everywhere in this page that you see `fig.show()`, you can display the same figure in a Dash application by passing it to the `figure` argument of the [`Graph` component](https://dash.plot.ly/dash-core-components/graph) from the built-in `dash_core_components` package like this:\n", + "\n", + "```python\n", + "import plotly.graph_objects as go # or plotly.express as px\n", + "fig = go.Figure() # or any Plotly Express function e.g. px.bar(...)\n", + "# fig.add_trace( ... )\n", + "# fig.update_layout( ... )\n", + "\n", + "from dash import Dash, dcc, html\n", + "\n", + "app = Dash()\n", + "app.layout = html.Div([\n", + " dcc.Graph(figure=fig)\n", + "])\n", + "\n", + "app.run_server(debug=True, use_reloader=False) # Turn off reloader if inside Jupyter\n", + "```" + ] + } + ], + "metadata": { + "jupytext": { + "notebook_metadata_filter": "all" + }, + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "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.7.3" + }, + "plotly": { + "description": "How to make 3D Line Plots", + "display_as": "3d_charts", + "language": "python", + "layout": "base", + "name": "3D Line Plots", + "order": 7, + "page_type": "u-guide", + "permalink": "python/3d-line-plots/", + "thumbnail": "thumbnail/3d-line.jpg" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/3d-mesh.ipynb b/3d-mesh.ipynb new file mode 100644 index 000000000..ca45f5e54 --- /dev/null +++ b/3d-mesh.ipynb @@ -0,0 +1,307 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "9d7fea44", + "metadata": {}, + "source": [ + "### Simple 3D Mesh example ###\n", + "\n", + "`go.Mesh3d` draws a 3D set of triangles with vertices given by `x`, `y` and `z`. If only coordinates are given, an algorithm such as [Delaunay triangulation](https://en.wikipedia.org/wiki/Delaunay_triangulation) is used to draw the triangles. Otherwise the triangles can be given using the `i`, `j` and `k` parameters (see examples below)." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "9f1a44ed", + "metadata": {}, + "outputs": [], + "source": [ + "import plotly.graph_objects as go\n", + "import numpy as np\n", + "\n", + "# Download data set from plotly repo\n", + "pts = np.loadtxt(np.DataSource().open('https://raw.githubusercontent.com/plotly/datasets/master/mesh_dataset.txt'))\n", + "x, y, z = pts.T\n", + "\n", + "fig = go.Figure(data=[go.Mesh3d(x=x, y=y, z=z, color='lightpink', opacity=0.50)])\n", + "fig.show()" + ] + }, + { + "cell_type": "markdown", + "id": "17893fbc", + "metadata": {}, + "source": [ + "### 3D Mesh example with Alphahull" + ] + }, + { + "cell_type": "markdown", + "id": "bc0241aa", + "metadata": {}, + "source": [ + "The `alphahull` parameter sets the shape of the mesh. If the value is -1 (default value) then [Delaunay triangulation](https://en.wikipedia.org/wiki/Delaunay_triangulation) is used. If >0 then the [alpha-shape algorithm](https://en.wikipedia.org/wiki/Alpha_shape) is used. If 0, the [convex hull](https://en.wikipedia.org/wiki/Convex_hull) is represented (resulting in a convex body)." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "00680451", + "metadata": {}, + "outputs": [], + "source": [ + "import plotly.graph_objects as go\n", + "import numpy as np\n", + "\n", + "pts = np.loadtxt(np.DataSource().open('https://raw.githubusercontent.com/plotly/datasets/master/mesh_dataset.txt'))\n", + "x, y, z = pts.T\n", + "\n", + "fig = go.Figure(data=[go.Mesh3d(x=x, y=y, z=z,\n", + " alphahull=5,\n", + " opacity=0.4,\n", + " color='cyan')])\n", + "fig.show()" + ] + }, + { + "cell_type": "markdown", + "id": "371bfe89", + "metadata": {}, + "source": [ + "### 3D Mesh in Dash\n", + "\n", + "[Dash](https://plotly.com/dash/) is the best way to build analytical apps in Python using Plotly figures. To run the app below, run `pip install dash`, click \"Download\" to get the code and run `python app.py`.\n", + "\n", + "Get started with [the official Dash docs](https://dash.plotly.com/installation) and **learn how to effortlessly [style](https://plotly.com/dash/design-kit/) & [deploy](https://plotly.com/dash/app-manager/) apps like this with Dash Enterprise.**\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "aac1af9e", + "metadata": { + "hide_code": true + }, + "outputs": [], + "source": [ + "from IPython.display import IFrame\n", + "snippet_url = 'https://python-docs-dash-snippets.herokuapp.com/python-docs-dash-snippets/'\n", + "IFrame(snippet_url + '3d-mesh', width='100%', height=1200)" + ] + }, + { + "cell_type": "markdown", + "id": "408c13af", + "metadata": {}, + "source": [ + "

Sign up for Dash Club → Free cheat sheets plus updates from Chris Parmer and Adam Schroeder delivered to your inbox every two months. Includes tips and tricks, community apps, and deep dives into the Dash architecture.\n", + "Join now.

" + ] + }, + { + "cell_type": "markdown", + "id": "38f24da2", + "metadata": {}, + "source": [ + "### Mesh Tetrahedron\n", + "\n", + "In this example we use the `i`, `j` and `k` parameters to specify manually the geometry of the triangles of the mesh." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "c692a6ca", + "metadata": {}, + "outputs": [], + "source": [ + "import plotly.graph_objects as go\n", + "\n", + "fig = go.Figure(data=[\n", + " go.Mesh3d(\n", + " x=[0, 1, 2, 0],\n", + " y=[0, 0, 1, 2],\n", + " z=[0, 2, 0, 1],\n", + " colorbar=dict(title=dict(text='z')),\n", + " colorscale=[[0, 'gold'],\n", + " [0.5, 'mediumturquoise'],\n", + " [1, 'magenta']],\n", + " # Intensity of each vertex, which will be interpolated and color-coded\n", + " intensity=[0, 0.33, 0.66, 1],\n", + " # i, j and k give the vertices of triangles\n", + " # here we represent the 4 triangles of the tetrahedron surface\n", + " i=[0, 0, 0, 1],\n", + " j=[1, 2, 3, 2],\n", + " k=[2, 3, 1, 3],\n", + " name='y',\n", + " showscale=True\n", + " )\n", + "])\n", + "\n", + "fig.show()" + ] + }, + { + "cell_type": "markdown", + "id": "8b40fffa", + "metadata": {}, + "source": [ + "### Mesh Cube" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "c8c1940f", + "metadata": {}, + "outputs": [], + "source": [ + "import plotly.graph_objects as go\n", + "import numpy as np\n", + "\n", + "fig = go.Figure(data=[\n", + " go.Mesh3d(\n", + " # 8 vertices of a cube\n", + " x=[0, 0, 1, 1, 0, 0, 1, 1],\n", + " y=[0, 1, 1, 0, 0, 1, 1, 0],\n", + " z=[0, 0, 0, 0, 1, 1, 1, 1],\n", + " colorbar=dict(title=dict(text='z')),\n", + " colorscale=[[0, 'gold'],\n", + " [0.5, 'mediumturquoise'],\n", + " [1, 'magenta']],\n", + " # Intensity of each vertex, which will be interpolated and color-coded\n", + " intensity = np.linspace(0, 1, 8, endpoint=True),\n", + " # i, j and k give the vertices of triangles\n", + " i = [7, 0, 0, 0, 4, 4, 6, 6, 4, 0, 3, 2],\n", + " j = [3, 4, 1, 2, 5, 6, 5, 2, 0, 1, 6, 3],\n", + " k = [0, 7, 2, 3, 6, 7, 1, 1, 5, 5, 7, 6],\n", + " name='y',\n", + " showscale=True\n", + " )\n", + "])\n", + "\n", + "fig.show()" + ] + }, + { + "cell_type": "markdown", + "id": "f0a23f75", + "metadata": {}, + "source": [ + "### Intensity values defined on vertices or cells\n", + "\n", + "The `intensitymode` attribute of `go.Mesh3d` can be set to `vertex` (default mode, in which case intensity values are interpolated between values defined on vertices), or to `cell` (value of the whole cell, no interpolation). Note that the `intensity` parameter should have the same length as the number of vertices or cells, depending on the `intensitymode`.\n", + "\n", + "Whereas the previous example used the default `intensitymode='vertex'`, we plot here the same mesh with `intensitymode='cell'`." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "aa789aa8", + "metadata": {}, + "outputs": [], + "source": [ + "import plotly.graph_objects as go\n", + "fig = go.Figure(data=[\n", + " go.Mesh3d(\n", + " # 8 vertices of a cube\n", + " x=[0, 0, 1, 1, 0, 0, 1, 1],\n", + " y=[0, 1, 1, 0, 0, 1, 1, 0],\n", + " z=[0, 0, 0, 0, 1, 1, 1, 1],\n", + " colorbar=dict(title=dict(text='z')),\n", + " colorscale=[[0, 'gold'],\n", + " [0.5, 'mediumturquoise'],\n", + " [1, 'magenta']],\n", + " # Intensity of each vertex, which will be interpolated and color-coded\n", + " intensity = np.linspace(0, 1, 12, endpoint=True),\n", + " intensitymode='cell',\n", + " # i, j and k give the vertices of triangles\n", + " i = [7, 0, 0, 0, 4, 4, 6, 6, 4, 0, 3, 2],\n", + " j = [3, 4, 1, 2, 5, 6, 5, 2, 0, 1, 6, 3],\n", + " k = [0, 7, 2, 3, 6, 7, 1, 1, 5, 5, 7, 6],\n", + " name='y',\n", + " showscale=True\n", + " )\n", + "])\n", + "\n", + "fig.show()" + ] + }, + { + "cell_type": "markdown", + "id": "7aa43e87", + "metadata": {}, + "source": [ + "## Reference\n", + "See https://plotly.com/python/reference/mesh3d/ for more information and chart attribute options!\n" + ] + }, + { + "cell_type": "markdown", + "id": "646752e0", + "metadata": {}, + "source": [ + "### What About Dash?\n", + "\n", + "[Dash](https://dash.plot.ly/) is an open-source framework for building analytical applications, with no Javascript required, and it is tightly integrated with the Plotly graphing library.\n", + "\n", + "Learn about how to install Dash at https://dash.plot.ly/installation.\n", + "\n", + "Everywhere in this page that you see `fig.show()`, you can display the same figure in a Dash application by passing it to the `figure` argument of the [`Graph` component](https://dash.plot.ly/dash-core-components/graph) from the built-in `dash_core_components` package like this:\n", + "\n", + "```python\n", + "import plotly.graph_objects as go # or plotly.express as px\n", + "fig = go.Figure() # or any Plotly Express function e.g. px.bar(...)\n", + "# fig.add_trace( ... )\n", + "# fig.update_layout( ... )\n", + "\n", + "from dash import Dash, dcc, html\n", + "\n", + "app = Dash()\n", + "app.layout = html.Div([\n", + " dcc.Graph(figure=fig)\n", + "])\n", + "\n", + "app.run_server(debug=True, use_reloader=False) # Turn off reloader if inside Jupyter\n", + "```" + ] + } + ], + "metadata": { + "jupytext": { + "notebook_metadata_filter": "all" + }, + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "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.8.8" + }, + "plotly": { + "description": "How to make 3D Mesh Plots", + "display_as": "3d_charts", + "language": "python", + "layout": "base", + "name": "3D Mesh Plots", + "order": 9, + "page_type": "u-guide", + "permalink": "python/3d-mesh/", + "thumbnail": "thumbnail/3d-mesh.jpg" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/3d-scatter-plots.ipynb b/3d-scatter-plots.ipynb new file mode 100644 index 000000000..a9536d7a0 --- /dev/null +++ b/3d-scatter-plots.ipynb @@ -0,0 +1,262 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "68b3647a", + "metadata": {}, + "source": [ + "## 3D scatter plot with Plotly Express\n", + "\n", + "[Plotly Express](/python/plotly-express/) is the easy-to-use, high-level interface to Plotly, which [operates on a variety of types of data](/python/px-arguments/) and produces [easy-to-style figures](/python/styling-plotly-express/).\n", + "\n", + "Like the [2D scatter plot](https://plotly.com/python/line-and-scatter/) `px.scatter`, the 3D function `px.scatter_3d` plots individual data in three-dimensional space." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "995a175e", + "metadata": {}, + "outputs": [], + "source": [ + "import plotly.express as px\n", + "df = px.data.iris()\n", + "fig = px.scatter_3d(df, x='sepal_length', y='sepal_width', z='petal_width',\n", + " color='species')\n", + "fig.show()" + ] + }, + { + "cell_type": "markdown", + "id": "da26f783", + "metadata": {}, + "source": [ + "A 4th dimension of the data can be represented thanks to the color of the markers. Also, values from the `species` column are used below to assign symbols to markers." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "40b3f0b5", + "metadata": {}, + "outputs": [], + "source": [ + "import plotly.express as px\n", + "df = px.data.iris()\n", + "fig = px.scatter_3d(df, x='sepal_length', y='sepal_width', z='petal_width',\n", + " color='petal_length', symbol='species')\n", + "fig.show()" + ] + }, + { + "cell_type": "markdown", + "id": "953cabfe", + "metadata": {}, + "source": [ + "#### Style 3d scatter plot\n", + "\n", + "It is possible to customize the style of the figure through the parameters of `px.scatter_3d` for some options, or by updating the traces or the layout of the figure through `fig.update`." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "8a18dd2c", + "metadata": {}, + "outputs": [], + "source": [ + "import plotly.express as px\n", + "df = px.data.iris()\n", + "fig = px.scatter_3d(df, x='sepal_length', y='sepal_width', z='petal_width',\n", + " color='petal_length', size='petal_length', size_max=18,\n", + " symbol='species', opacity=0.7)\n", + "\n", + "# tight layout\n", + "fig.update_layout(margin=dict(l=0, r=0, b=0, t=0))\n", + "fig.show()" + ] + }, + { + "cell_type": "markdown", + "id": "ff8eda18", + "metadata": {}, + "source": [ + "#### 3d scatter plots in Dash\n", + "\n", + "[Dash](https://plotly.com/dash/) is the best way to build analytical apps in Python using Plotly figures. To run the app below, run `pip install dash`, click \"Download\" to get the code and run `python app.py`.\n", + "\n", + "Get started with [the official Dash docs](https://dash.plotly.com/installation) and **learn how to effortlessly [style](https://plotly.com/dash/design-kit/) & [deploy](https://plotly.com/dash/app-manager/) apps like this with Dash Enterprise.**\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "3d508c3c", + "metadata": { + "hide_code": true + }, + "outputs": [], + "source": [ + "from IPython.display import IFrame\n", + "snippet_url = 'https://python-docs-dash-snippets.herokuapp.com/python-docs-dash-snippets/'\n", + "IFrame(snippet_url + '3d-scatter-plots', width='100%', height=1200)" + ] + }, + { + "cell_type": "markdown", + "id": "e4bf10ef", + "metadata": {}, + "source": [ + "

Sign up for Dash Club → Free cheat sheets plus updates from Chris Parmer and Adam Schroeder delivered to your inbox every two months. Includes tips and tricks, community apps, and deep dives into the Dash architecture.\n", + "Join now.

" + ] + }, + { + "cell_type": "markdown", + "id": "4fbe85f7", + "metadata": {}, + "source": [ + "### 3D Scatter Plot with go.Scatter3d\n", + "\n", + "#### Basic 3D Scatter Plot\n", + "\n", + "If Plotly Express does not provide a good starting point, it is also possible to use [the more generic `go.Scatter3D` class from `plotly.graph_objects`](/python/graph-objects/).\n", + "Like the [2D scatter plot](https://plotly.com/python/line-and-scatter/) `go.Scatter`, `go.Scatter3d` plots individual data in three-dimensional space." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "6103a6f6", + "metadata": {}, + "outputs": [], + "source": [ + "import plotly.graph_objects as go\n", + "import numpy as np\n", + "\n", + "# Helix equation\n", + "t = np.linspace(0, 10, 50)\n", + "x, y, z = np.cos(t), np.sin(t), t\n", + "\n", + "fig = go.Figure(data=[go.Scatter3d(x=x, y=y, z=z,\n", + " mode='markers')])\n", + "fig.show()" + ] + }, + { + "cell_type": "markdown", + "id": "20bc57df", + "metadata": {}, + "source": [ + "#### 3D Scatter Plot with Colorscaling and Marker Styling" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "759c8dc7", + "metadata": {}, + "outputs": [], + "source": [ + "import plotly.graph_objects as go\n", + "import numpy as np\n", + "\n", + "# Helix equation\n", + "t = np.linspace(0, 20, 100)\n", + "x, y, z = np.cos(t), np.sin(t), t\n", + "\n", + "fig = go.Figure(data=[go.Scatter3d(\n", + " x=x,\n", + " y=y,\n", + " z=z,\n", + " mode='markers',\n", + " marker=dict(\n", + " size=12,\n", + " color=z, # set color to an array/list of desired values\n", + " colorscale='Viridis', # choose a colorscale\n", + " opacity=0.8\n", + " )\n", + ")])\n", + "\n", + "# tight layout\n", + "fig.update_layout(margin=dict(l=0, r=0, b=0, t=0))\n", + "fig.show()" + ] + }, + { + "cell_type": "markdown", + "id": "9f595ca6", + "metadata": {}, + "source": [ + "#### Reference\n", + "\n", + "See [function reference for `px.scatter_3d()`](https://plotly.com/python-api-reference/generated/plotly.express.scatter_3d) or https://plotly.com/python/reference/scatter3d/ for more information and chart attribute options!\n" + ] + }, + { + "cell_type": "markdown", + "id": "cdb74a25", + "metadata": {}, + "source": [ + "### What About Dash?\n", + "\n", + "[Dash](https://dash.plot.ly/) is an open-source framework for building analytical applications, with no Javascript required, and it is tightly integrated with the Plotly graphing library.\n", + "\n", + "Learn about how to install Dash at https://dash.plot.ly/installation.\n", + "\n", + "Everywhere in this page that you see `fig.show()`, you can display the same figure in a Dash application by passing it to the `figure` argument of the [`Graph` component](https://dash.plot.ly/dash-core-components/graph) from the built-in `dash_core_components` package like this:\n", + "\n", + "```python\n", + "import plotly.graph_objects as go # or plotly.express as px\n", + "fig = go.Figure() # or any Plotly Express function e.g. px.bar(...)\n", + "# fig.add_trace( ... )\n", + "# fig.update_layout( ... )\n", + "\n", + "from dash import Dash, dcc, html\n", + "\n", + "app = Dash()\n", + "app.layout = html.Div([\n", + " dcc.Graph(figure=fig)\n", + "])\n", + "\n", + "app.run_server(debug=True, use_reloader=False) # Turn off reloader if inside Jupyter\n", + "```" + ] + } + ], + "metadata": { + "jupytext": { + "notebook_metadata_filter": "all" + }, + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "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.8.8" + }, + "plotly": { + "description": "How to make 3D scatter plots in Python with Plotly.", + "display_as": "3d_charts", + "language": "python", + "layout": "base", + "name": "3D Scatter Plots", + "order": 2, + "page_type": "example_index", + "permalink": "python/3d-scatter-plots/", + "thumbnail": "thumbnail/3d-scatter.jpg" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/3d-subplots.ipynb b/3d-subplots.ipynb new file mode 100644 index 000000000..fb96e27ca --- /dev/null +++ b/3d-subplots.ipynb @@ -0,0 +1,143 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "ecd14db2", + "metadata": {}, + "source": [ + "#### 3D Surface Subplots" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "39dfd6bd", + "metadata": {}, + "outputs": [], + "source": [ + "import plotly.graph_objects as go\n", + "from plotly.subplots import make_subplots\n", + "\n", + "import numpy as np\n", + "\n", + "# Initialize figure with 4 3D subplots\n", + "fig = make_subplots(\n", + " rows=2, cols=2,\n", + " specs=[[{'type': 'surface'}, {'type': 'surface'}],\n", + " [{'type': 'surface'}, {'type': 'surface'}]])\n", + "\n", + "# Generate data\n", + "x = np.linspace(-5, 80, 10)\n", + "y = np.linspace(-5, 60, 10)\n", + "xGrid, yGrid = np.meshgrid(y, x)\n", + "z = xGrid ** 3 + yGrid ** 3\n", + "\n", + "# adding surfaces to subplots.\n", + "fig.add_trace(\n", + " go.Surface(x=x, y=y, z=z, colorscale='Viridis', showscale=False),\n", + " row=1, col=1)\n", + "\n", + "fig.add_trace(\n", + " go.Surface(x=x, y=y, z=z, colorscale='RdBu', showscale=False),\n", + " row=1, col=2)\n", + "\n", + "fig.add_trace(\n", + " go.Surface(x=x, y=y, z=z, colorscale='YlOrRd', showscale=False),\n", + " row=2, col=1)\n", + "\n", + "fig.add_trace(\n", + " go.Surface(x=x, y=y, z=z, colorscale='YlGnBu', showscale=False),\n", + " row=2, col=2)\n", + "\n", + "fig.update_layout(\n", + " title_text='3D subplots with different colorscales',\n", + " height=800,\n", + " width=800\n", + ")\n", + "\n", + "fig.show()" + ] + }, + { + "cell_type": "markdown", + "id": "1d68f30c", + "metadata": {}, + "source": [ + "#### Reference" + ] + }, + { + "cell_type": "markdown", + "id": "aa835b1e", + "metadata": {}, + "source": [ + "See https://plotly.com/python/subplots/ for more information regarding subplots!" + ] + }, + { + "cell_type": "markdown", + "id": "a5a1e648", + "metadata": {}, + "source": [ + "### What About Dash?\n", + "\n", + "[Dash](https://dash.plot.ly/) is an open-source framework for building analytical applications, with no Javascript required, and it is tightly integrated with the Plotly graphing library.\n", + "\n", + "Learn about how to install Dash at https://dash.plot.ly/installation.\n", + "\n", + "Everywhere in this page that you see `fig.show()`, you can display the same figure in a Dash application by passing it to the `figure` argument of the [`Graph` component](https://dash.plot.ly/dash-core-components/graph) from the built-in `dash_core_components` package like this:\n", + "\n", + "```python\n", + "import plotly.graph_objects as go # or plotly.express as px\n", + "fig = go.Figure() # or any Plotly Express function e.g. px.bar(...)\n", + "# fig.add_trace( ... )\n", + "# fig.update_layout( ... )\n", + "\n", + "from dash import Dash, dcc, html\n", + "\n", + "app = Dash()\n", + "app.layout = html.Div([\n", + " dcc.Graph(figure=fig)\n", + "])\n", + "\n", + "app.run_server(debug=True, use_reloader=False) # Turn off reloader if inside Jupyter\n", + "```" + ] + } + ], + "metadata": { + "jupytext": { + "notebook_metadata_filter": "all" + }, + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "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.7.3" + }, + "plotly": { + "description": "3D Subplots in Plotly", + "display_as": "3d_charts", + "language": "python", + "layout": "base", + "name": "3D Subplots", + "order": 4, + "page_type": "example_index", + "permalink": "python/3d-subplots/", + "thumbnail": "thumbnail/3d-subplots.jpg" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/3d-surface-plots.ipynb b/3d-surface-plots.ipynb new file mode 100644 index 000000000..c5f3de809 --- /dev/null +++ b/3d-surface-plots.ipynb @@ -0,0 +1,329 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "736dfdea", + "metadata": {}, + "source": [ + "#### Topographical 3D Surface Plot" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "441c8d35", + "metadata": {}, + "outputs": [], + "source": [ + "import plotly.graph_objects as go\n", + "\n", + "import pandas as pd\n", + "\n", + "# Read data from a csv\n", + "z_data = pd.read_csv('https://raw.githubusercontent.com/plotly/datasets/master/api_docs/mt_bruno_elevation.csv')\n", + "\n", + "fig = go.Figure(data=[go.Surface(z=z_data.values)])\n", + "\n", + "fig.update_layout(title=dict(text='Mt Bruno Elevation'), autosize=False,\n", + " width=500, height=500,\n", + " margin=dict(l=65, r=50, b=65, t=90))\n", + "\n", + "fig.show()" + ] + }, + { + "cell_type": "markdown", + "id": "93afd034", + "metadata": {}, + "source": [ + "### Passing x and y data to 3D Surface Plot\n", + "\n", + "If you do not specify `x` and `y` coordinates, integer indices are used for the `x` and `y` axis. You can also pass `x` and `y` values to `go.Surface`." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "57d7b28d", + "metadata": { + "lines_to_next_cell": 2 + }, + "outputs": [], + "source": [ + "import plotly.graph_objects as go\n", + "import pandas as pd\n", + "import numpy as np\n", + "# Read data from a csv\n", + "z_data = pd.read_csv('https://raw.githubusercontent.com/plotly/datasets/master/api_docs/mt_bruno_elevation.csv')\n", + "z = z_data.values\n", + "sh_0, sh_1 = z.shape\n", + "x, y = np.linspace(0, 1, sh_0), np.linspace(0, 1, sh_1)\n", + "fig = go.Figure(data=[go.Surface(z=z, x=x, y=y)])\n", + "fig.update_layout(title=dict(text='Mt Bruno Elevation'), autosize=False,\n", + " width=500, height=500,\n", + " margin=dict(l=65, r=50, b=65, t=90))\n", + "fig.show()" + ] + }, + { + "cell_type": "markdown", + "id": "d406618d", + "metadata": {}, + "source": [ + "#### Surface Plot With Contours" + ] + }, + { + "cell_type": "markdown", + "id": "7b022a86", + "metadata": {}, + "source": [ + "Display and customize contour data for each axis using the `contours` attribute ([reference](https://plotly.com/python/reference/surface/#surface-contours))." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "8338fe90", + "metadata": { + "lines_to_next_cell": 0 + }, + "outputs": [], + "source": [ + "import plotly.graph_objects as go\n", + "\n", + "import pandas as pd\n", + "\n", + "# Read data from a csv\n", + "z_data = pd.read_csv('https://raw.githubusercontent.com/plotly/datasets/master/api_docs/mt_bruno_elevation.csv')\n", + "\n", + "fig = go.Figure(data=[go.Surface(z=z_data.values)])\n", + "fig.update_traces(contours_z=dict(show=True, usecolormap=True,\n", + " highlightcolor=\"limegreen\", project_z=True))\n", + "fig.update_layout(title=dict(text='Mt Bruno Elevation'), autosize=False,\n", + " scene_camera_eye=dict(x=1.87, y=0.88, z=-0.64),\n", + " width=500, height=500,\n", + " margin=dict(l=65, r=50, b=65, t=90)\n", + ")\n", + "\n", + "fig.show()" + ] + }, + { + "cell_type": "markdown", + "id": "b9b39a41", + "metadata": {}, + "source": [ + "#### Configure Surface Contour Levels\n", + "This example shows how to slice the surface graph on the desired position for each of x, y and z axis. [contours.x.start](https://plotly.com/python/reference/surface/#surface-contours-x-start) sets the starting contour level value, `end` sets the end of it, and `size` sets the step between each contour level." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "d9e14060", + "metadata": { + "lines_to_next_cell": 0 + }, + "outputs": [], + "source": [ + "import plotly.graph_objects as go\n", + "\n", + "fig = go.Figure(go.Surface(\n", + " contours = {\n", + " \"x\": {\"show\": True, \"start\": 1.5, \"end\": 2, \"size\": 0.04, \"color\":\"white\"},\n", + " \"z\": {\"show\": True, \"start\": 0.5, \"end\": 0.8, \"size\": 0.05}\n", + " },\n", + " x = [1,2,3,4,5],\n", + " y = [1,2,3,4,5],\n", + " z = [\n", + " [0, 1, 0, 1, 0],\n", + " [1, 0, 1, 0, 1],\n", + " [0, 1, 0, 1, 0],\n", + " [1, 0, 1, 0, 1],\n", + " [0, 1, 0, 1, 0]\n", + " ]))\n", + "fig.update_layout(\n", + " scene = {\n", + " \"xaxis\": {\"nticks\": 20},\n", + " \"zaxis\": {\"nticks\": 4},\n", + " 'camera_eye': {\"x\": 0, \"y\": -1, \"z\": 0.5},\n", + " \"aspectratio\": {\"x\": 1, \"y\": 1, \"z\": 0.2}\n", + " })\n", + "fig.show()" + ] + }, + { + "cell_type": "markdown", + "id": "b110fc8a", + "metadata": {}, + "source": [ + "#### Multiple 3D Surface Plots" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "786c3650", + "metadata": {}, + "outputs": [], + "source": [ + "import plotly.graph_objects as go\n", + "import numpy as np\n", + "\n", + "z1 = np.array([\n", + " [8.83,8.89,8.81,8.87,8.9,8.87],\n", + " [8.89,8.94,8.85,8.94,8.96,8.92],\n", + " [8.84,8.9,8.82,8.92,8.93,8.91],\n", + " [8.79,8.85,8.79,8.9,8.94,8.92],\n", + " [8.79,8.88,8.81,8.9,8.95,8.92],\n", + " [8.8,8.82,8.78,8.91,8.94,8.92],\n", + " [8.75,8.78,8.77,8.91,8.95,8.92],\n", + " [8.8,8.8,8.77,8.91,8.95,8.94],\n", + " [8.74,8.81,8.76,8.93,8.98,8.99],\n", + " [8.89,8.99,8.92,9.1,9.13,9.11],\n", + " [8.97,8.97,8.91,9.09,9.11,9.11],\n", + " [9.04,9.08,9.05,9.25,9.28,9.27],\n", + " [9,9.01,9,9.2,9.23,9.2],\n", + " [8.99,8.99,8.98,9.18,9.2,9.19],\n", + " [8.93,8.97,8.97,9.18,9.2,9.18]\n", + "])\n", + "\n", + "z2 = z1 + 1\n", + "z3 = z1 - 1\n", + "\n", + "fig = go.Figure(data=[\n", + " go.Surface(z=z1),\n", + " go.Surface(z=z2, showscale=False, opacity=0.9),\n", + " go.Surface(z=z3, showscale=False, opacity=0.9)\n", + "\n", + "])\n", + "\n", + "fig.show()" + ] + }, + { + "cell_type": "markdown", + "id": "46a24113", + "metadata": {}, + "source": [ + "### Setting the Surface Color\n", + "\n", + "You can use the `surfacecolor` attribute to define the color of the surface of your figure. In this example, the surface color represents the distance from the origin, rather than the default, which is the `z` value." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "70618223", + "metadata": {}, + "outputs": [], + "source": [ + "import plotly.graph_objects as go\n", + "from plotly.subplots import make_subplots\n", + "\n", + "# Equation of ring cyclide\n", + "# see https://en.wikipedia.org/wiki/Dupin_cyclide\n", + "import numpy as np\n", + "a, b, d = 1.32, 1., 0.8\n", + "c = a**2 - b**2\n", + "u, v = np.mgrid[0:2*np.pi:100j, 0:2*np.pi:100j]\n", + "x = (d * (c - a * np.cos(u) * np.cos(v)) + b**2 * np.cos(u)) / (a - c * np.cos(u) * np.cos(v))\n", + "y = b * np.sin(u) * (a - d*np.cos(v)) / (a - c * np.cos(u) * np.cos(v))\n", + "z = b * np.sin(v) * (c*np.cos(u) - d) / (a - c * np.cos(u) * np.cos(v))\n", + "\n", + "fig = make_subplots(rows=1, cols=2,\n", + " specs=[[{'is_3d': True}, {'is_3d': True}]],\n", + " subplot_titles=['Color corresponds to z', 'Color corresponds to distance to origin'],\n", + " )\n", + "\n", + "fig.add_trace(go.Surface(x=x, y=y, z=z, colorbar_x=-0.07), 1, 1)\n", + "fig.add_trace(go.Surface(x=x, y=y, z=z, surfacecolor=x**2 + y**2 + z**2), 1, 2)\n", + "fig.update_layout(title_text=\"Ring cyclide\")\n", + "fig.show()" + ] + }, + { + "cell_type": "markdown", + "id": "a013d895", + "metadata": {}, + "source": [ + "#### Reference" + ] + }, + { + "cell_type": "markdown", + "id": "1cb1ce14", + "metadata": {}, + "source": [ + "See https://plotly.com/python/reference/surface/ for more information!\n" + ] + }, + { + "cell_type": "markdown", + "id": "8440a0e7", + "metadata": {}, + "source": [ + "### What About Dash?\n", + "\n", + "[Dash](https://dash.plot.ly/) is an open-source framework for building analytical applications, with no Javascript required, and it is tightly integrated with the Plotly graphing library.\n", + "\n", + "Learn about how to install Dash at https://dash.plot.ly/installation.\n", + "\n", + "Everywhere in this page that you see `fig.show()`, you can display the same figure in a Dash application by passing it to the `figure` argument of the [`Graph` component](https://dash.plot.ly/dash-core-components/graph) from the built-in `dash_core_components` package like this:\n", + "\n", + "```python\n", + "import plotly.graph_objects as go # or plotly.express as px\n", + "fig = go.Figure() # or any Plotly Express function e.g. px.bar(...)\n", + "# fig.add_trace( ... )\n", + "# fig.update_layout( ... )\n", + "\n", + "from dash import Dash, dcc, html\n", + "\n", + "app = Dash()\n", + "app.layout = html.Div([\n", + " dcc.Graph(figure=fig)\n", + "])\n", + "\n", + "app.run_server(debug=True, use_reloader=False) # Turn off reloader if inside Jupyter\n", + "```" + ] + } + ], + "metadata": { + "jupytext": { + "notebook_metadata_filter": "all" + }, + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "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.6.7" + }, + "plotly": { + "description": "How to make 3D-surface plots in Python", + "display_as": "3d_charts", + "language": "python", + "layout": "base", + "name": "3D Surface Plots", + "order": 3, + "page_type": "example_index", + "permalink": "python/3d-surface-plots/", + "redirect_from": "python/3d-surface-coloring/", + "thumbnail": "thumbnail/3d-surface.jpg" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/3d-volume.ipynb b/3d-volume.ipynb new file mode 100644 index 000000000..07cc98e96 --- /dev/null +++ b/3d-volume.ipynb @@ -0,0 +1,379 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "65d7f386", + "metadata": {}, + "source": [ + "A volume plot with `go.Volume` shows several partially transparent isosurfaces for volume rendering. The API of `go.Volume` is close to the one of `go.Isosurface`. However, whereas [isosurface plots](/python/3d-isosurface-plots/) show all surfaces with the same opacity, tweaking the `opacityscale` parameter of `go.Volume` results in a depth effect and better volume rendering.\n", + "\n", + "## Simple volume plot with go.Volume\n", + "\n", + "In the three examples below, note that the default colormap is different whether isomin and isomax have the same sign or not." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "84f212a5", + "metadata": {}, + "outputs": [], + "source": [ + "import plotly.graph_objects as go\n", + "import numpy as np\n", + "X, Y, Z = np.mgrid[-8:8:40j, -8:8:40j, -8:8:40j]\n", + "values = np.sin(X*Y*Z) / (X*Y*Z)\n", + "\n", + "fig = go.Figure(data=go.Volume(\n", + " x=X.flatten(),\n", + " y=Y.flatten(),\n", + " z=Z.flatten(),\n", + " value=values.flatten(),\n", + " isomin=0.1,\n", + " isomax=0.8,\n", + " opacity=0.1, # needs to be small to see through all surfaces\n", + " surface_count=17, # needs to be a large number for good volume rendering\n", + " ))\n", + "fig.show()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "5ab55025", + "metadata": {}, + "outputs": [], + "source": [ + "import plotly.graph_objects as go\n", + "import numpy as np\n", + "X, Y, Z = np.mgrid[-1:1:30j, -1:1:30j, -1:1:30j]\n", + "values = np.sin(np.pi*X) * np.cos(np.pi*Z) * np.sin(np.pi*Y)\n", + "\n", + "fig = go.Figure(data=go.Volume(\n", + " x=X.flatten(),\n", + " y=Y.flatten(),\n", + " z=Z.flatten(),\n", + " value=values.flatten(),\n", + " isomin=-0.1,\n", + " isomax=0.8,\n", + " opacity=0.1, # needs to be small to see through all surfaces\n", + " surface_count=21, # needs to be a large number for good volume rendering\n", + " ))\n", + "fig.show()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "dafe2527", + "metadata": {}, + "outputs": [], + "source": [ + "import numpy as np\n", + "import plotly.graph_objects as go\n", + "\n", + "# Generate nicely looking random 3D-field\n", + "np.random.seed(0)\n", + "l = 30\n", + "X, Y, Z = np.mgrid[:l, :l, :l]\n", + "vol = np.zeros((l, l, l))\n", + "pts = (l * np.random.rand(3, 15)).astype(int)\n", + "vol[tuple(indices for indices in pts)] = 1\n", + "from scipy import ndimage\n", + "vol = ndimage.gaussian_filter(vol, 4)\n", + "vol /= vol.max()\n", + "\n", + "fig = go.Figure(data=go.Volume(\n", + " x=X.flatten(), y=Y.flatten(), z=Z.flatten(),\n", + " value=vol.flatten(),\n", + " isomin=0.2,\n", + " isomax=0.7,\n", + " opacity=0.1,\n", + " surface_count=25,\n", + " ))\n", + "fig.update_layout(scene_xaxis_showticklabels=False,\n", + " scene_yaxis_showticklabels=False,\n", + " scene_zaxis_showticklabels=False)\n", + "fig.show()" + ] + }, + { + "cell_type": "markdown", + "id": "39b71922", + "metadata": {}, + "source": [ + "### Defining the opacity scale of volume plots\n", + "\n", + "In order to see through the volume, the different isosurfaces need to be partially transparent. This transparency is controlled by a global parameter, `opacity`, as well as an opacity scale mapping scalar values to opacity levels. The figure below shows that changing the opacity scale changes a lot the visualization, so that `opacityscale` should be chosen carefully (`uniform` corresponds to a uniform opacity, `min`/`max` maps the minimum/maximum value to a maximal opacity, and `extremes` maps both the minimum and maximum values to maximal opacity, with a dip in between)." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "c182a71b", + "metadata": {}, + "outputs": [], + "source": [ + "import plotly.graph_objects as go\n", + "from plotly.subplots import make_subplots\n", + "fig = make_subplots(\n", + " rows=2, cols=2,\n", + " specs=[[{'type': 'volume'}, {'type': 'volume'}],\n", + " [{'type': 'volume'}, {'type': 'volume'}]])\n", + "\n", + "import numpy as np\n", + "\n", + "X, Y, Z = np.mgrid[-8:8:30j, -8:8:30j, -8:8:30j]\n", + "values = np.sin(X*Y*Z) / (X*Y*Z)\n", + "\n", + "\n", + "fig.add_trace(go.Volume(\n", + " opacityscale=\"uniform\",\n", + " ), row=1, col=1)\n", + "fig.add_trace(go.Volume(\n", + " opacityscale=\"extremes\",\n", + " ), row=1, col=2)\n", + "fig.add_trace(go.Volume(\n", + " opacityscale=\"min\",\n", + " ), row=2, col=1)\n", + "fig.add_trace(go.Volume(\n", + " opacityscale=\"max\",\n", + " ), row=2, col=2)\n", + "fig.update_traces(x=X.flatten(), y=Y.flatten(), z=Z.flatten(), value=values.flatten(),\n", + " isomin=0.15, isomax=0.9, opacity=0.1, surface_count=15)\n", + "fig.show()" + ] + }, + { + "cell_type": "markdown", + "id": "d479e45a", + "metadata": {}, + "source": [ + "### Defining a custom opacity scale\n", + "\n", + "It is also possible to define a custom opacity scale, mapping scalar values to relative opacity values (between 0 and 1, the maximum opacity is given by the opacity keyword). This is useful to make a range of values completely transparent, as in the example below between -0.2 and 0.2." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "deb8e368", + "metadata": {}, + "outputs": [], + "source": [ + "import plotly.graph_objects as go\n", + "import numpy as np\n", + "X, Y, Z = np.mgrid[-1:1:30j, -1:1:30j, -1:1:30j]\n", + "values = np.sin(np.pi*X) * np.cos(np.pi*Z) * np.sin(np.pi*Y)\n", + "\n", + "fig = go.Figure(data=go.Volume(\n", + " x=X.flatten(),\n", + " y=Y.flatten(),\n", + " z=Z.flatten(),\n", + " value=values.flatten(),\n", + " isomin=-0.5,\n", + " isomax=0.5,\n", + " opacity=0.1, # max opacity\n", + " opacityscale=[[-0.5, 1], [-0.2, 0], [0.2, 0], [0.5, 1]],\n", + " surface_count=21,\n", + " colorscale='RdBu'\n", + " ))\n", + "fig.show()" + ] + }, + { + "cell_type": "markdown", + "id": "79ce228c", + "metadata": {}, + "source": [ + "### Adding caps to a volume plot\n", + "\n", + "For a clearer visualization of internal surfaces, it is possible to remove the caps (color-coded surfaces on the sides of the visualization domain). Caps are visible by default. Compare below with and without caps." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "26f36dee", + "metadata": {}, + "outputs": [], + "source": [ + "import numpy as np\n", + "import plotly.graph_objects as go\n", + "\n", + "\n", + "X, Y, Z = np.mgrid[:1:20j, :1:20j, :1:20j]\n", + "vol = (X - 1)**2 + (Y - 1)**2 + Z**2\n", + "\n", + "\n", + "fig = go.Figure(data=go.Volume(\n", + " x=X.flatten(), y=Y.flatten(), z=Z.flatten(),\n", + " value=vol.flatten(),\n", + " isomin=0.2,\n", + " isomax=0.7,\n", + " opacity=0.2,\n", + " surface_count=21,\n", + " caps= dict(x_show=True, y_show=True, z_show=True, x_fill=1), # with caps (default mode)\n", + " ))\n", + "\n", + "# Change camera view for a better view of the sides, XZ plane\n", + "# (see https://plotly.com/python/v3/3d-camera-controls/)\n", + "fig.update_layout(scene_camera = dict(\n", + " up=dict(x=0, y=0, z=1),\n", + " center=dict(x=0, y=0, z=0),\n", + " eye=dict(x=0.1, y=2.5, z=0.1)\n", + "))\n", + "\n", + "fig.show()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "ba0c2960", + "metadata": {}, + "outputs": [], + "source": [ + "import numpy as np\n", + "import plotly.graph_objects as go\n", + "\n", + "X, Y, Z = np.mgrid[:1:20j, :1:20j, :1:20j]\n", + "vol = (X - 1)**2 + (Y - 1)**2 + Z**2\n", + "\n", + "\n", + "fig = go.Figure(data=go.Volume(\n", + " x=X.flatten(), y=Y.flatten(), z=Z.flatten(),\n", + " value=vol.flatten(),\n", + " isomin=0.2,\n", + " isomax=0.7,\n", + " opacity=0.2,\n", + " surface_count=21,\n", + " caps= dict(x_show=False, y_show=False, z_show=False), # no caps\n", + " ))\n", + "fig.update_layout(scene_camera = dict(\n", + " up=dict(x=0, y=0, z=1),\n", + " center=dict(x=0, y=0, z=0),\n", + " eye=dict(x=0.1, y=2.5, z=0.1)\n", + "))\n", + "\n", + "fig.show()" + ] + }, + { + "cell_type": "markdown", + "id": "59c15e53", + "metadata": {}, + "source": [ + "### Adding slices to a volume plot\n", + "\n", + "Slices through the volume can be added to the volume plot. In this example the isosurfaces are only partially filled so that the slice is more visible, and the caps were removed for the same purpose." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "de9a557f", + "metadata": {}, + "outputs": [], + "source": [ + "import numpy as np\n", + "import plotly.graph_objects as go\n", + "\n", + "X, Y, Z = np.mgrid[:1:20j, :1:20j, :1:20j]\n", + "vol = (X - 1)**2 + (Y - 1)**2 + Z**2\n", + "\n", + "\n", + "fig = go.Figure(data=go.Volume(\n", + " x=X.flatten(), y=Y.flatten(), z=Z.flatten(),\n", + " value=vol.flatten(),\n", + " isomin=0.2,\n", + " isomax=0.7,\n", + " opacity=0.2,\n", + " surface_count=21,\n", + " slices_z=dict(show=True, locations=[0.4]),\n", + " surface=dict(fill=0.5, pattern='odd'),\n", + " caps= dict(x_show=False, y_show=False, z_show=False), # no caps\n", + " ))\n", + "\n", + "fig.show()" + ] + }, + { + "cell_type": "markdown", + "id": "16e1d91d", + "metadata": {}, + "source": [ + "#### Reference\n", + "See https://plotly.com/python/reference/volume/ for more information and chart attribute options!\n", + "\n", + "#### See also\n", + "[3D isosurface documentation](/python/3d-isosurface-plots/)" + ] + }, + { + "cell_type": "markdown", + "id": "cc2f4157", + "metadata": {}, + "source": [ + "### What About Dash?\n", + "\n", + "[Dash](https://dash.plot.ly/) is an open-source framework for building analytical applications, with no Javascript required, and it is tightly integrated with the Plotly graphing library.\n", + "\n", + "Learn about how to install Dash at https://dash.plot.ly/installation.\n", + "\n", + "Everywhere in this page that you see `fig.show()`, you can display the same figure in a Dash application by passing it to the `figure` argument of the [`Graph` component](https://dash.plot.ly/dash-core-components/graph) from the built-in `dash_core_components` package like this:\n", + "\n", + "```python\n", + "import plotly.graph_objects as go # or plotly.express as px\n", + "fig = go.Figure() # or any Plotly Express function e.g. px.bar(...)\n", + "# fig.add_trace( ... )\n", + "# fig.update_layout( ... )\n", + "\n", + "from dash import Dash, dcc, html\n", + "\n", + "app = Dash()\n", + "app.layout = html.Div([\n", + " dcc.Graph(figure=fig)\n", + "])\n", + "\n", + "app.run_server(debug=True, use_reloader=False) # Turn off reloader if inside Jupyter\n", + "```" + ] + } + ], + "metadata": { + "jupytext": { + "notebook_metadata_filter": "all" + }, + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "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.7.3" + }, + "plotly": { + "description": "How to make 3D Volume Plots in Python with Plotly.", + "display_as": "3d_charts", + "language": "python", + "layout": "base", + "name": "3D Volume Plots", + "order": 11, + "page_type": "u-guide", + "permalink": "python/3d-volume-plots/", + "thumbnail": "thumbnail/3d-volume-plots.jpg" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md deleted file mode 100644 index bc8371522..000000000 --- a/CODE_OF_CONDUCT.md +++ /dev/null @@ -1,43 +0,0 @@ -# Contributor Covenant Code of Conduct - -## Our Pledge - -In the interest of fostering an open and welcoming environment, we as contributors and maintainers pledge to making participation in our project and our community a harassment-free experience for everyone, regardless of age, body size, disability, ethnicity, gender identity and expression, level of experience, nationality, personal appearance, race, religion, or sexual identity and orientation. - -## Our Standards - -Examples of behavior that contributes to creating a positive environment include: - -* Using welcoming and inclusive language -* Being respectful of differing viewpoints and experiences -* Gracefully accepting constructive criticism -* Focusing on what is best for the community -* Showing empathy towards other community members - -Examples of unacceptable behavior by participants include: - -* The use of sexualized language or imagery and unwelcome sexual attention or advances -* Trolling, insulting/derogatory comments, and personal or political attacks -* Public or private harassment -* Publishing others' private information, such as a physical or electronic address, without explicit permission -* Other conduct which could reasonably be considered inappropriate in a professional setting - -## Our Responsibilities - -Project maintainers are responsible for clarifying the standards of acceptable behavior and are expected to take appropriate and fair corrective action in response to any instances of unacceptable behavior. - -Project maintainers have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct, or to ban temporarily or permanently any contributor for other behaviors that they deem inappropriate, threatening, offensive, or harmful. - -## Scope - -This Code of Conduct applies both within project spaces and in public spaces when an individual is representing the project or its community. Examples of representing a project or community include using an official project e-mail address, posting via an official social media account, or acting as an appointed representative at an online or offline event. Representation of a project may be further defined and clarified by project maintainers. - -## Enforcement - -Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by contacting the project team at accounts@plot.ly. All complaints will be reviewed and investigated and will result in a response that is deemed necessary and appropriate to the circumstances. The project team is obligated to maintain confidentiality with regard to the reporter of an incident. Further details of specific enforcement policies may be posted separately. - -Project maintainers who do not follow or enforce the Code of Conduct in good faith may face temporary or permanent repercussions as determined by other members of the project's leadership. - -## Attribution - -This Code of Conduct is adapted from the [Contributor Covenant](http://contributor-covenant.org), version 1.4, available at [http://contributor-covenant.org/version/1/4](http://contributor-covenant.org/version/1/4/), and may also be found online at . diff --git a/LaTeX.ipynb b/LaTeX.ipynb new file mode 100644 index 000000000..19aebf493 --- /dev/null +++ b/LaTeX.ipynb @@ -0,0 +1,125 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "02e93bef", + "metadata": {}, + "source": [ + "#### LaTeX Typesetting\n", + "\n", + "Figure titles, axis labels and annotations all accept LaTeX directives for rendering mathematical formulas and notation, when the entire label is surrounded by dollar signs `$...$`. This rendering is handled by the [MathJax library](https://www.npmjs.com/package/mathjax?activeTab=versions), which must be loaded in the environment where figures are being rendered. MathJax is included by default in Jupyter-like environments. When embedding Plotly figures in other contexts it may be required to ensure that MathJax is separately loaded, for example via a `\n", + "```" + ] + }, + { + "cell_type": "markdown", + "id": "b01e6501", + "metadata": {}, + "source": [ + "### WebGL for Scatter Performance\n", + "\n", + "In the examples below we show that it is possible to represent up to around a million points with WebGL-enabled traces.\n", + "For larger datasets, or for a clearer visualization of the density of points,\n", + "it is also possible to use [datashader](/python/datashader/).\n", + "\n", + "### WebGL with Plotly Express\n", + "\n", + "The `render_mode` argument to supported Plotly Express functions (e.g. `scatter` and `scatter_polar`) can be used to enable WebGL rendering.\n", + "\n", + "> **Note** The default `render_mode` is `\"auto\"`, in which case Plotly Express will automatically set `render_mode=\"webgl\"` if the input data is more than 1,000 rows long. In this case, WebGl can be disabled by setting `render_mode=svg`.\n", + "\n", + "Here is an example that creates a 100,000 point scatter plot using Plotly Express with WebGL rendering explicitly enabled." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "6410b10a", + "metadata": { + "lines_to_next_cell": 2 + }, + "outputs": [], + "source": [ + "import plotly.express as px\n", + "\n", + "import pandas as pd\n", + "import numpy as np\n", + "\n", + "np.random.seed(1)\n", + "\n", + "N = 100000\n", + "\n", + "df = pd.DataFrame(dict(x=np.random.randn(N),\n", + " y=np.random.randn(N)))\n", + "\n", + "fig = px.scatter(df, x=\"x\", y=\"y\", render_mode='webgl')\n", + "\n", + "fig.update_traces(marker_line=dict(width=1, color='DarkSlateGray'))\n", + "\n", + "fig.show()" + ] + }, + { + "cell_type": "markdown", + "id": "7abea7f3", + "metadata": {}, + "source": [ + "#### WebGL with 1,000,000 points with Graph Objects\n", + "\n", + "If Plotly Express does not provide a good starting point for creating a chart, you can use [the more generic `go.Scattergl` class from `plotly.graph_objects`](/python/graph-objects/)." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "96a0f1cc", + "metadata": {}, + "outputs": [], + "source": [ + "import plotly.graph_objects as go\n", + "\n", + "import numpy as np\n", + "\n", + "N = 1_000_000\n", + "\n", + "fig = go.Figure()\n", + "\n", + "fig.add_trace(\n", + " go.Scattergl(\n", + " x = np.random.randn(N),\n", + " y = np.random.randn(N),\n", + " mode = 'markers',\n", + " marker = dict(\n", + " line = dict(\n", + " width = 1,\n", + " color = 'DarkSlateGrey')\n", + " )\n", + " )\n", + ")\n", + "\n", + "fig.show()" + ] + }, + { + "cell_type": "markdown", + "id": "4ac9fa27", + "metadata": {}, + "source": [ + "See https://plotly.com/python/reference/scattergl/ for more information and chart attribute options!\n", + "\n", + "## Datashader\n", + "\n", + "Use [Datashader](https://datashader.org/) to reduce the size of a dataset passed to the browser for rendering by creating a rasterized representation of the dataset. This makes it ideal for working with datasets of tens to hundreds of millions of points.\n", + "\n", + "### Passing Datashader Rasters as a Tile Map Image Layer\n", + "\n", + "The following example shows the spatial distribution of taxi rides in New York City, which are concentrated on major avenues. For more details about tile-based maps, see [the tile map layers tutorial](/python/tile-map-layers)." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "0782b8e5", + "metadata": {}, + "outputs": [], + "source": [ + "import pandas as pd\n", + "df = pd.read_csv('https://raw.githubusercontent.com/plotly/datasets/master/uber-rides-data1.csv')\n", + "dff = df.query('Lat < 40.82').query('Lat > 40.70').query('Lon > -74.02').query('Lon < -73.91')\n", + "\n", + "import datashader as ds\n", + "cvs = ds.Canvas(plot_width=1000, plot_height=1000)\n", + "agg = cvs.points(dff, x='Lon', y='Lat')\n", + "# agg is an xarray object, see http://xarray.pydata.org/en/stable/ for more details\n", + "coords_lat, coords_lon = agg.coords['Lat'].values, agg.coords['Lon'].values\n", + "# Corners of the image\n", + "coordinates = [[coords_lon[0], coords_lat[0]],\n", + " [coords_lon[-1], coords_lat[0]],\n", + " [coords_lon[-1], coords_lat[-1]],\n", + " [coords_lon[0], coords_lat[-1]]]\n", + "\n", + "from colorcet import fire\n", + "import datashader.transfer_functions as tf\n", + "img = tf.shade(agg, cmap=fire)[::-1].to_pil()\n", + "\n", + "import plotly.express as px\n", + "# Trick to create rapidly a figure with map axes\n", + "fig = px.scatter_map(dff[:1], lat='Lat', lon='Lon', zoom=12)\n", + "# Add the datashader image as a tile map layer image\n", + "fig.update_layout(\n", + " map_style=\"carto-darkmatter\",\n", + " map_layers=[{\"sourcetype\": \"image\", \"source\": img, \"coordinates\": coordinates}],\n", + ")\n", + "fig.show()" + ] + }, + { + "cell_type": "markdown", + "id": "37a77c1e", + "metadata": {}, + "source": [ + "### Exploring Correlations of a Large Dataset\n", + "\n", + "Here we explore the flight delay dataset from https://www.kaggle.com/usdot/flight-delays. In order to get a visual impression of the correlation between features, we generate a datashader rasterized array which we plot using a `Heatmap` trace. It creates a much clearer visualization than a scatter plot of (even a fraction of) the data points, as shown below." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "232b15e0", + "metadata": {}, + "outputs": [], + "source": [ + "import plotly.graph_objects as go\n", + "import pandas as pd\n", + "import numpy as np\n", + "import datashader as ds\n", + "df = pd.read_parquet('https://raw.githubusercontent.com/plotly/datasets/master/2015_flights.parquet')\n", + "fig = go.Figure(go.Scattergl(x=df['SCHEDULED_DEPARTURE'][::200],\n", + " y=df['DEPARTURE_DELAY'][::200],\n", + " mode='markers')\n", + ")\n", + "fig.update_layout(title_text='A busy plot')\n", + "fig.show()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "5958f338", + "metadata": {}, + "outputs": [], + "source": [ + "import plotly.express as px\n", + "import pandas as pd\n", + "import numpy as np\n", + "import datashader as ds\n", + "df = pd.read_parquet('https://raw.githubusercontent.com/plotly/datasets/master/2015_flights.parquet')\n", + "\n", + "cvs = ds.Canvas(plot_width=100, plot_height=100)\n", + "agg = cvs.points(df, 'SCHEDULED_DEPARTURE', 'DEPARTURE_DELAY')\n", + "zero_mask = agg.values == 0\n", + "agg.values = np.log10(agg.values, where=np.logical_not(zero_mask))\n", + "agg.values[zero_mask] = np.nan\n", + "fig = px.imshow(agg, origin='lower', labels={'color':'Log10(count)'})\n", + "fig.update_traces(hoverongaps=False)\n", + "fig.update_layout(coloraxis_colorbar=dict(title='Count', tickprefix='1.e'))\n", + "fig.show()" + ] + }, + { + "cell_type": "markdown", + "id": "c5cde2d8", + "metadata": {}, + "source": [ + "Instead of using Datashader, it would theoretically be possible to create a [2d histogram](/python/2d-histogram-contour/) with Plotly, but this is not recommended because you would need to load the whole dataset of around 5M rows in the browser for plotly.js to compute the heatmap.\n" + ] + }, + { + "cell_type": "markdown", + "id": "f282bd7b", + "metadata": {}, + "source": [ + "### What About Dash?\n", + "\n", + "[Dash](https://dash.plot.ly/) is an open-source framework for building analytical applications, with no Javascript required, and it is tightly integrated with the Plotly graphing library.\n", + "\n", + "Learn about how to install Dash at https://dash.plot.ly/installation.\n", + "\n", + "Everywhere in this page that you see `fig.show()`, you can display the same figure in a Dash application by passing it to the `figure` argument of the [`Graph` component](https://dash.plot.ly/dash-core-components/graph) from the built-in `dash_core_components` package like this:\n", + "\n", + "```python\n", + "import plotly.graph_objects as go # or plotly.express as px\n", + "fig = go.Figure() # or any Plotly Express function e.g. px.bar(...)\n", + "# fig.add_trace( ... )\n", + "# fig.update_layout( ... )\n", + "\n", + "from dash import Dash, dcc, html\n", + "\n", + "app = Dash()\n", + "app.layout = html.Div([\n", + " dcc.Graph(figure=fig)\n", + "])\n", + "\n", + "app.run_server(debug=True, use_reloader=False) # Turn off reloader if inside Jupyter\n", + "```" + ] + } + ], + "metadata": { + "jupytext": { + "notebook_metadata_filter": "all" + }, + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "language": "python", + "name": "python3" + }, + "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.11.10" + }, + "plotly": { + "description": "Recommendations for increased speed, improved interactivity, and the ability to plot even more data!", + "display_as": "basic", + "language": "python", + "layout": "base", + "name": "High Performance Visualization", + "order": 14, + "permalink": "python/performance/", + "redirect_from": [ + "python/webgl-vs-svg/", + "python/datashader/" + ], + "thumbnail": "thumbnail/webgl.jpg" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/pie-charts.ipynb b/pie-charts.ipynb new file mode 100644 index 000000000..b9b6568c3 --- /dev/null +++ b/pie-charts.ipynb @@ -0,0 +1,597 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "df65087b", + "metadata": {}, + "source": [ + "A pie chart is a circular statistical chart, which is divided into sectors to illustrate numerical proportion.\n", + "\n", + "If you're looking instead for a multilevel hierarchical pie-like chart, go to the\n", + "[Sunburst tutorial](/python/sunburst-charts/).\n", + "\n", + "### Pie chart with plotly express\n", + "\n", + "[Plotly Express](/python/plotly-express/) is the easy-to-use, high-level interface to Plotly, which [operates on a variety of types of data](/python/px-arguments/) and produces [easy-to-style figures](/python/styling-plotly-express/).\n", + "\n", + "In `px.pie`, data visualized by the sectors of the pie is set in `values`. The sector labels are set in `names`." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "37ba1c01", + "metadata": {}, + "outputs": [], + "source": [ + "import plotly.express as px\n", + "df = px.data.gapminder().query(\"year == 2007\").query(\"continent == 'Europe'\")\n", + "df.loc[df['pop'] < 2.e6, 'country'] = 'Other countries' # Represent only large countries\n", + "fig = px.pie(df, values='pop', names='country', title='Population of European continent')\n", + "fig.show()" + ] + }, + { + "cell_type": "markdown", + "id": "5479c898", + "metadata": {}, + "source": [ + "### Pie chart with repeated labels\n", + "\n", + "Lines of the dataframe with the same value for `names` are grouped together in the same sector." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "bc5de69f", + "metadata": {}, + "outputs": [], + "source": [ + "import plotly.express as px\n", + "# This dataframe has 244 lines, but 4 distinct values for `day`\n", + "df = px.data.tips()\n", + "fig = px.pie(df, values='tip', names='day')\n", + "fig.show()" + ] + }, + { + "cell_type": "markdown", + "id": "64af64f9", + "metadata": {}, + "source": [ + "### Pie chart in Dash\n", + "\n", + "[Dash](https://plotly.com/dash/) is the best way to build analytical apps in Python using Plotly figures. To run the app below, run `pip install dash`, click \"Download\" to get the code and run `python app.py`.\n", + "\n", + "Get started with [the official Dash docs](https://dash.plotly.com/installation) and **learn how to effortlessly [style](https://plotly.com/dash/design-kit/) & [deploy](https://plotly.com/dash/app-manager/) apps like this with Dash Enterprise.**" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "c0ae07d5", + "metadata": { + "hide_code": true + }, + "outputs": [], + "source": [ + "from IPython.display import IFrame\n", + "snippet_url = 'https://python-docs-dash-snippets.herokuapp.com/python-docs-dash-snippets/'\n", + "IFrame(snippet_url + 'pie-charts', width='100%', height=1200)" + ] + }, + { + "cell_type": "markdown", + "id": "d3b4fb45", + "metadata": {}, + "source": [ + "

Sign up for Dash Club → Free cheat sheets plus updates from Chris Parmer and Adam Schroeder delivered to your inbox every two months. Includes tips and tricks, community apps, and deep dives into the Dash architecture.\n", + "Join now.

" + ] + }, + { + "cell_type": "markdown", + "id": "9404fa65", + "metadata": {}, + "source": [ + "### Setting the color of pie sectors with px.pie" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "8e5fb699", + "metadata": {}, + "outputs": [], + "source": [ + "import plotly.express as px\n", + "df = px.data.tips()\n", + "fig = px.pie(df, values='tip', names='day', color_discrete_sequence=px.colors.sequential.RdBu)\n", + "fig.show()" + ] + }, + { + "cell_type": "markdown", + "id": "3e951f44", + "metadata": {}, + "source": [ + "### Using an explicit mapping for discrete colors\n", + "\n", + "For more information about discrete colors, see the [dedicated page](/python/discrete-color)." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "4fbac578", + "metadata": {}, + "outputs": [], + "source": [ + "import plotly.express as px\n", + "df = px.data.tips()\n", + "fig = px.pie(df, values='tip', names='day', color='day',\n", + " color_discrete_map={'Thur':'lightcyan',\n", + " 'Fri':'cyan',\n", + " 'Sat':'royalblue',\n", + " 'Sun':'darkblue'})\n", + "fig.show()" + ] + }, + { + "cell_type": "markdown", + "id": "93ee1210", + "metadata": {}, + "source": [ + "### Customizing a pie chart created with px.pie\n", + "\n", + "In the example below, we first create a pie chart with `px,pie`, using some of its options such as `hover_data` (which columns should appear in the hover) or `labels` (renaming column names). For further tuning, we call `fig.update_traces` to set other parameters of the chart (you can also use `fig.update_layout` for changing the layout)." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "9e768d9d", + "metadata": {}, + "outputs": [], + "source": [ + "import plotly.express as px\n", + "df = px.data.gapminder().query(\"year == 2007\").query(\"continent == 'Americas'\")\n", + "fig = px.pie(df, values='pop', names='country',\n", + " title='Population of American continent',\n", + " hover_data=['lifeExp'], labels={'lifeExp':'life expectancy'})\n", + "fig.update_traces(textposition='inside', textinfo='percent+label')\n", + "fig.show()" + ] + }, + { + "cell_type": "markdown", + "id": "6346ce37", + "metadata": {}, + "source": [ + "### Basic Pie Chart with go.Pie\n", + "\n", + "If Plotly Express does not provide a good starting point, it is also possible to use [the more generic `go.Pie` class from `plotly.graph_objects`](/python/graph-objects/).\n", + "\n", + "In `go.Pie`, data visualized by the sectors of the pie is set in `values`. The sector labels are set in `labels`. The sector colors are set in `marker.colors`.\n", + "\n", + "If you're looking instead for a multilevel hierarchical pie-like chart, go to the\n", + "[Sunburst tutorial](/python/sunburst-charts/)." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "157b820e", + "metadata": {}, + "outputs": [], + "source": [ + "import plotly.graph_objects as go\n", + "\n", + "labels = ['Oxygen','Hydrogen','Carbon_Dioxide','Nitrogen']\n", + "values = [4500, 2500, 1053, 500]\n", + "\n", + "fig = go.Figure(data=[go.Pie(labels=labels, values=values)])\n", + "fig.show()" + ] + }, + { + "cell_type": "markdown", + "id": "336d83f4", + "metadata": {}, + "source": [ + "### Styled Pie Chart\n", + "\n", + "Colors can be given as RGB triplets or hexadecimal strings, or with [CSS color names](https://www.w3schools.com/cssref/css_colors.asp) as below." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "1a67ca8f", + "metadata": {}, + "outputs": [], + "source": [ + "import plotly.graph_objects as go\n", + "colors = ['gold', 'mediumturquoise', 'darkorange', 'lightgreen']\n", + "\n", + "fig = go.Figure(data=[go.Pie(labels=['Oxygen','Hydrogen','Carbon_Dioxide','Nitrogen'],\n", + " values=[4500,2500,1053,500])])\n", + "fig.update_traces(hoverinfo='label+percent', textinfo='value', textfont_size=20,\n", + " marker=dict(colors=colors, line=dict(color='#000000', width=2)))\n", + "fig.show()" + ] + }, + { + "cell_type": "markdown", + "id": "7a289605", + "metadata": {}, + "source": [ + "### Controlling text fontsize with uniformtext\n", + "\n", + "If you want all the text labels to have the same size, you can use the `uniformtext` layout parameter. The `minsize` attribute sets the font size, and the `mode` attribute sets what happens for labels which cannot fit with the desired fontsize: either `hide` them or `show` them with overflow. In the example below we also force the text to be inside with `textposition`, otherwise text labels which do not fit are displayed outside of pie sectors." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "a34a6bfa", + "metadata": {}, + "outputs": [], + "source": [ + "import plotly.express as px\n", + "\n", + "df = px.data.gapminder().query(\"continent == 'Asia'\")\n", + "fig = px.pie(df, values='pop', names='country')\n", + "fig.update_traces(textposition='inside')\n", + "fig.update_layout(uniformtext_minsize=12, uniformtext_mode='hide')\n", + "fig.show()" + ] + }, + { + "cell_type": "markdown", + "id": "d180a2d3", + "metadata": {}, + "source": [ + "#### Controlling text orientation inside pie sectors\n", + "\n", + "The `insidetextorientation` attribute controls the orientation of text inside sectors. With\n", + "\"auto\" the texts may automatically be rotated to fit with the maximum size inside the slice. Using \"horizontal\" (resp. \"radial\", \"tangential\") forces text to be horizontal (resp. radial or tangential)\n", + "\n", + "For a figure `fig` created with plotly express, use `fig.update_traces(insidetextorientation='...')` to change the text orientation." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "81ec146c", + "metadata": {}, + "outputs": [], + "source": [ + "import plotly.graph_objects as go\n", + "\n", + "labels = ['Oxygen','Hydrogen','Carbon_Dioxide','Nitrogen']\n", + "values = [4500, 2500, 1053, 500]\n", + "\n", + "fig = go.Figure(data=[go.Pie(labels=labels, values=values, textinfo='label+percent',\n", + " insidetextorientation='radial'\n", + " )])\n", + "fig.show()" + ] + }, + { + "cell_type": "markdown", + "id": "94773247", + "metadata": {}, + "source": [ + "### Donut Chart" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "533da5bd", + "metadata": {}, + "outputs": [], + "source": [ + "import plotly.graph_objects as go\n", + "\n", + "labels = ['Oxygen','Hydrogen','Carbon_Dioxide','Nitrogen']\n", + "values = [4500, 2500, 1053, 500]\n", + "\n", + "# Use `hole` to create a donut-like pie chart\n", + "fig = go.Figure(data=[go.Pie(labels=labels, values=values, hole=.3)])\n", + "fig.show()" + ] + }, + { + "cell_type": "markdown", + "id": "259071cc", + "metadata": {}, + "source": [ + "### Pulling sectors out from the center\n", + "\n", + "For a \"pulled-out\" or \"exploded\" layout of the pie chart, use the `pull` argument. It can be a scalar for pulling all sectors or an array to pull only some of the sectors." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "0b7e5a3b", + "metadata": {}, + "outputs": [], + "source": [ + "import plotly.graph_objects as go\n", + "\n", + "labels = ['Oxygen','Hydrogen','Carbon_Dioxide','Nitrogen']\n", + "values = [4500, 2500, 1053, 500]\n", + "\n", + "# pull is given as a fraction of the pie radius\n", + "fig = go.Figure(data=[go.Pie(labels=labels, values=values, pull=[0, 0, 0.2, 0])])\n", + "fig.show()" + ] + }, + { + "cell_type": "markdown", + "id": "6243c86d", + "metadata": {}, + "source": [ + "### Pie Charts in subplots" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "7f27251c", + "metadata": {}, + "outputs": [], + "source": [ + "import plotly.graph_objects as go\n", + "from plotly.subplots import make_subplots\n", + "\n", + "labels = [\"US\", \"China\", \"European Union\", \"Russian Federation\", \"Brazil\", \"India\",\n", + " \"Rest of World\"]\n", + "\n", + "# Create subplots: use 'domain' type for Pie subplot\n", + "fig = make_subplots(rows=1, cols=2, specs=[[{'type':'domain'}, {'type':'domain'}]])\n", + "fig.add_trace(go.Pie(labels=labels, values=[16, 15, 12, 6, 5, 4, 42], name=\"GHG Emissions\"),\n", + " 1, 1)\n", + "fig.add_trace(go.Pie(labels=labels, values=[27, 11, 25, 8, 1, 3, 25], name=\"CO2 Emissions\"),\n", + " 1, 2)\n", + "\n", + "# Use `hole` to create a donut-like pie chart\n", + "fig.update_traces(hole=.4, hoverinfo=\"label+percent+name\")\n", + "\n", + "fig.update_layout(\n", + " title_text=\"Global Emissions 1990-2011\",\n", + " # Add annotations in the center of the donut pies.\n", + " annotations=[dict(text='GHG', x=sum(fig.get_subplot(1, 1).x) / 2, y=0.5,\n", + " font_size=20, showarrow=False, xanchor=\"center\"),\n", + " dict(text='CO2', x=sum(fig.get_subplot(1, 2).x) / 2, y=0.5,\n", + " font_size=20, showarrow=False, xanchor=\"center\")])\n", + "fig.show()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "b715dfdc", + "metadata": {}, + "outputs": [], + "source": [ + "import plotly.graph_objects as go\n", + "from plotly.subplots import make_subplots\n", + "\n", + "labels = ['1st', '2nd', '3rd', '4th', '5th']\n", + "\n", + "# Define color sets of paintings\n", + "night_colors = ['rgb(56, 75, 126)', 'rgb(18, 36, 37)', 'rgb(34, 53, 101)',\n", + " 'rgb(36, 55, 57)', 'rgb(6, 4, 4)']\n", + "sunflowers_colors = ['rgb(177, 127, 38)', 'rgb(205, 152, 36)', 'rgb(99, 79, 37)',\n", + " 'rgb(129, 180, 179)', 'rgb(124, 103, 37)']\n", + "irises_colors = ['rgb(33, 75, 99)', 'rgb(79, 129, 102)', 'rgb(151, 179, 100)',\n", + " 'rgb(175, 49, 35)', 'rgb(36, 73, 147)']\n", + "cafe_colors = ['rgb(146, 123, 21)', 'rgb(177, 180, 34)', 'rgb(206, 206, 40)',\n", + " 'rgb(175, 51, 21)', 'rgb(35, 36, 21)']\n", + "\n", + "# Create subplots, using 'domain' type for pie charts\n", + "specs = [[{'type':'domain'}, {'type':'domain'}], [{'type':'domain'}, {'type':'domain'}]]\n", + "fig = make_subplots(rows=2, cols=2, specs=specs)\n", + "\n", + "# Define pie charts\n", + "fig.add_trace(go.Pie(labels=labels, values=[38, 27, 18, 10, 7], name='Starry Night',\n", + " marker_colors=night_colors), 1, 1)\n", + "fig.add_trace(go.Pie(labels=labels, values=[28, 26, 21, 15, 10], name='Sunflowers',\n", + " marker_colors=sunflowers_colors), 1, 2)\n", + "fig.add_trace(go.Pie(labels=labels, values=[38, 19, 16, 14, 13], name='Irises',\n", + " marker_colors=irises_colors), 2, 1)\n", + "fig.add_trace(go.Pie(labels=labels, values=[31, 24, 19, 18, 8], name='The Night Café',\n", + " marker_colors=cafe_colors), 2, 2)\n", + "\n", + "# Tune layout and hover info\n", + "fig.update_traces(hoverinfo='label+percent+name', textinfo='none')\n", + "fig.update(layout_title_text='Van Gogh: 5 Most Prominent Colors Shown Proportionally',\n", + " layout_showlegend=False)\n", + "\n", + "fig = go.Figure(fig)\n", + "fig.show()" + ] + }, + { + "cell_type": "markdown", + "id": "3e4ead02", + "metadata": {}, + "source": [ + "#### Plot chart with area proportional to total count\n", + "\n", + "Plots in the same `scalegroup` are represented with an area proportional to their total size." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "d1e607d0", + "metadata": {}, + "outputs": [], + "source": [ + "import plotly.graph_objects as go\n", + "from plotly.subplots import make_subplots\n", + "\n", + "labels = [\"Asia\", \"Europe\", \"Africa\", \"Americas\", \"Oceania\"]\n", + "\n", + "fig = make_subplots(1, 2, specs=[[{'type':'domain'}, {'type':'domain'}]],\n", + " subplot_titles=['1980', '2007'])\n", + "fig.add_trace(go.Pie(labels=labels, values=[4, 7, 1, 7, 0.5], scalegroup='one',\n", + " name=\"World GDP 1980\"), 1, 1)\n", + "fig.add_trace(go.Pie(labels=labels, values=[21, 15, 3, 19, 1], scalegroup='one',\n", + " name=\"World GDP 2007\"), 1, 2)\n", + "\n", + "fig.update_layout(title_text='World GDP')\n", + "fig.show()" + ] + }, + { + "cell_type": "markdown", + "id": "fe18d587", + "metadata": {}, + "source": [ + "### Pattern Fills\n", + "\n", + "*New in 5.15*\n", + "\n", + "Pie charts support [patterns](/python/pattern-hatching-texture/) (also known as hatching or texture) in addition to color." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "99cd8241", + "metadata": {}, + "outputs": [], + "source": [ + "import plotly.graph_objects as go\n", + "\n", + "labels = [\"Oxygen\", \"Hydrogen\", \"Carbon_Dioxide\", \"Nitrogen\"]\n", + "values = [4500, 2500, 1053, 500]\n", + "colors = [\"gold\", \"mediumturquoise\", \"darkorange\", \"lightgreen\"]\n", + "\n", + "fig = go.Figure(\n", + " data=[\n", + " go.Pie(\n", + " labels=labels,\n", + " values=values,\n", + " textfont_size=20,\n", + " marker=dict(colors=colors, pattern=dict(shape=[\".\", \"x\", \"+\", \"-\"]))\n", + " )\n", + " ]\n", + ")\n", + "\n", + "fig.show()" + ] + }, + { + "cell_type": "markdown", + "id": "0331d3ad", + "metadata": {}, + "source": [ + "### See Also: Sunburst charts\n", + "\n", + "For multilevel pie charts representing hierarchical data, you can use the `Sunburst` chart. A simple example is given below, for more information see the [tutorial on Sunburst charts](/python/sunburst-charts/)." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "43ec0c03", + "metadata": {}, + "outputs": [], + "source": [ + "import plotly.graph_objects as go\n", + "\n", + "fig =go.Figure(go.Sunburst(\n", + " labels=[\"Eve\", \"Cain\", \"Seth\", \"Enos\", \"Noam\", \"Abel\", \"Awan\", \"Enoch\", \"Azura\"],\n", + " parents=[\"\", \"Eve\", \"Eve\", \"Seth\", \"Seth\", \"Eve\", \"Eve\", \"Awan\", \"Eve\" ],\n", + " values=[10, 14, 12, 10, 2, 6, 6, 4, 4],\n", + "))\n", + "fig.update_layout(margin = dict(t=0, l=0, r=0, b=0))\n", + "\n", + "fig.show()\n" + ] + }, + { + "cell_type": "markdown", + "id": "5ea434b0", + "metadata": {}, + "source": [ + "#### Reference\n", + "\n", + "See [function reference for `px.pie()`](https://plotly.com/python-api-reference/generated/plotly.express.pie) or https://plotly.com/python/reference/pie/ for more information and chart attribute options!\n" + ] + }, + { + "cell_type": "markdown", + "id": "f6016158", + "metadata": {}, + "source": [ + "### What About Dash?\n", + "\n", + "[Dash](https://dash.plot.ly/) is an open-source framework for building analytical applications, with no Javascript required, and it is tightly integrated with the Plotly graphing library.\n", + "\n", + "Learn about how to install Dash at https://dash.plot.ly/installation.\n", + "\n", + "Everywhere in this page that you see `fig.show()`, you can display the same figure in a Dash application by passing it to the `figure` argument of the [`Graph` component](https://dash.plot.ly/dash-core-components/graph) from the built-in `dash_core_components` package like this:\n", + "\n", + "```python\n", + "import plotly.graph_objects as go # or plotly.express as px\n", + "fig = go.Figure() # or any Plotly Express function e.g. px.bar(...)\n", + "# fig.add_trace( ... )\n", + "# fig.update_layout( ... )\n", + "\n", + "from dash import Dash, dcc, html\n", + "\n", + "app = Dash()\n", + "app.layout = html.Div([\n", + " dcc.Graph(figure=fig)\n", + "])\n", + "\n", + "app.run_server(debug=True, use_reloader=False) # Turn off reloader if inside Jupyter\n", + "```" + ] + } + ], + "metadata": { + "jupytext": { + "notebook_metadata_filter": "all" + }, + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "language": "python", + "name": "python3" + }, + "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.11" + }, + "plotly": { + "description": "How to make Pie Charts.", + "display_as": "basic", + "language": "python", + "layout": "base", + "name": "Pie Charts", + "order": 4, + "page_type": "example_index", + "permalink": "python/pie-charts/", + "thumbnail": "thumbnail/pie-chart.jpg" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/plot-data-from-csv.ipynb b/plot-data-from-csv.ipynb new file mode 100644 index 000000000..2a151630f --- /dev/null +++ b/plot-data-from-csv.ipynb @@ -0,0 +1,191 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "e44c91b4", + "metadata": {}, + "source": [ + "CSV or comma-delimited-values is a very popular format for storing structured data. In this tutorial, we will see how to plot beautiful graphs using csv data, and Pandas. We will learn how to import csv data from an external source (a url), and plot it using Plotly and pandas.\n", + "\n", + "First we import the data and look at it." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "539e7551", + "metadata": {}, + "outputs": [], + "source": [ + "import pandas as pd\n", + "df = pd.read_csv('https://raw.githubusercontent.com/plotly/datasets/master/2014_apple_stock.csv')\n", + "df.head()" + ] + }, + { + "cell_type": "markdown", + "id": "e7c139c0", + "metadata": {}, + "source": [ + "### Plot from CSV with Plotly Express" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "adffa54d", + "metadata": {}, + "outputs": [], + "source": [ + "import pandas as pd\n", + "import plotly.express as px\n", + "\n", + "df = pd.read_csv('https://raw.githubusercontent.com/plotly/datasets/master/2014_apple_stock.csv')\n", + "\n", + "fig = px.line(df, x = 'AAPL_x', y = 'AAPL_y', title='Apple Share Prices over time (2014)')\n", + "fig.show()" + ] + }, + { + "cell_type": "markdown", + "id": "752de8a1", + "metadata": {}, + "source": [ + "### Plot from CSV in Dash\n", + "\n", + "[Dash](https://plotly.com/dash/) is the best way to build analytical apps in Python using Plotly figures. To run the app below, run `pip install dash`, click \"Download\" to get the code and run `python app.py`.\n", + "\n", + "Get started with [the official Dash docs](https://dash.plotly.com/installation) and **learn how to effortlessly [style](https://plotly.com/dash/design-kit/) & [deploy](https://plotly.com/dash/app-manager/) apps like this with Dash Enterprise.**\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "b004d673", + "metadata": { + "hide_code": true + }, + "outputs": [], + "source": [ + "from IPython.display import IFrame\n", + "snippet_url = 'https://python-docs-dash-snippets.herokuapp.com/python-docs-dash-snippets/'\n", + "IFrame(snippet_url + 'plot-data-from-csv', width='100%', height=1200)" + ] + }, + { + "cell_type": "markdown", + "id": "de3b211f", + "metadata": {}, + "source": [ + "

Sign up for Dash Club → Free cheat sheets plus updates from Chris Parmer and Adam Schroeder delivered to your inbox every two months. Includes tips and tricks, community apps, and deep dives into the Dash architecture.\n", + "Join now.

" + ] + }, + { + "cell_type": "markdown", + "id": "96bb9848", + "metadata": {}, + "source": [ + "### Plot from CSV with `graph_objects`" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "77444e2f", + "metadata": {}, + "outputs": [], + "source": [ + "import pandas as pd\n", + "import plotly.graph_objects as go\n", + "\n", + "df = pd.read_csv('https://raw.githubusercontent.com/plotly/datasets/master/2014_apple_stock.csv')\n", + "\n", + "fig = go.Figure(go.Scatter(x = df['AAPL_x'], y = df['AAPL_y'],\n", + " name='Share Prices (in USD)'))\n", + "\n", + "fig.update_layout(title=dict(text='Apple Share Prices over time (2014)'),\n", + " plot_bgcolor='rgb(230, 230,230)',\n", + " showlegend=True)\n", + "\n", + "fig.show()" + ] + }, + { + "cell_type": "markdown", + "id": "3956a24e", + "metadata": {}, + "source": [ + "#### Reference\n", + "\n", + "See https://plotly.com/python/getting-started for more information about Plotly's Python API!\n" + ] + }, + { + "cell_type": "markdown", + "id": "a963f6e2", + "metadata": {}, + "source": [ + "### What About Dash?\n", + "\n", + "[Dash](https://dash.plot.ly/) is an open-source framework for building analytical applications, with no Javascript required, and it is tightly integrated with the Plotly graphing library.\n", + "\n", + "Learn about how to install Dash at https://dash.plot.ly/installation.\n", + "\n", + "Everywhere in this page that you see `fig.show()`, you can display the same figure in a Dash application by passing it to the `figure` argument of the [`Graph` component](https://dash.plot.ly/dash-core-components/graph) from the built-in `dash_core_components` package like this:\n", + "\n", + "```python\n", + "import plotly.graph_objects as go # or plotly.express as px\n", + "fig = go.Figure() # or any Plotly Express function e.g. px.bar(...)\n", + "# fig.add_trace( ... )\n", + "# fig.update_layout( ... )\n", + "\n", + "from dash import Dash, dcc, html\n", + "\n", + "app = Dash()\n", + "app.layout = html.Div([\n", + " dcc.Graph(figure=fig)\n", + "])\n", + "\n", + "app.run_server(debug=True, use_reloader=False) # Turn off reloader if inside Jupyter\n", + "```" + ] + } + ], + "metadata": { + "jupytext": { + "notebook_metadata_filter": "all" + }, + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "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.8.8" + }, + "plotly": { + "description": "How to create charts from csv files with Plotly and Python", + "display_as": "advanced_opt", + "has_thumbnail": false, + "language": "python", + "layout": "base", + "name": "Plot CSV Data", + "order": 1, + "page_type": "example_index", + "permalink": "python/plot-data-from-csv/", + "thumbnail": "thumbnail/csv.jpg" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/plotly-express.ipynb b/plotly-express.ipynb new file mode 100644 index 000000000..70cf8f909 --- /dev/null +++ b/plotly-express.ipynb @@ -0,0 +1,995 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "f1a9bde6", + "metadata": {}, + "source": [ + "### Overview\n", + "\n", + "The `plotly.express` module (usually imported as `px`) contains functions that can create entire figures at once, and is referred to as Plotly Express or PX. Plotly Express is a built-in part of the `plotly` library, and is the recommended starting point for creating most common figures. Every Plotly Express function uses [graph objects](/python/graph-objects/) internally and returns a `plotly.graph_objects.Figure` instance. Throughout the `plotly` documentation, you will find the Plotly Express way of building figures at the top of any applicable page, followed by a section on how to use graph objects to build similar figures. Any figure created in a single function call with Plotly Express could be created using graph objects alone, but with between 5 and 100 times more code.\n", + "\n", + "Plotly Express provides [more than 30 functions for creating different types of figures](https://plotly.com/python-api-reference/plotly.express.html). The API for these functions was carefully designed to be as consistent and easy to learn as possible, making it easy to switch from a scatter plot to a bar chart to a histogram to a sunburst chart throughout a data exploration session. *Scroll down for a gallery of Plotly Express plots, each made in a single function call.*\n", + "\n", + "Here is a talk from the [SciPy 2021 conference](https://www.scipy2021.scipy.org/) that gives a good introduction to Plotly Express and [Dash](https://dash.plotly.com/):" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "524e6d36", + "metadata": { + "hide_code": true + }, + "outputs": [], + "source": [ + "%%html\n", + "
\n", + "\n", + "
" + ] + }, + { + "cell_type": "markdown", + "id": "1f94c3be", + "metadata": {}, + "source": [ + "Plotly Express currently includes the following functions:\n", + "\n", + "* **Basics**: [`scatter`](/python/line-and-scatter/), [`line`](/python/line-charts/), [`area`](/python/filled-area-plots/), [`bar`](/python/bar-charts/), [`funnel`](/python/funnel-charts/), [`timeline`](https://plotly.com/python/gantt/)\n", + "* **Part-of-Whole**: [`pie`](/python/pie-charts/), [`sunburst`](/python/sunburst-charts/), [`treemap`](/python/treemaps/), [`icicle`](/python/icicle-charts/), [`funnel_area`](/python/funnel-charts/)\n", + "* **1D Distributions**: [`histogram`](/python/histograms/), [`box`](/python/box-plots/), [`violin`](/python/violin/), [`strip`](/python/strip-charts/), [`ecdf`](/python/ecdf-plots/)\n", + "* **2D Distributions**: [`density_heatmap`](/python/2D-Histogram/), [`density_contour`](/python/2d-histogram-contour/)\n", + "* **Matrix or Image Input**: [`imshow`](/python/imshow/)\n", + "* **3-Dimensional**: [`scatter_3d`](/python/3d-scatter-plots/), [`line_3d`](/python/3d-line-plots/)\n", + "* **Multidimensional**: [`scatter_matrix`](/python/splom/), [`parallel_coordinates`](/python/parallel-coordinates-plot/), [`parallel_categories`](/python/parallel-categories-diagram/)\n", + "* **Tile Maps**: [`scatter_map`](/python/tile-scatter-maps/), [`line_map`](/python/lines-on-tile-maps/), [`choropleth_map`](/python/tile-county-choropleth/), [`density_map`](/python/tile-density-heatmaps/)\n", + "* **Outline Maps**: [`scatter_geo`](/python/scatter-plots-on-maps/), [`line_geo`](/python/lines-on-maps/), [`choropleth`](/python/choropleth-maps/)\n", + "* **Polar Charts**: [`scatter_polar`](/python/polar-chart/), [`line_polar`](/python/polar-chart/), [`bar_polar`](/python/wind-rose-charts/)\n", + "* **Ternary Charts**: [`scatter_ternary`](/python/ternary-plots/), [`line_ternary`](/python/ternary-plots/)\n", + "\n", + "### High-Level Features\n", + "\n", + "The Plotly Express API in general offers the following features:\n", + "\n", + "* **A single entry point into `plotly`**: just `import plotly.express as px` and get access to [all the plotting functions](https://plotly.com/python-api-reference/plotly.express.html), plus [built-in demo datasets under `px.data`](https://plotly.com/python-api-reference/generated/plotly.data.html#module-plotly.data) and [built-in color scales and sequences under `px.color`](https://plotly.com/python-api-reference/generated/plotly.colors.html#module-plotly.colors). Every PX function returns a `plotly.graph_objects.Figure` object, so you can edit it using all the same methods like [`update_layout` and `add_trace`](https://plotly.com/python/creating-and-updating-figures/#updating-figures).\n", + "* **Sensible, Overridable Defaults**: PX functions will infer sensible defaults wherever possible, and will always let you override them.\n", + "* **Flexible Input Formats**: PX functions [accept input in a variety of formats](/python/px-arguments/), from `list`s and `dict`s to [long-form or wide-form `DataFrame`s](/python/wide-form/) to [`numpy` arrays and `xarrays`](/python/imshow/) to [GeoPandas `GeoDataFrames`](/python/maps/).\n", + "* **Automatic Trace and Layout configuration**: PX functions will create one [trace](/python/figure-structure) per animation frame for each unique combination of data values mapped to discrete color, symbol, line-dash, facet-row and/or facet-column. Traces' [`legendgroup` and `showlegend` attributes](https://plotly.com/python/legend/) are set such that only one legend item appears per unique combination of discrete color, symbol and/or line-dash. Traces are automatically linked to a correctly-configured [subplot of the appropriate type](/python/figure-structure).\n", + "* **Automatic Figure Labelling**: PX functions [label axes, legends and colorbars](https://plotly.com/python/figure-labels/) based in the input `DataFrame` or `xarray`, and provide [extra control with the `labels` argument](/python/styling-plotly-express/).\n", + "* **Automatic Hover Labels**: PX functions populate the hover-label using the labels mentioned above, and provide [extra control with the `hover_name` and `hover_data` arguments](/python/hover-text-and-formatting/).\n", + "* **Styling Control**: PX functions [read styling information from the default figure template](/python/styling-plotly-express/), and support commonly-needed [cosmetic controls like `category_orders` and `color_discrete_map`](/python/styling-plotly-express/) to precisely control categorical variables.\n", + "* **Uniform Color Handling**: PX functions automatically switch between [continuous](/python/colorscales/) and [categorical color](/python/discrete-color/) based on the input type.\n", + "* **Faceting**: the 2D-cartesian plotting functions support [row, column and wrapped facetting with `facet_row`, `facet_col` and `facet_col_wrap` arguments](/python/facet-plots/).\n", + "* **Marginal Plots**: the 2D-cartesian plotting functions support [marginal distribution plots](/python/marginal-plots/) with the `marginal`, `marginal_x` and `marginal_y` arguments.\n", + "* **A Pandas backend**: the 2D-cartesian plotting functions are available as [a Pandas plotting backend](/python/pandas-backend/) so you can call them via `df.plot()`.\n", + "* **Trendlines**: `px.scatter` supports [built-in trendlines with accessible model output](/python/linear-fits/).\n", + "* **Animations**: many PX functions support [simple animation support via the `animation_frame` and `animation_group` arguments](/python/animations/).\n", + "* **Automatic WebGL switching**: for sufficiently large scatter plots, PX will automatically [use WebGL for hardware-accelerated rendering](https://plotly.com/python/webgl-vs-svg/)." + ] + }, + { + "cell_type": "markdown", + "id": "68c522d3", + "metadata": {}, + "source": [ + "### Plotly Express in Dash\n", + "\n", + "[Dash](https://plotly.com/dash/) is the best way to build analytical apps in Python using Plotly figures. To run the app below, run `pip install dash`, click \"Download\" to get the code and run `python app.py`.\n", + "\n", + "Get started with [the official Dash docs](https://dash.plotly.com/installation) and **learn how to effortlessly [style](https://plotly.com/dash/design-kit/) & [deploy](https://plotly.com/dash/app-manager/) apps like this with Dash Enterprise.**\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "6a7f348a", + "metadata": { + "hide_code": true + }, + "outputs": [], + "source": [ + "from IPython.display import IFrame\n", + "snippet_url = 'https://python-docs-dash-snippets.herokuapp.com/python-docs-dash-snippets/'\n", + "IFrame(snippet_url + 'plotly-express', width='100%', height=1200)" + ] + }, + { + "cell_type": "markdown", + "id": "30a48d3f", + "metadata": {}, + "source": [ + "### Gallery\n", + "\n", + "The following set of figures is just a sampling of what can be done with Plotly Express.\n", + "\n", + "#### Scatter, Line, Area and Bar Charts\n", + "\n", + "**Read more about [scatter plots](/python/line-and-scatter/) and [discrete color](/python/discrete-color/).**" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "0914ee6f", + "metadata": {}, + "outputs": [], + "source": [ + "import plotly.express as px\n", + "df = px.data.iris()\n", + "fig = px.scatter(df, x=\"sepal_width\", y=\"sepal_length\", color=\"species\")\n", + "fig.show()" + ] + }, + { + "cell_type": "markdown", + "id": "1c71c10b", + "metadata": {}, + "source": [ + "**Read more about [trendlines](/python/linear-fits/) and [templates](/python/templates/) and [marginal distribution plots](https://plotly.com/python/marginal-plots/).**" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "8082c78f", + "metadata": {}, + "outputs": [], + "source": [ + "import plotly.express as px\n", + "df = px.data.iris()\n", + "fig = px.scatter(df, x=\"sepal_width\", y=\"sepal_length\", color=\"species\", marginal_y=\"violin\",\n", + " marginal_x=\"box\", trendline=\"ols\", template=\"simple_white\")\n", + "fig.show()" + ] + }, + { + "cell_type": "markdown", + "id": "819e0e03", + "metadata": {}, + "source": [ + "**Read more about [error bars](/python/error-bars/).**" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "209f82e7", + "metadata": {}, + "outputs": [], + "source": [ + "import plotly.express as px\n", + "df = px.data.iris()\n", + "df[\"e\"] = df[\"sepal_width\"]/100\n", + "fig = px.scatter(df, x=\"sepal_width\", y=\"sepal_length\", color=\"species\", error_x=\"e\", error_y=\"e\")\n", + "fig.show()" + ] + }, + { + "cell_type": "markdown", + "id": "165e0436", + "metadata": {}, + "source": [ + "**Read more about [bar charts](/python/bar-charts/).**" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "ec9f856e", + "metadata": {}, + "outputs": [], + "source": [ + "import plotly.express as px\n", + "df = px.data.tips()\n", + "fig = px.bar(df, x=\"sex\", y=\"total_bill\", color=\"smoker\", barmode=\"group\")\n", + "fig.show()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "bffd5571", + "metadata": {}, + "outputs": [], + "source": [ + "import plotly.express as px\n", + "df = px.data.medals_long()\n", + "\n", + "fig = px.bar(df, x=\"medal\", y=\"count\", color=\"nation\",\n", + " pattern_shape=\"nation\", pattern_shape_sequence=[\".\", \"x\", \"+\"])\n", + "fig.show()" + ] + }, + { + "cell_type": "markdown", + "id": "e8fdcecb", + "metadata": {}, + "source": [ + "**Read more about [facet plots](/python/facet-plots/).**" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "8a8ccf63", + "metadata": {}, + "outputs": [], + "source": [ + "import plotly.express as px\n", + "df = px.data.tips()\n", + "fig = px.bar(df, x=\"sex\", y=\"total_bill\", color=\"smoker\", barmode=\"group\", facet_row=\"time\", facet_col=\"day\",\n", + " category_orders={\"day\": [\"Thur\", \"Fri\", \"Sat\", \"Sun\"], \"time\": [\"Lunch\", \"Dinner\"]})\n", + "fig.show()" + ] + }, + { + "cell_type": "markdown", + "id": "3d9a887c", + "metadata": {}, + "source": [ + "**Read more about [scatterplot matrices (SPLOMs)](/python/splom/).**\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "d2e0c89e", + "metadata": {}, + "outputs": [], + "source": [ + "import plotly.express as px\n", + "df = px.data.iris()\n", + "fig = px.scatter_matrix(df, dimensions=[\"sepal_width\", \"sepal_length\", \"petal_width\", \"petal_length\"], color=\"species\")\n", + "fig.show()" + ] + }, + { + "cell_type": "markdown", + "id": "5935b773", + "metadata": {}, + "source": [ + "**Read more about [parallel coordinates](/python/parallel-coordinates-plot/) and [parallel categories](/python/parallel-categories-diagram/), as well as [continuous color](/python/colorscales/).**" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "f6bb9db1", + "metadata": {}, + "outputs": [], + "source": [ + "import plotly.express as px\n", + "df = px.data.iris()\n", + "fig = px.parallel_coordinates(df, color=\"species_id\", labels={\"species_id\": \"Species\",\n", + " \"sepal_width\": \"Sepal Width\", \"sepal_length\": \"Sepal Length\",\n", + " \"petal_width\": \"Petal Width\", \"petal_length\": \"Petal Length\", },\n", + " color_continuous_scale=px.colors.diverging.Tealrose, color_continuous_midpoint=2)\n", + "fig.show()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "fc0e0e7f", + "metadata": {}, + "outputs": [], + "source": [ + "import plotly.express as px\n", + "df = px.data.tips()\n", + "fig = px.parallel_categories(df, color=\"size\", color_continuous_scale=px.colors.sequential.Inferno)\n", + "fig.show()" + ] + }, + { + "cell_type": "markdown", + "id": "ed83bb92", + "metadata": {}, + "source": [ + "**Read more about [hover labels](/python/hover-text-and-formatting/).**" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "d42865c5", + "metadata": {}, + "outputs": [], + "source": [ + "import plotly.express as px\n", + "df = px.data.gapminder()\n", + "fig = px.scatter(df.query(\"year==2007\"), x=\"gdpPercap\", y=\"lifeExp\", size=\"pop\", color=\"continent\",\n", + " hover_name=\"country\", log_x=True, size_max=60)\n", + "fig.show()" + ] + }, + { + "cell_type": "markdown", + "id": "afddd670", + "metadata": {}, + "source": [ + "**Read more about [animations](/python/animations/).**" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "d69c64f6", + "metadata": {}, + "outputs": [], + "source": [ + "import plotly.express as px\n", + "df = px.data.gapminder()\n", + "fig = px.scatter(df, x=\"gdpPercap\", y=\"lifeExp\", animation_frame=\"year\", animation_group=\"country\",\n", + " size=\"pop\", color=\"continent\", hover_name=\"country\", facet_col=\"continent\",\n", + " log_x=True, size_max=45, range_x=[100,100000], range_y=[25,90])\n", + "fig.show()" + ] + }, + { + "cell_type": "markdown", + "id": "b9c09b7f", + "metadata": {}, + "source": [ + "**Read more about [line charts](/python/line-charts/).**" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "03297ffa", + "metadata": {}, + "outputs": [], + "source": [ + "import plotly.express as px\n", + "df = px.data.gapminder()\n", + "fig = px.line(df, x=\"year\", y=\"lifeExp\", color=\"continent\", line_group=\"country\", hover_name=\"country\",\n", + " line_shape=\"spline\", render_mode=\"svg\")\n", + "fig.show()" + ] + }, + { + "cell_type": "markdown", + "id": "7e699f37", + "metadata": {}, + "source": [ + "**Read more about [area charts](/python/filled-area-plots/).**" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "f5c26e5e", + "metadata": {}, + "outputs": [], + "source": [ + "import plotly.express as px\n", + "df = px.data.gapminder()\n", + "fig = px.area(df, x=\"year\", y=\"pop\", color=\"continent\", line_group=\"country\")\n", + "fig.show()" + ] + }, + { + "cell_type": "markdown", + "id": "b6129321", + "metadata": {}, + "source": [ + "**Read more about [timeline/Gantt charts](/python/gantt/).**" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "40bed355", + "metadata": {}, + "outputs": [], + "source": [ + "import plotly.express as px\n", + "import pandas as pd\n", + "\n", + "df = pd.DataFrame([\n", + " dict(Task=\"Job A\", Start='2009-01-01', Finish='2009-02-28', Resource=\"Alex\"),\n", + " dict(Task=\"Job B\", Start='2009-03-05', Finish='2009-04-15', Resource=\"Alex\"),\n", + " dict(Task=\"Job C\", Start='2009-02-20', Finish='2009-05-30', Resource=\"Max\")\n", + "])\n", + "\n", + "fig = px.timeline(df, x_start=\"Start\", x_end=\"Finish\", y=\"Resource\", color=\"Resource\")\n", + "fig.show()" + ] + }, + { + "cell_type": "markdown", + "id": "7fdc76b3", + "metadata": {}, + "source": [ + "**Read more about [funnel charts](/python/funnel-charts/).**" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "2fd6013b", + "metadata": {}, + "outputs": [], + "source": [ + "import plotly.express as px\n", + "data = dict(\n", + " number=[39, 27.4, 20.6, 11, 2],\n", + " stage=[\"Website visit\", \"Downloads\", \"Potential customers\", \"Requested price\", \"Invoice sent\"])\n", + "fig = px.funnel(data, x='number', y='stage')\n", + "fig.show()" + ] + }, + { + "cell_type": "markdown", + "id": "ce7dd08d", + "metadata": {}, + "source": [ + "### Part to Whole Charts\n", + "\n", + "**Read more about [pie charts](/python/pie-charts/).**" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "10941437", + "metadata": {}, + "outputs": [], + "source": [ + "import plotly.express as px\n", + "df = px.data.gapminder().query(\"year == 2007\").query(\"continent == 'Europe'\")\n", + "df.loc[df['pop'] < 2.e6, 'country'] = 'Other countries' # Represent only large countries\n", + "fig = px.pie(df, values='pop', names='country', title='Population of European continent')\n", + "fig.show()" + ] + }, + { + "cell_type": "markdown", + "id": "f61990a3", + "metadata": {}, + "source": [ + "**Read more about [sunburst charts](/python/sunburst-charts/).**" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "defd787f", + "metadata": {}, + "outputs": [], + "source": [ + "import plotly.express as px\n", + "\n", + "df = px.data.gapminder().query(\"year == 2007\")\n", + "fig = px.sunburst(df, path=['continent', 'country'], values='pop',\n", + " color='lifeExp', hover_data=['iso_alpha'])\n", + "fig.show()" + ] + }, + { + "cell_type": "markdown", + "id": "a7d4db25", + "metadata": {}, + "source": [ + "**Read more about [treemaps](/python/treemaps/).**" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "a9c543b8", + "metadata": {}, + "outputs": [], + "source": [ + "import plotly.express as px\n", + "import numpy as np\n", + "df = px.data.gapminder().query(\"year == 2007\")\n", + "fig = px.treemap(df, path=[px.Constant('world'), 'continent', 'country'], values='pop',\n", + " color='lifeExp', hover_data=['iso_alpha'])\n", + "fig.show()" + ] + }, + { + "cell_type": "markdown", + "id": "d08ea82b", + "metadata": {}, + "source": [ + "**Read more about [icicle charts](/python/icicle-charts/).**" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "c6894f3d", + "metadata": {}, + "outputs": [], + "source": [ + "import plotly.express as px\n", + "import numpy as np\n", + "df = px.data.gapminder().query(\"year == 2007\")\n", + "fig = px.icicle(df, path=[px.Constant('world'), 'continent', 'country'], values='pop',\n", + " color='lifeExp', hover_data=['iso_alpha'])\n", + "fig.show()" + ] + }, + { + "cell_type": "markdown", + "id": "97af44df", + "metadata": {}, + "source": [ + "#### Distributions\n", + "\n", + "**Read more about [histograms](/python/histograms/).**" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "69138586", + "metadata": {}, + "outputs": [], + "source": [ + "import plotly.express as px\n", + "df = px.data.tips()\n", + "fig = px.histogram(df, x=\"total_bill\", y=\"tip\", color=\"sex\", marginal=\"rug\", hover_data=df.columns)\n", + "fig.show()" + ] + }, + { + "cell_type": "markdown", + "id": "ce55e739", + "metadata": {}, + "source": [ + "**Read more about [box plots](/python/box-plots/).**" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "63cb5c0a", + "metadata": {}, + "outputs": [], + "source": [ + "import plotly.express as px\n", + "df = px.data.tips()\n", + "fig = px.box(df, x=\"day\", y=\"total_bill\", color=\"smoker\", notched=True)\n", + "fig.show()" + ] + }, + { + "cell_type": "markdown", + "id": "fc5739aa", + "metadata": {}, + "source": [ + "**Read more about [violin plots](/python/violin/).**" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "2bdf121e", + "metadata": {}, + "outputs": [], + "source": [ + "import plotly.express as px\n", + "df = px.data.tips()\n", + "fig = px.violin(df, y=\"tip\", x=\"smoker\", color=\"sex\", box=True, points=\"all\", hover_data=df.columns)\n", + "fig.show()" + ] + }, + { + "cell_type": "markdown", + "id": "d002f5d4", + "metadata": {}, + "source": [ + "**Read more about [Empirical Cumulative Distribution Function (ECDF) charts](https://plotly.com/python/ecdf-plots/).**" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "f8939525", + "metadata": {}, + "outputs": [], + "source": [ + "import plotly.express as px\n", + "df = px.data.tips()\n", + "fig = px.ecdf(df, x=\"total_bill\", color=\"sex\")\n", + "fig.show()" + ] + }, + { + "cell_type": "markdown", + "id": "c61159b0", + "metadata": {}, + "source": [ + "**Read more about [strip charts](https://plotly.com/python/strip-charts/).**" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "6a15288a", + "metadata": {}, + "outputs": [], + "source": [ + "import plotly.express as px\n", + "df = px.data.tips()\n", + "fig = px.strip(df, x=\"total_bill\", y=\"time\", orientation=\"h\", color=\"smoker\")\n", + "fig.show()" + ] + }, + { + "cell_type": "markdown", + "id": "36f0a91c", + "metadata": {}, + "source": [ + "**Read more about [density contours, also known as 2D histogram contours](/python/2d-histogram-contour/).**" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "93cd2345", + "metadata": {}, + "outputs": [], + "source": [ + "import plotly.express as px\n", + "df = px.data.iris()\n", + "fig = px.density_contour(df, x=\"sepal_width\", y=\"sepal_length\")\n", + "fig.show()" + ] + }, + { + "cell_type": "markdown", + "id": "96e0a3e5", + "metadata": {}, + "source": [ + "**Read more about [density heatmaps, also known as 2D histograms](/python/2D-Histogram/).**" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "a1be6230", + "metadata": {}, + "outputs": [], + "source": [ + "import plotly.express as px\n", + "df = px.data.iris()\n", + "fig = px.density_heatmap(df, x=\"sepal_width\", y=\"sepal_length\", marginal_x=\"rug\", marginal_y=\"histogram\")\n", + "fig.show()" + ] + }, + { + "cell_type": "markdown", + "id": "b35fb9d3", + "metadata": {}, + "source": [ + "### Images and Heatmaps\n", + "\n", + "**Read more about [heatmaps and images](/python/imshow/).**" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "140412d7", + "metadata": {}, + "outputs": [], + "source": [ + "import plotly.express as px\n", + "data=[[1, 25, 30, 50, 1], [20, 1, 60, 80, 30], [30, 60, 1, 5, 20]]\n", + "fig = px.imshow(data,\n", + " labels=dict(x=\"Day of Week\", y=\"Time of Day\", color=\"Productivity\"),\n", + " x=['Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday'],\n", + " y=['Morning', 'Afternoon', 'Evening']\n", + " )\n", + "fig.update_xaxes(side=\"top\")\n", + "fig.show()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "96a0e2b1", + "metadata": {}, + "outputs": [], + "source": [ + "import plotly.express as px\n", + "from skimage import io\n", + "img = io.imread('https://upload.wikimedia.org/wikipedia/commons/thumb/0/00/Crab_Nebula.jpg/240px-Crab_Nebula.jpg')\n", + "fig = px.imshow(img)\n", + "fig.show()" + ] + }, + { + "cell_type": "markdown", + "id": "d37b9834", + "metadata": {}, + "source": [ + "#### Tile Maps\n", + "\n", + "**Read more about [tile maps](/python/tile-map-layers/) and [point on tile maps](/python/tile-scatter-maps/).**" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "1176054d", + "metadata": {}, + "outputs": [], + "source": [ + "import plotly.express as px\n", + "df = px.data.carshare()\n", + "fig = px.scatter_map(df, lat=\"centroid_lat\", lon=\"centroid_lon\", color=\"peak_hour\", size=\"car_hours\",\n", + " color_continuous_scale=px.colors.cyclical.IceFire, size_max=15, zoom=10,\n", + " map_style=\"carto-positron\")\n", + "fig.show()" + ] + }, + { + "cell_type": "markdown", + "id": "97ce613c", + "metadata": {}, + "source": [ + "**Read more about [tile map GeoJSON choropleths](/python/tile-county-choropleth/).**" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "32d9eee5", + "metadata": {}, + "outputs": [], + "source": [ + "import plotly.express as px\n", + "\n", + "df = px.data.election()\n", + "geojson = px.data.election_geojson()\n", + "\n", + "fig = px.choropleth_map(df, geojson=geojson, color=\"Bergeron\",\n", + " locations=\"district\", featureidkey=\"properties.district\",\n", + " center={\"lat\": 45.5517, \"lon\": -73.7073},\n", + " map_style=\"carto-positron\", zoom=9)\n", + "fig.show()" + ] + }, + { + "cell_type": "markdown", + "id": "58c123d6", + "metadata": {}, + "source": [ + "### Outline Maps\n", + "\n", + "**Read more about [outline symbol maps](/python/scatter-plots-on-maps/).**" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "914f6dfa", + "metadata": { + "lines_to_next_cell": 2 + }, + "outputs": [], + "source": [ + "import plotly.express as px\n", + "df = px.data.gapminder()\n", + "fig = px.scatter_geo(df, locations=\"iso_alpha\", color=\"continent\", hover_name=\"country\", size=\"pop\",\n", + " animation_frame=\"year\", projection=\"natural earth\")\n", + "fig.show()" + ] + }, + { + "cell_type": "markdown", + "id": "92b3c82e", + "metadata": {}, + "source": [ + "**Read more about [choropleth maps](/python/choropleth-maps/).**" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "23ddb7d2", + "metadata": { + "lines_to_next_cell": 2 + }, + "outputs": [], + "source": [ + "import plotly.express as px\n", + "df = px.data.gapminder()\n", + "fig = px.choropleth(df, locations=\"iso_alpha\", color=\"lifeExp\", hover_name=\"country\", animation_frame=\"year\", range_color=[20,80])\n", + "fig.show()" + ] + }, + { + "cell_type": "markdown", + "id": "3714eeda", + "metadata": {}, + "source": [ + "#### Polar Coordinates\n", + "\n", + "**Read more about [polar plots](/python/polar-chart/).**" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "aafa0fd1", + "metadata": {}, + "outputs": [], + "source": [ + "import plotly.express as px\n", + "df = px.data.wind()\n", + "fig = px.scatter_polar(df, r=\"frequency\", theta=\"direction\", color=\"strength\", symbol=\"strength\",\n", + " color_discrete_sequence=px.colors.sequential.Plasma_r)\n", + "fig.show()" + ] + }, + { + "cell_type": "markdown", + "id": "63a74e0c", + "metadata": {}, + "source": [ + "**Read more about [radar charts](https://plotly.com/python/radar-chart/).**" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "e028b187", + "metadata": {}, + "outputs": [], + "source": [ + "import plotly.express as px\n", + "df = px.data.wind()\n", + "fig = px.line_polar(df, r=\"frequency\", theta=\"direction\", color=\"strength\", line_close=True,\n", + " color_discrete_sequence=px.colors.sequential.Plasma_r)\n", + "fig.show()" + ] + }, + { + "cell_type": "markdown", + "id": "cc62db45", + "metadata": {}, + "source": [ + "**Read more about [polar bar charts](/python/wind-rose-charts/).**" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "140eec15", + "metadata": {}, + "outputs": [], + "source": [ + "import plotly.express as px\n", + "df = px.data.wind()\n", + "fig = px.bar_polar(df, r=\"frequency\", theta=\"direction\", color=\"strength\", template=\"plotly_dark\",\n", + " color_discrete_sequence= px.colors.sequential.Plasma_r)\n", + "fig.show()" + ] + }, + { + "cell_type": "markdown", + "id": "7196420a", + "metadata": {}, + "source": [ + "#### 3D Coordinates\n", + "\n", + "**Read more about [3D scatter plots](/python/3d-scatter-plots/).**" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "45ebb33a", + "metadata": {}, + "outputs": [], + "source": [ + "import plotly.express as px\n", + "df = px.data.election()\n", + "fig = px.scatter_3d(df, x=\"Joly\", y=\"Coderre\", z=\"Bergeron\", color=\"winner\", size=\"total\", hover_name=\"district\",\n", + " symbol=\"result\", color_discrete_map = {\"Joly\": \"blue\", \"Bergeron\": \"green\", \"Coderre\":\"red\"})\n", + "fig.show()" + ] + }, + { + "cell_type": "markdown", + "id": "1e46d7a0", + "metadata": {}, + "source": [ + "#### Ternary Coordinates\n", + "\n", + "**Read more about [ternary charts](/python/ternary-plots/).**" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "2467fe04", + "metadata": { + "lines_to_next_cell": 2 + }, + "outputs": [], + "source": [ + "import plotly.express as px\n", + "df = px.data.election()\n", + "fig = px.scatter_ternary(df, a=\"Joly\", b=\"Coderre\", c=\"Bergeron\", color=\"winner\", size=\"total\", hover_name=\"district\",\n", + " size_max=15, color_discrete_map = {\"Joly\": \"blue\", \"Bergeron\": \"green\", \"Coderre\":\"red\"} )\n", + "fig.show()" + ] + }, + { + "cell_type": "markdown", + "id": "1ef3ce5a", + "metadata": {}, + "source": [ + "### What About Dash?\n", + "\n", + "[Dash](https://dash.plot.ly/) is an open-source framework for building analytical applications, with no Javascript required, and it is tightly integrated with the Plotly graphing library.\n", + "\n", + "Learn about how to install Dash at https://dash.plot.ly/installation.\n", + "\n", + "Everywhere in this page that you see `fig.show()`, you can display the same figure in a Dash application by passing it to the `figure` argument of the [`Graph` component](https://dash.plot.ly/dash-core-components/graph) from the built-in `dash_core_components` package like this:\n", + "\n", + "```python\n", + "import plotly.graph_objects as go # or plotly.express as px\n", + "fig = go.Figure() # or any Plotly Express function e.g. px.bar(...)\n", + "# fig.add_trace( ... )\n", + "# fig.update_layout( ... )\n", + "\n", + "from dash import Dash, dcc, html\n", + "\n", + "app = Dash()\n", + "app.layout = html.Div([\n", + " dcc.Graph(figure=fig)\n", + "])\n", + "\n", + "app.run_server(debug=True, use_reloader=False) # Turn off reloader if inside Jupyter\n", + "```" + ] + } + ], + "metadata": { + "jupytext": { + "notebook_metadata_filter": "all" + }, + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "language": "python", + "name": "python3" + }, + "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.4" + }, + "plotly": { + "description": "Plotly Express is a terse, consistent, high-level API for creating figures.", + "display_as": "file_settings", + "language": "python", + "layout": "base", + "name": "Plotly Express", + "order": 4, + "page_type": "example_index", + "permalink": "python/plotly-express/", + "thumbnail": "thumbnail/plotly-express.png" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/polar-chart.ipynb b/polar-chart.ipynb new file mode 100644 index 000000000..61dfa2f3d --- /dev/null +++ b/polar-chart.ipynb @@ -0,0 +1,626 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "a868a059", + "metadata": {}, + "source": [ + "## Polar chart with Plotly Express\n", + "\n", + "A polar chart represents data along radial and angular axes. With Plotly Express, it is possible to represent polar data as scatter markers with `px.scatter_polar`, and as lines with `px.line_polar`.\n", + "\n", + "[Plotly Express](/python/plotly-express/) is the easy-to-use, high-level interface to Plotly, which [operates on a variety of types of data](/python/px-arguments/) and produces [easy-to-style figures](/python/styling-plotly-express/).\n", + "\n", + "For other types of arguments, see the section below using `go.Scatterpolar`.\n", + "\n", + "The radial and angular coordinates are given with the `r` and `theta` arguments of `px.scatter_polar`. In the example below the `theta` data are categorical, but numerical data are possible too and the most common case." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "19a2f513", + "metadata": {}, + "outputs": [], + "source": [ + "import plotly.express as px\n", + "df = px.data.wind()\n", + "fig = px.scatter_polar(df, r=\"frequency\", theta=\"direction\")\n", + "fig.show()" + ] + }, + { + "cell_type": "markdown", + "id": "226c079f", + "metadata": {}, + "source": [ + "The \"strength\" column corresponds to strength categories of the wind, and there is a frequency value for each direction and strength. Below we use the strength column to encode the color, symbol and size of the markers." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "7348a8ab", + "metadata": {}, + "outputs": [], + "source": [ + "import plotly.express as px\n", + "df = px.data.wind()\n", + "fig = px.scatter_polar(df, r=\"frequency\", theta=\"direction\",\n", + " color=\"strength\", symbol=\"strength\", size=\"frequency\",\n", + " color_discrete_sequence=px.colors.sequential.Plasma_r)\n", + "fig.show()" + ] + }, + { + "cell_type": "markdown", + "id": "573eca0f", + "metadata": {}, + "source": [ + "For a line polar plot, use `px.line_polar`:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "a2421f38", + "metadata": {}, + "outputs": [], + "source": [ + "import plotly.express as px\n", + "df = px.data.wind()\n", + "fig = px.line_polar(df, r=\"frequency\", theta=\"direction\", color=\"strength\", line_close=True,\n", + " color_discrete_sequence=px.colors.sequential.Plasma_r,\n", + " template=\"plotly_dark\",)\n", + "fig.show()" + ] + }, + { + "cell_type": "markdown", + "id": "4c80db58", + "metadata": {}, + "source": [ + "See also the [wind rose page](https://plotly.com/python/wind-rose-charts/) for more wind rose visualizations in polar coordinates.\n", + "\n", + "You can plot less than a whole circle with the `range_theta` argument, and also control the `start_angle` and `direction`:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "9ee0ad2d", + "metadata": {}, + "outputs": [], + "source": [ + "import plotly.express as px\n", + "fig = px.scatter_polar(r=range(0,90,10), theta=range(0,90,10),\n", + " range_theta=[0,90], start_angle=0, direction=\"counterclockwise\")\n", + "fig.show()" + ] + }, + { + "cell_type": "markdown", + "id": "eaa9ce4c", + "metadata": {}, + "source": [ + "## Polar Scatter Plot with go.Scatterpolar\n", + "\n", + "If Plotly Express does not provide a good starting point, you can use [the more generic `go.Scatterpolar` class from `plotly.graph_objects`](/python/graph-objects/). All the options are documented in the [reference page](https://plotly.com/python/reference/scatterpolar/).\n", + "\n", + "#### Basic Polar Chart" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "2b283a16", + "metadata": {}, + "outputs": [], + "source": [ + "import plotly.graph_objects as go\n", + "\n", + "fig = go.Figure(data=\n", + " go.Scatterpolar(\n", + " r = [0.5,1,2,2.5,3,4],\n", + " theta = [35,70,120,155,205,240],\n", + " mode = 'markers',\n", + " ))\n", + "\n", + "fig.update_layout(showlegend=False)\n", + "fig.show()" + ] + }, + { + "cell_type": "markdown", + "id": "0236bc6f", + "metadata": {}, + "source": [ + "#### Line Polar Chart" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "11284d1d", + "metadata": {}, + "outputs": [], + "source": [ + "import plotly.graph_objects as go\n", + "\n", + "import pandas as pd\n", + "\n", + "df = pd.read_csv(\"https://raw.githubusercontent.com/plotly/datasets/master/polar_dataset.csv\")\n", + "\n", + "fig = go.Figure()\n", + "fig.add_trace(go.Scatterpolar(\n", + " r = df['x1'],\n", + " theta = df['y'],\n", + " mode = 'lines',\n", + " name = 'Figure 8',\n", + " line_color = 'peru'\n", + " ))\n", + "fig.add_trace(go.Scatterpolar(\n", + " r = df['x2'],\n", + " theta = df['y'],\n", + " mode = 'lines',\n", + " name = 'Cardioid',\n", + " line_color = 'darkviolet'\n", + " ))\n", + "fig.add_trace(go.Scatterpolar(\n", + " r = df['x3'],\n", + " theta = df['y'],\n", + " mode = 'lines',\n", + " name = 'Hypercardioid',\n", + " line_color = 'deepskyblue'\n", + " ))\n", + "\n", + "\n", + "fig.update_layout(\n", + " title = 'Mic Patterns',\n", + " showlegend = False\n", + ")\n", + "\n", + "fig.show()" + ] + }, + { + "cell_type": "markdown", + "id": "4bd787fd", + "metadata": {}, + "source": [ + "#### Polar Bar Chart\n", + "\n", + "a.k.a matplotlib logo in a few lines of code" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "0f6f6a13", + "metadata": {}, + "outputs": [], + "source": [ + "import plotly.graph_objects as go\n", + "\n", + "fig = go.Figure(go.Barpolar(\n", + " r=[3.5, 1.5, 2.5, 4.5, 4.5, 4, 3],\n", + " theta=[65, 15, 210, 110, 312.5, 180, 270],\n", + " width=[20,15,10,20,15,30,15,],\n", + " marker_color=[\"#E4FF87\", '#709BFF', '#709BFF', '#FFAA70', '#FFAA70', '#FFDF70', '#B6FFB4'],\n", + " marker_line_color=\"black\",\n", + " marker_line_width=2,\n", + " opacity=0.8\n", + "))\n", + "\n", + "fig.update_layout(\n", + " template=None,\n", + " polar = dict(\n", + " radialaxis = dict(range=[0, 5], showticklabels=False, ticks=''),\n", + " angularaxis = dict(showticklabels=False, ticks='')\n", + " )\n", + ")\n", + "\n", + "fig.show()" + ] + }, + { + "cell_type": "markdown", + "id": "c2a3423a", + "metadata": {}, + "source": [ + "#### Categorical Polar Chart" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "f4a5012e", + "metadata": {}, + "outputs": [], + "source": [ + "import plotly.graph_objects as go\n", + "from plotly.subplots import make_subplots\n", + "\n", + "fig = make_subplots(rows=2, cols=2, specs=[[{'type': 'polar'}]*2]*2)\n", + "\n", + "fig.add_trace(go.Scatterpolar(\n", + " name = \"angular categories\",\n", + " r = [5, 4, 2, 4, 5],\n", + " theta = [\"a\", \"b\", \"c\", \"d\", \"a\"],\n", + " ), 1, 1)\n", + "fig.add_trace(go.Scatterpolar(\n", + " name = \"radial categories\",\n", + " r = [\"a\", \"b\", \"c\", \"d\", \"b\", \"f\", \"a\"],\n", + " theta = [1, 4, 2, 1.5, 1.5, 6, 5],\n", + " thetaunit = \"radians\",\n", + " ), 1, 2)\n", + "fig.add_trace(go.Scatterpolar(\n", + " name = \"angular categories (w/ categoryarray)\",\n", + " r = [5, 4, 2, 4, 5],\n", + " theta = [\"a\", \"b\", \"c\", \"d\", \"a\"],\n", + " ), 2, 1)\n", + "fig.add_trace(go.Scatterpolar(\n", + " name = \"radial categories (w/ category descending)\",\n", + " r = [\"a\", \"b\", \"c\", \"d\", \"b\", \"f\", \"a\", \"a\"],\n", + " theta = [45, 90, 180, 200, 300, 15, 20, 45],\n", + " ), 2, 2)\n", + "\n", + "fig.update_traces(fill='toself')\n", + "fig.update_layout(\n", + " polar = dict(\n", + " radialaxis_angle = -45,\n", + " angularaxis = dict(\n", + " direction = \"clockwise\",\n", + " period = 6)\n", + " ),\n", + " polar2 = dict(\n", + " radialaxis = dict(\n", + " angle = 180,\n", + " tickangle = -180 # so that tick labels are not upside down\n", + " )\n", + " ),\n", + " polar3 = dict(\n", + " sector = [80, 400],\n", + " radialaxis_angle = -45,\n", + " angularaxis_categoryarray = [\"d\", \"a\", \"c\", \"b\"]\n", + " ),\n", + " polar4 = dict(\n", + " radialaxis_categoryorder = \"category descending\",\n", + " angularaxis = dict(\n", + " thetaunit = \"radians\",\n", + " dtick = 0.3141592653589793\n", + " ))\n", + ")\n", + "\n", + "fig.show()" + ] + }, + { + "cell_type": "markdown", + "id": "be2f9a4e", + "metadata": {}, + "source": [ + "#### Polar Chart Sector" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "7400e8e4", + "metadata": {}, + "outputs": [], + "source": [ + "import plotly.graph_objects as go\n", + "from plotly.subplots import make_subplots\n", + "\n", + "fig = make_subplots(rows=1, cols=2, specs=[[{'type': 'polar'}]*2])\n", + "\n", + "fig.add_trace(go.Scatterpolar(), 1, 1)\n", + "fig.add_trace(go.Scatterpolar(), 1, 2)\n", + "\n", + "# Same data for the two Scatterpolar plots, we will only change the sector in the layout\n", + "fig.update_traces(mode = \"lines+markers\",\n", + " r = [1,2,3,4,5],\n", + " theta = [0,90,180,360,0],\n", + " line_color = \"magenta\",\n", + " marker = dict(\n", + " color = \"royalblue\",\n", + " symbol = \"square\",\n", + " size = 8\n", + " ))\n", + "\n", + "# The sector is [0, 360] by default, we update it for the first plot only\n", + "fig.update_layout(\n", + " showlegend = False,\n", + " polar = dict(# setting parameters for the second plot would be polar2=dict(...)\n", + " sector = [150,210],\n", + " ))\n", + "\n", + "\n", + "fig.show()" + ] + }, + { + "cell_type": "markdown", + "id": "cb4c7023", + "metadata": {}, + "source": [ + "#### Polar Chart Directions" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "439d3b7d", + "metadata": {}, + "outputs": [], + "source": [ + "import plotly.graph_objects as go\n", + "from plotly.subplots import make_subplots\n", + "\n", + "fig = make_subplots(rows=1, cols=2, specs=[[{'type': 'polar'}, {'type': 'polar'}]])\n", + "\n", + "r = [1,2,3,4,5]\n", + "theta = [0,90,180,360,0]\n", + "\n", + "fig.add_trace(go.Scatterpolar(), 1, 1)\n", + "fig.add_trace(go.Scatterpolar(), 1, 2)\n", + "\n", + "# Same data for the two Scatterpolar plots, we will only change the direction in the layout\n", + "fig.update_traces(r= r, theta=theta,\n", + " mode=\"lines+markers\", line_color='indianred',\n", + " marker=dict(color='lightslategray', size=8, symbol='square'))\n", + "fig.update_layout(\n", + " showlegend = False,\n", + " polar = dict(\n", + " radialaxis_tickfont_size = 8,\n", + " angularaxis = dict(\n", + " tickfont_size=8,\n", + " rotation=90, # start position of angular axis\n", + " direction=\"counterclockwise\"\n", + " )\n", + " ),\n", + " polar2 = dict(\n", + " radialaxis_tickfont_size = 8,\n", + " angularaxis = dict(\n", + " tickfont_size = 8,\n", + " rotation = 90,\n", + " direction = \"clockwise\"\n", + " ),\n", + " ))\n", + "\n", + "fig.show()" + ] + }, + { + "cell_type": "markdown", + "id": "ea63c6ce", + "metadata": {}, + "source": [ + "#### Webgl Polar Chart\n", + "\n", + "The `go.Scatterpolargl` trace uses the [WebGL](https://en.wikipedia.org/wiki/WebGL) plotting engine for GPU-accelerated rendering." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "caf20a1e", + "metadata": {}, + "outputs": [], + "source": [ + "import plotly.graph_objects as go\n", + "import pandas as pd\n", + "\n", + "df = pd.read_csv(\"https://raw.githubusercontent.com/plotly/datasets/master/hobbs-pearson-trials.csv\")\n", + "\n", + "fig = go.Figure()\n", + "\n", + "fig.add_trace(go.Scatterpolargl(\n", + " r = df.trial_1_r,\n", + " theta = df.trial_1_theta,\n", + " name = \"Trial 1\",\n", + " marker=dict(size=15, color=\"mediumseagreen\")\n", + " ))\n", + "fig.add_trace(go.Scatterpolargl(\n", + " r = df.trial_2_r,\n", + " theta = df.trial_2_theta,\n", + " name = \"Trial 2\",\n", + " marker=dict(size=20, color=\"darkorange\")\n", + " ))\n", + "fig.add_trace(go.Scatterpolargl(\n", + " r = df.trial_3_r,\n", + " theta = df.trial_3_theta,\n", + " name = \"Trial 3\",\n", + " marker=dict(size=12, color=\"mediumpurple\")\n", + " ))\n", + "fig.add_trace(go.Scatterpolargl(\n", + " r = df.trial_4_r,\n", + " theta = df.trial_4_theta,\n", + " name = \"Trial 4\",\n", + " marker=dict(size=22, color = \"magenta\")\n", + " ))\n", + "fig.add_trace(go.Scatterpolargl(\n", + " r = df.trial_5_r,\n", + " theta = df.trial_5_theta,\n", + " name = \"Trial 5\",\n", + " marker=dict(size=19, color = \"limegreen\")\n", + " ))\n", + "fig.add_trace(go.Scatterpolargl(\n", + " r = df.trial_6_r,\n", + " theta = df.trial_6_theta,\n", + " name = \"Trial 6\",\n", + " marker=dict(size=10, color = \"gold\")\n", + " ))\n", + "\n", + "# Common parameters for all traces\n", + "fig.update_traces(mode=\"markers\", marker=dict(line_color='white', opacity=0.7))\n", + "\n", + "fig.update_layout(\n", + " title = \"Hobbs-Pearson Trials\",\n", + " font_size = 15,\n", + " showlegend = False,\n", + " polar = dict(\n", + " bgcolor = \"rgb(223, 223, 223)\",\n", + " angularaxis = dict(\n", + " linewidth = 3,\n", + " showline=True,\n", + " linecolor='black'\n", + " ),\n", + " radialaxis = dict(\n", + " side = \"counterclockwise\",\n", + " showline = True,\n", + " linewidth = 2,\n", + " gridcolor = \"white\",\n", + " gridwidth = 2,\n", + " )\n", + " ),\n", + " paper_bgcolor = \"rgb(223, 223, 223)\"\n", + ")\n", + "\n", + "fig.show()" + ] + }, + { + "cell_type": "markdown", + "id": "101f7d0a", + "metadata": {}, + "source": [ + "#### Polar Chart Subplots" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "4b5feea5", + "metadata": {}, + "outputs": [], + "source": [ + "import plotly.graph_objects as go\n", + "from plotly.subplots import make_subplots\n", + "\n", + "fig = make_subplots(rows=2, cols=2, specs=[[{'type': 'polar'}]*2]*2)\n", + "\n", + "fig.add_trace(go.Scatterpolar(\n", + " r = [1, 2, 3],\n", + " theta = [50, 100, 200],\n", + " marker_symbol = \"square\"\n", + " ), 1, 1)\n", + "fig.add_trace(go.Scatterpolar(\n", + " r = [1, 2, 3],\n", + " theta = [1, 2, 3],\n", + " thetaunit = \"radians\"\n", + " ), 1, 1)\n", + "fig.add_trace(go.Scatterpolar(\n", + " r = [\"a\", \"b\", \"c\", \"b\"],\n", + " theta = [\"D\", \"C\", \"B\", \"A\"],\n", + " subplot = \"polar2\"\n", + " ), 1, 2)\n", + "fig.add_trace(go.Scatterpolar(\n", + " r = [50, 300, 900],\n", + " theta = [0, 90, 180],\n", + " subplot = \"polar3\"\n", + " ), 2, 1)\n", + "fig.add_trace(go.Scatterpolar(\n", + " mode = \"lines\",\n", + " r = [3, 3, 4, 3],\n", + " theta = [0, 45, 90, 270],\n", + " fill = \"toself\",\n", + " subplot = \"polar4\"\n", + " ), 2, 2)\n", + "\n", + "\n", + "fig.update_layout(\n", + " polar = dict(\n", + " radialaxis_range = [1, 4],\n", + " angularaxis_thetaunit = \"radians\"\n", + " ),\n", + " polar3 = dict(\n", + " radialaxis = dict(type = \"log\", tickangle = 45),\n", + " sector = [0, 180]\n", + " ),\n", + " polar4 = dict(\n", + " radialaxis = dict(visible = False, range = [0, 6])),\n", + " showlegend = False\n", + ")\n", + "\n", + "fig.show()" + ] + }, + { + "cell_type": "markdown", + "id": "d874bad9", + "metadata": {}, + "source": [ + "#### Reference\n", + "\n", + "See [function reference for `px.(scatter_polar)`](https://plotly.com/python-api-reference/generated/plotly.express.scatter_polar) or [function reference for `px.(line_polar)`](https://plotly.com/python-api-reference/generated/plotly.express.line_polar) or https://plotly.com/python/reference/scatterpolar/ for more information and chart attribute options!\n" + ] + }, + { + "cell_type": "markdown", + "id": "1d998c60", + "metadata": {}, + "source": [ + "### What About Dash?\n", + "\n", + "[Dash](https://dash.plot.ly/) is an open-source framework for building analytical applications, with no Javascript required, and it is tightly integrated with the Plotly graphing library.\n", + "\n", + "Learn about how to install Dash at https://dash.plot.ly/installation.\n", + "\n", + "Everywhere in this page that you see `fig.show()`, you can display the same figure in a Dash application by passing it to the `figure` argument of the [`Graph` component](https://dash.plot.ly/dash-core-components/graph) from the built-in `dash_core_components` package like this:\n", + "\n", + "```python\n", + "import plotly.graph_objects as go # or plotly.express as px\n", + "fig = go.Figure() # or any Plotly Express function e.g. px.bar(...)\n", + "# fig.add_trace( ... )\n", + "# fig.update_layout( ... )\n", + "\n", + "from dash import Dash, dcc, html\n", + "\n", + "app = Dash()\n", + "app.layout = html.Div([\n", + " dcc.Graph(figure=fig)\n", + "])\n", + "\n", + "app.run_server(debug=True, use_reloader=False) # Turn off reloader if inside Jupyter\n", + "```" + ] + } + ], + "metadata": { + "jupytext": { + "notebook_metadata_filter": "all" + }, + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "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.6.8" + }, + "plotly": { + "description": "How to make polar charts in Python with Plotly.", + "display_as": "scientific", + "language": "python", + "layout": "base", + "name": "Polar Charts", + "order": 16, + "page_type": "u-guide", + "permalink": "python/polar-chart/", + "thumbnail": "thumbnail/polar.gif" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/px-arguments.ipynb b/px-arguments.ipynb new file mode 100644 index 000000000..3764911e7 --- /dev/null +++ b/px-arguments.ipynb @@ -0,0 +1,517 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "0b0460d6", + "metadata": {}, + "source": [ + "Plotly Express (`px`) is the high-level interface to Plotly and provides functions for generating charts. `px` functions support data provided in a number of different formats (long, wide, and mixed) and as different types of objects, including pandas and Polars dataframes.\n", + "\n", + "## Data for the Examples\n", + "\n", + "The examples on this page use datasets available in the `data` package in `px`. `px.data` contains functions that when called return a dataset as a dataframe. Some of the datasets included in `px.data` are:\n", + "\n", + "- `carshare` - Each row represents the availability of car-sharing services near the centroid of a zone in Montreal over a month-long period.\n", + "- `election` - Each row represents voting results for an electoral district in the 2013 Montreal mayoral election.\n", + "- `iris` - Each row represents a flower.\n", + "\n", + "To access the `iris` dataset, we call its function and assign it to a variable:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "07bc4521", + "metadata": {}, + "outputs": [], + "source": [ + "import plotly.express as px\n", + "\n", + "df = px.data.iris()\n", + "df.head()" + ] + }, + { + "cell_type": "markdown", + "id": "61b92c40", + "metadata": {}, + "source": [ + "By default `px.data` functions return a pandas `DataFrame` object, but you can specify an alternative dataframe type using `return_type`. `pandas`, `polars`, `pyarrow`, `modin`, and `cuDF` are supported return types." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "ee7480a2", + "metadata": {}, + "outputs": [], + "source": [ + "df = px.data.iris(return_type='polars')\n", + "df.head()" + ] + }, + { + "cell_type": "markdown", + "id": "1150bc84", + "metadata": {}, + "source": [ + "## Long, Wide, and Mixed-Form Data\n", + "\n", + "There are three common conventions for storing column-oriented data, usually in a data frame with column names:\n", + "\n", + "* **long-form data** has one row per observation, and one column per variable. This is suitable for storing and displaying multivariate data i.e. with dimension greater than 2. This format is sometimes called \"tidy\".\n", + "* **wide-form data** has one row per value of one of the first variable, and one column per value of the second variable. This is suitable for storing and displaying 2-dimensional data.\n", + "* **mixed-form data** is a hybrid of long-form and wide-form data, with one row per value of one variable, and some columns representing values of another, and some columns representing more variables. See the [wide-form documentation](/python/wide-form/) for examples of how to use Plotly Express to visualize this kind of data.\n", + "\n", + "Every Plotly Express function can operate on long-form data (other than `px.imshow` which operates only on wide-form input), and in addition, the following 2D-Cartesian functions can operate on wide-form and mixed-form data: `px.scatter`, `px.line`, `px.area`, `px.bar`, `px.histogram`, `px.violin`, `px.box`, `px.strip`, `px.funnel`, `px.density_heatmap` and `px.density_contour`.\n", + "\n", + "By way of example here is the same data, represented in long-form first, and then in wide-form:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "4c14d895", + "metadata": {}, + "outputs": [], + "source": [ + "import plotly.express as px\n", + "long_df = px.data.medals_long()\n", + "long_df" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "01e76d70", + "metadata": {}, + "outputs": [], + "source": [ + "import plotly.express as px\n", + "wide_df = px.data.medals_wide()\n", + "wide_df" + ] + }, + { + "cell_type": "markdown", + "id": "75cdf3d8", + "metadata": {}, + "source": [ + "Plotly Express can produce the same plot from either form:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "343e4149", + "metadata": {}, + "outputs": [], + "source": [ + "import plotly.express as px\n", + "long_df = px.data.medals_long()\n", + "\n", + "fig = px.bar(long_df, x=\"nation\", y=\"count\", color=\"medal\", title=\"Long-Form Input\")\n", + "fig.show()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "b4dba5f5", + "metadata": {}, + "outputs": [], + "source": [ + "import plotly.express as px\n", + "wide_df = px.data.medals_wide()\n", + "\n", + "fig = px.bar(wide_df, x=\"nation\", y=[\"gold\", \"silver\", \"bronze\"], title=\"Wide-Form Input\")\n", + "fig.show()" + ] + }, + { + "cell_type": "markdown", + "id": "9000b417", + "metadata": {}, + "source": [ + "You might notice that y-axis and legend labels are slightly different for the second plot: they are \"value\" and \"variable\", respectively, and this is also reflected in the hoverlabel text. Note that the labels \"medal\" and \"count\" do not appear in the wide-form data frame, so in this case, you must supply these yourself, or [you can use a data frame with named row- and column-indexes](/python/wide-form/). You can [rename these labels with the `labels` argument](/python/styling-plotly-express/):" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "ba34abac", + "metadata": {}, + "outputs": [], + "source": [ + "import plotly.express as px\n", + "wide_df = px.data.medals_wide()\n", + "\n", + "fig = px.bar(wide_df, x=\"nation\", y=[\"gold\", \"silver\", \"bronze\"], title=\"Wide-Form Input, relabelled\",\n", + " labels={\"value\": \"count\", \"variable\": \"medal\"})\n", + "fig.show()" + ] + }, + { + "cell_type": "markdown", + "id": "4f6d2a54", + "metadata": {}, + "source": [ + "Many more examples of wide-form and messy data input can be found in our [detailed wide-form support documentation](/python/wide-form/)." + ] + }, + { + "cell_type": "markdown", + "id": "4071b2f9", + "metadata": {}, + "source": [ + "## Dataframe Input\n", + "\n", + "The first argument of every `px` function is `data_frame`. If you provide a dataframe as a `px` function's first argument, you can then specify column names as strings from the dataframe as other arguments.\n", + "\n", + "### Supported DataFrame Types\n", + "\n", + "`px` functions natively support pandas, Polars, and PyArrow dataframes. `px` uses [Narwhals](https://narwhals-dev.github.io/narwhals/) to provide this native dataframe support. Other types of dataframes that are currently supported by Narwhals, for example cuDF and Modin, may also work with `px`.\n", + "\n", + "You can also pass dataframes that are not natively supported, but which support the [dataframe interchange protocol](https://data-apis.org/dataframe-protocol/latest/).\n", + "\n", + "PySpark dataframes are also supported and are converted to pandas dataframes internally by Plotly Express.\n", + "\n", + "#### Additional Dependencies Required\n", + "\n", + "- Plotly Express requires NumPy. You can install it with `pip install numpy` if it's not installed by the dataframe library you are using.\n", + "- To use [trendlines](/python/linear-fits/), you'll also need to have pandas installed.\n", + "- To use PySpark dataframes, you'll need to have pandas installed. To use dataframes that support the dataframe interchange protocol, you'll need to have PyArrow installed.\n", + "\n", + "### Example: Using a Pandas DataFrame with `px.bar`\n", + "\n", + "Here, we create a pandas `DataFrame`, pass it to `px.bar` as its first argument, and then use the `\"sepal_length\"` column for the x-axis and the `\"sepal_width\"` for the y-axis." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "50962b05", + "metadata": {}, + "outputs": [], + "source": [ + "import plotly.express as px\n", + "\n", + "df = px.data.iris()\n", + "\n", + "fig = px.scatter(df, x='sepal_length', y='sepal_width', color='species', size='petal_length')\n", + "fig.show()" + ] + }, + { + "cell_type": "markdown", + "id": "1ace2a98", + "metadata": {}, + "source": [ + "### Example: Polars DataFrame with `px.bar`\n", + "\n", + "`px` provides native support for dataframe types other than pandas, including Polars:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "1de8750e", + "metadata": {}, + "outputs": [], + "source": [ + "import plotly.express as px\n", + "\n", + "df = px.data.iris(return_type='polars')\n", + "\n", + "fig = px.scatter(df, x='sepal_length', y='sepal_width', color='species', size='petal_length')\n", + "fig.show()" + ] + }, + { + "cell_type": "markdown", + "id": "fc1957bb", + "metadata": {}, + "source": [ + "### Using the Index of a Dataframe\n", + "\n", + "If the dataframe you are using has an index, it is also possible to use that index as an argument. In the following example, the index is used for the hover data." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "ef3b1de8", + "metadata": {}, + "outputs": [], + "source": [ + "import plotly.express as px\n", + "df = px.data.iris()\n", + "fig = px.scatter(df, x=df.sepal_length, y=df.sepal_width, size=df.petal_length,\n", + " hover_data=[df.index])\n", + "fig.show()" + ] + }, + { + "cell_type": "markdown", + "id": "ddf0bf77", + "metadata": {}, + "source": [ + "### Using Columns from Multiple Dataframes\n", + "\n", + "You can also use columns from multiple dataframes in one `px` function, as long as all the dataframe columns you use have the same length. In this example, we pass `df1` as the `data_frame` argument to `px.bar` and then us a column from `df2` for the `y` argument." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "e99cd3a5", + "metadata": {}, + "outputs": [], + "source": [ + "import plotly.express as px\n", + "import pandas as pd\n", + "\n", + "df1 = pd.DataFrame(dict(time=[10, 20, 30], sales=[10, 8, 30]))\n", + "df2 = pd.DataFrame(dict(market=[4, 2, 5]))\n", + "fig = px.bar(df1, x=\"time\", y=df2.market, color=\"sales\")\n", + "fig.show()" + ] + }, + { + "cell_type": "markdown", + "id": "ceb78391", + "metadata": {}, + "source": [ + "### Using labels to pass names\n", + "\n", + "The `labels` argument can be used to override the names used for axis titles, legend entries and hovers." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "5d74c979", + "metadata": {}, + "outputs": [], + "source": [ + "import plotly.express as px\n", + "import pandas as pd\n", + "\n", + "df = px.data.gapminder()\n", + "gdp = df['pop'] * df['gdpPercap']\n", + "fig = px.bar(df, x='year', y=gdp, color='continent', labels={'y':'gdp'},\n", + " hover_data=['country'],\n", + " title='Evolution of world GDP')\n", + "fig.show()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "4d24ac5c", + "metadata": {}, + "outputs": [], + "source": [ + "## Other Input Data" + ] + }, + { + "cell_type": "markdown", + "id": "aa3fdd7f", + "metadata": {}, + "source": [ + "### Input Data as array-like columns: NumPy arrays, lists...\n", + "\n", + "`px` arguments can also be array-like objects such as lists, NumPy arrays, in both long-form or wide-form (for certain functions)." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "f2576635", + "metadata": {}, + "outputs": [], + "source": [ + "import plotly.express as px\n", + "\n", + "# List arguments\n", + "fig = px.line(x=[1, 2, 3, 4], y=[3, 5, 4, 8])\n", + "fig.show()" + ] + }, + { + "cell_type": "markdown", + "id": "3867ef6b", + "metadata": {}, + "source": [ + "List arguments can also be passed in as a list of lists, which triggers [wide-form data processing](/python/wide-form/), with the downside that the resulting traces will need to be manually renamed via `fig.data[].name = \"name\"`." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "da23d326", + "metadata": {}, + "outputs": [], + "source": [ + "import plotly.express as px\n", + "\n", + "# List arguments in wide form\n", + "series1 = [3, 5, 4, 8]\n", + "series2 = [5, 4, 8, 3]\n", + "fig = px.line(x=[1, 2, 3, 4], y=[series1, series2])\n", + "fig.show()" + ] + }, + { + "cell_type": "markdown", + "id": "cfc75382", + "metadata": {}, + "source": [ + "### Passing dictionaries or array-likes as the data_frame argument\n", + "\n", + "The `data_frame` argument can also accept a `dict` or `array` in addition to DataFrame objects. Using a dictionary can be a convenient way to pass column names used in axis titles, legend entries and hovers without creating a dataframe." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "661ab716", + "metadata": {}, + "outputs": [], + "source": [ + "import plotly.express as px\n", + "import numpy as np\n", + "N = 10000\n", + "np.random.seed(0)\n", + "fig = px.density_contour(dict(effect_size=5 + np.random.randn(N),\n", + " waiting_time=np.random.poisson(size=N)),\n", + " x=\"effect_size\", y=\"waiting_time\")\n", + "fig.show()" + ] + }, + { + "cell_type": "markdown", + "id": "54293425", + "metadata": {}, + "source": [ + "To pass a `dict` or an array (such as a NumPy `ndarray`) to the `data_frame` parameter, you'll need to have pandas installed, because `plotly.express` internally converts the `dict` or array to a pandas DataFrame.\n", + "\n", + "#### Integer column names\n", + "\n", + "When the `data_frame` argument is a NumPy array, column names are integer corresponding to the columns of the array. In this case, keyword names are used in axis, legend and hovers. This is also the case for a pandas DataFrame with integer column names. Use the `labels` argument to override these names." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "d166f2e7", + "metadata": {}, + "outputs": [], + "source": [ + "import numpy as np\n", + "import plotly.express as px\n", + "\n", + "ar = np.arange(100).reshape((10, 10))\n", + "fig = px.scatter(ar, x=2, y=6, size=1, color=5)\n", + "fig.show()" + ] + }, + { + "cell_type": "markdown", + "id": "5ea55244", + "metadata": {}, + "source": [ + "### Mixing dataframes and other types\n", + "\n", + "It is possible to mix dataframe columns, NumPy arrays and lists as arguments. Remember that the only column names to be used correspond to columns in the `data_frame` argument, use `labels` to override names displayed in axis titles, legend entries or hovers." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "f3f114a5", + "metadata": { + "lines_to_next_cell": 2 + }, + "outputs": [], + "source": [ + "import plotly.express as px\n", + "import numpy as np\n", + "import pandas as pd\n", + "\n", + "df = px.data.gapminder()\n", + "gdp = np.log(df['pop'] * df['gdpPercap']) # NumPy array\n", + "fig = px.bar(df, x='year', y=gdp, color='continent', labels={'y':'log gdp'},\n", + " hover_data=['country'],\n", + " title='Evolution of world GDP')\n", + "fig.show()" + ] + }, + { + "cell_type": "markdown", + "id": "08ad3f6d", + "metadata": {}, + "source": [ + "### What About Dash?\n", + "\n", + "[Dash](https://dash.plot.ly/) is an open-source framework for building analytical applications, with no Javascript required, and it is tightly integrated with the Plotly graphing library.\n", + "\n", + "Learn about how to install Dash at https://dash.plot.ly/installation.\n", + "\n", + "Everywhere in this page that you see `fig.show()`, you can display the same figure in a Dash application by passing it to the `figure` argument of the [`Graph` component](https://dash.plot.ly/dash-core-components/graph) from the built-in `dash_core_components` package like this:\n", + "\n", + "```python\n", + "import plotly.graph_objects as go # or plotly.express as px\n", + "fig = go.Figure() # or any Plotly Express function e.g. px.bar(...)\n", + "# fig.add_trace( ... )\n", + "# fig.update_layout( ... )\n", + "\n", + "from dash import Dash, dcc, html\n", + "\n", + "app = Dash()\n", + "app.layout = html.Div([\n", + " dcc.Graph(figure=fig)\n", + "])\n", + "\n", + "app.run_server(debug=True, use_reloader=False) # Turn off reloader if inside Jupyter\n", + "```" + ] + } + ], + "metadata": { + "jupytext": { + "notebook_metadata_filter": "all" + }, + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "language": "python", + "name": "python3" + }, + "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.11.10" + }, + "plotly": { + "description": "Input data arguments accepted by Plotly Express functions", + "display_as": "file_settings", + "language": "python", + "layout": "base", + "name": "Plotly Express Arguments", + "order": 19, + "page_type": "u-guide", + "permalink": "python/px-arguments/", + "thumbnail": "thumbnail/plotly-express.png" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/quiver-plots.ipynb b/quiver-plots.ipynb new file mode 100644 index 000000000..539a844f5 --- /dev/null +++ b/quiver-plots.ipynb @@ -0,0 +1,152 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "463fa082", + "metadata": {}, + "source": [ + "Quiver plots can be made using a [figure factory](/python/figure-factories/) as detailed in this page.\n", + "\n", + "#### Basic Quiver Plot" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "0b13e9e2", + "metadata": {}, + "outputs": [], + "source": [ + "import plotly.figure_factory as ff\n", + "\n", + "import numpy as np\n", + "\n", + "x,y = np.meshgrid(np.arange(0, 2, .2), np.arange(0, 2, .2))\n", + "u = np.cos(x)*y\n", + "v = np.sin(x)*y\n", + "\n", + "fig = ff.create_quiver(x, y, u, v)\n", + "fig.show()" + ] + }, + { + "cell_type": "markdown", + "id": "c9fc34a0", + "metadata": {}, + "source": [ + "#### Quiver Plot with Points" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "8980143c", + "metadata": {}, + "outputs": [], + "source": [ + "import plotly.figure_factory as ff\n", + "import plotly.graph_objects as go\n", + "\n", + "import numpy as np\n", + "\n", + "x,y = np.meshgrid(np.arange(-2, 2, .2),\n", + " np.arange(-2, 2, .25))\n", + "z = x*np.exp(-x**2 - y**2)\n", + "v, u = np.gradient(z, .2, .2)\n", + "\n", + "# Create quiver figure\n", + "fig = ff.create_quiver(x, y, u, v,\n", + " scale=.25,\n", + " arrow_scale=.4,\n", + " name='quiver',\n", + " line_width=1)\n", + "\n", + "# Add points to figure\n", + "fig.add_trace(go.Scatter(x=[-.7, .75], y=[0,0],\n", + " mode='markers',\n", + " marker_size=12,\n", + " name='points'))\n", + "\n", + "fig.show()" + ] + }, + { + "cell_type": "markdown", + "id": "3476ff96", + "metadata": {}, + "source": [ + "#### See also\n", + "\n", + "[Cone plot](/python/cone-plot) for the 3D equivalent of quiver plots.\n", + "\n", + "#### Reference\n", + "\n", + "For more info on `ff.create_quiver()`, see the [full function reference](https://plotly.com/python-api-reference/generated/plotly.figure_factory.create_quiver.html)" + ] + }, + { + "cell_type": "markdown", + "id": "0d47b536", + "metadata": {}, + "source": [ + "### What About Dash?\n", + "\n", + "[Dash](https://dash.plot.ly/) is an open-source framework for building analytical applications, with no Javascript required, and it is tightly integrated with the Plotly graphing library.\n", + "\n", + "Learn about how to install Dash at https://dash.plot.ly/installation.\n", + "\n", + "Everywhere in this page that you see `fig.show()`, you can display the same figure in a Dash application by passing it to the `figure` argument of the [`Graph` component](https://dash.plot.ly/dash-core-components/graph) from the built-in `dash_core_components` package like this:\n", + "\n", + "```python\n", + "import plotly.graph_objects as go # or plotly.express as px\n", + "fig = go.Figure() # or any Plotly Express function e.g. px.bar(...)\n", + "# fig.add_trace( ... )\n", + "# fig.update_layout( ... )\n", + "\n", + "from dash import Dash, dcc, html\n", + "\n", + "app = Dash()\n", + "app.layout = html.Div([\n", + " dcc.Graph(figure=fig)\n", + "])\n", + "\n", + "app.run_server(debug=True, use_reloader=False) # Turn off reloader if inside Jupyter\n", + "```" + ] + } + ], + "metadata": { + "jupytext": { + "notebook_metadata_filter": "all" + }, + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "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.7.3" + }, + "plotly": { + "description": "How to make a quiver plot in Python. A quiver plot displays velocity vectors a arrows.", + "display_as": "scientific", + "language": "python", + "layout": "base", + "name": "Quiver Plots", + "order": 10, + "permalink": "python/quiver-plots/", + "thumbnail": "thumbnail/quiver-plot.jpg" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/radar-chart.ipynb b/radar-chart.ipynb new file mode 100644 index 000000000..4c6a05cef --- /dev/null +++ b/radar-chart.ipynb @@ -0,0 +1,221 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "4c856dcd", + "metadata": {}, + "source": [ + "A [Radar Chart](https://en.wikipedia.org/wiki/Radar_chart) (also known as a spider plot or star plot) displays multivariate data in the form of a two-dimensional chart of quantitative variables represented on axes originating from the center. The relative position and angle of the axes is typically uninformative. It is equivalent to a [parallel coordinates plot](/python/parallel-coordinates-plot/) with the axes arranged radially.\n", + "\n", + "For a Radar Chart, use a [polar chart](/python/polar-chart/) with categorical angular variables, with `px.line_polar`, or with `go.Scatterpolar`. See [more examples of polar charts](/python/polar-chart/).\n", + "\n", + "#### Radar Chart with Plotly Express\n", + "\n", + "[Plotly Express](/python/plotly-express/) is the easy-to-use, high-level interface to Plotly, which [operates on a variety of types of data](/python/px-arguments/) and produces [easy-to-style figures](/python/styling-plotly-express/).\n", + "\n", + "Use `line_close=True` for closed lines." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "306c1ddd", + "metadata": {}, + "outputs": [], + "source": [ + "import plotly.express as px\n", + "import pandas as pd\n", + "df = pd.DataFrame(dict(\n", + " r=[1, 5, 2, 2, 3],\n", + " theta=['processing cost','mechanical properties','chemical stability',\n", + " 'thermal stability', 'device integration']))\n", + "fig = px.line_polar(df, r='r', theta='theta', line_close=True)\n", + "fig.show()" + ] + }, + { + "cell_type": "markdown", + "id": "d4232aa9", + "metadata": {}, + "source": [ + "For a filled line in a Radar Chart, update the figure created with `px.line_polar` with `fig.update_traces`." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "cd45aa2e", + "metadata": {}, + "outputs": [], + "source": [ + "import plotly.express as px\n", + "import pandas as pd\n", + "df = pd.DataFrame(dict(\n", + " r=[1, 5, 2, 2, 3],\n", + " theta=['processing cost','mechanical properties','chemical stability',\n", + " 'thermal stability', 'device integration']))\n", + "fig = px.line_polar(df, r='r', theta='theta', line_close=True)\n", + "fig.update_traces(fill='toself')\n", + "fig.show()" + ] + }, + { + "cell_type": "markdown", + "id": "44b9907a", + "metadata": {}, + "source": [ + "### Basic Radar Chart with go.Scatterpolar" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "da535dc6", + "metadata": {}, + "outputs": [], + "source": [ + "import plotly.graph_objects as go\n", + "\n", + "fig = go.Figure(data=go.Scatterpolar(\n", + " r=[1, 5, 2, 2, 3],\n", + " theta=['processing cost','mechanical properties','chemical stability', 'thermal stability',\n", + " 'device integration'],\n", + " fill='toself'\n", + "))\n", + "\n", + "fig.update_layout(\n", + " polar=dict(\n", + " radialaxis=dict(\n", + " visible=True\n", + " ),\n", + " ),\n", + " showlegend=False\n", + ")\n", + "\n", + "fig.show()" + ] + }, + { + "cell_type": "markdown", + "id": "6ed7af2b", + "metadata": {}, + "source": [ + "#### Multiple Trace Radar Chart" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "58ddf8c1", + "metadata": {}, + "outputs": [], + "source": [ + "import plotly.graph_objects as go\n", + "\n", + "categories = ['processing cost','mechanical properties','chemical stability',\n", + " 'thermal stability', 'device integration']\n", + "\n", + "fig = go.Figure()\n", + "\n", + "fig.add_trace(go.Scatterpolar(\n", + " r=[1, 5, 2, 2, 3],\n", + " theta=categories,\n", + " fill='toself',\n", + " name='Product A'\n", + "))\n", + "fig.add_trace(go.Scatterpolar(\n", + " r=[4, 3, 2.5, 1, 2],\n", + " theta=categories,\n", + " fill='toself',\n", + " name='Product B'\n", + "))\n", + "\n", + "fig.update_layout(\n", + " polar=dict(\n", + " radialaxis=dict(\n", + " visible=True,\n", + " range=[0, 5]\n", + " )),\n", + " showlegend=False\n", + ")\n", + "\n", + "fig.show()" + ] + }, + { + "cell_type": "markdown", + "id": "5143beea", + "metadata": {}, + "source": [ + "#### Reference\n", + "\n", + "See [function reference for `px.(line_polar)`](https://plotly.com/python-api-reference/generated/plotly.express.line_polar) or https://plotly.com/python/reference/scatterpolar/ for more information and chart attribute options!\n" + ] + }, + { + "cell_type": "markdown", + "id": "382566f9", + "metadata": {}, + "source": [ + "### What About Dash?\n", + "\n", + "[Dash](https://dash.plot.ly/) is an open-source framework for building analytical applications, with no Javascript required, and it is tightly integrated with the Plotly graphing library.\n", + "\n", + "Learn about how to install Dash at https://dash.plot.ly/installation.\n", + "\n", + "Everywhere in this page that you see `fig.show()`, you can display the same figure in a Dash application by passing it to the `figure` argument of the [`Graph` component](https://dash.plot.ly/dash-core-components/graph) from the built-in `dash_core_components` package like this:\n", + "\n", + "```python\n", + "import plotly.graph_objects as go # or plotly.express as px\n", + "fig = go.Figure() # or any Plotly Express function e.g. px.bar(...)\n", + "# fig.add_trace( ... )\n", + "# fig.update_layout( ... )\n", + "\n", + "from dash import Dash, dcc, html\n", + "\n", + "app = Dash()\n", + "app.layout = html.Div([\n", + " dcc.Graph(figure=fig)\n", + "])\n", + "\n", + "app.run_server(debug=True, use_reloader=False) # Turn off reloader if inside Jupyter\n", + "```" + ] + } + ], + "metadata": { + "jupytext": { + "notebook_metadata_filter": "all" + }, + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "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.6.7" + }, + "plotly": { + "description": "How to make radar charts in Python with Plotly.", + "display_as": "scientific", + "language": "python", + "layout": "base", + "name": "Radar Charts", + "order": 17, + "page_type": "u-guide", + "permalink": "python/radar-chart/", + "thumbnail": "thumbnail/radar.gif" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/random-walk.ipynb b/random-walk.ipynb new file mode 100644 index 000000000..54218a9f1 --- /dev/null +++ b/random-walk.ipynb @@ -0,0 +1,248 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "c4f40474", + "metadata": {}, + "source": [ + "A [random walk](https://en.wikipedia.org/wiki/Random_walk) can be thought of as a random process in which a token or a marker is randomly moved around some space, that is, a space with a metric used to compute distance. It is more commonly conceptualized in one dimension ($\\mathbb{Z}$), two dimensions ($\\mathbb{Z}^2$) or three dimensions ($\\mathbb{Z}^3$) in Cartesian space, where $\\mathbb{Z}$ represents the set of integers. In the visualizations below, we will be using [scatter plots](https://plotly.com/python/line-and-scatter/) as well as a colorscale to denote the time sequence of the walk.\n", + "\n", + "#### Random Walk in 1D\n", + "\n", + "The jitter in the data points along the x and y axes are meant to illuminate where the points are being drawn and what the tendency of the random walk is." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "5ef1503c", + "metadata": {}, + "outputs": [], + "source": [ + "import plotly.graph_objects as go\n", + "import numpy as np\n", + "np.random.seed(1)\n", + "\n", + "l = 100\n", + "steps = np.random.choice([-1, 1], size=l) + 0.05 * np.random.randn(l) # l steps\n", + "position = np.cumsum(steps) # integrate the position by summing steps values\n", + "y = 0.05 * np.random.randn(l)\n", + "\n", + "fig = go.Figure(data=go.Scatter(\n", + " x=position,\n", + " y=y,\n", + " mode='markers',\n", + " name='Random Walk in 1D',\n", + " marker=dict(\n", + " color=np.arange(l),\n", + " size=7,\n", + " colorscale='Reds',\n", + " showscale=True,\n", + " )\n", + "))\n", + "\n", + "fig.update_layout(yaxis_range=[-1, 1])\n", + "fig.show()" + ] + }, + { + "cell_type": "markdown", + "id": "dbc9227f", + "metadata": {}, + "source": [ + "#### Random Walk in 2D" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "7fc1e8c1", + "metadata": {}, + "outputs": [], + "source": [ + "import plotly.graph_objects as go\n", + "import numpy as np\n", + "\n", + "l = 1000\n", + "x_steps = np.random.choice([-1, 1], size=l) + 0.2 * np.random.randn(l) # l steps\n", + "y_steps = np.random.choice([-1, 1], size=l) + 0.2 * np.random.randn(l) # l steps\n", + "x_position = np.cumsum(x_steps) # integrate the position by summing steps values\n", + "y_position = np.cumsum(y_steps) # integrate the position by summing steps values\n", + "\n", + "fig = go.Figure(data=go.Scatter(\n", + " x=x_position,\n", + " y=y_position,\n", + " mode='markers',\n", + " name='Random Walk',\n", + " marker=dict(\n", + " color=np.arange(l),\n", + " size=8,\n", + " colorscale='Greens',\n", + " showscale=True\n", + " )\n", + "))\n", + "\n", + "fig.show()" + ] + }, + { + "cell_type": "markdown", + "id": "623e7a83", + "metadata": {}, + "source": [ + "#### Random walk and diffusion\n", + "\n", + "In the two following charts we show the link between random walks and diffusion. We compute a large number `N` of random walks representing for examples molecules in a small drop of chemical. While all trajectories start at 0, after some time the spatial distribution of points is a Gaussian distribution. Also, the average distance to the origin grows as $\\sqrt(t)$." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "768d0107", + "metadata": {}, + "outputs": [], + "source": [ + "import plotly.graph_objects as go\n", + "import numpy as np\n", + "\n", + "l = 1000\n", + "N = 10000\n", + "steps = np.random.choice([-1, 1], size=(N, l)) + 0.05 * np.random.standard_normal((N, l)) # l steps\n", + "position = np.cumsum(steps, axis=1) # integrate all positions by summing steps values along time axis\n", + "\n", + "fig = go.Figure(data=go.Histogram(x=position[:, -1])) # positions at final time step\n", + "fig.show()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "56b35cd9", + "metadata": {}, + "outputs": [], + "source": [ + "import plotly.graph_objects as go\n", + "from plotly.subplots import make_subplots\n", + "import numpy as np\n", + "\n", + "l = 1000\n", + "N = 10000\n", + "t = np.arange(l)\n", + "steps = np.random.choice([-1, 1], size=(N, l)) + 0.05 * np.random.standard_normal((N, l)) # l steps\n", + "position = np.cumsum(steps, axis=1) # integrate the position by summing steps values\n", + "average_distance = np.std(position, axis=0) # average distance\n", + "\n", + "fig = make_subplots(1, 2)\n", + "fig.add_trace(go.Scatter(x=t, y=average_distance, name='mean distance'), 1, 1)\n", + "fig.add_trace(go.Scatter(x=t, y=average_distance**2, name='mean squared distance'), 1, 2)\n", + "fig.update_xaxes(title_text='$t$')\n", + "fig.update_yaxes(title_text='$l$', col=1)\n", + "fig.update_yaxes(title_text='$l^2$', col=2)\n", + "fig.update_layout(showlegend=False)\n", + "fig.show()" + ] + }, + { + "cell_type": "markdown", + "id": "a3443f75", + "metadata": {}, + "source": [ + "#### Advanced Tip\n", + "\n", + "We can formally think of a 1D random walk as a point jumping along the integer number line. Let $Z_i$ be a random variable that takes on the values +1 and -1. Let this random variable represent the steps we take in the random walk in 1D (where +1 means right and -1 means left). Also, as with the above visualizations, let us assume that the probability of moving left and right is just $\\frac{1}{2}$. Then, consider the sum\n", + "\n", + "$$\n", + "\\begin{align*}\n", + "S_n = \\sum_{i=0}^{n}{Z_i}\n", + "\\end{align*}\n", + "$$\n", + "\n", + "where S_n represents the point that the random walk ends up on after n steps have been taken.\n", + "\n", + "To find the `expected value` of $S_n$, we can compute it directly. Since each $Z_i$ is independent, we have\n", + "\n", + "$$\n", + "\\begin{align*}\n", + "\\mathbb{E}(S_n) = \\sum_{i=0}^{n}{\\mathbb{E}(Z_i)}\n", + "\\end{align*}\n", + "$$\n", + "\n", + "but since $Z_i$ takes on the values +1 and -1 then\n", + "\n", + "$$\n", + "\\begin{align*}\n", + "\\mathbb{E}(Z_i) = 1 \\cdot P(Z_i=1) + -1 \\cdot P(Z_i=-1) = \\frac{1}{2} - \\frac{1}{2} = 0\n", + "\\end{align*}\n", + "$$\n", + "\n", + "Therefore, we expect our random walk to hover around $0$ regardless of how many steps we take in our walk.\n" + ] + }, + { + "cell_type": "markdown", + "id": "35dc8346", + "metadata": {}, + "source": [ + "### What About Dash?\n", + "\n", + "[Dash](https://dash.plot.ly/) is an open-source framework for building analytical applications, with no Javascript required, and it is tightly integrated with the Plotly graphing library.\n", + "\n", + "Learn about how to install Dash at https://dash.plot.ly/installation.\n", + "\n", + "Everywhere in this page that you see `fig.show()`, you can display the same figure in a Dash application by passing it to the `figure` argument of the [`Graph` component](https://dash.plot.ly/dash-core-components/graph) from the built-in `dash_core_components` package like this:\n", + "\n", + "```python\n", + "import plotly.graph_objects as go # or plotly.express as px\n", + "fig = go.Figure() # or any Plotly Express function e.g. px.bar(...)\n", + "# fig.add_trace( ... )\n", + "# fig.update_layout( ... )\n", + "\n", + "from dash import Dash, dcc, html\n", + "\n", + "app = Dash()\n", + "app.layout = html.Div([\n", + " dcc.Graph(figure=fig)\n", + "])\n", + "\n", + "app.run_server(debug=True, use_reloader=False) # Turn off reloader if inside Jupyter\n", + "```" + ] + } + ], + "metadata": { + "jupytext": { + "notebook_metadata_filter": "all" + }, + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "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.6.7" + }, + "plotly": { + "description": "Learn how to use Python to make a Random Walk", + "display_as": "advanced_opt", + "has_thumbnail": false, + "language": "python", + "layout": "base", + "name": "Random Walk", + "order": 2, + "page_type": "example_index", + "permalink": "python/random-walk/", + "thumbnail": "/images/static-image" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/range-slider.ipynb b/range-slider.ipynb new file mode 100644 index 000000000..fbcdf41aa --- /dev/null +++ b/range-slider.ipynb @@ -0,0 +1,429 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "51d5a088", + "metadata": {}, + "source": [ + "#### Basic Range Slider and Range Selectors" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "07195771", + "metadata": {}, + "outputs": [], + "source": [ + "import plotly.graph_objects as go\n", + "\n", + "import pandas as pd\n", + "\n", + "# Load data\n", + "df = pd.read_csv(\n", + " \"https://raw.githubusercontent.com/plotly/datasets/master/finance-charts-apple.csv\")\n", + "df.columns = [col.replace(\"AAPL.\", \"\") for col in df.columns]\n", + "\n", + "# Create figure\n", + "fig = go.Figure()\n", + "\n", + "fig.add_trace(\n", + " go.Scatter(x=list(df.Date), y=list(df.High)))\n", + "\n", + "# Set title\n", + "fig.update_layout(\n", + " title_text=\"Time series with range slider and selectors\"\n", + ")\n", + "\n", + "# Add range slider\n", + "fig.update_layout(\n", + " xaxis=dict(\n", + " rangeselector=dict(\n", + " buttons=list([\n", + " dict(count=1,\n", + " label=\"1m\",\n", + " step=\"month\",\n", + " stepmode=\"backward\"),\n", + " dict(count=6,\n", + " label=\"6m\",\n", + " step=\"month\",\n", + " stepmode=\"backward\"),\n", + " dict(count=1,\n", + " label=\"YTD\",\n", + " step=\"year\",\n", + " stepmode=\"todate\"),\n", + " dict(count=1,\n", + " label=\"1y\",\n", + " step=\"year\",\n", + " stepmode=\"backward\"),\n", + " dict(step=\"all\")\n", + " ])\n", + " ),\n", + " rangeslider=dict(\n", + " visible=True\n", + " ),\n", + " type=\"date\"\n", + " )\n", + ")\n", + "\n", + "fig.show()" + ] + }, + { + "cell_type": "markdown", + "id": "4428e6a4", + "metadata": {}, + "source": [ + "#### Range Slider with Vertically Stacked Subplots" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "564e98bc", + "metadata": {}, + "outputs": [], + "source": [ + "import plotly.graph_objects as go\n", + "\n", + "# Create figure\n", + "fig = go.Figure()\n", + "\n", + "# Add traces\n", + "fig.add_trace(go.Scatter(\n", + " x=[\"2013-01-15\", \"2013-01-29\", \"2013-02-26\", \"2013-04-19\", \"2013-07-02\",\n", + " \"2013-08-27\",\n", + " \"2013-10-22\", \"2014-01-20\", \"2014-05-05\", \"2014-07-01\", \"2015-02-09\",\n", + " \"2015-04-13\",\n", + " \"2015-05-13\", \"2015-06-08\", \"2015-08-05\", \"2016-02-25\"],\n", + " y=[\"8\", \"3\", \"2\", \"10\", \"5\", \"5\", \"6\", \"8\", \"3\", \"3\", \"7\", \"5\", \"10\", \"10\", \"9\",\n", + " \"14\"],\n", + " name=\"var0\",\n", + " text=[\"8\", \"3\", \"2\", \"10\", \"5\", \"5\", \"6\", \"8\", \"3\", \"3\", \"7\", \"5\", \"10\", \"10\", \"9\",\n", + " \"14\"],\n", + " yaxis=\"y\",\n", + "))\n", + "\n", + "fig.add_trace(go.Scatter(\n", + " x=[\"2015-04-13\", \"2015-05-13\", \"2015-06-08\", \"2015-08-05\", \"2016-02-25\"],\n", + " y=[\"53.0\", \"69.0\", \"89.0\", \"41.0\", \"41.0\"],\n", + " name=\"var1\",\n", + " text=[\"53.0\", \"69.0\", \"89.0\", \"41.0\", \"41.0\"],\n", + " yaxis=\"y2\",\n", + "))\n", + "\n", + "fig.add_trace(go.Scatter(\n", + " x=[\"2013-01-29\", \"2013-02-26\", \"2013-04-19\", \"2013-07-02\", \"2013-08-27\",\n", + " \"2013-10-22\",\n", + " \"2014-01-20\", \"2014-04-09\", \"2014-05-05\", \"2014-07-01\", \"2014-09-30\",\n", + " \"2015-02-09\",\n", + " \"2015-04-13\", \"2015-06-08\", \"2016-02-25\"],\n", + " y=[\"9.6\", \"4.6\", \"2.7\", \"8.3\", \"18\", \"7.3\", \"3\", \"7.5\", \"1.0\", \"0.5\", \"2.8\", \"9.2\",\n", + " \"13\", \"5.8\", \"6.9\"],\n", + " name=\"var2\",\n", + " text=[\"9.6\", \"4.6\", \"2.7\", \"8.3\", \"18\", \"7.3\", \"3\", \"7.5\", \"1.0\", \"0.5\", \"2.8\",\n", + " \"9.2\",\n", + " \"13\", \"5.8\", \"6.9\"],\n", + " yaxis=\"y3\",\n", + "))\n", + "\n", + "fig.add_trace(go.Scatter(\n", + " x=[\"2013-01-29\", \"2013-02-26\", \"2013-04-19\", \"2013-07-02\", \"2013-08-27\",\n", + " \"2013-10-22\",\n", + " \"2014-01-20\", \"2014-04-09\", \"2014-05-05\", \"2014-07-01\", \"2014-09-30\",\n", + " \"2015-02-09\",\n", + " \"2015-04-13\", \"2015-06-08\", \"2016-02-25\"],\n", + " y=[\"6.9\", \"7.5\", \"7.3\", \"7.3\", \"6.9\", \"7.1\", \"8\", \"7.8\", \"7.4\", \"7.9\", \"7.9\", \"7.6\",\n", + " \"7.2\", \"7.2\", \"8.0\"],\n", + " name=\"var3\",\n", + " text=[\"6.9\", \"7.5\", \"7.3\", \"7.3\", \"6.9\", \"7.1\", \"8\", \"7.8\", \"7.4\", \"7.9\", \"7.9\",\n", + " \"7.6\",\n", + " \"7.2\", \"7.2\", \"8.0\"],\n", + " yaxis=\"y4\",\n", + "))\n", + "\n", + "fig.add_trace(go.Scatter(\n", + " x=[\"2013-02-26\", \"2013-07-02\", \"2013-09-26\", \"2013-10-22\", \"2013-12-04\",\n", + " \"2014-01-02\",\n", + " \"2014-01-20\", \"2014-05-05\", \"2014-07-01\", \"2015-02-09\", \"2015-05-05\"],\n", + " y=[\"290\", \"1078\", \"263\", \"407\", \"660\", \"740\", \"33\", \"374\", \"95\", \"734\", \"3000\"],\n", + " name=\"var4\",\n", + " text=[\"290\", \"1078\", \"263\", \"407\", \"660\", \"740\", \"33\", \"374\", \"95\", \"734\", \"3000\"],\n", + " yaxis=\"y5\",\n", + "))\n", + "\n", + "# style all the traces\n", + "fig.update_traces(\n", + " hoverinfo=\"name+x+text\",\n", + " line={\"width\": 0.5},\n", + " marker={\"size\": 8},\n", + " mode=\"lines+markers\",\n", + " showlegend=False\n", + ")\n", + "\n", + "# Add annotations\n", + "fig.update_layout(\n", + " annotations=[\n", + " dict(\n", + " x=\"2013-06-01\",\n", + " y=0,\n", + " arrowcolor=\"rgba(63, 81, 181, 0.2)\",\n", + " arrowsize=0.3,\n", + " ax=0,\n", + " ay=30,\n", + " text=\"state1\",\n", + " xref=\"x\",\n", + " yanchor=\"bottom\",\n", + " yref=\"y\"\n", + " ),\n", + " dict(\n", + " x=\"2014-09-13\",\n", + " y=0,\n", + " arrowcolor=\"rgba(76, 175, 80, 0.1)\",\n", + " arrowsize=0.3,\n", + " ax=0,\n", + " ay=30,\n", + " text=\"state2\",\n", + " xref=\"x\",\n", + " yanchor=\"bottom\",\n", + " yref=\"y\"\n", + " )\n", + " ],\n", + ")\n", + "\n", + "# Add shapes\n", + "fig.update_layout(\n", + " shapes=[\n", + " dict(\n", + " fillcolor=\"rgba(63, 81, 181, 0.2)\",\n", + " line={\"width\": 0},\n", + " type=\"rect\",\n", + " x0=\"2013-01-15\",\n", + " x1=\"2013-10-17\",\n", + " xref=\"x\",\n", + " y0=0,\n", + " y1=0.95,\n", + " yref=\"paper\"\n", + " ),\n", + " dict(\n", + " fillcolor=\"rgba(76, 175, 80, 0.1)\",\n", + " line={\"width\": 0},\n", + " type=\"rect\",\n", + " x0=\"2013-10-22\",\n", + " x1=\"2015-08-05\",\n", + " xref=\"x\",\n", + " y0=0,\n", + " y1=0.95,\n", + " yref=\"paper\"\n", + " )\n", + " ]\n", + ")\n", + "\n", + "# Update axes\n", + "fig.update_layout(\n", + " xaxis=dict(\n", + " autorange=True,\n", + " range=[\"2012-10-31 18:36:37.3129\", \"2016-05-10 05:23:22.6871\"],\n", + " rangeslider=dict(\n", + " autorange=True,\n", + " range=[\"2012-10-31 18:36:37.3129\", \"2016-05-10 05:23:22.6871\"]\n", + " ),\n", + " type=\"date\"\n", + " ),\n", + " yaxis=dict(\n", + " anchor=\"x\",\n", + " autorange=True,\n", + " domain=[0, 0.2],\n", + " linecolor=\"#673ab7\",\n", + " mirror=True,\n", + " range=[-60.0858369099, 28.4406294707],\n", + " showline=True,\n", + " side=\"right\",\n", + " tickfont={\"color\": \"#673ab7\"},\n", + " tickmode=\"auto\",\n", + " ticks=\"\",\n", + " title=dict(\n", + " font=dict(\n", + " color=\"#673ab7\"\n", + " )\n", + " ),\n", + " type=\"linear\",\n", + " zeroline=False\n", + " ),\n", + " yaxis2=dict(\n", + " anchor=\"x\",\n", + " autorange=True,\n", + " domain=[0.2, 0.4],\n", + " linecolor=\"#E91E63\",\n", + " mirror=True,\n", + " range=[29.3787777032, 100.621222297],\n", + " showline=True,\n", + " side=\"right\",\n", + " tickfont={\"color\": \"#E91E63\"},\n", + " tickmode=\"auto\",\n", + " ticks=\"\",\n", + " title=dict(\n", + " font=dict(\n", + " color=\"#E91E63\"\n", + " )\n", + " ),\n", + " type=\"linear\",\n", + " zeroline=False\n", + " ),\n", + " yaxis3=dict(\n", + " anchor=\"x\",\n", + " autorange=True,\n", + " domain=[0.4, 0.6],\n", + " linecolor=\"#795548\",\n", + " mirror=True,\n", + " range=[-3.73690396239, 22.2369039624],\n", + " showline=True,\n", + " side=\"right\",\n", + " tickfont={\"color\": \"#795548\"},\n", + " tickmode=\"auto\",\n", + " ticks=\"\",\n", + " title=dict(\n", + " text=\"mg/L\",\n", + " font=dict(\n", + " color=\"#795548\"\n", + " )\n", + " ),\n", + " type=\"linear\",\n", + " zeroline=False\n", + " ),\n", + " yaxis4=dict(\n", + " anchor=\"x\",\n", + " autorange=True,\n", + " domain=[0.6, 0.8],\n", + " linecolor=\"#607d8b\",\n", + " mirror=True,\n", + " range=[6.63368032236, 8.26631967764],\n", + " showline=True,\n", + " side=\"right\",\n", + " tickfont={\"color\": \"#607d8b\"},\n", + " tickmode=\"auto\",\n", + " ticks=\"\",\n", + " title=dict(\n", + " text=\"mmol/L\",\n", + " font=dict(\n", + " color=\"#607d8b\"\n", + " )\n", + " ),\n", + " type=\"linear\",\n", + " zeroline=False\n", + " ),\n", + " yaxis5=dict(\n", + " anchor=\"x\",\n", + " autorange=True,\n", + " domain=[0.8, 1],\n", + " linecolor=\"#2196F3\",\n", + " mirror=True,\n", + " range=[-685.336803224, 3718.33680322],\n", + " showline=True,\n", + " side=\"right\",\n", + " tickfont={\"color\": \"#2196F3\"},\n", + " tickmode=\"auto\",\n", + " ticks=\"\",\n", + " title=dict(\n", + " text=\"mg/Kg\",\n", + " font=dict(\n", + " color=\"#2196F3\"\n", + " )\n", + " ),\n", + " type=\"linear\",\n", + " zeroline=False\n", + " )\n", + ")\n", + "\n", + "# Update layout\n", + "fig.update_layout(\n", + " dragmode=\"zoom\",\n", + " hovermode=\"x\",\n", + " legend=dict(traceorder=\"reversed\"),\n", + " height=600,\n", + " template=\"plotly_white\",\n", + " margin=dict(\n", + " t=100,\n", + " b=100\n", + " ),\n", + ")\n", + "\n", + "fig.show()" + ] + }, + { + "cell_type": "markdown", + "id": "4259cedf", + "metadata": {}, + "source": [ + "#### Reference\n", + "See https://plotly.com/python/reference/layout/xaxis/#layout-xaxis-rangeselector
and https://plotly.com/python/reference/layout/xaxis/#layout-xaxis-rangeslider
for more information and attribute options!\n" + ] + }, + { + "cell_type": "markdown", + "id": "501e39fd", + "metadata": {}, + "source": [ + "### What About Dash?\n", + "\n", + "[Dash](https://dash.plot.ly/) is an open-source framework for building analytical applications, with no Javascript required, and it is tightly integrated with the Plotly graphing library.\n", + "\n", + "Learn about how to install Dash at https://dash.plot.ly/installation.\n", + "\n", + "Everywhere in this page that you see `fig.show()`, you can display the same figure in a Dash application by passing it to the `figure` argument of the [`Graph` component](https://dash.plot.ly/dash-core-components/graph) from the built-in `dash_core_components` package like this:\n", + "\n", + "```python\n", + "import plotly.graph_objects as go # or plotly.express as px\n", + "fig = go.Figure() # or any Plotly Express function e.g. px.bar(...)\n", + "# fig.add_trace( ... )\n", + "# fig.update_layout( ... )\n", + "\n", + "from dash import Dash, dcc, html\n", + "\n", + "app = Dash()\n", + "app.layout = html.Div([\n", + " dcc.Graph(figure=fig)\n", + "])\n", + "\n", + "app.run_server(debug=True, use_reloader=False) # Turn off reloader if inside Jupyter\n", + "```" + ] + } + ], + "metadata": { + "jupytext": { + "notebook_metadata_filter": "all" + }, + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "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.7.2" + }, + "plotly": { + "description": "Now you can implement range sliders and selectors in your Plotly graphs purely with python!", + "display_as": "controls", + "language": "python", + "layout": "base", + "name": "Range Slider and Selector", + "order": 3, + "page_type": "example_index", + "permalink": "python/range-slider/", + "thumbnail": "thumbnail/sliders.jpg" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/raw.githubusercontent.com/plotly/datasets/master/mesh_dataset.txt b/raw.githubusercontent.com/plotly/datasets/master/mesh_dataset.txt new file mode 100644 index 000000000..6f5a588bc --- /dev/null +++ b/raw.githubusercontent.com/plotly/datasets/master/mesh_dataset.txt @@ -0,0 +1,100 @@ +0.689484 -0.014459 0.428751 +0.476483 0.162624 0.449488 +0.586770 0.102149 0.769481 +1.068768 0.678668 1.023915 +1.438060 0.683633 0.752397 +1.483567 0.140479 1.389489 +1.163844 0.125522 1.098527 +1.491083 0.317640 0.760376 +1.970356 0.389628 0.631279 +1.961689 0.144644 0.254462 +1.823355 0.309693 0.151225 +1.868743 0.171245 -0.160235 +1.437345 0.781093 -0.569670 +1.248269 0.429151 -0.795403 +1.108745 0.233132 -0.744953 +1.670774 -0.080785 -0.820339 +1.503391 -0.179939 -0.791839 +1.406028 0.025858 -0.967830 +1.971070 0.309369 -0.651420 +1.853437 0.658340 -0.661080 +1.361601 0.443953 -0.126488 +1.139926 1.295734 0.114152 +1.949556 1.032481 0.417303 +2.323108 0.665570 0.615544 +2.558936 0.227190 0.385185 +2.650508 0.817791 0.186861 +2.431305 1.189158 -0.085800 +2.026224 1.167509 -0.120976 +1.971259 0.499964 -0.035002 +1.168452 0.683057 -0.309909 +1.342775 0.672141 -0.208439 +1.129037 0.183479 -0.485355 +0.871781 0.069470 -0.734185 +0.855148 0.587134 -0.574637 +0.395625 0.606762 -1.004597 +1.051331 0.804046 -1.250838 +1.309558 0.672450 -1.174746 +1.876837 0.506276 -1.376205 +2.620561 0.556901 -1.528117 +2.508878 0.445584 -1.501028 +2.646833 0.160679 -1.605397 +2.448341 -0.003327 -1.875326 +1.610501 0.031129 -2.069439 +1.925691 -0.412504 -2.138976 +1.918400 -0.344678 -2.299420 +1.910517 -0.246417 -2.245133 +2.450736 -0.725501 -2.828320 +2.381144 -1.214003 -2.812268 +2.159600 -1.524586 -2.873087 +2.379856 -1.845657 -2.758474 +2.254824 -1.268736 -3.020816 +2.320000 -1.171255 -3.604322 +1.698536 -1.233225 -3.369023 +1.527831 -1.374688 -3.394583 +1.530377 -1.355630 -3.295790 +1.672040 -1.146433 -3.038195 +1.534793 -1.167195 -3.646896 +1.886021 -0.864813 -3.523450 +2.062097 -0.488551 -3.273344 +2.128729 0.153843 -3.081451 +2.057646 0.302593 -2.701675 +2.155564 0.568636 -2.718267 +2.698137 0.182249 -2.875416 +3.066121 0.562960 -2.909034 +3.925938 0.252966 -3.011479 +4.240422 -0.220912 -2.795740 +3.907132 -0.328587 -2.430246 +3.750647 -0.193345 -2.166711 +2.918651 -0.238123 -2.143549 +2.980647 0.261956 -2.218529 +2.992436 0.081961 -2.173249 +2.710340 -0.490590 -1.838208 +2.693541 -0.259896 -1.613949 +2.361263 -0.187197 -1.208869 +1.897547 -0.032457 -0.792615 +2.345557 0.049672 -1.164745 +2.007566 0.121710 -1.080368 +1.599818 -0.105825 -1.086920 +1.182395 -0.745416 -0.878417 +1.469907 -0.795117 -0.562867 +1.460792 -0.450440 -0.599801 +1.113180 -0.514758 -0.508419 +0.879555 -0.639992 -0.672436 +1.351152 -0.329654 -0.829762 +1.392628 -0.215191 -0.596751 +1.926062 -0.375350 -0.554818 +1.211311 -1.208597 -0.556275 +0.913065 -1.064432 -0.494078 +1.363192 -1.338370 -0.238343 +2.032102 -1.405084 -0.335621 +1.738637 -1.456743 0.028875 +1.249448 -1.299677 0.102270 +0.610179 -1.236958 0.315250 +0.853197 -0.482796 0.164532 +0.794531 -0.346327 -0.462431 +0.635271 -0.689043 -0.492473 +0.545627 -0.840409 -0.344744 +0.137978 -1.164410 -0.537114 +0.552325 -1.242295 -0.476395 +0.417952 -1.794645 -0.288381 diff --git a/renderers.ipynb b/renderers.ipynb new file mode 100644 index 000000000..6edbe72ce --- /dev/null +++ b/renderers.ipynb @@ -0,0 +1,448 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "62572586", + "metadata": {}, + "source": [ + "# Displaying Figures\n", + "\n", + "Plotly's Python graphing library, `plotly.py`, gives you a wide range of options for how and where to display your figures.\n", + "\n", + "In general, there are five different approaches you can take in order to display `plotly` figures:\n", + "\n", + " 1. Using the `renderers` framework in the context of a script or notebook (the main topic of this page)\n", + " 2. Using [Dash](https://dash.plot.ly) in a web app context\n", + " 3. Using a [`FigureWidget` rather than a `Figure`](https://plotly.com/python/figurewidget/) in an [`ipywidgets` context](https://ipywidgets.readthedocs.io/en/stable/)\n", + " 4. By [exporting to an HTML file](https://plotly.com/python/interactive-html-export/) and loading that file in a browser immediately or later\n", + " 5. By [rendering the figure to a static image file using Kaleido](https://plotly.com/python/static-image-export/) such as PNG, JPEG, SVG, PDF or EPS and loading the resulting file in any viewer\n", + "\n", + "Each of the first three approaches is discussed below.\n", + "\n", + "### Displaying Figures Using The `renderers` Framework\n", + "\n", + "The renderers framework is a flexible approach for displaying `plotly.py` figures in a variety of contexts. To display a figure using the renderers framework, you call the `.show()` method on a graph object figure, or pass the figure to the `plotly.io.show` function. With either approach, `plotly.py` will display the figure using the current default renderer(s)." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "f6ce8be5", + "metadata": {}, + "outputs": [], + "source": [ + "import plotly.graph_objects as go\n", + "fig = go.Figure(\n", + " data=[go.Bar(y=[2, 1, 3])],\n", + " layout_title_text=\"A Figure Displayed with fig.show()\"\n", + ")\n", + "fig.show()" + ] + }, + { + "cell_type": "markdown", + "id": "c2647134", + "metadata": {}, + "source": [ + "In most situations, you can omit the call to `.show()` and allow the figure to display itself." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "b1b6dc87", + "metadata": {}, + "outputs": [], + "source": [ + "import plotly.graph_objects as go\n", + "fig = go.Figure(\n", + " data=[go.Bar(y=[2, 1, 3])],\n", + " layout_title_text=\"A Figure Displaying Itself\"\n", + ")\n", + "fig" + ] + }, + { + "cell_type": "markdown", + "id": "f2c7d9b6", + "metadata": {}, + "source": [ + "> To be precise, figures will display themselves using the current default renderer when the two following conditions are true. First, the last expression in a cell must evaluate to a figure. Second, `plotly.py` must be running from within an `IPython` kernel.\n", + "\n", + "**In many contexts, an appropriate renderer will be chosen automatically and you will not need to perform any additional configuration.** These contexts include the classic [Jupyter Notebook](https://jupyter.org/), [JupyterLab](https://jupyterlab.readthedocs.io/en/stable/), [Visual Studio Code notebooks](https://code.visualstudio.com/docs/python/jupyter-support), [Google Colaboratory](https://colab.research.google.com/notebooks/intro.ipynb), [Kaggle](https://www.kaggle.com/kernels) notebooks, [Azure](https://notebooks.azure.com/) notebooks, and the [Python interactive shell](https://www.python.org/shell/).\n", + "\n", + "Additional contexts are supported by choosing a compatible renderer including the [IPython console](https://docs.spyder-ide.org/ipythonconsole.html), [QtConsole](https://qtconsole.readthedocs.io/en/stable/), [Spyder](https://www.spyder-ide.org/), and more.\n", + "\n", + "Next, we will show how to configure the default renderer. After that, we will describe all of the built-in renderers and discuss why you might choose to use each one.\n", + "\n", + "> Note: The `renderers` framework is a generalization of the `plotly.offline.iplot` and `plotly.offline.plot` functions that were the recommended way to display figures prior to `plotly.py` version 4. These functions have been reimplemented using the `renderers` framework and are still supported for backward compatibility, but they will not be discussed here." + ] + }, + { + "cell_type": "markdown", + "id": "b81b4667", + "metadata": {}, + "source": [ + "#### Setting The Default Renderer\n", + "The current and available renderers are configured using the `plotly.io.renderers` configuration object. Display this object to see the current default renderer and the list of all available renderers." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "03bdcd63", + "metadata": {}, + "outputs": [], + "source": [ + "import plotly.io as pio\n", + "pio.renderers" + ] + }, + { + "cell_type": "markdown", + "id": "d8783b41", + "metadata": {}, + "source": [ + "The default renderer that you see when you display `pio.renderers` might be different than what is shown here. This is because `plotly.py` attempts to autodetect an appropriate renderer at startup. You can change the default renderer by assigning the name of an available renderer to the `pio.renderers.default` property. For example, to switch to the `'browser'` renderer, which opens figures in a tab of the default web browser, you would run the following.\n", + "\n", + "> Note: Default renderers persist for the duration of a single session, but they do not persist across sessions. If you are working in an `IPython` kernel, this means that default renderers will persist for the life of the kernel, but they will not persist across kernel restarts." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "8e608075", + "metadata": {}, + "outputs": [], + "source": [ + "import plotly.io as pio\n", + "pio.renderers.default = \"browser\"" + ] + }, + { + "cell_type": "markdown", + "id": "87b6789c", + "metadata": {}, + "source": [ + "It is also possible to set the default renderer using a system environment variable. At startup, `plotly.py` checks for the existence of an environment variable named `PLOTLY_RENDERER`. If this environment variable is set to the name of an available renderer, this renderer is set as the default.\n", + "\n", + "#### Overriding The Default Renderer\n", + "It is also possible to override the default renderer temporarily by passing the name of an available renderer as the `renderer` keyword argument to the `show()` method. Here is an example of displaying a figure using the `svg` renderer (described below) without changing the default renderer." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "b2734d0d", + "metadata": {}, + "outputs": [], + "source": [ + "import plotly.graph_objects as go\n", + "fig = go.Figure(\n", + " data=[go.Bar(y=[2, 1, 3])],\n", + " layout_title_text=\"A Figure Displayed with the 'svg' Renderer\"\n", + ")\n", + "fig.show(renderer=\"svg\")" + ] + }, + { + "cell_type": "markdown", + "id": "f4c10f8e", + "metadata": {}, + "source": [ + "#### Built-in Renderers\n", + "In this section, we will describe the built-in renderers so that you can choose the one(s) that best suit your needs.\n", + "\n", + "##### Interactive Renderers\n", + "Interactive renderers display figures using the plotly.js JavaScript library and are fully interactive, supporting pan, zoom, hover tooltips, etc.\n", + "\n", + "###### `notebook`\n", + "This renderer is intended for use in the classic [Jupyter Notebook](https://jupyter.org/install.html) (not JupyterLab). The full plotly.js JavaScript library bundle is added to the notebook the first time a figure is rendered, so this renderer will work without an Internet connection.\n", + "\n", + "This renderer is a good choice for notebooks that will be exported to HTML files (Either using [nbconvert](https://nbconvert.readthedocs.io/en/latest/) or the \"Download as HTML\" menu action) because the exported HTML files will work without an Internet connection.\n", + "\n", + "> Note: Adding the plotly.js bundle to the notebook adds a few megabytes to the notebook size. If you can count on always having an Internet connection, you may want to consider using the `notebook_connected` renderer if notebook size is a constraint.\n", + "\n", + "###### `notebook_connected`\n", + "This renderer is the same as `notebook` renderer, except the plotly.js JavaScript library bundle is loaded from an online CDN location. This saves a few megabytes in notebook size, but an Internet connection is required in order to display figures that are rendered this way.\n", + "\n", + "This renderer is a good choice for notebooks that will be shared with [nbviewer](https://nbviewer.jupyter.org/) since users must have an active Internet connection to access nbviewer in the first place.\n", + "\n", + "###### `kaggle` and `azure`\n", + "These are aliases for `notebook_connected` because this renderer is a good choice for use with [Kaggle kernels](https://www.kaggle.com/docs/notebooks) and [Azure Notebooks](https://notebooks.azure.com/).\n", + "\n", + "###### `colab`\n", + "This is a custom renderer for use with [Google Colab](https://colab.research.google.com).\n", + "\n", + "###### `browser`\n", + "This renderer will open a figure in a browser tab using the default web browser. This renderer can only be used when the Python kernel is running locally on the same machine as the web browser, so it is not compatible with Jupyter Hub or online notebook services.\n", + "\n", + "> Implementation Note 1: In this context, the \"default browser\" is the browser that is chosen by the Python [`webbrowser`](https://docs.python.org/3.7/library/webbrowser.html) module.\n", + "\n", + "> Implementation Note 2: The `browser` renderer works by setting up a single use local webserver on a local port. Since the webserver is shut down as soon as the figure is served to the browser, the figure will not be restored if the browser is refreshed.\n", + "\n", + "###### `firefox`, `chrome`, and `chromium`\n", + "These renderers are the same as the `browser` renderer, but they force the use of a particular browser.\n", + "\n", + "###### `iframe` and `iframe_connected`\n", + "These renderers write figures out as standalone HTML files and then display [`iframe`](https://www.w3schools.com/html/html_iframe.asp) elements that reference these HTML files. The `iframe` renderer will include the plotly.js JavaScript bundle in each HTML file that is written, while the `iframe_connected` renderer includes only a reference to an online CDN location from which to load plotly.js. Consequently, the `iframe_connected` renderer outputs files that are smaller than the `iframe` renderer, but it requires an Internet connection while the `iframe` renderer can operate offline.\n", + "\n", + "This renderer may be useful when working with notebooks than contain lots of large figures. When using the `notebook` or `notebook_connected` renderer, all of the data for all of the figures in a notebook are stored inline in the notebook itself. If this would result in a prohibitively large notebook size, an `iframe` or `iframe_connected` renderer could be used instead. With the `iframe` renderers, the figure data are stored in the individual HTML files rather than in the notebook itself, resulting in a smaller notebook size.\n", + "\n", + "> Implementation Note: The HTML files written by the `iframe` renderers are stored in a subdirectory named `iframe_figures`. The HTML files are given names based on the execution number of the notebook cell that produced the figure. This means that each time a notebook kernel is restarted, any prior HTML files will be overwritten. This also means that you should not store multiple notebooks using an `iframe` renderer in the same directory, because this could result in figures from one notebook overwriting figures from another notebook." + ] + }, + { + "cell_type": "markdown", + "id": "fd1cd3de", + "metadata": {}, + "source": [ + "###### `plotly_mimetype`\n", + "\n", + "The `plotly_mimetype` renderer creates a specification of the figure (called a MIME-type bundle), and requests that the current user interface displays it. User interfaces that support this renderer include [JupyterLab](https://jupyterlab.readthedocs.io/en/stable/), [nteract](https://nteract.io/), and the Visual Studio Code [notebook interface](https://code.visualstudio.com/docs/python/jupyter-support).\n", + "\n", + "###### `jupyterlab`, `nteract`, and `vscode`\n", + "These are aliases for `plotly_mimetype` since this renderer is a good choice when working in JupyterLab, nteract, and the Visual Studio Code notebook interface. Note that in VSCode Notebooks, the version of Plotly.js that is used to render is provided by the [vscode-python extension](https://code.visualstudio.com/docs/languages/python) and often trails the latest version by several weeks, so the latest features of `plotly` may not be available in VSCode right away. The situation is similar for Nteract.\n", + "\n", + "##### Static Image Renderers\n", + "A set of renderers is provided for displaying figures as static images. See the [Static Image Export](https://plot.ly/python/static-image-export/) page for more information on getting set up.\n", + "\n", + "###### `png`, `jpeg`, and `svg`\n", + "These renderers display figures as static `.png`, `.jpeg`, and `.svg` files, respectively. These renderers are useful for user interfaces that do not support inline HTML output, but do support inline static images. Examples include the [QtConsole](https://qtconsole.readthedocs.io/en/stable/), [Spyder](https://www.spyder-ide.org/), and the PyCharm [notebook interface](https://www.jetbrains.com/help/pycharm/jupyter-notebook-support.html)." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "784ec3b1", + "metadata": {}, + "outputs": [], + "source": [ + "import plotly.graph_objects as go\n", + "fig = go.Figure(\n", + " data=[go.Bar(y=[2, 1, 3])],\n", + " layout_title_text=\"A Figure Displayed with the 'png' Renderer\"\n", + ")\n", + "fig.show(renderer=\"png\")" + ] + }, + { + "cell_type": "markdown", + "id": "59c8c9d5", + "metadata": {}, + "source": [ + "###### PDF\n", + "This renderer displays figures as static PDF files. This is especially useful for notebooks that will be exported to PDF files using the LaTeX export capabilities of [`nbconvert`](https://nbconvert.readthedocs.io/en/latest/).\n", + "\n", + "##### Other Miscellaneous Renderers\n", + "\n", + "###### JSON\n", + "In editors that support it (JupyterLab, nteract, and the Visual Studio Code notebook interface), this renderer displays the JSON representation of a figure in a collapsible interactive tree structure. This can be very useful for examining the structure of complex figures.\n", + "\n", + "##### Multiple Renderers\n", + "You can specify that multiple renderers should be used by joining their names on `\"+\"` characters. This is useful when writing code that needs to support multiple contexts. For example, if a notebook specifies a default renderer string of `\"notebook+plotly_mimetype+pdf\"`then this notebook would be able to run in the classic Jupyter Notebook, in JupyterLab, and it would support being exported to PDF using `nbconvert`.\n", + "\n", + "#### Customizing Built-In Renderers\n", + "Most built-in renderers have configuration options to customize their behavior. To view a description of a renderer, including its configuration options, access the renderer object using dictionary-style key lookup on the `plotly.io.renderers` configuration object and then display it. Here is an example of accessing and displaying the `png` renderer." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "56ee4f12", + "metadata": {}, + "outputs": [], + "source": [ + "import plotly.io as pio\n", + "png_renderer = pio.renderers[\"png\"]\n", + "png_renderer" + ] + }, + { + "cell_type": "markdown", + "id": "534260b2", + "metadata": {}, + "source": [ + "From this output, you can see that the `png` renderer supports 3 properties: `width`, `height`, and `scale`. You can customize these properties by assigning new values to them.\n", + "\n", + "Here is an example that customizes the `png` renderer to change the resulting image size, sets the `png` renderer as the default, and then displays a figure." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "7eb09ffc", + "metadata": {}, + "outputs": [], + "source": [ + "import plotly.io as pio\n", + "png_renderer = pio.renderers[\"png\"]\n", + "png_renderer.width = 500\n", + "png_renderer.height = 500\n", + "\n", + "pio.renderers.default = \"png\"\n", + "\n", + "import plotly.graph_objects as go\n", + "fig = go.Figure(\n", + " data=[go.Bar(y=[2, 1, 3])],\n", + " layout_title_text=\"A Figure Displayed with the 'png' Renderer\"\n", + ")\n", + "fig.show()" + ] + }, + { + "cell_type": "markdown", + "id": "9767a393", + "metadata": {}, + "source": [ + "You can also override the values of renderer parameters temporarily by passing them as keyword arguments to the `show()` method. For example" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "4f3d0253", + "metadata": {}, + "outputs": [], + "source": [ + "import plotly.graph_objects as go\n", + "\n", + "fig = go.Figure(\n", + " data=[go.Bar(y=[2, 1, 3])],\n", + " layout_title_text=\"A Figure Displayed with the 'png' Renderer\"\n", + ")\n", + "fig.show(renderer=\"png\", width=800, height=300)" + ] + }, + { + "cell_type": "markdown", + "id": "a8dbfee7", + "metadata": {}, + "source": [ + "### Displaying figures in Dash\n", + "\n", + "[Dash](https://plotly.com/dash/) is the best way to build analytical apps in Python using Plotly figures. To run the app below, run `pip install dash`, click \"Download\" to get the code and run `python app.py`.\n", + "\n", + "Get started with [the official Dash docs](https://dash.plotly.com/installation) and **learn how to effortlessly [style](https://plotly.com/dash/design-kit/) & [deploy](https://plotly.com/dash/app-manager/) apps like this with Dash Enterprise.**\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "9814db1b", + "metadata": { + "hide_code": true + }, + "outputs": [], + "source": [ + "from IPython.display import IFrame\n", + "snippet_url = 'https://python-docs-dash-snippets.herokuapp.com/python-docs-dash-snippets/'\n", + "IFrame(snippet_url + 'renderers', width='100%', height=1200)" + ] + }, + { + "cell_type": "markdown", + "id": "5100eb09", + "metadata": {}, + "source": [ + "

Sign up for Dash Club → Free cheat sheets plus updates from Chris Parmer and Adam Schroeder delivered to your inbox every two months. Includes tips and tricks, community apps, and deep dives into the Dash architecture.\n", + "Join now.

" + ] + }, + { + "cell_type": "markdown", + "id": "5591b518", + "metadata": {}, + "source": [ + "## Displaying Figures Using `ipywidgets`\n", + "Plotly figures can be displayed in [ipywidgets](https://ipywidgets.readthedocs.io/en/stable/) contexts using `plotly.graph_objects.FigureWidget` objects. `FigureWidget` is a figure graph object (just like `plotly.graph_objects.Figure`), so you can add traces to it and update it just like a regular `Figure`. But `FigureWidget` is also an `ipywidgets` object, which means that you can display it alongside other `ipywidgets` to build user interfaces right in the notebook.\n", + "\n", + "See the [Plotly FigureWidget Overview](https://plot.ly/python/figurewidget/) for more information on integrating `plotly.py` figures with `ipywidgets`.\n", + "\n", + "It is important to note that `FigureWidget` does not use the renderers framework discussed above, so you should not use the `plotly.io.show` function on `FigureWidget` objects." + ] + }, + { + "cell_type": "markdown", + "id": "6dd39674", + "metadata": {}, + "source": [ + "## Performance\n", + "\n", + "No matter the approach chosen to display a figure, [the figure data structure](https://plotly.com/python/figure-structure/) is first (automatically, internally) serialized into a JSON string before being transferred from the Python context to the browser (or [to an HTML file first](https://plotly.com/python/interactive-html-export/) or [to Kaleido for static image export](https://plotly.com/python/static-image-export/)).\n", + "\n", + "*New in v5.0*\n", + "\n", + "The default JSON serialization mechanism can be slow for figures with many data points or with large `numpy` arrays or data frames. **If [the `orjson` package](https://github.com/ijl/orjson) is installed**, `plotly` will use that instead of the built-in `json` package, which can lead to **5-10x** speedups for large figures.\n", + "\n", + "Once a figure is serialized to JSON, it must be rendered by a browser, either immediately in the user's browser, at some later point if the figure is exported to HTML, or immediately in Kaleido's internal headless browser for static image export. Rendering time is generally proportional to the total number of data points in the figure, the number of traces and the number of subplots. In situations where rendering performance is slow, we recommend considering [the use of `plotly` WebGL traces](/python/webgl-vs-svg/) to exploit GPU-accelerated rendering in the browser, or [using the Datashader library to do Python-side rendering](/python/datashader/) before using `px.imshow()` to render the figure.\n" + ] + }, + { + "cell_type": "markdown", + "id": "aecdce6f", + "metadata": {}, + "source": [ + "### What About Dash?\n", + "\n", + "[Dash](https://dash.plot.ly/) is an open-source framework for building analytical applications, with no Javascript required, and it is tightly integrated with the Plotly graphing library.\n", + "\n", + "Learn about how to install Dash at https://dash.plot.ly/installation.\n", + "\n", + "Everywhere in this page that you see `fig.show()`, you can display the same figure in a Dash application by passing it to the `figure` argument of the [`Graph` component](https://dash.plot.ly/dash-core-components/graph) from the built-in `dash_core_components` package like this:\n", + "\n", + "```python\n", + "import plotly.graph_objects as go # or plotly.express as px\n", + "fig = go.Figure() # or any Plotly Express function e.g. px.bar(...)\n", + "# fig.add_trace( ... )\n", + "# fig.update_layout( ... )\n", + "\n", + "from dash import Dash, dcc, html\n", + "\n", + "app = Dash()\n", + "app.layout = html.Div([\n", + " dcc.Graph(figure=fig)\n", + "])\n", + "\n", + "app.run_server(debug=True, use_reloader=False) # Turn off reloader if inside Jupyter\n", + "```" + ] + } + ], + "metadata": { + "jupytext": { + "notebook_metadata_filter": "all" + }, + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "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.8.8" + }, + "plotly": { + "description": "Displaying Figures using Plotly's Python graphing library", + "display_as": "file_settings", + "language": "python", + "layout": "base", + "name": "Displaying Figures", + "order": 3, + "page_type": "example_index", + "permalink": "python/renderers/", + "redirect_from": "python/offline/", + "thumbnail": "thumbnail/displaying-figures.png" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/sankey-diagram.ipynb b/sankey-diagram.ipynb new file mode 100644 index 000000000..00cd1d925 --- /dev/null +++ b/sankey-diagram.ipynb @@ -0,0 +1,490 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "5eeaf83c", + "metadata": {}, + "source": [ + "A [Sankey diagram](https://en.wikipedia.org/wiki/Sankey_diagram) is a flow diagram, in which the width of arrows is proportional to the flow quantity." + ] + }, + { + "cell_type": "markdown", + "id": "c15ad75a", + "metadata": {}, + "source": [ + "### Basic Sankey Diagram\n", + "Sankey diagrams visualize the contributions to a flow by defining [source](https://plotly.com/python/reference/sankey/#sankey-link-source) to represent the source node, [target](https://plotly.com/python/reference/sankey/#sankey-link-target) for the target node, [value](https://plotly.com/python/reference/sankey/#sankey-link-value) to set the flow volume, and [label](https://plotly.com/python/reference/sankey/#sankey-node-label) that shows the node name." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "5532eac3", + "metadata": {}, + "outputs": [], + "source": [ + "import plotly.graph_objects as go\n", + "\n", + "fig = go.Figure(data=[go.Sankey(\n", + " node = dict(\n", + " pad = 15,\n", + " thickness = 20,\n", + " line = dict(color = \"black\", width = 0.5),\n", + " label = [\"A1\", \"A2\", \"B1\", \"B2\", \"C1\", \"C2\"],\n", + " color = \"blue\"\n", + " ),\n", + " link = dict(\n", + " source = [0, 1, 0, 2, 3, 3], # indices correspond to labels, eg A1, A2, A1, B1, ...\n", + " target = [2, 3, 3, 4, 4, 5],\n", + " value = [8, 4, 2, 8, 4, 2]\n", + " ))])\n", + "\n", + "fig.update_layout(title_text=\"Basic Sankey Diagram\", font_size=10)\n", + "fig.show()" + ] + }, + { + "cell_type": "markdown", + "id": "a8973cf4", + "metadata": {}, + "source": [ + "### More complex Sankey diagram with colored links" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "15366c1c", + "metadata": {}, + "outputs": [], + "source": [ + "import plotly.graph_objects as go\n", + "import urllib, json\n", + "\n", + "url = 'https://raw.githubusercontent.com/plotly/plotly.js/master/test/image/mocks/sankey_energy.json'\n", + "response = urllib.request.urlopen(url)\n", + "data = json.loads(response.read())\n", + "\n", + "# override gray link colors with 'source' colors\n", + "opacity = 0.4\n", + "# change 'magenta' to its 'rgba' value to add opacity\n", + "data['data'][0]['node']['color'] = ['rgba(255,0,255, 0.8)' if color == \"magenta\" else color for color in data['data'][0]['node']['color']]\n", + "data['data'][0]['link']['color'] = [data['data'][0]['node']['color'][src].replace(\"0.8\", str(opacity))\n", + " for src in data['data'][0]['link']['source']]\n", + "\n", + "fig = go.Figure(data=[go.Sankey(\n", + " valueformat = \".0f\",\n", + " valuesuffix = \"TWh\",\n", + " # Define nodes\n", + " node = dict(\n", + " pad = 15,\n", + " thickness = 15,\n", + " line = dict(color = \"black\", width = 0.5),\n", + " label = data['data'][0]['node']['label'],\n", + " color = data['data'][0]['node']['color']\n", + " ),\n", + " # Add links\n", + " link = dict(\n", + " source = data['data'][0]['link']['source'],\n", + " target = data['data'][0]['link']['target'],\n", + " value = data['data'][0]['link']['value'],\n", + " label = data['data'][0]['link']['label'],\n", + " color = data['data'][0]['link']['color']\n", + "))])\n", + "\n", + "fig.update_layout(title_text=\"Energy forecast for 2050
Source: Department of Energy & Climate Change, Tom Counsell via Mike Bostock\",\n", + " font_size=10)\n", + "fig.show()" + ] + }, + { + "cell_type": "markdown", + "id": "177e990f", + "metadata": {}, + "source": [ + "### Sankey Diagram in Dash\n", + "\n", + "[Dash](https://plotly.com/dash/) is the best way to build analytical apps in Python using Plotly figures. To run the app below, run `pip install dash`, click \"Download\" to get the code and run `python app.py`.\n", + "\n", + "Get started with [the official Dash docs](https://dash.plotly.com/installation) and **learn how to effortlessly [style](https://plotly.com/dash/design-kit/) & [deploy](https://plotly.com/dash/app-manager/) apps like this with Dash Enterprise.**\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "4a86355c", + "metadata": { + "hide_code": true + }, + "outputs": [], + "source": [ + "from IPython.display import IFrame\n", + "snippet_url = 'https://python-docs-dash-snippets.herokuapp.com/python-docs-dash-snippets/'\n", + "IFrame(snippet_url + 'sankey-diagram', width='100%', height=1200)" + ] + }, + { + "cell_type": "markdown", + "id": "5158528f", + "metadata": {}, + "source": [ + "

Sign up for Dash Club → Free cheat sheets plus updates from Chris Parmer and Adam Schroeder delivered to your inbox every two months. Includes tips and tricks, community apps, and deep dives into the Dash architecture.\n", + "Join now.

" + ] + }, + { + "cell_type": "markdown", + "id": "bc5ea349", + "metadata": {}, + "source": [ + "### Style Sankey Diagram\n", + "This example also uses [hovermode](https://plotly.com/python/reference/layout/#layout-hovermode) to enable multiple tooltips." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "8d33d732", + "metadata": {}, + "outputs": [], + "source": [ + "import plotly.graph_objects as go\n", + "import urllib, json\n", + "\n", + "url = 'https://raw.githubusercontent.com/plotly/plotly.js/master/test/image/mocks/sankey_energy.json'\n", + "response = urllib.request.urlopen(url)\n", + "data = json.loads(response.read())\n", + "\n", + "fig = go.Figure(data=[go.Sankey(\n", + " valueformat = \".0f\",\n", + " valuesuffix = \"TWh\",\n", + " node = dict(\n", + " pad = 15,\n", + " thickness = 15,\n", + " line = dict(color = \"black\", width = 0.5),\n", + " label = data['data'][0]['node']['label'],\n", + " color = data['data'][0]['node']['color']\n", + " ),\n", + " link = dict(\n", + " source = data['data'][0]['link']['source'],\n", + " target = data['data'][0]['link']['target'],\n", + " value = data['data'][0]['link']['value'],\n", + " label = data['data'][0]['link']['label']\n", + " ))])\n", + "\n", + "fig.update_layout(\n", + " hovermode = 'x',\n", + " title=dict(text=\"Energy forecast for 2050
Source: Department of Energy & Climate Change, Tom Counsell via Mike Bostock\"),\n", + " font=dict(size = 10, color = 'white'),\n", + " plot_bgcolor='black',\n", + " paper_bgcolor='black'\n", + ")\n", + "\n", + "fig.show()" + ] + }, + { + "cell_type": "markdown", + "id": "17130c4d", + "metadata": {}, + "source": [ + "### Link Hover Color\n", + "\n", + "*New in 5.19*\n", + "\n", + "Set `link.hovercolor` to change the colors of links on hover. `link.hovercolor` accepts either one color, specified as a string, that will apply to all links, or a list of colors to specify different colors for each link. Here, we use a list to specify a different color for each link:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "54703338", + "metadata": {}, + "outputs": [], + "source": [ + "import plotly.graph_objects as go\n", + "\n", + "fig = go.Figure(data=[go.Sankey(\n", + " node = dict(\n", + " pad = 15,\n", + " thickness = 20,\n", + " line = dict(color = \"black\", width = 0.5),\n", + " label = [\"A1\", \"A2\", \"B1\", \"B2\", \"C1\", \"C2\"],\n", + " color = \"blue\"\n", + " ),\n", + " link = dict(\n", + " source = [0, 1, 0, 2, 3, 3],\n", + " target = [2, 3, 3, 4, 4, 5],\n", + " value = [8, 4, 2, 8, 4, 2],\n", + " hovercolor=[\"midnightblue\", \"lightskyblue\", \"gold\", \"mediumturquoise\", \"lightgreen\", \"cyan\"],\n", + " ))])\n", + "\n", + "fig.update_layout(title_text=\"Basic Sankey Diagram\", font_size=10)\n", + "fig.show()" + ] + }, + { + "cell_type": "markdown", + "id": "fa2f8f60", + "metadata": {}, + "source": [ + "### Hovertemplate and customdata of Sankey diagrams\n", + "\n", + "Links and nodes have their own hovertemplate, in which link- or node-specific attributes can be displayed. To add more data to links and nodes, it is possible to use the `customdata` attribute of `link` and `nodes`, as in the following example. For more information about hovertemplate and customdata, please see the [tutorial on hover text](/python/hover-text-and-formatting/)." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "dc84cac7", + "metadata": {}, + "outputs": [], + "source": [ + "import plotly.graph_objects as go\n", + "\n", + "fig = go.Figure(data=[go.Sankey(\n", + " node = dict(\n", + " pad = 15,\n", + " thickness = 20,\n", + " line = dict(color = \"black\", width = 0.5),\n", + " label = [\"A1\", \"A2\", \"B1\", \"B2\", \"C1\", \"C2\"],\n", + " customdata = [\"Long name A1\", \"Long name A2\", \"Long name B1\", \"Long name B2\",\n", + " \"Long name C1\", \"Long name C2\"],\n", + " hovertemplate='Node %{customdata} has total value %{value}',\n", + " color = \"blue\"\n", + " ),\n", + " link = dict(\n", + " source = [0, 1, 0, 2, 3, 3], # indices correspond to labels, eg A1, A2, A2, B1, ...\n", + " target = [2, 3, 3, 4, 4, 5],\n", + " value = [8, 4, 2, 8, 4, 2],\n", + " customdata = [\"q\",\"r\",\"s\",\"t\",\"u\",\"v\"],\n", + " hovertemplate='Link from node %{source.customdata}
'+\n", + " 'to node%{target.customdata}
has value %{value}'+\n", + " '
and data %{customdata}',\n", + " ))])\n", + "\n", + "fig.update_layout(title_text=\"Basic Sankey Diagram\", font_size=10)\n", + "fig.show()" + ] + }, + { + "cell_type": "markdown", + "id": "ca44c383", + "metadata": {}, + "source": [ + "### Define Node Position\n", + "\n", + "The following example sets [node.x](https://plotly.com/python/reference/sankey/#sankey-node-x) and `node.y` to place nodes in the specified locations, except in the `snap arrangement` (default behaviour when `node.x` and `node.y` are not defined) to avoid overlapping of the nodes, therefore, an automatic snapping of elements will be set to define the padding between nodes via [nodepad](https://plotly.com/python/reference/sankey/#sankey-node-pad). The other possible arrangements are: 1) perpendicular 2) freeform 3) fixed" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "3efec8b0", + "metadata": {}, + "outputs": [], + "source": [ + "import plotly.graph_objects as go\n", + "\n", + "fig = go.Figure(go.Sankey(\n", + " arrangement = \"snap\",\n", + " node = {\n", + " \"label\": [\"A\", \"B\", \"C\", \"D\", \"E\", \"F\"],\n", + " \"x\": [0.2, 0.1, 0.5, 0.7, 0.3, 0.5],\n", + " \"y\": [0.7, 0.5, 0.2, 0.4, 0.2, 0.3],\n", + " 'pad':10}, # 10 Pixels\n", + " link = {\n", + " \"source\": [0, 0, 1, 2, 5, 4, 3, 5],\n", + " \"target\": [5, 3, 4, 3, 0, 2, 2, 3],\n", + " \"value\": [1, 2, 1, 1, 1, 1, 1, 2]}))\n", + "\n", + "fig.show()" + ] + }, + { + "cell_type": "markdown", + "id": "769fc5e1", + "metadata": {}, + "source": [ + "### Sankey Diagram with Arrow Links\n", + "\n", + "*New in 5.10*\n", + "\n", + "Create a Sankey diagram with arrow links by setting the `arrowlen` attribute of `link`:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "7d79175d", + "metadata": {}, + "outputs": [], + "source": [ + "import plotly.graph_objects as go\n", + "\n", + "fig = go.Figure(go.Sankey(\n", + " arrangement='snap',\n", + " node=dict(\n", + " label=['A', 'B', 'C', 'D', 'E', 'F'],\n", + " x=[0.2, 0.1, 0.5, 0.7, 0.3, 0.5],\n", + " y=[0.7, 0.5, 0.2, 0.4, 0.2, 0.3],\n", + " pad=10,\n", + " align=\"right\",\n", + " ),\n", + " link=dict(\n", + " arrowlen=15,\n", + " source=[0, 0, 1, 2, 5, 4, 3, 5],\n", + " target=[5, 3, 4, 3, 0, 2, 2, 3],\n", + " value=[1, 2, 1, 1, 1, 1, 1, 2]\n", + " )\n", + "))\n", + "\n", + "fig.show()" + ] + }, + { + "cell_type": "markdown", + "id": "04cc0951", + "metadata": {}, + "source": [ + "### Node Alignment\n", + "\n", + "*New in 5.19*\n", + "\n", + "You can set the alignment of nodes using `node.align`. Here are two examples with the same `source` and `target`. The first example has nodes aligned \"left\" and the second has nodes aligned \"right\". `node.align` also supports \"center\" and \"justify\". \"justify\" is the default if `node.align` is not set, and is similar to aligning to the \"left\", except that nodes without outgoing links are moved to the right of the figure." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "8bc29e19", + "metadata": {}, + "outputs": [], + "source": [ + "import plotly.graph_objects as go\n", + "\n", + "fig = go.Figure(go.Sankey(\n", + " arrangement='snap',\n", + " node=dict(\n", + " label=[\"0\", \"1\", \"2\", \"3\", \"4\", \"5\"],\n", + " align='left'\n", + "\n", + " ),\n", + " link=dict(\n", + " arrowlen=15,\n", + " source=[0, 1, 4, 2, 1],\n", + " target=[1, 4, 5, 4, 3],\n", + " value=[4, 2, 3, 1, 2]\n", + " )\n", + "))\n", + "\n", + "fig.show()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "7c29546c", + "metadata": {}, + "outputs": [], + "source": [ + "import plotly.graph_objects as go\n", + "\n", + "fig = go.Figure(go.Sankey(\n", + " arrangement='snap',\n", + " node=dict(\n", + " label=[\"0\", \"1\", \"2\", \"3\", \"4\", \"5\"],\n", + " align=\"right\",\n", + " ),\n", + " link=dict(\n", + " arrowlen=15,\n", + " source=[0, 1, 4, 2, 1],\n", + " target=[1, 4, 5, 4, 3],\n", + " value=[4, 2, 3, 1, 2]\n", + " )\n", + "))\n", + "\n", + "fig.show()" + ] + }, + { + "cell_type": "markdown", + "id": "996eb718", + "metadata": {}, + "source": [ + "### Reference\n", + "\n", + "See [https://plotly.com/python/reference/sankey](https://plotly.com/python/reference/sankey/) for more information and options!\n" + ] + }, + { + "cell_type": "markdown", + "id": "a23e8feb", + "metadata": {}, + "source": [ + "### What About Dash?\n", + "\n", + "[Dash](https://dash.plot.ly/) is an open-source framework for building analytical applications, with no Javascript required, and it is tightly integrated with the Plotly graphing library.\n", + "\n", + "Learn about how to install Dash at https://dash.plot.ly/installation.\n", + "\n", + "Everywhere in this page that you see `fig.show()`, you can display the same figure in a Dash application by passing it to the `figure` argument of the [`Graph` component](https://dash.plot.ly/dash-core-components/graph) from the built-in `dash_core_components` package like this:\n", + "\n", + "```python\n", + "import plotly.graph_objects as go # or plotly.express as px\n", + "fig = go.Figure() # or any Plotly Express function e.g. px.bar(...)\n", + "# fig.add_trace( ... )\n", + "# fig.update_layout( ... )\n", + "\n", + "from dash import Dash, dcc, html\n", + "\n", + "app = Dash()\n", + "app.layout = html.Div([\n", + " dcc.Graph(figure=fig)\n", + "])\n", + "\n", + "app.run_server(debug=True, use_reloader=False) # Turn off reloader if inside Jupyter\n", + "```" + ] + } + ], + "metadata": { + "jupytext": { + "notebook_metadata_filter": "all" + }, + "kernel_info": { + "name": "python2" + }, + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "language": "python", + "name": "python3" + }, + "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.11" + }, + "plotly": { + "description": "How to make Sankey Diagrams in Python with Plotly.", + "display_as": "basic", + "language": "python", + "layout": "base", + "name": "Sankey Diagram", + "order": 12, + "page_type": "u-guide", + "permalink": "python/sankey-diagram/", + "thumbnail": "thumbnail/sankey.jpg" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/scatter-plots-on-maps.ipynb b/scatter-plots-on-maps.ipynb new file mode 100644 index 000000000..659e6c5cf --- /dev/null +++ b/scatter-plots-on-maps.ipynb @@ -0,0 +1,353 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "a86d88b2", + "metadata": {}, + "source": [ + "#### Base Map Configuration\n", + "\n", + "Plotly figures made with [Plotly Express](/python/plotly-express/) `px.scatter_geo`, `px.line_geo` or `px.choropleth` functions or containing `go.Choropleth` or `go.Scattergeo` [graph objects](/python/graph-objects/) have a `go.layout.Geo` object which can be used to [control the appearance of the base map](/python/map-configuration/) onto which data is plotted.\n", + "\n", + "### Geographical Scatter Plot with px.scatter_geo\n", + "\n", + "Here we show the [Plotly Express](/python/plotly-express/) function `px.scatter_geo` for a geographical scatter plot. The `size` argument is used to set the size of markers from a given column of the DataFrame.\n", + "\n", + "[Plotly Express](/python/plotly-express/) is the easy-to-use, high-level interface to Plotly, which [operates on a variety of types of data](/python/px-arguments/) and produces [easy-to-style figures](/python/styling-plotly-express/)." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "a85f7765", + "metadata": {}, + "outputs": [], + "source": [ + "import plotly.express as px\n", + "df = px.data.gapminder().query(\"year == 2007\")\n", + "fig = px.scatter_geo(df, locations=\"iso_alpha\",\n", + " size=\"pop\", # size of markers, \"pop\" is one of the columns of gapminder\n", + " )\n", + "fig.show()" + ] + }, + { + "cell_type": "markdown", + "id": "efe2da8e", + "metadata": {}, + "source": [ + "#### Customize geographical scatter plot" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "f40e677b", + "metadata": {}, + "outputs": [], + "source": [ + "import plotly.express as px\n", + "df = px.data.gapminder().query(\"year == 2007\")\n", + "fig = px.scatter_geo(df, locations=\"iso_alpha\",\n", + " color=\"continent\", # which column to use to set the color of markers\n", + " hover_name=\"country\", # column added to hover information\n", + " size=\"pop\", # size of markers\n", + " projection=\"natural earth\")\n", + "fig.show()" + ] + }, + { + "cell_type": "markdown", + "id": "d5f51695", + "metadata": {}, + "source": [ + "### Basic Example with GeoPandas\n", + "\n", + "`px.scatter_geo` can work well with [GeoPandas](https://geopandas.org/) dataframes whose `geometry` is of type `Point`." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "a3161524", + "metadata": {}, + "outputs": [], + "source": [ + "import plotly.express as px\n", + "import geopandas as gpd\n", + "\n", + "geo_df = gpd.read_file(gpd.datasets.get_path('naturalearth_cities'))\n", + "\n", + "px.set_mapbox_access_token(open(\".mapbox_token\").read())\n", + "fig = px.scatter_geo(geo_df,\n", + " lat=geo_df.geometry.y,\n", + " lon=geo_df.geometry.x,\n", + " hover_name=\"name\")\n", + "fig.show()" + ] + }, + { + "cell_type": "markdown", + "id": "bbe64e10", + "metadata": {}, + "source": [ + "### U.S. Airports Map\n", + "\n", + "Here we show how to use `go.Scattergeo` from `plotly.graph_objects`.\n", + "\n", + "#### Simple U.S. Airports Map" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "6bc1888e", + "metadata": {}, + "outputs": [], + "source": [ + "import plotly.graph_objects as go\n", + "\n", + "import pandas as pd\n", + "\n", + "df = pd.read_csv('https://raw.githubusercontent.com/plotly/datasets/master/2011_february_us_airport_traffic.csv')\n", + "df['text'] = df['airport'] + '' + df['city'] + ', ' + df['state'] + '' + 'Arrivals: ' + df['cnt'].astype(str)\n", + "\n", + "fig = go.Figure(data=go.Scattergeo(\n", + " lon = df['long'],\n", + " lat = df['lat'],\n", + " text = df['text'],\n", + " mode = 'markers',\n", + " marker_color = df['cnt'],\n", + " ))\n", + "\n", + "fig.update_layout(\n", + " title = 'Most trafficked US airports
(Hover for airport names)',\n", + " geo_scope='usa',\n", + " )\n", + "fig.show()" + ] + }, + { + "cell_type": "markdown", + "id": "0eb65459", + "metadata": {}, + "source": [ + "#### Styled U.S. Airports Map" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "98a11171", + "metadata": {}, + "outputs": [], + "source": [ + "import plotly.graph_objects as go\n", + "\n", + "import pandas as pd\n", + "\n", + "df = pd.read_csv('https://raw.githubusercontent.com/plotly/datasets/master/2011_february_us_airport_traffic.csv')\n", + "df['text'] = df['airport'] + '' + df['city'] + ', ' + df['state'] + '' + 'Arrivals: ' + df['cnt'].astype(str)\n", + "\n", + "\n", + "fig = go.Figure(data=go.Scattergeo(\n", + " locationmode = 'USA-states',\n", + " lon = df['long'],\n", + " lat = df['lat'],\n", + " text = df['text'],\n", + " mode = 'markers',\n", + " marker = dict(\n", + " size = 8,\n", + " opacity = 0.8,\n", + " reversescale = True,\n", + " autocolorscale = False,\n", + " symbol = 'square',\n", + " line = dict(\n", + " width=1,\n", + " color='rgba(102, 102, 102)'\n", + " ),\n", + " colorscale = 'Blues',\n", + " cmin = 0,\n", + " color = df['cnt'],\n", + " cmax = df['cnt'].max(),\n", + " colorbar=dict(\n", + " title=dict(\n", + " text=\"Incoming flights
February 2011\"\n", + " )\n", + " )\n", + " )))\n", + "\n", + "fig.update_layout(\n", + " title = 'Most trafficked US airports
(Hover for airport names)',\n", + " geo = dict(\n", + " scope='usa',\n", + " projection_type='albers usa',\n", + " showland = True,\n", + " landcolor = \"rgb(250, 250, 250)\",\n", + " subunitcolor = \"rgb(217, 217, 217)\",\n", + " countrycolor = \"rgb(217, 217, 217)\",\n", + " countrywidth = 0.5,\n", + " subunitwidth = 0.5\n", + " ),\n", + " )\n", + "fig.show()" + ] + }, + { + "cell_type": "markdown", + "id": "15dbd6d3", + "metadata": {}, + "source": [ + "### North American Precipitation Map" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "340f9df5", + "metadata": {}, + "outputs": [], + "source": [ + "import plotly.graph_objects as go\n", + "\n", + "import pandas as pd\n", + "\n", + "df = pd.read_csv('https://raw.githubusercontent.com/plotly/datasets/master/2015_06_30_precipitation.csv')\n", + "\n", + "scl = [0,\"rgb(150,0,90)\"],[0.125,\"rgb(0, 0, 200)\"],[0.25,\"rgb(0, 25, 255)\"],\\\n", + "[0.375,\"rgb(0, 152, 255)\"],[0.5,\"rgb(44, 255, 150)\"],[0.625,\"rgb(151, 255, 0)\"],\\\n", + "[0.75,\"rgb(255, 234, 0)\"],[0.875,\"rgb(255, 111, 0)\"],[1,\"rgb(255, 0, 0)\"]\n", + "\n", + "fig = go.Figure(data=go.Scattergeo(\n", + " lat = df['Lat'],\n", + " lon = df['Lon'],\n", + " text = df['Globvalue'].astype(str) + ' inches',\n", + " marker = dict(\n", + " color = df['Globvalue'],\n", + " colorscale = scl,\n", + " reversescale = True,\n", + " opacity = 0.7,\n", + " size = 2,\n", + " colorbar = dict(\n", + " title = dict(\n", + " side=\"right\"\n", + " ),\n", + " outlinecolor = \"rgba(68, 68, 68, 0)\",\n", + " ticks = \"outside\",\n", + " showticksuffix = \"last\",\n", + " dtick = 0.1\n", + " )\n", + " )\n", + "))\n", + "\n", + "fig.update_layout(\n", + " geo = dict(\n", + " scope = 'north america',\n", + " showland = True,\n", + " landcolor = \"rgb(212, 212, 212)\",\n", + " subunitcolor = \"rgb(255, 255, 255)\",\n", + " countrycolor = \"rgb(255, 255, 255)\",\n", + " showlakes = True,\n", + " lakecolor = \"rgb(255, 255, 255)\",\n", + " showsubunits = True,\n", + " showcountries = True,\n", + " resolution = 50,\n", + " projection = dict(\n", + " type = 'conic conformal',\n", + " rotation_lon = -100\n", + " ),\n", + " lonaxis = dict(\n", + " showgrid = True,\n", + " gridwidth = 0.5,\n", + " range= [ -140.0, -55.0 ],\n", + " dtick = 5\n", + " ),\n", + " lataxis = dict (\n", + " showgrid = True,\n", + " gridwidth = 0.5,\n", + " range= [ 20.0, 60.0 ],\n", + " dtick = 5\n", + " )\n", + " ),\n", + " title=dict(text='US Precipitation 06-30-2015
Source: NOAA'),\n", + ")\n", + "fig.show()" + ] + }, + { + "cell_type": "markdown", + "id": "3cec8777", + "metadata": {}, + "source": [ + "#### Reference\n", + "\n", + "See [function reference for `px.(scatter_geo)`](https://plotly.com/python-api-reference/generated/plotly.express.scatter_geo) or https://plotly.com/python/reference/scattergeo/ and https://plotly.com/python/reference/layout/geo/ for more information and chart attribute options!\n" + ] + }, + { + "cell_type": "markdown", + "id": "54a9a193", + "metadata": {}, + "source": [ + "### What About Dash?\n", + "\n", + "[Dash](https://dash.plot.ly/) is an open-source framework for building analytical applications, with no Javascript required, and it is tightly integrated with the Plotly graphing library.\n", + "\n", + "Learn about how to install Dash at https://dash.plot.ly/installation.\n", + "\n", + "Everywhere in this page that you see `fig.show()`, you can display the same figure in a Dash application by passing it to the `figure` argument of the [`Graph` component](https://dash.plot.ly/dash-core-components/graph) from the built-in `dash_core_components` package like this:\n", + "\n", + "```python\n", + "import plotly.graph_objects as go # or plotly.express as px\n", + "fig = go.Figure() # or any Plotly Express function e.g. px.bar(...)\n", + "# fig.add_trace( ... )\n", + "# fig.update_layout( ... )\n", + "\n", + "from dash import Dash, dcc, html\n", + "\n", + "app = Dash()\n", + "app.layout = html.Div([\n", + " dcc.Graph(figure=fig)\n", + "])\n", + "\n", + "app.run_server(debug=True, use_reloader=False) # Turn off reloader if inside Jupyter\n", + "```" + ] + } + ], + "metadata": { + "jupytext": { + "notebook_metadata_filter": "all" + }, + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "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.7.7" + }, + "plotly": { + "description": "How to make scatter plots on maps in Python. Scatter plots on maps highlight geographic areas and can be colored by value.", + "display_as": "maps", + "language": "python", + "layout": "base", + "name": "Scatter Plots on Maps", + "order": 12, + "page_type": "u-guide", + "permalink": "python/scatter-plots-on-maps/", + "thumbnail": "thumbnail/scatter-plot-on-maps.jpg" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/selections.ipynb b/selections.ipynb new file mode 100644 index 000000000..636bb0e03 --- /dev/null +++ b/selections.ipynb @@ -0,0 +1,472 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "3bb68803", + "metadata": {}, + "source": [ + "## Adding Selections to Cartesian Subplots\n", + "\n", + "*New in 5.10*\n", + "\n", + "You can add persistent selections to a rendered figure using the **Box Select** and **Lasso Select** tools in the mode bar.\n", + "To add multiple selections, select **Shift** when making new selections.\n", + "To clear a selection, double-click it. On a subplot you can clear all selections by double-clicking any unselected area of the subplot.\n" + ] + }, + { + "cell_type": "markdown", + "id": "e995d998", + "metadata": {}, + "source": [ + "You can also add selections to a figure that displays when it renders using `fig.add_selection`.\n", + "Here, we add a rectangular selection with a region between `3.0` and `6.5` on the x axis and between `3.5` and `5.5` on the y axis.\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "8a65d727", + "metadata": {}, + "outputs": [], + "source": [ + "import plotly.express as px\n", + "\n", + "df = px.data.iris()\n", + "\n", + "fig = px.scatter(df, x=\"sepal_width\", y=\"sepal_length\")\n", + "fig.add_selection(x0=3.0, y0=6.5, x1=3.5, y1=5.5)\n", + "\n", + "fig.show()" + ] + }, + { + "cell_type": "markdown", + "id": "9cf24e3b", + "metadata": {}, + "source": [ + "## Selections Using a Custom SVG" + ] + }, + { + "cell_type": "markdown", + "id": "7a7db4e3", + "metadata": {}, + "source": [ + "In the above example, we added a rectangular selection. You can also render a custom SVG for a selection by defining a `path` that can include single or multiple polygons. Here, we create a selection with a single polygon path \"M2,6.5L4,7.5L4,6Z\".\n", + "\n", + "Please note that multiple polygons e.g. \"M0,0L0,10L10,10,L10,0Z M2,2L2,8L8,8,L8,2Z\" could be used to subtract certain regions from the selection." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "71ae4a76", + "metadata": {}, + "outputs": [], + "source": [ + "import plotly.express as px\n", + "\n", + "df = px.data.iris()\n", + "\n", + "fig = px.scatter(df, x=\"sepal_width\", y=\"sepal_length\")\n", + "fig.add_selection(path=\"M2,6.5L4,7.5L4,6Z\")\n", + "\n", + "fig.show()" + ] + }, + { + "cell_type": "markdown", + "id": "73f0f76d", + "metadata": {}, + "source": [ + "## Styling Selections" + ] + }, + { + "cell_type": "markdown", + "id": "13d36a02", + "metadata": {}, + "source": [ + "In the above example, we added a selection to the figure that is displayed when the figure renders.\n", + "`fig.add_selection` accepts additional properties that you can use to style the selection. Here, we add a `color`, `width`, and specify the `dash` type for the selection.\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "67853d6f", + "metadata": {}, + "outputs": [], + "source": [ + "import plotly.express as px\n", + "\n", + "df = px.data.iris()\n", + "\n", + "fig = px.scatter(df, x=\"sepal_width\", y=\"sepal_length\")\n", + "fig.add_selection(\n", + " x0=2.5, y0=6.5, x1=3.5, y1=5.5,\n", + " line=dict(\n", + " color=\"Crimson\",\n", + " width=2,\n", + " dash=\"dash\",\n", + " ))\n", + "\n", + "fig.show()\n" + ] + }, + { + "cell_type": "markdown", + "id": "1b3d7250", + "metadata": {}, + "source": [ + "## Styling New Selections\n", + "\n", + "You can style new selections made on the figure by setting properties on `newselection`.\n", + "Try making a new selection on the figure to try it out." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "9541d18c", + "metadata": {}, + "outputs": [], + "source": [ + "import plotly.express as px\n", + "\n", + "df = px.data.iris()\n", + "\n", + "fig = px.scatter(df, x=\"sepal_width\", y=\"sepal_length\")\n", + "\n", + "fig.update_layout(dragmode='select',\n", + " newselection=dict(line=dict(color='blue')))\n", + "\n", + "fig.show()" + ] + }, + { + "cell_type": "markdown", + "id": "2b4960f6", + "metadata": {}, + "source": [ + "## Fill Color for Active Selections\n", + "\n", + "You can style the active selection with `activeselection`. In this example, we set active selections (when created or clicked) to appear with a `fillcolor` of `yellow`." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "e3d954ac", + "metadata": {}, + "outputs": [], + "source": [ + "import plotly.express as px\n", + "\n", + "df = px.data.iris()\n", + "\n", + "fig = px.scatter(df, x=\"sepal_width\", y=\"sepal_length\")\n", + "fig.add_selection(x0=3.0, y0=6.5, x1=3.5, y1=5.5)\n", + "\n", + "fig.update_layout(dragmode='select',\n", + " activeselection=dict(fillcolor='yellow'))\n", + "\n", + "fig.show()" + ] + }, + { + "cell_type": "markdown", + "id": "7b6e3d7f", + "metadata": {}, + "source": [ + "## Selections with Time Series\n", + "\n", + "Selections are also supported on time series figures. Here, we add a rectangular selection with a region between the dates `2019-01-01\"` and `\"2019-10-01\"` on the x axis and between `0.95` and `1.15` on the y axis.\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "9806a81f", + "metadata": {}, + "outputs": [], + "source": [ + "import plotly.express as px\n", + "\n", + "df = px.data.stocks()\n", + "fig = px.line(df, x='date', y=\"GOOG\", markers=True)\n", + "fig.add_selection(x0=\"2019-01-01\", y0=0.95, x1=\"2019-10-01\", y1=1.15)\n", + "fig.show()" + ] + }, + { + "cell_type": "markdown", + "id": "196243fd", + "metadata": {}, + "source": [ + "## Referencing Selections on Multiple Cartesian Subplots" + ] + }, + { + "cell_type": "markdown", + "id": "ea8a037a", + "metadata": {}, + "source": [ + "You can add selections to multiple Cartesian subplots by specifying `xref` and/or `yref`. Here, we add one selection on the plot with axis ids `x` and `y2` and two selections to the the plot with axis ids `x` and `y`." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "d9bd906f", + "metadata": {}, + "outputs": [], + "source": [ + "import plotly.graph_objects as go\n", + "\n", + "import numpy as np\n", + "\n", + "np.random.seed(0)\n", + "t = np.linspace(-1, 1.2, 2000)\n", + "x = (t**3) + (0.3 * np.random.randn(2000))\n", + "y = (t**6) + (0.3 * np.random.randn(2000))\n", + "\n", + "fig = go.Figure()\n", + "fig.add_trace(go.Histogram2dContour(\n", + " x = x,\n", + " y = y,\n", + " colorscale = 'Blues',\n", + " reversescale = True,\n", + " xaxis = 'x',\n", + " yaxis = 'y'\n", + " ))\n", + "fig.add_trace(go.Scatter(\n", + " x = x,\n", + " y = y,\n", + " xaxis = 'x',\n", + " yaxis = 'y',\n", + " mode = 'markers',\n", + " marker = dict(\n", + " color = 'rgba(0,0,0,0.3)',\n", + " size = 3\n", + " )\n", + " ))\n", + "fig.add_trace(go.Histogram(\n", + " y = y,\n", + " xaxis = 'x2',\n", + " marker = dict(\n", + " color = 'rgba(0,0,0,1)'\n", + " )\n", + " ))\n", + "fig.add_trace(go.Histogram(\n", + " x = x,\n", + " yaxis = 'y2',\n", + " marker = dict(\n", + " color = 'rgba(0,0,0,1)'\n", + " )\n", + " ))\n", + "\n", + "fig.update_layout(\n", + " autosize = False,\n", + " xaxis = dict(\n", + " zeroline = False,\n", + " domain = [0,0.85],\n", + " showgrid = False\n", + " ),\n", + " yaxis = dict(\n", + " zeroline = False,\n", + " domain = [0,0.85],\n", + " showgrid = False\n", + " ),\n", + " xaxis2 = dict(\n", + " zeroline = False,\n", + " domain = [0.85,1],\n", + " showgrid = False\n", + " ),\n", + " yaxis2 = dict(\n", + " zeroline = False,\n", + " domain = [0.85,1],\n", + " showgrid = False\n", + " ),\n", + " height = 600,\n", + " width = 600,\n", + " bargap = 0,\n", + " hovermode = 'closest',\n", + " showlegend = False,\n", + " selections = [\n", + " dict(\n", + " x0 = 0.5,\n", + " x1 = -0.5,\n", + " xref = \"x\",\n", + " y0 = 190,\n", + " y1= 0,\n", + " yref = \"y2\",\n", + " line = dict(\n", + " color=\"yellow\"\n", + " )\n", + " ),\n", + " dict(\n", + " x0 = -0.2,\n", + " x1 = -1.5,\n", + " xref = \"x\",\n", + " y0 = 2,\n", + " y1= -1,\n", + " yref = \"y\",\n", + " line = dict(\n", + " color=\"yellow\"\n", + " )\n", + " ),\n", + " dict(\n", + " path= \"M0.75,2.39L0.98,3.38L1.46,3.68L1.80,3.35L2.01,2.51L1.67,1.15L1.18,0.50L0.65,0.66L0.54,0.83L0.49,1.56Z\",\n", + " xref= 'x',\n", + " yref = 'y',\n", + " line = dict(\n", + " color='yellow'\n", + " )\n", + " )\n", + " ]\n", + ")\n", + "\n", + "\n", + "fig.show()" + ] + }, + { + "cell_type": "markdown", + "id": "08cb5859", + "metadata": {}, + "source": [ + "## Referencing Selections on a Scatterplot Matrix" + ] + }, + { + "cell_type": "markdown", + "id": "f353b6f1", + "metadata": {}, + "source": [ + "You can add selections to a scatterplot matrix by specifying `xref` and/or `yref`. Here, we add one selection on the plot with axis ids `x2` and `y2` and another on the plot with ids `x3` and `y`." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "7d4a1ea8", + "metadata": { + "lines_to_next_cell": 0 + }, + "outputs": [], + "source": [ + "import plotly.express as px\n", + "\n", + "df = px.data.iris()\n", + "\n", + "fig = px.scatter_matrix(df,\n", + " dimensions=[\"sepal_length\", \"sepal_width\", \"petal_length\", \"petal_width\"],\n", + " color=\"species\")\n", + "\n", + "fig.update_layout(\n", + " xaxis = {\"matches\": \"y\"},\n", + " xaxis2 = {\"matches\": \"y2\"},\n", + " xaxis3 = {\"matches\": \"y3\"},\n", + " xaxis4 = {\"matches\": \"y4\"},\n", + " height = 900,\n", + " width = 750,\n", + " dragmode = 'select',\n", + " selections = [\n", + " dict(\n", + " x0 = 3,\n", + " x1 = 4,\n", + " xref = \"x2\",\n", + " y0 = 8,\n", + " y1= 6,\n", + " yref = \"y\"\n", + " ),\n", + " dict(\n", + " x0 = 5,\n", + " x1 = 1,\n", + " xref = \"x3\",\n", + " y0 = 5,\n", + " y1= 4,\n", + " yref = \"y\",\n", + " )\n", + " ]\n", + ")\n", + "\n", + "fig.show()" + ] + }, + { + "cell_type": "markdown", + "id": "2e6cc3e8", + "metadata": {}, + "source": [ + "\n" + ] + }, + { + "cell_type": "markdown", + "id": "94676d9e", + "metadata": {}, + "source": [ + "### What About Dash?\n", + "\n", + "[Dash](https://dash.plot.ly/) is an open-source framework for building analytical applications, with no Javascript required, and it is tightly integrated with the Plotly graphing library.\n", + "\n", + "Learn about how to install Dash at https://dash.plot.ly/installation.\n", + "\n", + "Everywhere in this page that you see `fig.show()`, you can display the same figure in a Dash application by passing it to the `figure` argument of the [`Graph` component](https://dash.plot.ly/dash-core-components/graph) from the built-in `dash_core_components` package like this:\n", + "\n", + "```python\n", + "import plotly.graph_objects as go # or plotly.express as px\n", + "fig = go.Figure() # or any Plotly Express function e.g. px.bar(...)\n", + "# fig.add_trace( ... )\n", + "# fig.update_layout( ... )\n", + "\n", + "from dash import Dash, dcc, html\n", + "\n", + "app = Dash()\n", + "app.layout = html.Div([\n", + " dcc.Graph(figure=fig)\n", + "])\n", + "\n", + "app.run_server(debug=True, use_reloader=False) # Turn off reloader if inside Jupyter\n", + "```" + ] + } + ], + "metadata": { + "jupytext": { + "notebook_metadata_filter": "all" + }, + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "language": "python", + "name": "python3" + }, + "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.9.0" + }, + "plotly": { + "description": "How to use selections in Python. Examples of adding and styling selections.", + "display_as": "file_settings", + "language": "python", + "layout": "base", + "name": "Selections", + "order": 38, + "permalink": "python/selections/", + "thumbnail": "thumbnail/ml_apps.png" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/setting-graph-size.ipynb b/setting-graph-size.ipynb new file mode 100644 index 000000000..3fd5d4a2a --- /dev/null +++ b/setting-graph-size.ipynb @@ -0,0 +1,339 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "9332d08f", + "metadata": {}, + "source": [ + "### Adjusting Height, Width, & Margins with Plotly Express\n", + "\n", + "[Plotly Express](/python/plotly-express/) is the easy-to-use, high-level interface to Plotly, which [operates on a variety of types of data](/python/px-arguments/) and produces [easy-to-style figures](/python/styling-plotly-express/)." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "2446910d", + "metadata": {}, + "outputs": [], + "source": [ + "import plotly.express as px\n", + "\n", + "df = px.data.tips()\n", + "fig = px.scatter(df, x=\"total_bill\", y=\"tip\", facet_col=\"sex\",\n", + " width=800, height=400)\n", + "\n", + "fig.update_layout(\n", + " margin=dict(l=20, r=20, t=20, b=20),\n", + " paper_bgcolor=\"LightSteelBlue\",\n", + ")\n", + "\n", + "fig.show()" + ] + }, + { + "cell_type": "markdown", + "id": "e46ee8d7", + "metadata": {}, + "source": [ + "### Adjusting graph size with Dash\n", + "\n", + "[Dash](https://plotly.com/dash/) is the best way to build analytical apps in Python using Plotly figures. To run the app below, run `pip install dash`, click \"Download\" to get the code and run `python app.py`.\n", + "\n", + "Get started with [the official Dash docs](https://dash.plotly.com/installation) and **learn how to effortlessly [style](https://plotly.com/dash/design-kit/) & [deploy](https://plotly.com/dash/app-manager/) apps like this with Dash Enterprise.**" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "e09cfca0", + "metadata": { + "hide_code": true + }, + "outputs": [], + "source": [ + "from IPython.display import IFrame\n", + "snippet_url = 'https://python-docs-dash-snippets.herokuapp.com/python-docs-dash-snippets/'\n", + "IFrame(snippet_url + 'setting-graph-size', width='100%', height=1200)" + ] + }, + { + "cell_type": "markdown", + "id": "db3324ab", + "metadata": {}, + "source": [ + "

Sign up for Dash Club → Free cheat sheets plus updates from Chris Parmer and Adam Schroeder delivered to your inbox every two months. Includes tips and tricks, community apps, and deep dives into the Dash architecture.\n", + "Join now.

" + ] + }, + { + "cell_type": "markdown", + "id": "fbc4e433", + "metadata": {}, + "source": [ + "### Adjusting Height, Width, & Margins With Graph Objects\n", + "\n", + "[Graph objects](/python/graph-objects/) are the low-level building blocks of figures which you can use instead of Plotly Express for greater control." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "535a05fa", + "metadata": {}, + "outputs": [], + "source": [ + "import plotly.graph_objects as go\n", + "\n", + "fig = go.Figure()\n", + "\n", + "fig.add_trace(go.Scatter(\n", + " x=[0, 1, 2, 3, 4, 5, 6, 7, 8],\n", + " y=[0, 1, 2, 3, 4, 5, 6, 7, 8]\n", + "))\n", + "\n", + "fig.update_layout(\n", + " autosize=False,\n", + " width=500,\n", + " height=500,\n", + " margin=dict(\n", + " l=50,\n", + " r=50,\n", + " b=100,\n", + " t=100,\n", + " pad=4\n", + " ),\n", + " paper_bgcolor=\"LightSteelBlue\",\n", + ")\n", + "\n", + "fig.show()" + ] + }, + { + "cell_type": "markdown", + "id": "75f24e46", + "metadata": {}, + "source": [ + "### Automatically Adjust Margins\n", + "\n", + "Set [automargin](https://plotly.com/python/reference/layout/xaxis/#layout-xaxis-automargin) to `True` and Plotly will automatically increase the margin size to prevent ticklabels from being cut off or overlapping with axis titles." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "60d9b7d5", + "metadata": {}, + "outputs": [], + "source": [ + "import plotly.graph_objects as go\n", + "\n", + "\n", + "fig = go.Figure()\n", + "\n", + "fig.add_trace(go.Bar(\n", + " x=[\"Apples\", \"Oranges\", \"Watermelon\", \"Pears\"],\n", + " y=[3, 2, 1, 4]\n", + "))\n", + "\n", + "fig.update_layout(\n", + " autosize=False,\n", + " width=500,\n", + " height=500,\n", + " yaxis=dict(\n", + " title=dict(\n", + " text=\"Y-axis Title\",\n", + " font=dict(\n", + " size=30\n", + " )\n", + " ),\n", + " ticktext=[\"Very long label\", \"long label\", \"3\", \"label\"],\n", + " tickvals=[1, 2, 3, 4],\n", + " tickmode=\"array\",\n", + " )\n", + ")\n", + "\n", + "fig.update_yaxes(automargin=True)\n", + "\n", + "fig.show()" + ] + }, + { + "cell_type": "markdown", + "id": "063db356", + "metadata": {}, + "source": [ + "### Automatically Adjust Specific Margins\n", + "\n", + "*New in 5.10*\n", + "\n", + "You can also set `automargin` for specific sides of the figure. Here, we set `automargin` on the `left` and `top` of the figure." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "c5806350", + "metadata": {}, + "outputs": [], + "source": [ + "import plotly.graph_objects as go\n", + "\n", + "\n", + "fig = go.Figure()\n", + "\n", + "fig.add_trace(go.Bar(\n", + " x=[\"Apples\", \"Oranges\", \"Watermelon\", \"Pears\"],\n", + " y=[3, 2, 1, 4]\n", + "))\n", + "\n", + "fig.update_layout(\n", + " autosize=False,\n", + " width=500,\n", + " height=500,\n", + " yaxis=dict(\n", + " title=dict(\n", + " text=\"Y-axis Title\",\n", + " font=dict(\n", + " size=30\n", + " )\n", + " ),\n", + " ticktext=[\"Very long label\", \"long label\", \"3\", \"label\"],\n", + " tickvals=[1, 2, 3, 4],\n", + " tickmode=\"array\",\n", + " )\n", + ")\n", + "\n", + "fig.update_yaxes(automargin='left+top')\n", + "\n", + "fig.show()" + ] + }, + { + "cell_type": "markdown", + "id": "906bf66c", + "metadata": {}, + "source": [ + "### Setting a Minimum Plot Size with Automargins\n", + "\n", + "*New in 5.11*\n", + "\n", + "To set a minimum width and height for a plot to be after automargin is applied, use `minreducedwidth` and `minreducedheight`. Here we set both to `250`." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "aa2a5a26", + "metadata": {}, + "outputs": [], + "source": [ + "import plotly.graph_objects as go\n", + "\n", + "\n", + "fig = go.Figure()\n", + "\n", + "fig.add_trace(go.Bar(\n", + " x=[\"Apples\", \"Oranges\", \"Watermelon\", \"Pears\"],\n", + " y=[3, 2, 1, 4]\n", + "))\n", + "\n", + "fig.update_layout(\n", + " autosize=False,\n", + " minreducedwidth=250,\n", + " minreducedheight=250,\n", + " width=450,\n", + " height=450,\n", + " yaxis=dict(\n", + " title=dict(\n", + " text=\"Y-axis Title\",\n", + " font=dict(\n", + " size=30\n", + " )\n", + " ),\n", + " ticktext=[\"Label\", \"Very long label\", \"Other label\", \"Very very long label\"],\n", + " tickvals=[1, 2, 3, 4],\n", + " tickmode=\"array\",\n", + " )\n", + ")\n", + "\n", + "\n", + "fig.show()" + ] + }, + { + "cell_type": "markdown", + "id": "09b60ef0", + "metadata": {}, + "source": [ + "#### Reference\n", + "\n", + "See https://plotly.com/python/reference/layout/ for more information and chart attribute options!\n" + ] + }, + { + "cell_type": "markdown", + "id": "ee957f2b", + "metadata": {}, + "source": [ + "### What About Dash?\n", + "\n", + "[Dash](https://dash.plot.ly/) is an open-source framework for building analytical applications, with no Javascript required, and it is tightly integrated with the Plotly graphing library.\n", + "\n", + "Learn about how to install Dash at https://dash.plot.ly/installation.\n", + "\n", + "Everywhere in this page that you see `fig.show()`, you can display the same figure in a Dash application by passing it to the `figure` argument of the [`Graph` component](https://dash.plot.ly/dash-core-components/graph) from the built-in `dash_core_components` package like this:\n", + "\n", + "```python\n", + "import plotly.graph_objects as go # or plotly.express as px\n", + "fig = go.Figure() # or any Plotly Express function e.g. px.bar(...)\n", + "# fig.add_trace( ... )\n", + "# fig.update_layout( ... )\n", + "\n", + "from dash import Dash, dcc, html\n", + "\n", + "app = Dash()\n", + "app.layout = html.Div([\n", + " dcc.Graph(figure=fig)\n", + "])\n", + "\n", + "app.run_server(debug=True, use_reloader=False) # Turn off reloader if inside Jupyter\n", + "```" + ] + } + ], + "metadata": { + "jupytext": { + "notebook_metadata_filter": "all" + }, + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "language": "python", + "name": "python3" + }, + "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.8.0" + }, + "plotly": { + "description": "How to manipulate the graph size, margins and background color.", + "display_as": "file_settings", + "language": "python", + "layout": "base", + "name": "Setting Graph Size", + "order": 11, + "permalink": "python/setting-graph-size/", + "thumbnail": "thumbnail/sizing.png" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/shapes.ipynb b/shapes.ipynb new file mode 100644 index 000000000..fee35bebc --- /dev/null +++ b/shapes.ipynb @@ -0,0 +1,1636 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "4d04791a", + "metadata": {}, + "source": [ + "### Adding Lines and Polygons to Figures\n", + "\n", + "As a general rule, there are two ways to add shapes (lines or polygons) to figures:\n", + "1. Trace types in the `scatter` family (e.g. `scatter`, `scatter3d`, `scattergeo` etc) can be drawn with `mode=\"lines\"` and optionally support a `fill=\"self\"` attribute, and so can be used to draw open or closed shapes on figures.\n", + "2. Standalone lines, ellipses and rectangles can be added to figures using `fig.add_shape()`, and they can be positioned absolutely within the figure, or they can be positioned relative to the axes of 2d cartesian subplots i.e. in data coordinates.\n", + "\n", + "*Note:* there are [special methods `add_hline`, `add_vline`, `add_hrect` and `add_vrect` for the common cases of wanting to draw horizontal or vertical lines or rectangles](/python/horizontal-vertical-shapes/) that are fixed to data coordinates in one axis and absolutely positioned in another.\n", + "\n", + "The differences between these two approaches are that:\n", + "* Traces can optionally support hover labels and can appear in legends.\n", + "* Shapes can be positioned absolutely or relative to data coordinates in 2d cartesian subplots only.\n", + "* Traces cannot be positioned absolutely but can be positioned relative to date coordinates in any subplot type.\n", + "* Traces also support [optional text](/python/text-and-annotations/), although there is a [textual equivalent to shapes in text annotations](/python/text-and-annotations/).\n" + ] + }, + { + "cell_type": "markdown", + "id": "cc76c4dd", + "metadata": {}, + "source": [ + "### Shape-drawing with Scatter traces\n", + "\n", + "There are two ways to draw filled shapes: scatter traces and [layout.shapes](https://plotly.com/python/reference/layout/shapes/#layout-shapes-items-shape-type) which is mostly useful for the 2d subplots, and defines the shape type to be drawn, and can be rectangle, circle, line, or path (a custom SVG path). You also can use [scatterpolar](https://plotly.com/python/polar-chart/#categorical-polar-chart), scattergeo, [scattermapbox](https://plotly.com/python/filled-area-on-mapbox/#filled-scattermapbox-trace) to draw filled shapes on any kind of subplots. To set an area to be filled with a solid color, you need to define [Scatter.fill=\"toself\"](https://plotly.com/python/reference/scatter/#scatter-fill) that connects the endpoints of the trace into a closed shape. If `mode=line` (default value), then you need to repeat the initial point of a shape at the end of the sequence to have a closed shape." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "e96453b8", + "metadata": {}, + "outputs": [], + "source": [ + "import plotly.graph_objects as go\n", + "\n", + "fig = go.Figure(go.Scatter(x=[0,1,2,0], y=[0,2,0,0], fill=\"toself\"))\n", + "fig.show()" + ] + }, + { + "cell_type": "markdown", + "id": "315a1777", + "metadata": {}, + "source": [ + "You can have more shapes either by adding [more traces](https://plotly.com/python/filled-area-plots/) or interrupting the series with `None`." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "cacc5701", + "metadata": {}, + "outputs": [], + "source": [ + "import plotly.graph_objects as go\n", + "\n", + "fig = go.Figure(go.Scatter(x=[0,1,2,0,None,3,3,5,5,3], y=[0,2,0,0,None,0.5,1.5,1.5,0.5,0.5], fill=\"toself\"))\n", + "fig.show()" + ] + }, + { + "cell_type": "markdown", + "id": "7b1465e9", + "metadata": {}, + "source": [ + "#### Shapes in Dash\n", + "\n", + "[Dash](https://plotly.com/dash/) is the best way to build analytical apps in Python using Plotly figures. To run the app below, run `pip install dash`, click \"Download\" to get the code and run `python app.py`.\n", + "\n", + "Get started with [the official Dash docs](https://dash.plotly.com/installation) and **learn how to effortlessly [style](https://plotly.com/dash/design-kit/) & [deploy](https://plotly.com/dash/app-manager/) apps like this with Dash Enterprise.**\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "cf4903f9", + "metadata": { + "hide_code": true + }, + "outputs": [], + "source": [ + "from IPython.display import IFrame\n", + "snippet_url = 'https://python-docs-dash-snippets.herokuapp.com/python-docs-dash-snippets/'\n", + "IFrame(snippet_url + 'shapes', width='100%', height=1200)" + ] + }, + { + "cell_type": "markdown", + "id": "4e47f5c5", + "metadata": {}, + "source": [ + "

Sign up for Dash Club → Free cheat sheets plus updates from Chris Parmer and Adam Schroeder delivered to your inbox every two months. Includes tips and tricks, community apps, and deep dives into the Dash architecture.\n", + "Join now.

" + ] + }, + { + "cell_type": "markdown", + "id": "eb710383", + "metadata": {}, + "source": [ + "#### Vertical and Horizontal Lines Positioned Relative to the Axis Data" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "e424e546", + "metadata": {}, + "outputs": [], + "source": [ + "import plotly.graph_objects as go\n", + "\n", + "fig = go.Figure()\n", + "\n", + "# Create scatter trace of text labels\n", + "fig.add_trace(go.Scatter(\n", + " x=[2, 3.5, 6],\n", + " y=[1, 1.5, 1],\n", + " text=[\"Vertical Line\",\n", + " \"Horizontal Dashed Line\",\n", + " \"Diagonal dotted Line\"],\n", + " mode=\"text\",\n", + "))\n", + "\n", + "# Set axes ranges\n", + "fig.update_xaxes(range=[0, 7])\n", + "fig.update_yaxes(range=[0, 2.5])\n", + "\n", + "# Add shapes\n", + "fig.add_shape(type=\"line\",\n", + " x0=1, y0=0, x1=1, y1=2,\n", + " line=dict(color=\"RoyalBlue\",width=3)\n", + ")\n", + "fig.add_shape(type=\"line\",\n", + " x0=2, y0=2, x1=5, y1=2,\n", + " line=dict(\n", + " color=\"LightSeaGreen\",\n", + " width=4,\n", + " dash=\"dashdot\",\n", + " )\n", + ")\n", + "fig.add_shape(type=\"line\",\n", + " x0=4, y0=0, x1=6, y1=2,\n", + " line=dict(\n", + " color=\"MediumPurple\",\n", + " width=4,\n", + " dash=\"dot\",\n", + " )\n", + ")\n", + "fig.update_shapes(dict(xref='x', yref='y'))\n", + "fig.show()" + ] + }, + { + "cell_type": "markdown", + "id": "f79d86a9", + "metadata": {}, + "source": [ + "#### Lines Positioned Relative to the Plot & to the Axis Data" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "430c3cdf", + "metadata": {}, + "outputs": [], + "source": [ + "import plotly.graph_objects as go\n", + "\n", + "fig = go.Figure()\n", + "\n", + "# Create scatter trace of text labels\n", + "fig.add_trace(go.Scatter(\n", + " x=[2, 6], y=[1, 1],\n", + " text=[\"Line positioned relative to the plot\",\n", + " \"Line positioned relative to the axes\"],\n", + " mode=\"text\",\n", + "))\n", + "\n", + "# Set axes ranges\n", + "fig.update_xaxes(range=[0, 8])\n", + "fig.update_yaxes(range=[0, 2])\n", + "\n", + "fig.add_shape(type=\"line\",\n", + " xref=\"x\", yref=\"y\",\n", + " x0=4, y0=0, x1=8, y1=1,\n", + " line=dict(\n", + " color=\"LightSeaGreen\",\n", + " width=3,\n", + " ),\n", + ")\n", + "fig.add_shape(type=\"line\",\n", + " xref=\"paper\", yref=\"paper\",\n", + " x0=0, y0=0, x1=0.5,\n", + " y1=0.5,\n", + " line=dict(\n", + " color=\"DarkOrange\",\n", + " width=3,\n", + " ),\n", + ")\n", + "\n", + "fig.show()" + ] + }, + { + "cell_type": "markdown", + "id": "bca21f48", + "metadata": {}, + "source": [ + "#### Rectangles Positioned Relative to the Axis Data" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "0f253367", + "metadata": {}, + "outputs": [], + "source": [ + "import plotly.graph_objects as go\n", + "\n", + "fig = go.Figure()\n", + "\n", + "fig.add_trace(go.Scatter(\n", + " x=[1.5, 4.5],\n", + " y=[0.75, 0.75],\n", + " text=[\"Unfilled Rectangle\", \"Filled Rectangle\"],\n", + " mode=\"text\",\n", + "))\n", + "\n", + "# Set axes properties\n", + "fig.update_xaxes(range=[0, 7], showgrid=False)\n", + "fig.update_yaxes(range=[0, 3.5])\n", + "\n", + "# Add shapes\n", + "fig.add_shape(type=\"rect\",\n", + " x0=1, y0=1, x1=2, y1=3,\n", + " line=dict(color=\"RoyalBlue\"),\n", + ")\n", + "fig.add_shape(type=\"rect\",\n", + " x0=3, y0=1, x1=6, y1=2,\n", + " line=dict(\n", + " color=\"RoyalBlue\",\n", + " width=2,\n", + " ),\n", + " fillcolor=\"LightSkyBlue\",\n", + ")\n", + "fig.update_shapes(dict(xref='x', yref='y'))\n", + "fig.show()" + ] + }, + { + "cell_type": "markdown", + "id": "1fc8f17b", + "metadata": {}, + "source": [ + "#### Rectangle Positioned Relative to the Plot & to the Axis Data" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "46262233", + "metadata": {}, + "outputs": [], + "source": [ + "import plotly.graph_objects as go\n", + "\n", + "fig = go.Figure()\n", + "\n", + "# Create scatter trace of text labels\n", + "fig.add_trace(go.Scatter(\n", + " x=[1.5, 3],\n", + " y=[2.5, 2.5],\n", + " text=[\"Rectangle reference to the plot\",\n", + " \"Rectangle reference to the axes\"],\n", + " mode=\"text\",\n", + "))\n", + "\n", + "# Set axes properties\n", + "fig.update_xaxes(range=[0, 4])\n", + "fig.update_yaxes(range=[0, 4])\n", + "\n", + "# Add shapes\n", + "fig.add_shape(type=\"rect\",\n", + " xref=\"x\", yref=\"y\",\n", + " x0=2.5, y0=0,\n", + " x1=3.5, y1=2,\n", + " line=dict(\n", + " color=\"RoyalBlue\",\n", + " width=3,\n", + " ),\n", + " fillcolor=\"LightSkyBlue\",\n", + ")\n", + "fig.add_shape(type=\"rect\",\n", + " xref=\"paper\", yref=\"paper\",\n", + " x0=0.25, y0=0,\n", + " x1=0.5, y1=0.5,\n", + " line=dict(\n", + " color=\"LightSeaGreen\",\n", + " width=3,\n", + " ),\n", + " fillcolor=\"PaleTurquoise\",\n", + ")\n", + "\n", + "fig.show()" + ] + }, + { + "cell_type": "markdown", + "id": "17cc071c", + "metadata": {}, + "source": [ + "#### A Rectangle Placed Relative to the Axis Position and Length\n", + "\n", + "A shape can be placed relative to an axis's position on the plot by adding the\n", + "string `' domain'` to the axis reference in the `xref` or `yref` attributes for\n", + "shapes.\n", + "The following code places a rectangle that starts at 60% and ends at 70% along\n", + "the x-axis, starting from the left, and starts at 80% and ends at 90% along the\n", + "y-axis, starting from the bottom." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "faa68d38", + "metadata": {}, + "outputs": [], + "source": [ + "import plotly.graph_objects as go\n", + "import plotly.express as px\n", + "\n", + "df = px.data.wind()\n", + "fig = px.scatter(df, y=\"frequency\")\n", + "\n", + "fig.update_layout(xaxis=dict(domain=[0, 0.5]), yaxis=dict(domain=[0.25, 0.75]))\n", + "\n", + "# Add a shape whose x and y coordinates refer to the domains of the x and y axes\n", + "fig.add_shape(type=\"rect\",\n", + " xref=\"x domain\", yref=\"y domain\",\n", + " x0=0.6, x1=0.7, y0=0.8, y1=0.9,\n", + ")\n", + "\n", + "fig.show()" + ] + }, + { + "cell_type": "markdown", + "id": "54f5b280", + "metadata": {}, + "source": [ + "#### Highlighting Time Series Regions with Rectangle Shapes\n", + "\n", + "*Note:* there are [special methods `add_hline`, `add_vline`, `add_hrect` and `add_vrect` for the common cases of wanting to draw horizontal or vertical lines or rectangles](/python/horizontal-vertical-shapes/) that are fixed to data coordinates in one axis and absolutely positioned in another.\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "5a60151a", + "metadata": {}, + "outputs": [], + "source": [ + "import plotly.graph_objects as go\n", + "\n", + "fig = go.Figure()\n", + "\n", + "# Add scatter trace for line\n", + "fig.add_trace(go.Scatter(\n", + " x=[\"2015-02-01\", \"2015-02-02\", \"2015-02-03\", \"2015-02-04\", \"2015-02-05\",\n", + " \"2015-02-06\", \"2015-02-07\", \"2015-02-08\", \"2015-02-09\", \"2015-02-10\",\n", + " \"2015-02-11\", \"2015-02-12\", \"2015-02-13\", \"2015-02-14\", \"2015-02-15\",\n", + " \"2015-02-16\", \"2015-02-17\", \"2015-02-18\", \"2015-02-19\", \"2015-02-20\",\n", + " \"2015-02-21\", \"2015-02-22\", \"2015-02-23\", \"2015-02-24\", \"2015-02-25\",\n", + " \"2015-02-26\", \"2015-02-27\", \"2015-02-28\"],\n", + " y=[-14, -17, -8, -4, -7, -10, -12, -14, -12, -7, -11, -7, -18, -14, -14,\n", + " -16, -13, -7, -8, -14, -8, -3, -9, -9, -4, -13, -9, -6],\n", + " mode=\"lines\",\n", + " name=\"temperature\"\n", + "))\n", + "\n", + "# Add shape regions\n", + "fig.add_vrect(\n", + " x0=\"2015-02-04\", x1=\"2015-02-06\",\n", + " fillcolor=\"LightSalmon\", opacity=0.5,\n", + " layer=\"below\", line_width=0,\n", + "),\n", + "\n", + "fig.add_vrect(\n", + " x0=\"2015-02-20\", x1=\"2015-02-22\",\n", + " fillcolor=\"LightSalmon\", opacity=0.5,\n", + " layer=\"below\", line_width=0,\n", + ")\n", + "\n", + "fig.show()" + ] + }, + { + "cell_type": "markdown", + "id": "4880f9fd", + "metadata": {}, + "source": [ + "#### Circles Positioned Relative to the Axis Data" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "48a28fa8", + "metadata": {}, + "outputs": [], + "source": [ + "import plotly.graph_objects as go\n", + "\n", + "fig = go.Figure()\n", + "\n", + "# Create scatter trace of text labels\n", + "fig.add_trace(go.Scatter(\n", + " x=[1.5, 3.5],\n", + " y=[0.75, 2.5],\n", + " text=[\"Unfilled Circle\",\n", + " \"Filled Circle\"],\n", + " mode=\"text\",\n", + "))\n", + "\n", + "# Set axes properties\n", + "fig.update_xaxes(range=[0, 4.5], zeroline=False)\n", + "fig.update_yaxes(range=[0, 4.5])\n", + "\n", + "# Add circles\n", + "fig.add_shape(type=\"circle\",\n", + " xref=\"x\", yref=\"y\",\n", + " x0=1, y0=1, x1=3, y1=3,\n", + " line_color=\"LightSeaGreen\",\n", + ")\n", + "fig.add_shape(type=\"circle\",\n", + " xref=\"x\", yref=\"y\",\n", + " fillcolor=\"PaleTurquoise\",\n", + " x0=3, y0=3, x1=4, y1=4,\n", + " line_color=\"LightSeaGreen\",\n", + ")\n", + "\n", + "# Set figure size\n", + "fig.update_layout(width=800, height=800)\n", + "\n", + "fig.show()" + ] + }, + { + "cell_type": "markdown", + "id": "609851fc", + "metadata": {}, + "source": [ + "#### Highlighting Clusters of Scatter Points with Circle Shapes" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "dd32a178", + "metadata": {}, + "outputs": [], + "source": [ + "import plotly.graph_objects as go\n", + "\n", + "import numpy as np\n", + "np.random.seed(1)\n", + "\n", + "# Generate data\n", + "x0 = np.random.normal(2, 0.45, 300)\n", + "y0 = np.random.normal(2, 0.45, 300)\n", + "\n", + "x1 = np.random.normal(6, 0.4, 200)\n", + "y1 = np.random.normal(6, 0.4, 200)\n", + "\n", + "# Create figure\n", + "fig = go.Figure()\n", + "\n", + "# Add scatter traces\n", + "fig.add_trace(go.Scatter(x=x0, y=y0, mode=\"markers\"))\n", + "fig.add_trace(go.Scatter(x=x1, y=y1, mode=\"markers\"))\n", + "\n", + "# Add shapes\n", + "fig.add_shape(type=\"circle\",\n", + " xref=\"x\", yref=\"y\",\n", + " x0=min(x0), y0=min(y0),\n", + " x1=max(x0), y1=max(y0),\n", + " opacity=0.2,\n", + " fillcolor=\"blue\",\n", + " line_color=\"blue\",\n", + ")\n", + "\n", + "fig.add_shape(type=\"circle\",\n", + " xref=\"x\", yref=\"y\",\n", + " x0=min(x1), y0=min(y1),\n", + " x1=max(x1), y1=max(y1),\n", + " opacity=0.2,\n", + " fillcolor=\"orange\",\n", + " line_color=\"orange\",\n", + ")\n", + "\n", + "\n", + "# Hide legend\n", + "fig.update_layout(showlegend=False)\n", + "\n", + "fig.show()" + ] + }, + { + "cell_type": "markdown", + "id": "41c8f596", + "metadata": {}, + "source": [ + "#### Venn Diagram with Circle Shapes" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "d6cf7a5d", + "metadata": {}, + "outputs": [], + "source": [ + "import plotly.graph_objects as go\n", + "\n", + "fig = go.Figure()\n", + "\n", + "# Create scatter trace of text labels\n", + "fig.add_trace(go.Scatter(\n", + " x=[1, 1.75, 2.5],\n", + " y=[1, 1, 1],\n", + " text=[\"$A$\", \"$A+B$\", \"$B$\"],\n", + " mode=\"text\",\n", + " textfont=dict(\n", + " color=\"black\",\n", + " size=18,\n", + " family=\"Arail\",\n", + " )\n", + "))\n", + "\n", + "# Update axes properties\n", + "fig.update_xaxes(\n", + " showticklabels=False,\n", + " showgrid=False,\n", + " zeroline=False,\n", + ")\n", + "\n", + "fig.update_yaxes(\n", + " showticklabels=False,\n", + " showgrid=False,\n", + " zeroline=False,\n", + ")\n", + "\n", + "# Add circles\n", + "fig.add_shape(type=\"circle\",\n", + " line_color=\"blue\", fillcolor=\"blue\",\n", + " x0=0, y0=0, x1=2, y1=2\n", + ")\n", + "fig.add_shape(type=\"circle\",\n", + " line_color=\"gray\", fillcolor=\"gray\",\n", + " x0=1.5, y0=0, x1=3.5, y1=2\n", + ")\n", + "fig.update_shapes(opacity=0.3, xref=\"x\", yref=\"y\")\n", + "\n", + "fig.update_layout(\n", + " margin=dict(l=20, r=20, b=100),\n", + " height=600, width=800,\n", + " plot_bgcolor=\"white\"\n", + ")\n", + "\n", + "fig.show()" + ] + }, + { + "cell_type": "markdown", + "id": "4628c7f7", + "metadata": {}, + "source": [ + "#### Adding Shapes to Subplots\n", + "Here we use the different axes (`x1`, `x2`) created by `make_subplots` as reference in order to draw shapes in figure subplots." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "24e04c15", + "metadata": {}, + "outputs": [], + "source": [ + "import plotly.graph_objects as go\n", + "from plotly.subplots import make_subplots\n", + "\n", + "# Create Subplots\n", + "fig = make_subplots(rows=2, cols=2)\n", + "\n", + "fig.add_trace(go.Scatter(x=[2, 6], y=[1,1]), row=1, col=1)\n", + "fig.add_trace(go.Bar(x=[1,2,3], y=[4,5,6]), row=1, col=2)\n", + "fig.add_trace(go.Scatter(x=[10,20], y=[40,50]), row=2, col=1)\n", + "fig.add_trace(go.Bar(x=[11,13,15], y=[8,11,20]), row=2, col=2)\n", + "\n", + "# Add shapes\n", + "fig.update_layout(\n", + " shapes=[\n", + " dict(type=\"line\", xref=\"x\", yref=\"y\",\n", + " x0=3, y0=0.5, x1=5, y1=0.8, line_width=3),\n", + " dict(type=\"rect\", xref=\"x2\", yref='y2',\n", + " x0=4, y0=2, x1=5, y1=6),\n", + " dict(type=\"rect\", xref=\"x3\", yref=\"y3\",\n", + " x0=10, y0=20, x1=15, y1=30),\n", + " dict(type=\"circle\", xref=\"x4\", yref=\"y4\",\n", + " x0=5, y0=12, x1=10, y1=18)])\n", + "fig.show()" + ] + }, + { + "cell_type": "markdown", + "id": "a49721ba", + "metadata": { + "lines_to_next_cell": 0 + }, + "source": [ + "#### Adding the Same Shapes to Multiple Subplots\n", + "The same shape can be added to multiple facets by using the `'all'`\n", + "keyword in the `row` and `col` arguments. For example" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "90cf1b6a", + "metadata": { + "lines_to_next_cell": 2 + }, + "outputs": [], + "source": [ + "import plotly.express as px\n", + "\n", + "df = px.data.tips()\n", + "fig = px.scatter(df, x=\"total_bill\", y=\"tip\", facet_row=\"smoker\", facet_col=\"sex\")\n", + "# Adds a rectangle to all facets\n", + "fig.add_shape(\n", + " dict(type=\"rect\", x0=25, x1=35, y0=4, y1=6, line_color=\"purple\"),\n", + " row=\"all\",\n", + " col=\"all\",\n", + ")\n", + "# Adds a line to all the rows of the second column\n", + "fig.add_shape(\n", + " dict(type=\"line\", x0=20, x1=25, y0=5, y1=6, line_color=\"yellow\"), row=\"all\", col=2\n", + ")\n", + "\n", + "# Adds a circle to all the columns of the first row\n", + "fig.add_shape(\n", + " dict(type=\"circle\", x0=10, y0=2, x1=20, y1=7), row=1, col=\"all\", line_color=\"green\"\n", + ")\n", + "fig.show()" + ] + }, + { + "cell_type": "markdown", + "id": "71ed7eb1", + "metadata": {}, + "source": [ + "#### SVG Paths" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "42487fce", + "metadata": {}, + "outputs": [], + "source": [ + "import plotly.graph_objects as go\n", + "\n", + "fig = go.Figure()\n", + "\n", + "# Create scatter trace of text labels\n", + "fig.add_trace(go.Scatter(\n", + " x=[2, 1, 8, 8],\n", + " y=[0.25, 9, 2, 6],\n", + " text=[\"Filled Triangle\",\n", + " \"Filled Polygon\",\n", + " \"Quadratic Bezier Curves\",\n", + " \"Cubic Bezier Curves\"],\n", + " mode=\"text\",\n", + "))\n", + "\n", + "# Update axes properties\n", + "fig.update_xaxes(\n", + " range=[0, 9],\n", + " zeroline=False,\n", + ")\n", + "\n", + "fig.update_yaxes(\n", + " range=[0, 11],\n", + " zeroline=False,\n", + ")\n", + "\n", + "# Add shapes\n", + "fig.update_layout(\n", + " shapes=[\n", + " # Quadratic Bezier Curves\n", + " dict(\n", + " type=\"path\",\n", + " path=\"M 4,4 Q 6,0 8,4\",\n", + " line_color=\"RoyalBlue\",\n", + " ),\n", + " # Cubic Bezier Curves\n", + " dict(\n", + " type=\"path\",\n", + " path=\"M 1,4 C 2,8 6,4 8,8\",\n", + " line_color=\"MediumPurple\",\n", + " ),\n", + " # filled Triangle\n", + " dict(\n", + " type=\"path\",\n", + " path=\" M 1 1 L 1 3 L 4 1 Z\",\n", + " fillcolor=\"LightPink\",\n", + " line_color=\"Crimson\",\n", + " ),\n", + " # filled Polygon\n", + " dict(\n", + " type=\"path\",\n", + " path=\" M 3,7 L2,8 L2,9 L3,10 L4,10 L5,9 L5,8 L4,7 Z\",\n", + " fillcolor=\"PaleTurquoise\",\n", + " line_color=\"LightSeaGreen\",\n", + " ),\n", + " ]\n", + ")\n", + "\n", + "fig.show()" + ] + }, + { + "cell_type": "markdown", + "id": "ce757adc", + "metadata": {}, + "source": [ + "#### Shifting Shapes on Categorical Axes\n", + "\n", + "*New in 5.23*\n", + "\n", + "When drawing shapes where `xref` or `yref` reference axes of type category or multicategory, you can shift `x0`, `x1`, `y0`, and `y1` away from the center of the category using `x0shift`, `x1shift`, `y0shift`, and `y1shift` by specifying a value between -1 and 1.\n", + "\n", + "-1 is the center of the previous category, 0 is the center of the referenced category, and 1 is the center of the next category.\n", + "\n", + "In the following example, the `x0` and `x1` values for both shapes reference category values on the x-axis.\n", + "\n", + "In this example, the first shape:\n", + "- Shifts `x0` half way between the center of category \"Germany\" and the center of the previous category by setting `x0shift=-0.5`\n", + "- Shifts `x1`half way between the center of category \"Germany\" and the center of the next category by setting `x1shift=0.5`\n", + "\n", + "The second shape:\n", + "- Shifts `x0` back to the center of the previous category by setting `x0shift=-1`\n", + "- Shifts `x1`forward to the center of the next category by setting `x1shift=1`" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "17e5532e", + "metadata": {}, + "outputs": [], + "source": [ + "import plotly.graph_objects as go\n", + "import plotly.express as px\n", + "\n", + "df = px.data.gapminder().query(\"continent == 'Europe' and year == 1952\")\n", + "\n", + "fig = go.Figure(\n", + " data=go.Bar(x=df[\"country\"], y=df[\"lifeExp\"], marker_color=\"LightSalmon\"),\n", + " layout=dict(\n", + " shapes=[\n", + " dict(\n", + " type=\"rect\",\n", + " x0=\"Germany\",\n", + " y0=0,\n", + " x1=\"Germany\",\n", + " y1=0.5,\n", + " xref=\"x\",\n", + " yref=\"paper\",\n", + " x0shift=-0.5,\n", + " x1shift=0.5,\n", + " line=dict(color=\"LightGreen\", width=4),\n", + " ),\n", + " dict(\n", + " type=\"rect\",\n", + " x0=\"Spain\",\n", + " y0=0,\n", + " x1=\"Spain\",\n", + " y1=0.5,\n", + " xref=\"x\",\n", + " yref=\"paper\",\n", + " x0shift=-1,\n", + " x1shift=1,\n", + " line=dict(color=\"MediumTurquoise\", width=4),\n", + " ),\n", + " ]\n", + " ),\n", + ")\n", + "\n", + "fig.update_layout(\n", + " title=dict(\n", + " text=\"GDP per Capita in Europe (1972)\"\n", + " ),\n", + " xaxis=dict(\n", + " title=dict(\n", + " text=\"Country\"\n", + " )\n", + " ),\n", + " yaxis=dict(\n", + " title=dict(\n", + " text=\"GDP per Capita\"\n", + " )\n", + " ),\n", + ")\n", + "\n", + "fig.show()\n" + ] + }, + { + "cell_type": "markdown", + "id": "80df966f", + "metadata": {}, + "source": [ + "### Drawing shapes with a Mouse on Cartesian plots\n", + "\n", + "_introduced in plotly 4.7_\n", + "\n", + "You can create layout shapes programmatically, but you can also draw shapes manually by setting the `dragmode` to one of the shape-drawing modes: `'drawline'`,`'drawopenpath'`, `'drawclosedpath'`, `'drawcircle'`, or `'drawrect'`. If you need to switch between different shape-drawing or other dragmodes (panning, selecting, etc.), [modebar buttons can be added](/python/configuration-options#add-optional-shapedrawing-buttons-to-modebar) in the `config` to select the dragmode. If you switch to a different dragmode such as pan or zoom, you will need to select the drawing tool in the modebar to go back to shape drawing.\n", + "\n", + "This shape-drawing feature is particularly interesting for annotating graphs, in particular [image traces](/python/imshow) or [layout images](/python/images).\n", + "\n", + "Once you have drawn shapes, you can select and modify an existing shape by clicking on its boundary (note the arrow pointer). Its fillcolor turns to pink to highlight the activated shape and then you can\n", + "- drag and resize it for lines, rectangles and circles/ellipses\n", + "- drag and move individual vertices for closed paths\n", + "- move individual vertices for open paths.\n", + "\n", + "An activated shape is deleted by clicking on the `eraseshape` button.\n", + "\n", + "Drawing or modifying a shape triggers a `relayout` event, which [can be captured by a callback inside a Dash application](https://dash.plotly.com/interactive-graphing)." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "44568f93", + "metadata": {}, + "outputs": [], + "source": [ + "import plotly.graph_objects as go\n", + "fig = go.Figure()\n", + "text=\"Click and drag here
to draw a rectangle

or select another shape
in the modebar\"\n", + "fig.add_annotation(\n", + " x=0.5,\n", + " y=0.5,\n", + " text=text,\n", + " xref=\"paper\",\n", + " yref=\"paper\",\n", + " showarrow=False,\n", + " font_size=20\n", + ")\n", + "# shape defined programatically\n", + "fig.add_shape(editable=True,\n", + " x0=-1, x1=0, y0=2, y1=3,\n", + " xref='x', yref='y')\n", + "# define dragmode and add modebar buttons\n", + "fig.update_layout(dragmode='drawrect')\n", + "fig.show(config={'modeBarButtonsToAdd':['drawline',\n", + " 'drawopenpath',\n", + " 'drawclosedpath',\n", + " 'drawcircle',\n", + " 'drawrect',\n", + " 'eraseshape'\n", + " ]})" + ] + }, + { + "cell_type": "markdown", + "id": "cef0b377", + "metadata": {}, + "source": [ + "### Style of user-drawn shapes\n", + "\n", + "The layout `newshape` attribute controls the visual appearance of new shapes drawn by the user. `newshape` attributes have the same names as layout shapes.\n", + "\n", + "_Note on shape opacity_: having a new shape's opacity > 0.5 makes it possible to activate a shape by clicking inside the shape (for opacity <= 0.5 you have to click on the border of the shape), but you cannot start a new shape within an existing shape (which is possible for an opacity <= 0.5)." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "c4a9ccd2", + "metadata": {}, + "outputs": [], + "source": [ + "import plotly.graph_objects as go\n", + "fig = go.Figure()\n", + "text=\"Click and drag
to draw a rectangle

or select another shape
in the modebar\"\n", + "fig.add_annotation(\n", + " x=0.5,\n", + " y=0.5,\n", + " text=text,\n", + " xref=\"paper\",\n", + " yref=\"paper\",\n", + " showarrow=False,\n", + " font_size=20\n", + ")\n", + "# shape defined programatically\n", + "fig.add_shape(line_color='yellow',\n", + " fillcolor='turquoise',\n", + " opacity=0.4,\n", + " editable=True,\n", + " x0=0, x1=1, y0=2, y1=3,\n", + " xref='x', yref='y'\n", + ")\n", + "fig.update_layout(dragmode='drawrect',\n", + " # style of new shapes\n", + " newshape=dict(line_color='yellow',\n", + " fillcolor='turquoise',\n", + " opacity=0.5))\n", + "fig.show(config={'modeBarButtonsToAdd':['drawline',\n", + " 'drawopenpath',\n", + " 'drawclosedpath',\n", + " 'drawcircle',\n", + " 'drawrect',\n", + " 'eraseshape'\n", + " ]})" + ] + }, + { + "cell_type": "markdown", + "id": "6445292e", + "metadata": {}, + "source": [ + "### Adding Text Labels to Shapes\n", + "\n", + "*New in 5.14*\n", + "\n", + "Add a text `label` to a shape by adding a `label` property to a shape with `text`. In this example, we add a `rect` and `line` shape and add a text label to both." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "ec46dab5", + "metadata": {}, + "outputs": [], + "source": [ + "import plotly.graph_objects as go\n", + "\n", + "fig = go.Figure()\n", + "\n", + "fig.add_shape(\n", + " type=\"rect\",\n", + " fillcolor='turquoise',\n", + " x0=1,\n", + " y0=1,\n", + " x1=2,\n", + " y1=3,\n", + " label=dict(text=\"Text in rectangle\")\n", + ")\n", + "fig.add_shape(\n", + " type=\"line\",\n", + " x0=3,\n", + " y0=0.5,\n", + " x1=5,\n", + " y1=0.8,\n", + " line_width=3,\n", + " label=dict(text=\"Text above line\")\n", + ")\n", + "\n", + "fig.show()\n" + ] + }, + { + "cell_type": "markdown", + "id": "569c264f", + "metadata": {}, + "source": [ + "#### Styling Text Labels\n", + "\n", + "Use the `font` property to configure the `color`, `size`, and `family` of the label font.\n", + "In this example, we change the label color of the first rectangle to \"DarkOrange\", set the size of the text above the line to 20, and change the font family and set the font size on the second rectangle." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "b72e7842", + "metadata": {}, + "outputs": [], + "source": [ + "import plotly.graph_objects as go\n", + "\n", + "fig = go.Figure()\n", + "\n", + "fig.add_shape(\n", + " type=\"rect\",\n", + " fillcolor='MediumSlateBlue',\n", + " x0=1,\n", + " y0=1,\n", + " x1=2,\n", + " y1=3,\n", + " label=dict(text=\"Text in rectangle\", font=dict(color=\"DarkOrange\")),\n", + ")\n", + "fig.add_shape(\n", + " type=\"line\",\n", + " x0=3,\n", + " y0=0.5,\n", + " x1=5,\n", + " y1=0.8,\n", + " line_width=3,\n", + " label=dict(text=\"Text above line\", font=dict(size=20)),\n", + ")\n", + "fig.add_shape(\n", + " type=\"rect\",\n", + " fillcolor='Lavender',\n", + " x0=2.5,\n", + " y0=2.5,\n", + " x1=5,\n", + " y1=3.5,\n", + " label=dict(\n", + " text=\"Text in rectangle 2\", font=dict(family=\"Courier New, monospace\", size=20)\n", + " ),\n", + ")\n", + "\n", + "fig.show()\n" + ] + }, + { + "cell_type": "markdown", + "id": "5b42a5de", + "metadata": {}, + "source": [ + "#### Setting Label Position\n", + "\n", + "Set a label's position relative to the shape by setting `textposition`. The default position for lines is `middle`. The default position for other shapes is `middle center`.\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "46c74139", + "metadata": {}, + "outputs": [], + "source": [ + "import plotly.graph_objects as go\n", + "\n", + "fig = go.Figure()\n", + "\n", + "fig.add_shape(\n", + " type=\"rect\",\n", + " fillcolor='Lavender',\n", + " x0=0,\n", + " y0=0,\n", + " x1=1.5,\n", + " y1=1.5,\n", + " label=dict(text=\"Text at middle center\"),\n", + ")\n", + "\n", + "fig.add_shape(\n", + " type=\"rect\",\n", + " fillcolor='Lavender',\n", + " x0=3,\n", + " y0=0,\n", + " x1=4.5,\n", + " y1=1.5,\n", + " label=dict(text=\"Text at top left\", textposition=\"top left\"),\n", + ")\n", + "\n", + "\n", + "fig.add_shape(\n", + " type=\"line\",\n", + " line_color=\"MediumSlateBlue\",\n", + " x0=3,\n", + " y0=2,\n", + " x1=5,\n", + " y1=3,\n", + " line_width=3,\n", + " label=dict(text=\"Text at start\", textposition=\"start\"),\n", + ")\n", + "\n", + "\n", + "fig.add_shape(\n", + " type=\"line\",\n", + " line_color=\"MediumSlateBlue\",\n", + " x0=0,\n", + " y0=2,\n", + " x1=2,\n", + " y1=3,\n", + " line_width=3,\n", + " label=dict(text=\"Text at middle\"),\n", + ")\n", + "\n", + "fig.show()\n" + ] + }, + { + "cell_type": "markdown", + "id": "90ab9727", + "metadata": {}, + "source": [ + "#### Setting Label Angle\n", + "\n", + "Use `textangle` to rotate a label by setting a value between -180 and 180. The default angle for a label on a line is the angle of the line. The default angle for a label on other shapes is 0. In this example, in the first shape, the label is at 45 degrees, and in the second, the label is at -45 degrees." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "cc87140f", + "metadata": {}, + "outputs": [], + "source": [ + "import plotly.graph_objects as go\n", + "\n", + "fig = go.Figure()\n", + "\n", + "fig.add_shape(\n", + " type=\"rect\",\n", + " fillcolor='LightGreen',\n", + " x0=0,\n", + " y0=0,\n", + " x1=2,\n", + " y1=2,\n", + " label=dict(text=\"Text at 45\", textangle=45),\n", + ")\n", + "\n", + "fig.add_shape(\n", + " type=\"rect\",\n", + " fillcolor='Gold',\n", + " x0=3,\n", + " y0=0,\n", + " x1=5,\n", + " y1=2,\n", + " label=dict(text=\"Text at -45\", textangle=-45),\n", + ")\n", + "\n", + "fig.show()\n" + ] + }, + { + "cell_type": "markdown", + "id": "2aa81117", + "metadata": {}, + "source": [ + "#### Setting Label Padding\n", + "\n", + "`padding` adds padding between the label and shape. This example shows one line with padding of 30px and another with the default padding, which is 3px." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "bb1d5569", + "metadata": {}, + "outputs": [], + "source": [ + "import plotly.graph_objects as go\n", + "\n", + "fig = go.Figure()\n", + "\n", + "fig.add_shape(\n", + " type=\"line\",\n", + " line_color=\"RoyalBlue\",\n", + " x0=3,\n", + " y0=0,\n", + " x1=5,\n", + " y1=3,\n", + " line_width=3,\n", + " label=dict(text=\"Label padding of 30px\", padding=30),\n", + ")\n", + "\n", + "fig.add_shape(\n", + " type=\"line\",\n", + " line_color=\"RoyalBlue\",\n", + " x0=0,\n", + " y0=0,\n", + " x1=2,\n", + " y1=3,\n", + " line_width=3,\n", + " label=dict(text=\"Default label padding of 3px\"),\n", + ")\n", + "\n", + "fig.show()" + ] + }, + { + "cell_type": "markdown", + "id": "1a806105", + "metadata": {}, + "source": [ + "#### Setting Label Anchors\n", + "\n", + "`xanchor` sets a label's horizontal positional anchor and `yanchor` sets its vertical position anchor.\n", + "Use `xanchor` to bind the `textposition` to the \"left\", \"center\" or \"right\" of the label text and `yanchor` to bind `textposition` to the \"top\", \"middle\" or \"bottom\" of the label text.\n", + "\n", + "In this example, `yanchor`is set to \"top\", instead of the default of \"bottom\" for lines, meaning the text displays below the line.\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "29973bbe", + "metadata": {}, + "outputs": [], + "source": [ + "import plotly.express as px\n", + "\n", + "df = px.data.stocks(indexed=True)\n", + "fig = px.line(df)\n", + "\n", + "fig.add_shape(\n", + " type=\"rect\",\n", + " x0=\"2018-09-24\",\n", + " y0=0,\n", + " x1=\"2018-12-18\",\n", + " y1=3,\n", + " line_width=0,\n", + " label=dict(text=\"Decline\", textposition=\"top center\", font=dict(size=20)),\n", + " fillcolor=\"green\",\n", + " opacity=0.25,\n", + ")\n", + "\n", + "fig.add_shape(\n", + " type=\"line\",\n", + " x0=min(df.index),\n", + " y0=1,\n", + " x1=max(df.index),\n", + " y1=1,\n", + " line_width=3,\n", + " line_dash=\"dot\",\n", + " label=dict(\n", + " text=\"Jan 1 2018 Baseline\",\n", + " textposition=\"end\",\n", + " font=dict(size=20, color=\"blue\"),\n", + " yanchor=\"top\",\n", + " ),\n", + ")\n", + "\n", + "fig.show()" + ] + }, + { + "cell_type": "markdown", + "id": "0113daab", + "metadata": {}, + "source": [ + "#### Variables in Shape Label Text\n", + "\n", + "*New in 5.15*\n", + "\n", + "Use `texttemplate` to add text with variables to shapes. You have access to raw variables (`x0`, `x1`, `y0`, `y1`), which use raw data values from the shape definition, and the following calculated variables:\n", + "\n", + "- `xcenter`: (x0 + x1) / 2\n", + "- `ycenter`: (y0 + y1) / 2\n", + "- `dx`: x1 - x0\n", + "- `dy`: y1 - y0\n", + "- `width`: abs(x1 - x0)\n", + "- `height`: abs(y1 - y0)\n", + "- `length` (for lines only): sqrt(dx^2 + dy^2)\n", + "- `slope`: (y1 - y0) / (x1 - x0)\n", + "\n", + "`texttemplate` supports d3 number and date formatting.\n", + "\n", + "Add a variable with \"%{variable}\". This example adds the raw variables `x0` and `y0` to a rectangle and shows the calculated variables `height`, `slope`, `length`, and `width` on three other shapes." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "ea5c8784", + "metadata": {}, + "outputs": [], + "source": [ + "import plotly.graph_objects as go\n", + "\n", + "fig = go.Figure()\n", + "\n", + "\n", + "fig.add_shape(\n", + " type=\"rect\",\n", + " fillcolor=\"MediumSlateBlue\",\n", + " x0=-0.5,\n", + " y0=-0.5,\n", + " x1=1,\n", + " y1=1,\n", + " label=dict(\n", + " texttemplate=\"x0 is %{x0:.3f}, y0 is %{y0:.3f}\", font=dict(color=\"DarkOrange\")\n", + " ),\n", + ")\n", + "\n", + "fig.add_shape(\n", + " type=\"rect\",\n", + " fillcolor=\"LightGreen\",\n", + " x0=1,\n", + " y0=1.75,\n", + " x1=2.25,\n", + " y1=3,\n", + " label=dict(texttemplate=\"Height: %{height:.3f}\", font=dict(color=\"DarkOrange\")),\n", + ")\n", + "fig.add_shape(\n", + " type=\"line\",\n", + " x0=3,\n", + " y0=0.5,\n", + " x1=5,\n", + " y1=1.5,\n", + " line_width=3,\n", + " label=dict(\n", + " texttemplate=\"Slope of %{slope:.3f} and length of %{length:.3f}\",\n", + " font=dict(size=20),\n", + " ),\n", + ")\n", + "fig.add_shape(\n", + " type=\"rect\",\n", + " fillcolor=\"Lavender\",\n", + " x0=2.5,\n", + " y0=2.5,\n", + " x1=5,\n", + " y1=3.5,\n", + " label=dict(\n", + " texttemplate=\"Width: %{width:.3f}\",\n", + " font=dict(family=\"Courier New, monospace\", size=20),\n", + " ),\n", + ")\n", + "\n", + "fig.show()\n" + ] + }, + { + "cell_type": "markdown", + "id": "cfd7e131", + "metadata": {}, + "source": [ + "#### Variables in Shape Label Text for New Shapes\n", + "\n", + "*New in 5.15*\n", + "\n", + "You can also use `texttemplate` to add text with variables to new shapes drawn on the graph.\n", + "\n", + "In this example, we enable drawing lines on the figure by adding `drawline` to `modeBarButtonsToAdd` in `config`. We then define a `texttemplate` for shapes that shows the calculated variable `dy`. Select **Draw line** in the modebar to try it out." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "b24a434c", + "metadata": {}, + "outputs": [], + "source": [ + "import plotly.graph_objects as go\n", + "from plotly import data\n", + "\n", + "df = data.stocks()\n", + "\n", + "fig = go.Figure(\n", + " data=go.Scatter(\n", + " x=df.date,\n", + " y=df.GOOG,\n", + " ),\n", + " layout=go.Layout(\n", + " yaxis=dict(\n", + " title=dict(\n", + " text=\"Price in USD\"\n", + " )),\n", + " newshape=dict(\n", + " label=dict(texttemplate=\"Change: %{dy:.2f}\")\n", + " ),\n", + " title=dict(text=\"Google Share Price 2018/2019\"),\n", + " ),\n", + ")\n", + "\n", + "\n", + "fig.show(\n", + " config={\n", + " \"modeBarButtonsToAdd\": [\n", + " \"drawline\",\n", + " ]\n", + " }\n", + ")" + ] + }, + { + "cell_type": "markdown", + "id": "54b26c80", + "metadata": {}, + "source": [ + "#### Shapes in the Legend\n", + "\n", + "*New in 5.16*\n", + "\n", + "You can add a shape to the legend by setting `showlegend=True` on the shape. In this example, we add the second shape to the legend. The name that appears for the shape in the legend is the shape's `name` if it is provided. If no `name` is provided, the shape label's `text` is used. If neither is provided, the legend item appears as \"shape \\\". For example, \"shape 1\"." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "12d66ac8", + "metadata": {}, + "outputs": [], + "source": [ + "import plotly.express as px\n", + "\n", + "df = px.data.stocks(indexed=True)\n", + "\n", + "fig = px.line(df)\n", + "\n", + "fig.add_shape(\n", + " type=\"rect\",\n", + " x0=\"2018-09-24\",\n", + " y0=0,\n", + " x1=\"2018-12-18\",\n", + " y1=3,\n", + " line_width=0,\n", + " label=dict(text=\"Decline\", textposition=\"top center\", font=dict(size=20)),\n", + " fillcolor=\"green\",\n", + " opacity=0.25,\n", + ")\n", + "\n", + "fig.add_shape(\n", + " showlegend=True,\n", + " type=\"line\",\n", + " x0=min(df.index),\n", + " y0=1,\n", + " x1=max(df.index),\n", + " y1=1,\n", + " line_width=3,\n", + " line_dash=\"dot\",\n", + " label=dict(\n", + " text=\"Jan 1 2018 Baseline\",\n", + " textposition=\"end\",\n", + " font=dict(size=20, color=\"blue\"),\n", + " yanchor=\"top\",\n", + " ),\n", + ")\n", + "\n", + "fig.show()" + ] + }, + { + "cell_type": "markdown", + "id": "f407cad8", + "metadata": {}, + "source": [ + "`newshape` also supports `showlegend`. In this example, each new line drawn on the graph appears in the legend." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "c0f72469", + "metadata": {}, + "outputs": [], + "source": [ + "import plotly.graph_objects as go\n", + "from plotly import data\n", + "\n", + "df = data.stocks()\n", + "\n", + "fig = go.Figure(\n", + " data=go.Scatter(\n", + " x=df.date,\n", + " y=df.AAPL,\n", + " name=\"Apple\"\n", + " ),\n", + " layout=go.Layout(\n", + " yaxis=dict(\n", + " title=dict(text=\"Price in USD\"),\n", + " ),\n", + " newshape=dict(\n", + " showlegend=True,\n", + " label=dict(texttemplate=\"Change: %{dy:.2f}\")\n", + " ),\n", + " title=dict(text=\"Apple Share Price 2018/2019\"),\n", + " ),\n", + ")\n", + "\n", + "\n", + "fig.show(\n", + " config={\n", + " \"modeBarButtonsToAdd\": [\n", + " \"drawline\",\n", + " ]\n", + " }\n", + ")" + ] + }, + { + "cell_type": "markdown", + "id": "258afbfb", + "metadata": {}, + "source": [ + "#### Shape Layer\n", + "\n", + "By default, shapes are drawn above traces. You can also configure them to be drawn between traces and gridlines with `layer=\"between\"` (new in 5.21), or below gridlines with `layer=\"below\"`." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "97677519", + "metadata": {}, + "outputs": [], + "source": [ + "import plotly.express as px\n", + "\n", + "df = px.data.stocks(indexed=True)\n", + "\n", + "fig = px.line(df)\n", + "\n", + "fig.add_shape(\n", + " type=\"rect\",\n", + " x0=\"2018-03-01\",\n", + " y0=0,\n", + " x1=\"2018-08-01\",\n", + " y1=3,\n", + " line_width=0,\n", + " layer=\"above\",\n", + " label=dict(text=\"Above\", textposition=\"top center\", font=dict(size=15)),\n", + " fillcolor=\"LightGreen\",\n", + " opacity=0.80,\n", + ")\n", + "\n", + "fig.add_shape(\n", + " type=\"rect\",\n", + " x0=\"2018-10-01\",\n", + " y0=0,\n", + " x1=\"2019-03-01\",\n", + " y1=3,\n", + " line_width=0,\n", + " layer=\"between\",\n", + " label=dict(text=\"Between\", textposition=\"top center\", font=dict(size=15)),\n", + " fillcolor=\"LightGreen\",\n", + " opacity=0.80,\n", + ")\n", + "\n", + "fig.add_shape(\n", + " type=\"rect\",\n", + " x0=\"2019-05-01\",\n", + " y0=0,\n", + " x1=\"2019-10-01\",\n", + " y1=3,\n", + " line_width=0,\n", + " layer=\"below\",\n", + " label=dict(text=\"Below\", textposition=\"top center\", font=dict(size=15)),\n", + " fillcolor=\"LightGreen\",\n", + " opacity=0.80,\n", + ")\n", + "\n", + "fig.show()" + ] + }, + { + "cell_type": "markdown", + "id": "63a74c58", + "metadata": {}, + "source": [ + "### Reference\n", + "See https://plotly.com/python/reference/layout/shapes/ for more information and chart attribute options!\n" + ] + }, + { + "cell_type": "markdown", + "id": "ae88fc2c", + "metadata": {}, + "source": [ + "### What About Dash?\n", + "\n", + "[Dash](https://dash.plot.ly/) is an open-source framework for building analytical applications, with no Javascript required, and it is tightly integrated with the Plotly graphing library.\n", + "\n", + "Learn about how to install Dash at https://dash.plot.ly/installation.\n", + "\n", + "Everywhere in this page that you see `fig.show()`, you can display the same figure in a Dash application by passing it to the `figure` argument of the [`Graph` component](https://dash.plot.ly/dash-core-components/graph) from the built-in `dash_core_components` package like this:\n", + "\n", + "```python\n", + "import plotly.graph_objects as go # or plotly.express as px\n", + "fig = go.Figure() # or any Plotly Express function e.g. px.bar(...)\n", + "# fig.add_trace( ... )\n", + "# fig.update_layout( ... )\n", + "\n", + "from dash import Dash, dcc, html\n", + "\n", + "app = Dash()\n", + "app.layout = html.Div([\n", + " dcc.Graph(figure=fig)\n", + "])\n", + "\n", + "app.run_server(debug=True, use_reloader=False) # Turn off reloader if inside Jupyter\n", + "```" + ] + } + ], + "metadata": { + "jupytext": { + "notebook_metadata_filter": "all" + }, + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "language": "python", + "name": "python3" + }, + "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.14" + }, + "plotly": { + "description": "How to make SVG shapes in python. Examples of lines, circle, rectangle, and path.", + "display_as": "file_settings", + "language": "python", + "layout": "base", + "name": "Shapes", + "order": 25, + "permalink": "python/shapes/", + "thumbnail": "thumbnail/shape.jpg" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/sliders.ipynb b/sliders.ipynb new file mode 100644 index 000000000..23c9412e5 --- /dev/null +++ b/sliders.ipynb @@ -0,0 +1,178 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "0f5ee89b", + "metadata": {}, + "source": [ + "### Simple Slider Control\n", + "Sliders can be used in Plotly to change the data displayed or style of a plot." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "7033d510", + "metadata": {}, + "outputs": [], + "source": [ + "import plotly.graph_objects as go\n", + "import numpy as np\n", + "\n", + "# Create figure\n", + "fig = go.Figure()\n", + "\n", + "# Add traces, one for each slider step\n", + "for step in np.arange(0, 5, 0.1):\n", + " fig.add_trace(\n", + " go.Scatter(\n", + " visible=False,\n", + " line=dict(color=\"#00CED1\", width=6),\n", + " name=\"𝜈 = \" + str(step),\n", + " x=np.arange(0, 10, 0.01),\n", + " y=np.sin(step * np.arange(0, 10, 0.01))))\n", + "\n", + "# Make 10th trace visible\n", + "fig.data[10].visible = True\n", + "\n", + "# Create and add slider\n", + "steps = []\n", + "for i in range(len(fig.data)):\n", + " step = dict(\n", + " method=\"update\",\n", + " args=[{\"visible\": [False] * len(fig.data)},\n", + " {\"title\": \"Slider switched to step: \" + str(i)}], # layout attribute\n", + " )\n", + " step[\"args\"][0][\"visible\"][i] = True # Toggle i'th trace to \"visible\"\n", + " steps.append(step)\n", + "\n", + "sliders = [dict(\n", + " active=10,\n", + " currentvalue={\"prefix\": \"Frequency: \"},\n", + " pad={\"t\": 50},\n", + " steps=steps\n", + ")]\n", + "\n", + "fig.update_layout(\n", + " sliders=sliders\n", + ")\n", + "\n", + "fig.show()" + ] + }, + { + "cell_type": "markdown", + "id": "da3ab81b", + "metadata": {}, + "source": [ + "#### Methods\n", + "The method determines which [plotly.js function](https://plot.ly/javascript/plotlyjs-function-reference/) will be used to update the chart. Plotly can use several [updatemenu](https://plot.ly/python/reference/layout/updatemenus/#layout-updatemenus-items-updatemenu-buttons-items-button-method) methods to add the slider:\n", + "- `\"update\"`: modify **data and layout** attributes (as above)\n", + "- `\"restyle\"`: modify **data** attributes\n", + "- `\"relayout\"`: modify **layout** attributes\n", + "- `\"animate\"`: start or pause an animation" + ] + }, + { + "cell_type": "markdown", + "id": "9a97e065", + "metadata": {}, + "source": [ + "### Sliders in Plotly Express\n", + "Plotly Express provide sliders, but with implicit animation using the `\"animate\"` method described above. The animation play button can be omitted by removing `updatemenus` in the `layout`:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "39159b73", + "metadata": {}, + "outputs": [], + "source": [ + "import plotly.express as px\n", + "\n", + "df = px.data.gapminder()\n", + "fig = px.scatter(df, x=\"gdpPercap\", y=\"lifeExp\", animation_frame=\"year\", animation_group=\"country\",\n", + " size=\"pop\", color=\"continent\", hover_name=\"country\",\n", + " log_x=True, size_max=55, range_x=[100,100000], range_y=[25,90])\n", + "\n", + "fig[\"layout\"].pop(\"updatemenus\") # optional, drop animation buttons\n", + "fig.show()" + ] + }, + { + "cell_type": "markdown", + "id": "540c2ac6", + "metadata": {}, + "source": [ + "#### Reference\n", + "Check out https://plotly.com/python/reference/layout/updatemenus/ for more information!\n" + ] + }, + { + "cell_type": "markdown", + "id": "08d49383", + "metadata": {}, + "source": [ + "### What About Dash?\n", + "\n", + "[Dash](https://dash.plot.ly/) is an open-source framework for building analytical applications, with no Javascript required, and it is tightly integrated with the Plotly graphing library.\n", + "\n", + "Learn about how to install Dash at https://dash.plot.ly/installation.\n", + "\n", + "Everywhere in this page that you see `fig.show()`, you can display the same figure in a Dash application by passing it to the `figure` argument of the [`Graph` component](https://dash.plot.ly/dash-core-components/graph) from the built-in `dash_core_components` package like this:\n", + "\n", + "```python\n", + "import plotly.graph_objects as go # or plotly.express as px\n", + "fig = go.Figure() # or any Plotly Express function e.g. px.bar(...)\n", + "# fig.add_trace( ... )\n", + "# fig.update_layout( ... )\n", + "\n", + "from dash import Dash, dcc, html\n", + "\n", + "app = Dash()\n", + "app.layout = html.Div([\n", + " dcc.Graph(figure=fig)\n", + "])\n", + "\n", + "app.run_server(debug=True, use_reloader=False) # Turn off reloader if inside Jupyter\n", + "```" + ] + } + ], + "metadata": { + "jupytext": { + "notebook_metadata_filter": "all" + }, + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "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.7.2" + }, + "plotly": { + "description": "How to add slider controls to your plots in Python with Plotly.", + "display_as": "controls", + "language": "python", + "layout": "base", + "name": "Sliders", + "order": 1.5, + "page_type": "example_index", + "permalink": "python/sliders/", + "thumbnail": "thumbnail/slider2017.gif" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/smith-charts.ipynb b/smith-charts.ipynb new file mode 100644 index 000000000..3578fac41 --- /dev/null +++ b/smith-charts.ipynb @@ -0,0 +1,174 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "e38fd208", + "metadata": {}, + "source": [ + "*New in v5.4*\n", + "\n", + "A [Smith Chart](https://en.wikipedia.org/wiki/Smith_chart) is a specialized chart for visualizing [complex numbers](https://en.wikipedia.org/wiki/Complex_number): numbers with both a real and imaginary part." + ] + }, + { + "cell_type": "markdown", + "id": "40da23ff", + "metadata": {}, + "source": [ + "### Smith Charts with Plotly Graph Objects" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "a15207bf", + "metadata": {}, + "outputs": [], + "source": [ + "import plotly.graph_objects as go\n", + "\n", + "fig = go.Figure(go.Scattersmith(imag=[0.5, 1, 2, 3], real=[0.5, 1, 2, 3]))\n", + "fig.show()" + ] + }, + { + "cell_type": "markdown", + "id": "a016e052", + "metadata": {}, + "source": [ + "### Smith Chart Subplots and Styling" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "26a66bf6", + "metadata": {}, + "outputs": [], + "source": [ + "import plotly.graph_objects as go\n", + "\n", + "fig = go.Figure()\n", + "\n", + "fig.add_trace(go.Scattersmith(\n", + " imag=[1],\n", + " real=[1],\n", + " marker_symbol='x',\n", + " marker_size=30,\n", + " marker_color=\"green\",\n", + " subplot=\"smith1\"\n", + "))\n", + "\n", + "fig.add_trace(go.Scattersmith(\n", + " imag=[1],\n", + " real=[1],\n", + " marker_symbol='x',\n", + " marker_size=30,\n", + " marker_color=\"pink\",\n", + " subplot=\"smith2\"\n", + "))\n", + "\n", + "fig.update_layout(\n", + " smith=dict(\n", + " realaxis_gridcolor='red',\n", + " imaginaryaxis_gridcolor='blue',\n", + " domain=dict(x=[0,0.45])\n", + " ),\n", + " smith2=dict(\n", + " realaxis_gridcolor='blue',\n", + " imaginaryaxis_gridcolor='red',\n", + " domain=dict(x=[0.55,1])\n", + " )\n", + ")\n", + "\n", + "fig.update_smiths(bgcolor=\"lightgrey\")\n", + "\n", + "fig.show()" + ] + }, + { + "cell_type": "markdown", + "id": "07a01f91", + "metadata": {}, + "source": [ + "#### Reference\n", + "See https://plotly.com/python/reference/scattersmith/ and https://plotly.com/python/reference/layout/smith/ for more information and chart attribute options!" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "3231e170", + "metadata": { + "lines_to_next_cell": 2 + }, + "outputs": [], + "source": [] + }, + { + "cell_type": "markdown", + "id": "5fbee4da", + "metadata": {}, + "source": [ + "### What About Dash?\n", + "\n", + "[Dash](https://dash.plot.ly/) is an open-source framework for building analytical applications, with no Javascript required, and it is tightly integrated with the Plotly graphing library.\n", + "\n", + "Learn about how to install Dash at https://dash.plot.ly/installation.\n", + "\n", + "Everywhere in this page that you see `fig.show()`, you can display the same figure in a Dash application by passing it to the `figure` argument of the [`Graph` component](https://dash.plot.ly/dash-core-components/graph) from the built-in `dash_core_components` package like this:\n", + "\n", + "```python\n", + "import plotly.graph_objects as go # or plotly.express as px\n", + "fig = go.Figure() # or any Plotly Express function e.g. px.bar(...)\n", + "# fig.add_trace( ... )\n", + "# fig.update_layout( ... )\n", + "\n", + "from dash import Dash, dcc, html\n", + "\n", + "app = Dash()\n", + "app.layout = html.Div([\n", + " dcc.Graph(figure=fig)\n", + "])\n", + "\n", + "app.run_server(debug=True, use_reloader=False) # Turn off reloader if inside Jupyter\n", + "```" + ] + } + ], + "metadata": { + "jupytext": { + "notebook_metadata_filter": "all" + }, + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "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.8.11" + }, + "plotly": { + "description": "How to make Smith Charts with plotly.", + "display_as": "scientific", + "language": "python", + "layout": "base", + "name": "Smith Charts", + "order": 20, + "page_type": "u-guide", + "permalink": "python/smith-charts/", + "thumbnail": "thumbnail/contourcarpet.jpg" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/smoothing.ipynb b/smoothing.ipynb new file mode 100644 index 000000000..04fafaab0 --- /dev/null +++ b/smoothing.ipynb @@ -0,0 +1,258 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "3de73642", + "metadata": {}, + "source": [ + "#### Imports\n", + "\n", + "The tutorial below imports [NumPy](http://www.numpy.org/), [Pandas](https://pandas.pydata.org/docs/user_guide/10min.html), [SciPy](https://www.scipy.org/) and [Plotly](https://plotly.com/python/getting-started/)." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "b9753fe7", + "metadata": {}, + "outputs": [], + "source": [ + "import plotly.graph_objects as go\n", + "\n", + "import numpy as np\n", + "import pandas as pd\n", + "import scipy\n", + "\n", + "from scipy import signal" + ] + }, + { + "cell_type": "markdown", + "id": "a91570a8", + "metadata": {}, + "source": [ + "#### Savitzky-Golay Filter\n", + "\n", + "`Smoothing` is a technique that is used to eliminate noise from a dataset. There are many algorithms and methods to accomplish this but all have the same general purpose of 'roughing out the edges' or 'smoothing' some data.\n", + "\n", + "There is reason to smooth data if there is little to no small-scale structure in the data. The danger to this thinking is that one may skew the representation of the data enough to change its perceived meaning, so for the sake of scientific honesty it is an imperative to at the very minimum explain one's reason's for using a smoothing algorithm to their dataset.\n", + "\n", + "In this example we use the [Savitzky-Golay Filter](https://en.wikipedia.org/wiki/Savitzky%E2%80%93Golay_filter), which fits subsequent windows of adjacent data with a low-order polynomial." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "68a0f460", + "metadata": {}, + "outputs": [], + "source": [ + "import plotly.graph_objects as go\n", + "\n", + "import numpy as np\n", + "import pandas as pd\n", + "import scipy\n", + "\n", + "from scipy import signal\n", + "\n", + "np.random.seed(1)\n", + "\n", + "x = np.linspace(0, 10, 100)\n", + "y = np.sin(x)\n", + "noise = 2 * np.random.random(len(x)) - 1 # uniformly distributed between -1 and 1\n", + "y_noise = y + noise\n", + "\n", + "fig = go.Figure()\n", + "fig.add_trace(go.Scatter(\n", + " x=x,\n", + " y=y,\n", + " mode='markers',\n", + " marker=dict(size=2, color='black'),\n", + " name='Sine'\n", + "))\n", + "\n", + "fig.add_trace(go.Scatter(\n", + " x=x,\n", + " y=y_noise,\n", + " mode='markers',\n", + " marker=dict(\n", + " size=6,\n", + " color='royalblue',\n", + " symbol='circle-open'\n", + " ),\n", + " name='Noisy Sine'\n", + "))\n", + "\n", + "fig.add_trace(go.Scatter(\n", + " x=x,\n", + " y=signal.savgol_filter(y_noise,\n", + " 53, # window size used for filtering\n", + " 3), # order of fitted polynomial\n", + " mode='markers',\n", + " marker=dict(\n", + " size=6,\n", + " color='mediumpurple',\n", + " symbol='triangle-up'\n", + " ),\n", + " name='Savitzky-Golay'\n", + "))\n", + "\n", + "\n", + "fig.show()" + ] + }, + { + "cell_type": "markdown", + "id": "2d3d6908", + "metadata": {}, + "source": [ + "#### Triangular Moving Average\n", + "\n", + "Another method for smoothing is a moving average. There are various forms of this, but the idea is to take a window of points in your dataset, compute an average of the points, then shift the window over by one point and repeat. This will generate a bunch of points which will result in the `smoothed` data.\n", + "\n", + "Let us look at the common `Simple Moving Average` first. In the 1D case we have a data set of $N$ points with y-values $y_1, y_2, ..., y_N$. Setting our window size to $n < N$, the new $i^{th}$ y-value after smoothing is computed as:\n", + "\n", + "$$\n", + "\\begin{align*}\n", + "SMA_i = \\frac{y_i + ... + y_{i+n}}{n}\n", + "\\end{align*}\n", + "$$\n", + "\n", + "In the `Triangular Moving Average`, two simple moving averages are computed on top of each other, in order to give more weight to closer (adjacent) points. This means that our $SMA_i$ are computed then a Triangular Moving Average $TMA_i$ is computed as:\n", + "\n", + "$$\n", + "\\begin{align*}\n", + "TMA_i = \\frac{SMA_i + ... + SMA_{i+n}}{n}\n", + "\\end{align*}\n", + "$$" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "0906141d", + "metadata": { + "lines_to_next_cell": 2 + }, + "outputs": [], + "source": [ + "def smoothTriangle(data, degree):\n", + " triangle=np.concatenate((np.arange(degree + 1), np.arange(degree)[::-1])) # up then down\n", + " smoothed=[]\n", + "\n", + " for i in range(degree, len(data) - degree * 2):\n", + " point=data[i:i + len(triangle)] * triangle\n", + " smoothed.append(np.sum(point)/np.sum(triangle))\n", + " # Handle boundaries\n", + " smoothed=[smoothed[0]]*int(degree + degree/2) + smoothed\n", + " while len(smoothed) < len(data):\n", + " smoothed.append(smoothed[-1])\n", + " return smoothed\n", + "\n", + "fig = go.Figure()\n", + "fig.add_trace(go.Scatter(\n", + " x=x,\n", + " y=y,\n", + " mode='markers',\n", + " marker=dict(\n", + " size=2,\n", + " color='rgb(0, 0, 0)',\n", + " ),\n", + " name='Sine'\n", + "))\n", + "\n", + "fig.add_trace(go.Scatter(\n", + " x=x,\n", + " y=y_noise,\n", + " mode='markers',\n", + " marker=dict(\n", + " size=6,\n", + " color='#5E88FC',\n", + " symbol='circle-open'\n", + " ),\n", + " name='Noisy Sine'\n", + "))\n", + "\n", + "fig.add_trace(go.Scatter(\n", + " x=x,\n", + " y=smoothTriangle(y_noise, 10), # setting degree to 10\n", + " mode='markers',\n", + " marker=dict(\n", + " size=6,\n", + " color='#C190F0',\n", + " symbol='triangle-up'\n", + " ),\n", + " name='Moving Triangle - Degree 10'\n", + "))\n", + "\n", + "fig.show()" + ] + }, + { + "cell_type": "markdown", + "id": "8bc74b6c", + "metadata": {}, + "source": [ + "### What About Dash?\n", + "\n", + "[Dash](https://dash.plot.ly/) is an open-source framework for building analytical applications, with no Javascript required, and it is tightly integrated with the Plotly graphing library.\n", + "\n", + "Learn about how to install Dash at https://dash.plot.ly/installation.\n", + "\n", + "Everywhere in this page that you see `fig.show()`, you can display the same figure in a Dash application by passing it to the `figure` argument of the [`Graph` component](https://dash.plot.ly/dash-core-components/graph) from the built-in `dash_core_components` package like this:\n", + "\n", + "```python\n", + "import plotly.graph_objects as go # or plotly.express as px\n", + "fig = go.Figure() # or any Plotly Express function e.g. px.bar(...)\n", + "# fig.add_trace( ... )\n", + "# fig.update_layout( ... )\n", + "\n", + "from dash import Dash, dcc, html\n", + "\n", + "app = Dash()\n", + "app.layout = html.Div([\n", + " dcc.Graph(figure=fig)\n", + "])\n", + "\n", + "app.run_server(debug=True, use_reloader=False) # Turn off reloader if inside Jupyter\n", + "```" + ] + } + ], + "metadata": { + "jupytext": { + "notebook_metadata_filter": "all" + }, + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "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.6.7" + }, + "plotly": { + "description": "Learn how to perform smoothing using various methods in Python.", + "display_as": "advanced_opt", + "has_thumbnail": false, + "language": "python", + "layout": "base", + "name": "Smoothing", + "order": 4, + "page_type": "example_index", + "permalink": "python/smoothing/", + "thumbnail": "/images/static-image" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/splom.ipynb b/splom.ipynb new file mode 100644 index 000000000..9e7fd7238 --- /dev/null +++ b/splom.ipynb @@ -0,0 +1,496 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "3125de4e", + "metadata": {}, + "source": [ + "### Scatter matrix with Plotly Express\n", + "\n", + "A scatterplot matrix is a matrix associated to n numerical arrays (data variables), $X_1,X_2,…,X_n$ , of the same length. The cell (i,j) of such a matrix displays the scatter plot of the variable Xi versus Xj.\n", + "\n", + "Here we show the Plotly Express function `px.scatter_matrix` to plot the scatter matrix for the columns of the dataframe. By default, all columns are considered.\n", + "\n", + "[Plotly Express](/python/plotly-express/) is the easy-to-use, high-level interface to Plotly, which [operates on a variety of types of data](/python/px-arguments/) and produces [easy-to-style figures](/python/styling-plotly-express/)." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "954f25ca", + "metadata": {}, + "outputs": [], + "source": [ + "import plotly.express as px\n", + "df = px.data.iris()\n", + "fig = px.scatter_matrix(df)\n", + "fig.show()" + ] + }, + { + "cell_type": "markdown", + "id": "747e30d3", + "metadata": {}, + "source": [ + "Specify the columns to be represented with the `dimensions` argument, and set colors using a column of the dataframe:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "33e2995c", + "metadata": {}, + "outputs": [], + "source": [ + "import plotly.express as px\n", + "df = px.data.iris()\n", + "fig = px.scatter_matrix(df,\n", + " dimensions=[\"sepal_length\", \"sepal_width\", \"petal_length\", \"petal_width\"],\n", + " color=\"species\")\n", + "fig.show()" + ] + }, + { + "cell_type": "markdown", + "id": "10291702", + "metadata": {}, + "source": [ + "#### Styled Scatter Matrix with Plotly Express\n", + "\n", + "The scatter matrix plot can be configured thanks to the parameters of `px.scatter_matrix`, but also thanks to `fig.update_traces` for fine tuning (see the next section to learn more about the options)." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "c1f59549", + "metadata": { + "lines_to_next_cell": 2 + }, + "outputs": [], + "source": [ + "import plotly.express as px\n", + "df = px.data.iris()\n", + "fig = px.scatter_matrix(df,\n", + " dimensions=[\"sepal_length\", \"sepal_width\", \"petal_length\", \"petal_width\"],\n", + " color=\"species\", symbol=\"species\",\n", + " title=\"Scatter matrix of iris data set\",\n", + " labels={col:col.replace('_', ' ') for col in df.columns}) # remove underscore\n", + "fig.update_traces(diagonal_visible=False)\n", + "fig.show()" + ] + }, + { + "cell_type": "markdown", + "id": "a4bde120", + "metadata": {}, + "source": [ + "### Scatter matrix (splom) with go.Splom\n", + "\n", + "If Plotly Express does not provide a good starting point, it is possible to use [the more generic `go.Splom` class from `plotly.graph_objects`](/python/graph-objects/). All its parameters are documented in the reference page https://plotly.com/python/reference/splom/.\n", + "\n", + "The Plotly splom trace implementation for the scatterplot matrix does not require to set $x=Xi$ , and $y=Xj$, for each scatter plot. All arrays, $X_1,X_2,…,X_n$ , are passed once, through a list of dicts called dimensions, i.e. each array/variable represents a dimension.\n", + "\n", + "A trace of type `splom` is defined as follows:\n", + "\n", + "```\n", + "trace=go.Splom(dimensions=[dict(label='string-1',\n", + " values=X1),\n", + " dict(label='string-2',\n", + " values=X2),\n", + " .\n", + " .\n", + " .\n", + " dict(label='string-n',\n", + " values=Xn)],\n", + " ....\n", + " )\n", + "```\n", + "\n", + "The label in each dimension is assigned to the axes titles of the corresponding matrix cell.\n" + ] + }, + { + "cell_type": "markdown", + "id": "10961817", + "metadata": {}, + "source": [ + "#### Splom of the Iris data set" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "cc305919", + "metadata": {}, + "outputs": [], + "source": [ + "import plotly.graph_objects as go\n", + "import pandas as pd\n", + "\n", + "df = pd.read_csv('https://raw.githubusercontent.com/plotly/datasets/master/iris-data.csv')\n", + "\n", + "# The Iris dataset contains four data variables, sepal length, sepal width, petal length,\n", + "# petal width, for 150 iris flowers. The flowers are labeled as `Iris-setosa`,\n", + "# `Iris-versicolor`, `Iris-virginica`.\n", + "\n", + "# Define indices corresponding to flower categories, using pandas label encoding\n", + "index_vals = df['class'].astype('category').cat.codes\n", + "\n", + "fig = go.Figure(data=go.Splom(\n", + " dimensions=[dict(label='sepal length',\n", + " values=df['sepal length']),\n", + " dict(label='sepal width',\n", + " values=df['sepal width']),\n", + " dict(label='petal length',\n", + " values=df['petal length']),\n", + " dict(label='petal width',\n", + " values=df['petal width'])],\n", + " text=df['class'],\n", + " marker=dict(color=index_vals,\n", + " showscale=False, # colors encode categorical variables\n", + " line_color='white', line_width=0.5)\n", + " ))\n", + "\n", + "\n", + "fig.update_layout(\n", + " title=dict(text='Iris Data set'),\n", + " dragmode='select',\n", + " width=600,\n", + " height=600,\n", + " hovermode='closest',\n", + ")\n", + "\n", + "fig.show()" + ] + }, + { + "cell_type": "markdown", + "id": "97a04999", + "metadata": {}, + "source": [ + "The scatter plots on the principal diagonal can be removed by setting `diagonal_visible=False`:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "9c9c73c5", + "metadata": {}, + "outputs": [], + "source": [ + "import plotly.graph_objects as go\n", + "import pandas as pd\n", + "\n", + "df = pd.read_csv('https://raw.githubusercontent.com/plotly/datasets/master/iris-data.csv')\n", + "index_vals = df['class'].astype('category').cat.codes\n", + "\n", + "fig = go.Figure(data=go.Splom(\n", + " dimensions=[dict(label='sepal length',\n", + " values=df['sepal length']),\n", + " dict(label='sepal width',\n", + " values=df['sepal width']),\n", + " dict(label='petal length',\n", + " values=df['petal length']),\n", + " dict(label='petal width',\n", + " values=df['petal width'])],\n", + " diagonal_visible=False, # remove plots on diagonal\n", + " text=df['class'],\n", + " marker=dict(color=index_vals,\n", + " showscale=False, # colors encode categorical variables\n", + " line_color='white', line_width=0.5)\n", + " ))\n", + "\n", + "\n", + "fig.update_layout(\n", + " title=dict(text='Iris Data set'),\n", + " width=600,\n", + " height=600,\n", + ")\n", + "\n", + "fig.show()" + ] + }, + { + "cell_type": "markdown", + "id": "8f4ae9ee", + "metadata": {}, + "source": [ + "To plot only the lower/upper half of the splom we switch the default `showlowerhalf=True`/`showupperhalf=True` to `False`:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "93b0dda2", + "metadata": {}, + "outputs": [], + "source": [ + "import plotly.graph_objects as go\n", + "import pandas as pd\n", + "\n", + "df = pd.read_csv('https://raw.githubusercontent.com/plotly/datasets/master/iris-data.csv')\n", + "index_vals = df['class'].astype('category').cat.codes\n", + "\n", + "fig = go.Figure(data=go.Splom(\n", + " dimensions=[dict(label='sepal length',\n", + " values=df['sepal length']),\n", + " dict(label='sepal width',\n", + " values=df['sepal width']),\n", + " dict(label='petal length',\n", + " values=df['petal length']),\n", + " dict(label='petal width',\n", + " values=df['petal width'])],\n", + " showupperhalf=False, # remove plots on diagonal\n", + " text=df['class'],\n", + " marker=dict(color=index_vals,\n", + " showscale=False, # colors encode categorical variables\n", + " line_color='white', line_width=0.5)\n", + " ))\n", + "\n", + "\n", + "fig.update_layout(\n", + " title=dict(text='Iris Data set'),\n", + " width=600,\n", + " height=600,\n", + ")\n", + "\n", + "fig.show()" + ] + }, + { + "cell_type": "markdown", + "id": "d9add7e7", + "metadata": {}, + "source": [ + "Each dict in the list dimensions has a key, visible, set by default on True. We can choose to remove a variable from splom, by setting `visible=False` in its corresponding dimension. In this case the default grid associated to the scatterplot matrix keeps its number of cells, but the cells in the row and column corresponding to the visible false dimension are empty:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "f3d034a7", + "metadata": {}, + "outputs": [], + "source": [ + "import plotly.graph_objects as go\n", + "import pandas as pd\n", + "\n", + "df = pd.read_csv('https://raw.githubusercontent.com/plotly/datasets/master/iris-data.csv')\n", + "index_vals = df['class'].astype('category').cat.codes\n", + "\n", + "fig = go.Figure(data=go.Splom(\n", + " dimensions=[dict(label='sepal length',\n", + " values=df['sepal length']),\n", + " dict(label='sepal width',\n", + " values=df['sepal width'],\n", + " visible=False),\n", + " dict(label='petal length',\n", + " values=df['petal length']),\n", + " dict(label='petal width',\n", + " values=df['petal width'])],\n", + " text=df['class'],\n", + " marker=dict(color=index_vals,\n", + " showscale=False, # colors encode categorical variables\n", + " line_color='white', line_width=0.5)\n", + " ))\n", + "\n", + "\n", + "fig.update_layout(\n", + " title=dict(text='Iris Data set'),\n", + " width=600,\n", + " height=600,\n", + ")\n", + "\n", + "fig.show()" + ] + }, + { + "cell_type": "markdown", + "id": "7fb9276b", + "metadata": {}, + "source": [ + "#### Splom for the diabetes dataset\n", + "\n", + "Diabetes dataset is downloaded from [kaggle](https://www.kaggle.com/uciml/pima-indians-diabetes-database/data). It is used to predict the onset of diabetes based on 8 diagnostic measures. The diabetes file contains the diagnostic measures for 768 patients, that are labeled as non-diabetic (Outcome=0), respectively diabetic (Outcome=1). The splom associated to the 8 variables can illustrate the strength of the relationship between pairs of measures for diabetic/nondiabetic patients." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "91568ab3", + "metadata": { + "lines_to_next_cell": 0 + }, + "outputs": [], + "source": [ + "import plotly.graph_objs as go\n", + "import pandas as pd\n", + "\n", + "dfd = pd.read_csv('https://raw.githubusercontent.com/plotly/datasets/master/diabetes.csv')\n", + "textd = ['non-diabetic' if cl==0 else 'diabetic' for cl in dfd['Outcome']]\n", + "\n", + "fig = go.Figure(data=go.Splom(\n", + " dimensions=[dict(label='Pregnancies', values=dfd['Pregnancies']),\n", + " dict(label='Glucose', values=dfd['Glucose']),\n", + " dict(label='BloodPressure', values=dfd['BloodPressure']),\n", + " dict(label='SkinThickness', values=dfd['SkinThickness']),\n", + " dict(label='Insulin', values=dfd['Insulin']),\n", + " dict(label='BMI', values=dfd['BMI']),\n", + " dict(label='DiabPedigreeFun', values=dfd['DiabetesPedigreeFunction']),\n", + " dict(label='Age', values=dfd['Age'])],\n", + " marker=dict(color=dfd['Outcome'],\n", + " size=5,\n", + " colorscale='Bluered',\n", + " line=dict(width=0.5,\n", + " color='rgb(230,230,230)')),\n", + " text=textd,\n", + " diagonal=dict(visible=False)))\n", + "\n", + "title = \"Scatterplot Matrix (SPLOM) for Diabetes Dataset
Data source:\"+\\\n", + " \" [1]\"\n", + "fig.update_layout(title=title,\n", + " dragmode='select',\n", + " width=1000,\n", + " height=1000,\n", + " hovermode='closest')\n", + "\n", + "fig.show()" + ] + }, + { + "cell_type": "markdown", + "id": "2925bcc9", + "metadata": {}, + "source": [ + "#### Hover Effects\n", + "\n", + "*New in 5.21*\n", + "\n", + "Set `hoversubplots='axis'` with `hovermode` set to `x`, `x unified`, `y`, or `y unified` for hover effects to appear across a column or row. For more on hover effects, see the [Hover Text and Formatting](/python/hover-text-and-formatting/) page." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "a658dab1", + "metadata": {}, + "outputs": [], + "source": [ + "import plotly.graph_objects as go\n", + "import pandas as pd\n", + "\n", + "df = pd.read_csv(\n", + " \"https://raw.githubusercontent.com/plotly/datasets/master/iris-data.csv\"\n", + ")\n", + "index_vals = df[\"class\"].astype(\"category\").cat.codes\n", + "\n", + "fig = go.Figure(\n", + " data=go.Splom(\n", + " dimensions=[\n", + " dict(label=\"sepal length\", values=df[\"sepal length\"]),\n", + " dict(label=\"sepal width\", values=df[\"sepal width\"]),\n", + " dict(label=\"petal length\", values=df[\"petal length\"]),\n", + " dict(label=\"petal width\", values=df[\"petal width\"]),\n", + " ],\n", + " showupperhalf=False,\n", + " text=df[\"class\"],\n", + " marker=dict(\n", + " color=index_vals,\n", + " showscale=False,\n", + " line_color=\"white\",\n", + " line_width=0.5,\n", + " ),\n", + " )\n", + ")\n", + "\n", + "\n", + "fig.update_layout(\n", + " title=dict(text=\"Iris Data set\"),\n", + " hoversubplots=\"axis\",\n", + " width=600,\n", + " height=600,\n", + " hovermode=\"x\",\n", + ")\n", + "\n", + "fig.show()\n" + ] + }, + { + "cell_type": "markdown", + "id": "27fe5bc3", + "metadata": {}, + "source": [ + "#### Reference\n", + "\n", + "See [function reference for `px.scatter_matrix()`](https://plotly.com/python-api-reference/generated/plotly.express.scatter_matrix) or https://plotly.com/python/reference/splom/ for more information and chart attribute options!\n" + ] + }, + { + "cell_type": "markdown", + "id": "130a45c0", + "metadata": {}, + "source": [ + "### What About Dash?\n", + "\n", + "[Dash](https://dash.plot.ly/) is an open-source framework for building analytical applications, with no Javascript required, and it is tightly integrated with the Plotly graphing library.\n", + "\n", + "Learn about how to install Dash at https://dash.plot.ly/installation.\n", + "\n", + "Everywhere in this page that you see `fig.show()`, you can display the same figure in a Dash application by passing it to the `figure` argument of the [`Graph` component](https://dash.plot.ly/dash-core-components/graph) from the built-in `dash_core_components` package like this:\n", + "\n", + "```python\n", + "import plotly.graph_objects as go # or plotly.express as px\n", + "fig = go.Figure() # or any Plotly Express function e.g. px.bar(...)\n", + "# fig.add_trace( ... )\n", + "# fig.update_layout( ... )\n", + "\n", + "from dash import Dash, dcc, html\n", + "\n", + "app = Dash()\n", + "app.layout = html.Div([\n", + " dcc.Graph(figure=fig)\n", + "])\n", + "\n", + "app.run_server(debug=True, use_reloader=False) # Turn off reloader if inside Jupyter\n", + "```" + ] + } + ], + "metadata": { + "jupytext": { + "notebook_metadata_filter": "all" + }, + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "language": "python", + "name": "python3" + }, + "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.11" + }, + "plotly": { + "description": "How to make scatterplot matrices or sploms natively in Python with Plotly.", + "display_as": "statistical", + "language": "python", + "layout": "base", + "name": "Scatterplot Matrix", + "order": 6, + "page_type": "u-guide", + "permalink": "python/splom/", + "redirect_from": "python/scatterplot-matrix/", + "thumbnail": "thumbnail/splom_image.jpg" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/static-image-export.ipynb b/static-image-export.ipynb new file mode 100644 index 000000000..5594340a8 --- /dev/null +++ b/static-image-export.ipynb @@ -0,0 +1,396 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "959e7969", + "metadata": {}, + "source": [ + "### Interactive vs Static Export\n", + "\n", + "Plotly figures are interactive when viewed in a web browser: you can hover over data points, pan and zoom axes, and show and hide traces by clicking or double-clicking on the legend. You can export figures either to static image file formats like PNG, JPEG, SVG or PDF or you can [export them to HTML files which can be opened in a browser and remain interactive](/python/interactive-html-export/). This page explains how to do the former.\n" + ] + }, + { + "cell_type": "markdown", + "id": "e71b73ed", + "metadata": {}, + "source": [ + "#### Install Dependencies\n", + "\n", + "Static image generation requires either [Kaleido](https://github.com/plotly/Kaleido) (recommended, supported as of `plotly` 4.9) or [orca](https://github.com/plotly/orca) (legacy as of `plotly` 4.9). The `kaleido` package can be installed using pip...\n", + "```\n", + "$ pip install -U kaleido\n", + "```\n", + "\n", + "or conda.\n", + "```\n", + "$ conda install -c conda-forge python-kaleido\n", + "```\n", + "\n", + "While Kaleido is now the recommended approach, image export can also be supported by the legacy [orca](https://github.com/plotly/orca) command line utility. See the [Orca Management](/python/orca-management/) section for instructions on installing, configuring, and troubleshooting orca.\n" + ] + }, + { + "cell_type": "markdown", + "id": "8ada41af", + "metadata": {}, + "source": [ + "### Create a Figure\n", + "\n", + "Now let's create a simple scatter plot with 100 random points of varying color and size." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "b94be3b9", + "metadata": {}, + "outputs": [], + "source": [ + "import plotly.graph_objects as go\n", + "import numpy as np\n", + "np.random.seed(1)\n", + "\n", + "N = 100\n", + "x = np.random.rand(N)\n", + "y = np.random.rand(N)\n", + "colors = np.random.rand(N)\n", + "sz = np.random.rand(N) * 30\n", + "\n", + "fig = go.Figure()\n", + "fig.add_trace(go.Scatter(\n", + " x=x,\n", + " y=y,\n", + " mode=\"markers\",\n", + " marker=go.scatter.Marker(\n", + " size=sz,\n", + " color=colors,\n", + " opacity=0.6,\n", + " colorscale=\"Viridis\"\n", + " )\n", + "))\n", + "\n", + "fig.show()" + ] + }, + { + "cell_type": "markdown", + "id": "335b59b7", + "metadata": {}, + "source": [ + "### Write Image File\n", + "\n", + "The `plotly.io.write_image` function is used to write an image to a file or file-like python object. You can also use the `.write_image` graph object figure method.\n", + "\n", + "Let's first create an output directory to store our images" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "747666bf", + "metadata": {}, + "outputs": [], + "source": [ + "import os\n", + "\n", + "if not os.path.exists(\"images\"):\n", + " os.mkdir(\"images\")" + ] + }, + { + "cell_type": "markdown", + "id": "60be73a2", + "metadata": {}, + "source": [ + "If you are running this notebook live, click to open the output directory so you can examine the images as they are written." + ] + }, + { + "cell_type": "markdown", + "id": "eaee1b2a", + "metadata": {}, + "source": [ + "#### Raster Formats: PNG, JPEG, and WebP" + ] + }, + { + "cell_type": "markdown", + "id": "2061c6c5", + "metadata": {}, + "source": [ + "plotly.py can output figures to several raster image formats including **PNG**, ...\n", + "\n", + "~~~python\n", + "fig.write_image(\"images/fig1.png\")\n", + "~~~\n", + "\n", + "**JPEG**, ...\n", + "\n", + "~~~python\n", + "fig.write_image(\"images/fig1.jpeg\")\n", + "~~~\n", + "\n", + "and **WebP**\n", + "\n", + "~~~python\n", + "fig.write_image(\"images/fig1.webp\")\n", + "~~~\n", + "\n", + "#### Vector Formats: SVG and PDF..." + ] + }, + { + "cell_type": "markdown", + "id": "29e18bb4", + "metadata": {}, + "source": [ + "plotly.py can also output figures in several vector formats including **SVG**, ...\n", + "\n", + "~~~python\n", + "fig.write_image(\"images/fig1.svg\")\n", + "~~~\n", + "\n", + "**PDF**, ...\n", + "\n", + "~~~python\n", + "fig.write_image(\"images/fig1.pdf\")\n", + "~~~\n", + "\n", + "and **EPS** (requires the poppler library)\n", + "\n", + "~~~python\n", + "fig.write_image(\"images/fig1.eps\")\n", + "~~~\n", + "\n", + "**Note:** It is important to note that any figures containing WebGL traces (i.e. of type `scattergl`, `contourgl`, `scatter3d`, `surface`, `mesh3d`, `scatterpolargl`, `cone`, `streamtube`, `splom`, or `parcoords`) that are exported in a vector format will include encapsulated rasters, instead of vectors, for some parts of the image." + ] + }, + { + "cell_type": "markdown", + "id": "0f9510b9", + "metadata": {}, + "source": [ + "### Image Export in Dash\n", + "\n", + "[Dash](https://plotly.com/dash/) is the best way to build analytical apps in Python using Plotly figures. To run the app below, run `pip install dash`, click \"Download\" to get the code and run `python app.py`.\n", + "\n", + "Get started with [the official Dash docs](https://dash.plotly.com/installation) and **learn how to effortlessly [style](https://plotly.com/dash/design-kit/) & [deploy](https://plotly.com/dash/app-manager/) apps like this with Dash Enterprise.**\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "76aee816", + "metadata": { + "hide_code": true + }, + "outputs": [], + "source": [ + "from IPython.display import IFrame\n", + "snippet_url = 'https://python-docs-dash-snippets.herokuapp.com/python-docs-dash-snippets/'\n", + "IFrame(snippet_url + 'static-image-export', width='100%', height=1200)" + ] + }, + { + "cell_type": "markdown", + "id": "802161bd", + "metadata": {}, + "source": [ + "### Get Image as Bytes\n", + "\n", + "The `plotly.io.to_image` function is used to return an image as a bytes object. You can also use the `.to_image` graph object figure method.\n", + "\n", + "Let convert the figure to a **PNG** bytes object..." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "5d87c78c", + "metadata": {}, + "outputs": [], + "source": [ + "img_bytes = fig.to_image(format=\"png\")" + ] + }, + { + "cell_type": "markdown", + "id": "527b0df1", + "metadata": {}, + "source": [ + "and then display the first 20 bytes." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "8692f0f3", + "metadata": {}, + "outputs": [], + "source": [ + "img_bytes[:20]" + ] + }, + { + "cell_type": "markdown", + "id": "2cc5cc1d", + "metadata": {}, + "source": [ + "#### Display Bytes as Image Using `IPython.display.Image`\n", + "A bytes object representing a PNG image can be displayed directly in the notebook using the `IPython.display.Image` class. This also works in the [Qt Console for Jupyter](https://qtconsole.readthedocs.io/en/stable/)!" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "22ba1dc6", + "metadata": {}, + "outputs": [], + "source": [ + "from IPython.display import Image\n", + "Image(img_bytes)" + ] + }, + { + "cell_type": "markdown", + "id": "02bdc695", + "metadata": {}, + "source": [ + "### Change Image Dimensions and Scale\n", + "In addition to the image format, the `to_image` and `write_image` functions provide arguments to specify the image `width` and `height` in logical pixels. They also provide a `scale` parameter that can be used to increase (`scale` > 1) or decrease (`scale` < 1) the physical resolution of the resulting image." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "91c80af8", + "metadata": {}, + "outputs": [], + "source": [ + "img_bytes = fig.to_image(format=\"png\", width=600, height=350, scale=2)\n", + "Image(img_bytes)" + ] + }, + { + "cell_type": "markdown", + "id": "98cf7535", + "metadata": {}, + "source": [ + "### Specify Image Export Engine\n", + "If `kaleido` is installed, it will automatically be used to perform image export. If it is not installed, plotly.py will attempt to use `orca` instead. The `engine` argument to the `to_image` and `write_image` functions can be used to override this default behavior.\n", + "\n", + "Here is an example of specifying that orca should be used:\n", + "~~~python\n", + "fig.to_image(format=\"png\", engine=\"orca\")\n", + "~~~\n", + "\n", + "And, here is an example of specifying that Kaleido should be used:\n", + "~~~python\n", + "fig.to_image(format=\"png\", engine=\"kaleido\")\n", + "~~~\n" + ] + }, + { + "cell_type": "markdown", + "id": "9b0eef35", + "metadata": {}, + "source": [ + "### Image Export Settings (Kaleido)\n", + "Various image export settings can be configured using the `plotly.io.kaleido.scope` object. For example, the `default_format` property can be used to specify that the default export format should be `svg` instead of `png`\n", + "\n", + "```python\n", + "import plotly.io as pio\n", + "pio.kaleido.scope.default_format = \"svg\"\n", + "```\n", + "\n", + "Here is a complete listing of the available image export settings:\n", + "\n", + " - **`default_width`**: The default pixel width to use on image export.\n", + " - **`default_height`**: The default pixel height to use on image export.\n", + " - **`default_scale`**: The default image scale factor applied on image export.\n", + " - **`default_format`**: The default image format used on export. One of `\"png\"`, `\"jpeg\"`, `\"webp\"`, `\"svg\"`, `\"pdf\"`, or `\"eps\"`.\n", + " - **`mathjax`**: Location of the MathJax bundle needed to render LaTeX characters. Defaults to a CDN location. If fully offline export is required, set this to a local MathJax bundle.\n", + " - **`topojson`**: Location of the topojson files needed to render choropleth traces. Defaults to a CDN location. If fully offline export is required, set this to a local directory containing the [Plotly.js topojson files](https://github.com/plotly/plotly.js/tree/master/dist/topojson).\n", + " - **`mapbox_access_token`**: The default Mapbox access token.\n" + ] + }, + { + "cell_type": "markdown", + "id": "2d022a7e", + "metadata": {}, + "source": [ + "### Image Export Settings (Orca)\n", + "See the [Orca Management](/python/orca-management/) section for information on how to specify image export settings when using orca.\n", + "\n", + "### Summary\n", + "In summary, to export high-quality static images from plotly.py, all you need to do is install the `kaleido` package and then use the `plotly.io.write_image` and `plotly.io.to_image` functions (or the `.write_image` and `.to_image` graph object figure methods).\n" + ] + }, + { + "cell_type": "markdown", + "id": "0aeebcd1", + "metadata": {}, + "source": [ + "### What About Dash?\n", + "\n", + "[Dash](https://dash.plot.ly/) is an open-source framework for building analytical applications, with no Javascript required, and it is tightly integrated with the Plotly graphing library.\n", + "\n", + "Learn about how to install Dash at https://dash.plot.ly/installation.\n", + "\n", + "Everywhere in this page that you see `fig.show()`, you can display the same figure in a Dash application by passing it to the `figure` argument of the [`Graph` component](https://dash.plot.ly/dash-core-components/graph) from the built-in `dash_core_components` package like this:\n", + "\n", + "```python\n", + "import plotly.graph_objects as go # or plotly.express as px\n", + "fig = go.Figure() # or any Plotly Express function e.g. px.bar(...)\n", + "# fig.add_trace( ... )\n", + "# fig.update_layout( ... )\n", + "\n", + "from dash import Dash, dcc, html\n", + "\n", + "app = Dash()\n", + "app.layout = html.Div([\n", + " dcc.Graph(figure=fig)\n", + "])\n", + "\n", + "app.run_server(debug=True, use_reloader=False) # Turn off reloader if inside Jupyter\n", + "```" + ] + } + ], + "metadata": { + "jupytext": { + "notebook_metadata_filter": "all" + }, + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "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.7.6" + }, + "plotly": { + "description": "Plotly allows you to save static images of your plots. Save the image to your local computer, or embed it inside your Jupyter notebooks as a static image.", + "display_as": "file_settings", + "language": "python", + "layout": "base", + "name": "Static Image Export", + "order": 6, + "page_type": "u-guide", + "permalink": "python/static-image-export/", + "thumbnail": "thumbnail/static-image-export.png" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/streamline-plots.ipynb b/streamline-plots.ipynb new file mode 100644 index 000000000..f4c714ce0 --- /dev/null +++ b/streamline-plots.ipynb @@ -0,0 +1,178 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "1bdf7e5e", + "metadata": {}, + "source": [ + "A Streamline plot is a representation based on a 2-D vector field interpreted as a velocity field, consisting of closed curves tangent to the velocity field. In the case of a stationary velocity field, streamlines coincide with trajectories (see also the [Wikipedia page on streamlines, streaklines and pathlines](https://en.wikipedia.org/wiki/Streamlines,_streaklines,_and_pathlines)).\n", + "\n", + "For the streamline [figure factory](/python/figure-factories/), one needs to provide\n", + "- uniformly spaced ranges of `x` and `y` values (1D)\n", + "- 2-D velocity values `u` and `v` defined on the cross-product (`np.meshgrid(x, y)`) of `x` and `y`.\n", + "\n", + "Velocity values are interpolated when determining the streamlines. Streamlines are initialized on the boundary of the `x-y` domain.\n", + "\n", + "#### Basic Streamline Plot\n", + "\n", + "Streamline plots can be made with a [figure factory](/python/figure-factories/) as detailed in this page." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "ccb7eb13", + "metadata": {}, + "outputs": [], + "source": [ + "import plotly.figure_factory as ff\n", + "\n", + "import numpy as np\n", + "\n", + "x = np.linspace(-3, 3, 100)\n", + "y = np.linspace(-3, 3, 100)\n", + "Y, X = np.meshgrid(x, y)\n", + "u = -1 - X**2 + Y\n", + "v = 1 + X - Y**2\n", + "\n", + "# Create streamline figure\n", + "fig = ff.create_streamline(x, y, u, v, arrow_scale=.1)\n", + "fig.show()" + ] + }, + { + "cell_type": "markdown", + "id": "42f153b6", + "metadata": {}, + "source": [ + "#### Streamline and Source Point Plot" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "eb11fb51", + "metadata": {}, + "outputs": [], + "source": [ + "import plotly.figure_factory as ff\n", + "import plotly.graph_objects as go\n", + "\n", + "import numpy as np\n", + "\n", + "N = 50\n", + "x_start, x_end = -2.0, 2.0\n", + "y_start, y_end = -1.0, 1.0\n", + "x = np.linspace(x_start, x_end, N)\n", + "y = np.linspace(y_start, y_end, N)\n", + "X, Y = np.meshgrid(x, y)\n", + "source_strength = 5.0\n", + "x_source, y_source = -1.0, 0.0\n", + "\n", + "# Compute the velocity field on the mesh grid\n", + "u = (source_strength/(2*np.pi) *\n", + " (X - x_source)/((X - x_source)**2 + (Y - y_source)**2))\n", + "v = (source_strength/(2*np.pi) *\n", + " (Y - y_source)/((X - x_source)**2 + (Y - y_source)**2))\n", + "\n", + "# Create streamline figure\n", + "fig = ff.create_streamline(x, y, u, v,\n", + " name='streamline')\n", + "\n", + "# Add source point\n", + "fig.add_trace(go.Scatter(x=[x_source], y=[y_source],\n", + " mode='markers',\n", + " marker_size=14,\n", + " name='source point'))\n", + "\n", + "fig.show()" + ] + }, + { + "cell_type": "markdown", + "id": "894dec6c", + "metadata": {}, + "source": [ + "#### See also\n", + "\n", + "For a 3D version of streamlines, use the trace `go.Streamtube` documented [here](/python/streamtube-plot/).\n", + "\n", + "For representing the 2-D vector field as arrows, see the [quiver plot tutorial](/python/quiver-plots/)." + ] + }, + { + "cell_type": "markdown", + "id": "204c6228", + "metadata": {}, + "source": [ + "#### Reference\n", + "\n", + "For more info on `ff.create_streamline()`, see the [full function reference](https://plotly.com/python-api-reference/generated/plotly.figure_factory.create_streamline.html)" + ] + }, + { + "cell_type": "markdown", + "id": "6e35de44", + "metadata": {}, + "source": [ + "### What About Dash?\n", + "\n", + "[Dash](https://dash.plot.ly/) is an open-source framework for building analytical applications, with no Javascript required, and it is tightly integrated with the Plotly graphing library.\n", + "\n", + "Learn about how to install Dash at https://dash.plot.ly/installation.\n", + "\n", + "Everywhere in this page that you see `fig.show()`, you can display the same figure in a Dash application by passing it to the `figure` argument of the [`Graph` component](https://dash.plot.ly/dash-core-components/graph) from the built-in `dash_core_components` package like this:\n", + "\n", + "```python\n", + "import plotly.graph_objects as go # or plotly.express as px\n", + "fig = go.Figure() # or any Plotly Express function e.g. px.bar(...)\n", + "# fig.add_trace( ... )\n", + "# fig.update_layout( ... )\n", + "\n", + "from dash import Dash, dcc, html\n", + "\n", + "app = Dash()\n", + "app.layout = html.Div([\n", + " dcc.Graph(figure=fig)\n", + "])\n", + "\n", + "app.run_server(debug=True, use_reloader=False) # Turn off reloader if inside Jupyter\n", + "```" + ] + } + ], + "metadata": { + "jupytext": { + "notebook_metadata_filter": "all" + }, + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "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.7.3" + }, + "plotly": { + "description": "How to make a streamline plot in Python. A streamline plot displays vector field data.", + "display_as": "scientific", + "language": "python", + "layout": "base", + "name": "Streamline Plots", + "order": 11, + "permalink": "python/streamline-plots/", + "thumbnail": "thumbnail/streamline.jpg" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/streamtube-plot.ipynb b/streamtube-plot.ipynb new file mode 100644 index 000000000..8ab0d6fa5 --- /dev/null +++ b/streamtube-plot.ipynb @@ -0,0 +1,220 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "aabc2474", + "metadata": {}, + "source": [ + "### Introduction" + ] + }, + { + "cell_type": "markdown", + "id": "2aa6336e", + "metadata": {}, + "source": [ + "In streamtube plots, attributes include `x`, `y`, and `z`, which set the coordinates of the vector field, and `u`, `v`, and `w`, which set the x, y, and z components of the vector field. Additionally, you can use `starts` to determine the streamtube's starting position." + ] + }, + { + "cell_type": "markdown", + "id": "4cd38bec", + "metadata": {}, + "source": [ + "### Basic Streamtube Plot" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "35dd759f", + "metadata": {}, + "outputs": [], + "source": [ + "import plotly.graph_objects as go\n", + "\n", + "fig = go.Figure(data=go.Streamtube(x=[0, 0, 0], y=[0, 1, 2], z=[0, 0, 0],\n", + " u=[0, 0, 0], v=[1, 1, 1], w=[0, 0, 0]))\n", + "fig.show()" + ] + }, + { + "cell_type": "markdown", + "id": "c0e1c460", + "metadata": {}, + "source": [ + "### Starting Position and Segments\n", + "\n", + "By default, streamlines are initialized in the x-z plane of minimal y value. You can change this behaviour by providing directly the starting points of streamtubes." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "c52a6930", + "metadata": {}, + "outputs": [], + "source": [ + "import plotly.graph_objects as go\n", + "\n", + "import pandas as pd\n", + "\n", + "df = pd.read_csv('https://raw.githubusercontent.com/plotly/datasets/master/streamtube-wind.csv').drop(['Unnamed: 0'],axis=1)\n", + "\n", + "fig = go.Figure(data=go.Streamtube(\n", + " x = df['x'],\n", + " y = df['y'],\n", + " z = df['z'],\n", + " u = df['u'],\n", + " v = df['v'],\n", + " w = df['w'],\n", + " starts = dict(\n", + " x = [80] * 16,\n", + " y = [20,30,40,50] * 4,\n", + " z = [0,0,0,0,5,5,5,5,10,10,10,10,15,15,15,15]\n", + " ),\n", + " sizeref = 0.3,\n", + " colorscale = 'Portland',\n", + " showscale = False,\n", + " maxdisplayed = 3000\n", + "))\n", + "\n", + "fig.update_layout(\n", + " scene = dict(\n", + " aspectratio = dict(\n", + " x = 2,\n", + " y = 1,\n", + " z = 0.3\n", + " )\n", + " ),\n", + " margin = dict(\n", + " t = 20,\n", + " b = 20,\n", + " l = 20,\n", + " r = 20\n", + " )\n", + ")\n", + "\n", + "fig.show()" + ] + }, + { + "cell_type": "markdown", + "id": "debac62c", + "metadata": {}, + "source": [ + "### Tube color and diameter\n", + "\n", + "The color of tubes is determined by their local norm, and the diameter of the field by the local [divergence](https://en.wikipedia.org/wiki/Divergence) of the vector field.\n", + "\n", + "In all cases below the norm is proportional to `z**2` but the direction of the vector is different, resulting in a different divergence field." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "52c903e0", + "metadata": {}, + "outputs": [], + "source": [ + "import plotly.graph_objects as go\n", + "from plotly.subplots import make_subplots\n", + "import numpy as np\n", + "\n", + "x, y, z = np.mgrid[0:10, 0:10, 0:10]\n", + "x = x.flatten()\n", + "y = y.flatten()\n", + "z = z.flatten()\n", + "\n", + "u = np.zeros_like(x)\n", + "v = np.zeros_like(y)\n", + "w = z**2\n", + "\n", + "fig = make_subplots(rows=1, cols=3, specs=[[{'is_3d': True}, {'is_3d': True}, {'is_3d':True}]])\n", + "\n", + "fig.add_trace(go.Streamtube(x=x, y=y, z=z, u=u, v=v, w=w), 1, 1)\n", + "fig.add_trace(go.Streamtube(x=x, y=y, z=z, u=w, v=v, w=u), 1, 2)\n", + "fig.add_trace(go.Streamtube(x=x, y=y, z=z, u=u, v=w, w=v), 1, 3)\n", + "\n", + "fig.update_layout(scene_camera_eye=dict(x=2, y=2, z=2),\n", + " scene2_camera_eye=dict(x=2, y=2, z=2),\n", + " scene3_camera_eye=dict(x=2, y=2, z=2))\n", + "fig.show()" + ] + }, + { + "cell_type": "markdown", + "id": "7b951917", + "metadata": {}, + "source": [ + "#### Reference\n", + "See https://plotly.com/python/reference/streamtube/ for more information and chart attribute options!" + ] + }, + { + "cell_type": "markdown", + "id": "dfc89011", + "metadata": {}, + "source": [ + "### What About Dash?\n", + "\n", + "[Dash](https://dash.plot.ly/) is an open-source framework for building analytical applications, with no Javascript required, and it is tightly integrated with the Plotly graphing library.\n", + "\n", + "Learn about how to install Dash at https://dash.plot.ly/installation.\n", + "\n", + "Everywhere in this page that you see `fig.show()`, you can display the same figure in a Dash application by passing it to the `figure` argument of the [`Graph` component](https://dash.plot.ly/dash-core-components/graph) from the built-in `dash_core_components` package like this:\n", + "\n", + "```python\n", + "import plotly.graph_objects as go # or plotly.express as px\n", + "fig = go.Figure() # or any Plotly Express function e.g. px.bar(...)\n", + "# fig.add_trace( ... )\n", + "# fig.update_layout( ... )\n", + "\n", + "from dash import Dash, dcc, html\n", + "\n", + "app = Dash()\n", + "app.layout = html.Div([\n", + " dcc.Graph(figure=fig)\n", + "])\n", + "\n", + "app.run_server(debug=True, use_reloader=False) # Turn off reloader if inside Jupyter\n", + "```" + ] + } + ], + "metadata": { + "jupytext": { + "notebook_metadata_filter": "all" + }, + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "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.7.3" + }, + "plotly": { + "description": "How to make 3D streamtube plots in Python with Plotly.", + "display_as": "3d_charts", + "language": "python", + "layout": "base", + "name": "3D Streamtube Plots", + "order": 13, + "page_type": "u-guide", + "permalink": "python/streamtube-plot/", + "thumbnail": "thumbnail/streamtube.jpg" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/strip-charts.ipynb b/strip-charts.ipynb new file mode 100644 index 000000000..9c41b0ef1 --- /dev/null +++ b/strip-charts.ipynb @@ -0,0 +1,129 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "0dfec1cc", + "metadata": {}, + "source": [ + "### Strip Charts with Plotly Express\n", + "\n", + "[Plotly Express](/python/plotly-express/) is the easy-to-use, high-level interface to Plotly, which [operates on a variety of types of data](/python/px-arguments/) and produces [easy-to-style figures](/python/styling-plotly-express/).\n", + "\n", + "The `px.strip()` function will make strip charts using underlying `box` traces with the box hidden.\n", + "\n", + "See also [box plots](/python/box-plots/) and [violin plots](/python/violin/)." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "00964310", + "metadata": {}, + "outputs": [], + "source": [ + "import plotly.express as px\n", + "\n", + "df = px.data.tips()\n", + "fig = px.strip(df, x=\"total_bill\", y=\"day\")\n", + "fig.show()" + ] + }, + { + "cell_type": "markdown", + "id": "b5cc1760", + "metadata": {}, + "source": [ + "Strip charts support [faceting](/python/facet-plots/) and [discrete color](/python/discrete-color/):" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "66faeffb", + "metadata": {}, + "outputs": [], + "source": [ + "import plotly.express as px\n", + "\n", + "df = px.data.tips()\n", + "fig = px.strip(df, x=\"total_bill\", y=\"time\", color=\"sex\", facet_col=\"day\")\n", + "fig.show()" + ] + }, + { + "cell_type": "markdown", + "id": "dbb4136f", + "metadata": {}, + "source": [ + "#### Reference\n", + "\n", + "See [function reference for `px.strip()`](https://plotly.com/python-api-reference/generated/plotly.express.strip) for more information and chart attribute options!\n" + ] + }, + { + "cell_type": "markdown", + "id": "9791689c", + "metadata": {}, + "source": [ + "### What About Dash?\n", + "\n", + "[Dash](https://dash.plot.ly/) is an open-source framework for building analytical applications, with no Javascript required, and it is tightly integrated with the Plotly graphing library.\n", + "\n", + "Learn about how to install Dash at https://dash.plot.ly/installation.\n", + "\n", + "Everywhere in this page that you see `fig.show()`, you can display the same figure in a Dash application by passing it to the `figure` argument of the [`Graph` component](https://dash.plot.ly/dash-core-components/graph) from the built-in `dash_core_components` package like this:\n", + "\n", + "```python\n", + "import plotly.graph_objects as go # or plotly.express as px\n", + "fig = go.Figure() # or any Plotly Express function e.g. px.bar(...)\n", + "# fig.add_trace( ... )\n", + "# fig.update_layout( ... )\n", + "\n", + "from dash import Dash, dcc, html\n", + "\n", + "app = Dash()\n", + "app.layout = html.Div([\n", + " dcc.Graph(figure=fig)\n", + "])\n", + "\n", + "app.run_server(debug=True, use_reloader=False) # Turn off reloader if inside Jupyter\n", + "```" + ] + } + ], + "metadata": { + "jupytext": { + "notebook_metadata_filter": "all" + }, + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "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.7.7" + }, + "plotly": { + "description": "Strip charts are like 1-dimensional jittered scatter plots.", + "display_as": "statistical", + "language": "python", + "layout": "base", + "name": "Strip Charts", + "order": 14, + "page_type": "u-guide", + "permalink": "python/strip-charts/", + "thumbnail": "thumbnail/figure-labels.png" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/styling-plotly-express.ipynb b/styling-plotly-express.ipynb new file mode 100644 index 000000000..6cd4705fa --- /dev/null +++ b/styling-plotly-express.ipynb @@ -0,0 +1,279 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "cd4a470d", + "metadata": {}, + "source": [ + "### Styling Figures made with Plotly Express\n", + "\n", + "[Plotly Express](/python/plotly-express/) is the easy-to-use, high-level interface to Plotly, which [operates on a variety of types of data](/python/px-arguments/). Every Plotly Express function returns [a `plotly.graph_objects.Figure` object](/python/graph-objects/) whose `data` and `layout` has been pre-populated according to the provided arguments.\n", + "\n", + "> You can style and customize figures made with Plotly Express _in all the same ways_ as you can style figures made more manually by explicitly assembling `graph_objects` into a figure.\n", + "\n", + "More specifically, here are the 4 ways you can style and customize figures made with Plotly Express:\n", + "\n", + "1. Control common parameters like width & height, titles, labeling and colors using built-in Plotly Express function arguments\n", + "2. Updating the figure attributes using [update methods or by directly setting attributes](/python/creating-and-updating-figures/)\n", + "3. Using Plotly's [theming/templating mechanism](/python/templates/) via the `template` argument to every Plotly Express function\n", + "4. Setting default values for common parameters using `px.defaults`\n", + "\n", + "### Built-in Plotly Express Styling Arguments\n", + "\n", + "Many common styling options can be set directly in the `px` function call. Every Plotly Express function accepts the following arguments:\n", + "\n", + "- `title` to set the figure title\n", + "- `width` and `height` to set the figure dimensions\n", + "- `template` to [set many styling parameters at once](/python/templates/) (see below for more details)\n", + "- `labels` to override the default axis and legend labels behaviour, which is to use the data frame column name if available, and otherwise to use the label name itself like \"x\", \"y\", \"color\" etc. `labels` accepts a `dict` whose keys are the label to rename and whose values are the desired labels. These labels appear in axis labels, legend and color bar titles, and in hover labels.\n", + "- `category_orders` to override the default category ordering behaviour, which is to use the order in which the data appears in the input. `category_orders` accepts a `dict` whose keys are the column name to reorder and whose values are a `list` of values in the desired order. These orderings apply everywhere categories appear: in legends, on axes, in bar stacks, in the order of facets, in the order of animation frames etc.\n", + "- `hover_data` and `hover_name` to control which attributes appear in the hover label and how they are formatted.\n", + "- [Various color-related attributes](/python/colorscales/) such as `color_continuous_scale`, `color_range`, `color_discrete_sequence` and/or `color_discrete_map` set the colors used in the figure. `color_discrete_map` accepts a dict whose keys are values mapped to `color` and whose values are the desired CSS colors.\n", + "\n", + "To illustrate each of these, here is a simple, default figure made with Plotly Express. Note the default orderings for the x-axis categories and the usage of lowercase & snake_case data frame columns for axis labelling." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "426c6993", + "metadata": {}, + "outputs": [], + "source": [ + "import plotly.express as px\n", + "df = px.data.tips()\n", + "fig = px.histogram(df, x=\"day\", y=\"total_bill\", color=\"sex\")\n", + "fig.show()" + ] + }, + { + "cell_type": "markdown", + "id": "34457cab", + "metadata": {}, + "source": [ + "Here is the same figure, restyled by adding some extra parameters to the initial Plotly Express call:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "11ff8d79", + "metadata": {}, + "outputs": [], + "source": [ + "import plotly.express as px\n", + "df = px.data.tips()\n", + "fig = px.histogram(df, x=\"day\", y=\"total_bill\", color=\"sex\",\n", + " title=\"Receipts by Payer Gender and Day of Week\",\n", + " width=600, height=400,\n", + " labels={ # replaces default labels by column name\n", + " \"sex\": \"Payer Gender\", \"day\": \"Day of Week\", \"total_bill\": \"Receipts\"\n", + " },\n", + " category_orders={ # replaces default order by column name\n", + " \"day\": [\"Thur\", \"Fri\", \"Sat\", \"Sun\"], \"sex\": [\"Male\", \"Female\"]\n", + " },\n", + " color_discrete_map={ # replaces default color mapping by value\n", + " \"Male\": \"RebeccaPurple\", \"Female\": \"MediumPurple\"\n", + " },\n", + " template=\"simple_white\"\n", + " )\n", + "fig.show()" + ] + }, + { + "cell_type": "markdown", + "id": "5034c6c4", + "metadata": {}, + "source": [ + "### Updating or Modifying Figures made with Plotly Express\n", + "\n", + "If none of the built-in Plotly Express arguments allow you to customize the figure the way you need to, you can use [the `update_*` and `add_*` methods](/python/creating-and-updating-figures/) on [the `plotly.graph_objects.Figure` object](/python/graph-objects/) returned by the PX function to make any further modifications to the figure. This approach is the one used throughout the Plotly.py documentation to [customize axes](/python/axes/), control [legends](/python/legend/) and [colorbars](/python/colorscales/), add [shapes](/python/shapes/) and [annotations](/python/text-and-annotations/) etc.\n", + "\n", + "Here is the same figure as above, with some additional customizations to the axes and legend via `.update_yaxes()`, and `.update_layout()`, as well as some annotations added via `.add_shape()` and `.add_annotation()`." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "ab6570b5", + "metadata": {}, + "outputs": [], + "source": [ + "import plotly.express as px\n", + "df = px.data.tips()\n", + "fig = px.histogram(df, x=\"day\", y=\"total_bill\", color=\"sex\",\n", + " title=\"Receipts by Payer Gender and Day of Week vs Target\",\n", + " width=600, height=400,\n", + " labels={\"sex\": \"Payer Gender\", \"day\": \"Day of Week\", \"total_bill\": \"Receipts\"},\n", + " category_orders={\"day\": [\"Thur\", \"Fri\", \"Sat\", \"Sun\"], \"sex\": [\"Male\", \"Female\"]},\n", + " color_discrete_map={\"Male\": \"RebeccaPurple\", \"Female\": \"MediumPurple\"},\n", + " template=\"simple_white\"\n", + " )\n", + "\n", + "fig.update_yaxes( # the y-axis is in dollars\n", + " tickprefix=\"$\", showgrid=True\n", + ")\n", + "\n", + "fig.update_layout( # customize font and legend orientation & position\n", + " font_family=\"Rockwell\",\n", + " legend=dict(\n", + " title=None, orientation=\"h\", y=1, yanchor=\"bottom\", x=0.5, xanchor=\"center\"\n", + " )\n", + ")\n", + "\n", + "fig.add_shape( # add a horizontal \"target\" line\n", + " type=\"line\", line_color=\"salmon\", line_width=3, opacity=1, line_dash=\"dot\",\n", + " x0=0, x1=1, xref=\"paper\", y0=950, y1=950, yref=\"y\"\n", + ")\n", + "\n", + "fig.add_annotation( # add a text callout with arrow\n", + " text=\"below target!\", x=\"Fri\", y=400, arrowhead=1, showarrow=True\n", + ")\n", + "\n", + "fig.show()" + ] + }, + { + "cell_type": "markdown", + "id": "3a4a2a35", + "metadata": {}, + "source": [ + "### How Plotly Express Works with Templates\n", + "\n", + "Plotly has a [theming system based on templates](/python/templates/) and figures created with Plotly Express interact smoothly with this system:\n", + "\n", + "- Plotly Express methods will use the default template if one is set in `plotly.io` (by default, this is set to `plotly`) or in `plotly.express.defaults` (see below)\n", + "- The template in use can always be overridden via the `template` argument to every PX function\n", + "- The default `color_continuous_scale` will be the value of `layout.colorscales.sequential` in the template in use, unless it is overridden via the corresponding function argument or via `plotly.express.defaults` (see below)\n", + "- The default `color_discrete_sequence` will be the value of `layout.colorway` in the template in use, unless it is overridden via the corresponding function argument or via `plotly.express.defaults` (see below)\n", + "\n", + "By way of example, in the following figure, simply setting the `template` argument will automatically change the default continuous color scale, even though we have not specified `color_continuous_scale` directly." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "c187cd04", + "metadata": {}, + "outputs": [], + "source": [ + "import plotly.express as px\n", + "df = px.data.iris()\n", + "fig = px.density_heatmap(df, x=\"sepal_width\", y=\"sepal_length\", template=\"seaborn\")\n", + "fig.show()" + ] + }, + { + "cell_type": "markdown", + "id": "50a7957f", + "metadata": {}, + "source": [ + "### Setting Plotly Express Styling Defaults\n", + "\n", + "Plotly Express supports a simple default-configuration system via the `plotly.express.defaults` singleton object. The values of the properties set on this object are used for the rest of the active session in place of `None` as the default values for any argument to a PX function with a matching name:\n", + "\n", + "- `width` and `height` can be set once globally for all Plotly Express functions\n", + "- `template` can override the setting of `plotly.io.templates.default` for all Plotly Express functions\n", + "- `color_continuous_scale` and `color_discrete_scale` can override the contents of the template in use for all Plotly Express functions that accept these arguments\n", + "- `line_dash_sequence`, `symbol_sequence` and `size_max` can be set once globally for all Plotly Express functions that accept these arguments\n", + "\n", + "To illustrate this \"defaults hierarchy\", in the following example:\n", + "\n", + "- we set the Plotly-wide default template to `simple_white`, but\n", + "- we override the default template for Plotly Express to be `ggplot2`, but\n", + "- we also set the default `color_continuous_scale`, and\n", + "- we set the default `height` and `width` to 400 by 600, but\n", + "- we override the default `width` to 400 via the function argument.\n", + "\n", + "As a result, any figure produced with Plotly Express thereafter uses the `ggplot2` settings for all attributes except for the continuous color scale (visible because `simple_white` doesn't set a plot background, and neither the `simple_white` nor `ggplot2` template uses `Blackbody` as a color scale), and uses the Plotly Express defaults for height but not width (visible because the figure height is the same as the figure width, despite the default)." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "49f62255", + "metadata": {}, + "outputs": [], + "source": [ + "import plotly.express as px\n", + "import plotly.io as pio\n", + "\n", + "pio.templates.default = \"simple_white\"\n", + "\n", + "px.defaults.template = \"ggplot2\"\n", + "px.defaults.color_continuous_scale = px.colors.sequential.Blackbody\n", + "px.defaults.width = 600\n", + "px.defaults.height = 400\n", + "\n", + "df = px.data.iris()\n", + "fig = px.scatter(df, x=\"sepal_width\", y=\"sepal_length\", color=\"sepal_length\", width=400)\n", + "fig.show()" + ] + }, + { + "cell_type": "markdown", + "id": "82a3a2e6", + "metadata": {}, + "source": [ + "### What About Dash?\n", + "\n", + "[Dash](https://dash.plot.ly/) is an open-source framework for building analytical applications, with no Javascript required, and it is tightly integrated with the Plotly graphing library.\n", + "\n", + "Learn about how to install Dash at https://dash.plot.ly/installation.\n", + "\n", + "Everywhere in this page that you see `fig.show()`, you can display the same figure in a Dash application by passing it to the `figure` argument of the [`Graph` component](https://dash.plot.ly/dash-core-components/graph) from the built-in `dash_core_components` package like this:\n", + "\n", + "```python\n", + "import plotly.graph_objects as go # or plotly.express as px\n", + "fig = go.Figure() # or any Plotly Express function e.g. px.bar(...)\n", + "# fig.add_trace( ... )\n", + "# fig.update_layout( ... )\n", + "\n", + "from dash import Dash, dcc, html\n", + "\n", + "app = Dash()\n", + "app.layout = html.Div([\n", + " dcc.Graph(figure=fig)\n", + "])\n", + "\n", + "app.run_server(debug=True, use_reloader=False) # Turn off reloader if inside Jupyter\n", + "```" + ] + } + ], + "metadata": { + "jupytext": { + "notebook_metadata_filter": "all" + }, + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "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.6.8" + }, + "plotly": { + "description": "Figures made with Plotly Express can be customized in all the same ways as figures made with graph objects, as well as with PX-specific function arguments.", + "display_as": "file_settings", + "language": "python", + "layout": "base", + "name": "Styling Plotly Express Figures", + "order": 30, + "page_type": "u-guide", + "permalink": "python/styling-plotly-express/", + "thumbnail": "thumbnail/plotly-express.png" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/subplots.ipynb b/subplots.ipynb new file mode 100644 index 000000000..d1637e238 --- /dev/null +++ b/subplots.ipynb @@ -0,0 +1,926 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "61d6bde1", + "metadata": {}, + "source": [ + "### Subplots and Plotly Express\n", + "\n", + "[Plotly Express](/python/plotly-express/) is the easy-to-use, high-level interface to Plotly, which [operates on a variety of types of data](/python/px-arguments/) and produces [easy-to-style figures](/python/styling-plotly-express/).\n", + "\n", + "Plotly Express does not support arbitrary subplot capabilities, instead it supports [faceting by a given data dimension](/python/facet-plots/), and it also supports [marginal charts to display distribution information](/python/marginal-plots/).\n", + "\n", + "This page documents the usage of the lower-level `plotly.subplots` module and the `make_subplots` function it exposes to construct figures with arbitrary subplots. **Plotly Express faceting uses `make_subplots` internally** so adding traces to Plotly Express facets works just as documented here, with `fig.add_trace(..., row=, col=)`." + ] + }, + { + "cell_type": "markdown", + "id": "bda1d632", + "metadata": {}, + "source": [ + "#### Simple Subplot\n", + "\n", + "Figures with subplots are created using the `make_subplots` function from the `plotly.subplots` module.\n", + "\n", + "Here is an example of creating a figure that includes two `scatter` traces which are side-by-side since there are 2 columns and 1 row in the subplot layout." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "efbaa621", + "metadata": {}, + "outputs": [], + "source": [ + "from plotly.subplots import make_subplots\n", + "import plotly.graph_objects as go\n", + "\n", + "fig = make_subplots(rows=1, cols=2)\n", + "\n", + "fig.add_trace(\n", + " go.Scatter(x=[1, 2, 3], y=[4, 5, 6]),\n", + " row=1, col=1\n", + ")\n", + "\n", + "fig.add_trace(\n", + " go.Scatter(x=[20, 30, 40], y=[50, 60, 70]),\n", + " row=1, col=2\n", + ")\n", + "\n", + "fig.update_layout(height=600, width=800, title_text=\"Side By Side Subplots\")\n", + "fig.show()" + ] + }, + { + "cell_type": "markdown", + "id": "d8162f12", + "metadata": {}, + "source": [ + "#### Stacked Subplots\n", + "\n", + "Here is an example of creating a figure with subplots that are stacked on top of each other since there are 3 rows and 1 column in the subplot layout." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "d5341fbb", + "metadata": {}, + "outputs": [], + "source": [ + "from plotly.subplots import make_subplots\n", + "import plotly.graph_objects as go\n", + "\n", + "fig = make_subplots(rows=3, cols=1)\n", + "\n", + "fig.add_trace(go.Scatter(\n", + " x=[3, 4, 5],\n", + " y=[1000, 1100, 1200],\n", + "), row=1, col=1)\n", + "\n", + "fig.add_trace(go.Scatter(\n", + " x=[2, 3, 4],\n", + " y=[100, 110, 120],\n", + "), row=2, col=1)\n", + "\n", + "fig.add_trace(go.Scatter(\n", + " x=[0, 1, 2],\n", + " y=[10, 11, 12]\n", + "), row=3, col=1)\n", + "\n", + "\n", + "fig.update_layout(height=600, width=600, title_text=\"Stacked Subplots\")\n", + "fig.show()" + ] + }, + { + "cell_type": "markdown", + "id": "5e324b2a", + "metadata": {}, + "source": [ + "#### Multiple Subplots\n", + "\n", + "Here is an example of creating a 2 x 2 subplot grid and populating each subplot with a single `scatter` trace." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "0a580957", + "metadata": {}, + "outputs": [], + "source": [ + "import plotly.graph_objects as go\n", + "from plotly.subplots import make_subplots\n", + "\n", + "fig = make_subplots(rows=2, cols=2, start_cell=\"bottom-left\")\n", + "\n", + "fig.add_trace(go.Scatter(x=[1, 2, 3], y=[4, 5, 6]),\n", + " row=1, col=1)\n", + "\n", + "fig.add_trace(go.Scatter(x=[20, 30, 40], y=[50, 60, 70]),\n", + " row=1, col=2)\n", + "\n", + "fig.add_trace(go.Scatter(x=[300, 400, 500], y=[600, 700, 800]),\n", + " row=2, col=1)\n", + "\n", + "fig.add_trace(go.Scatter(x=[4000, 5000, 6000], y=[7000, 8000, 9000]),\n", + " row=2, col=2)\n", + "\n", + "fig.show()" + ] + }, + { + "cell_type": "markdown", + "id": "79903a5a", + "metadata": {}, + "source": [ + "#### Multiple Subplots with Titles\n", + "The `subplot_titles` argument to `make_subplots` can be used to position text annotations as titles for each subplot.\n", + "\n", + "Here is an example of adding subplot titles to a 2 x 2 subplot grid of scatter traces." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "14f72e66", + "metadata": {}, + "outputs": [], + "source": [ + "from plotly.subplots import make_subplots\n", + "import plotly.graph_objects as go\n", + "\n", + "fig = make_subplots(\n", + " rows=2, cols=2,\n", + " subplot_titles=(\"Plot 1\", \"Plot 2\", \"Plot 3\", \"Plot 4\"))\n", + "\n", + "fig.add_trace(go.Scatter(x=[1, 2, 3], y=[4, 5, 6]),\n", + " row=1, col=1)\n", + "\n", + "fig.add_trace(go.Scatter(x=[20, 30, 40], y=[50, 60, 70]),\n", + " row=1, col=2)\n", + "\n", + "fig.add_trace(go.Scatter(x=[300, 400, 500], y=[600, 700, 800]),\n", + " row=2, col=1)\n", + "\n", + "fig.add_trace(go.Scatter(x=[4000, 5000, 6000], y=[7000, 8000, 9000]),\n", + " row=2, col=2)\n", + "\n", + "fig.update_layout(height=500, width=700,\n", + " title_text=\"Multiple Subplots with Titles\")\n", + "\n", + "fig.show()" + ] + }, + { + "cell_type": "markdown", + "id": "e4383c59", + "metadata": {}, + "source": [ + "#### Subplots with Annotations" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "e9ae2619", + "metadata": {}, + "outputs": [], + "source": [ + "from plotly.subplots import make_subplots\n", + "import plotly.graph_objects as go\n", + "\n", + "fig = make_subplots(rows=1, cols=2)\n", + "\n", + "fig.add_trace(\n", + " go.Scatter(\n", + " x=[1, 2, 3],\n", + " y=[4, 5, 6],\n", + " mode=\"markers+text\",\n", + " text=[\"Text A\", \"Text B\", \"Text C\"],\n", + " textposition=\"bottom center\"\n", + " ),\n", + " row=1, col=1\n", + ")\n", + "\n", + "fig.add_trace(\n", + " go.Scatter(\n", + " x=[20, 30, 40],\n", + " y=[50, 60, 70],\n", + " mode=\"markers+text\",\n", + " text=[\"Text D\", \"Text E\", \"Text F\"],\n", + " textposition=\"bottom center\"\n", + " ),\n", + " row=1, col=2\n", + ")\n", + "\n", + "fig.update_layout(height=600, width=800, title_text=\"Subplots with Annotations\")\n", + "\n", + "fig.show()" + ] + }, + { + "cell_type": "markdown", + "id": "bebde670", + "metadata": {}, + "source": [ + "#### Customize Subplot Column Widths and Row Heights\n", + "The `column_widths` argument to `make_subplots` can be used to customize the relative widths of the columns in a subplot grid. It should be set to a list of numbers with a length that matches the `cols` argument. These number will be normalized, so that they sum to 1, and used to compute the relative widths of the subplot grid columns. The `row_heights` argument serves the same purpose for controlling the relative heights of rows in the subplot grid.\n", + "\n", + "Here is an example of creating a figure with two scatter traces in side-by-side subplots. The left subplot is set to be wider than the right one." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "169b6f64", + "metadata": {}, + "outputs": [], + "source": [ + "import plotly.graph_objects as go\n", + "from plotly.subplots import make_subplots\n", + "\n", + "fig = make_subplots(rows=1, cols=2, column_widths=[0.7, 0.3])\n", + "\n", + "fig.add_trace(go.Scatter(x=[1, 2, 3], y=[4, 5, 6]),\n", + " row=1, col=1)\n", + "\n", + "fig.add_trace(go.Scatter(x=[20, 30, 40], y=[50, 60, 70]),\n", + " row=1, col=2)\n", + "\n", + "fig.show()" + ] + }, + { + "cell_type": "markdown", + "id": "8ed7d7b1", + "metadata": {}, + "source": [ + "#### Subplots in Dash\n", + "\n", + "[Dash](https://plotly.com/dash/) is the best way to build analytical apps in Python using Plotly figures. To run the app below, run `pip install dash`, click \"Download\" to get the code and run `python app.py`.\n", + "\n", + "Get started with [the official Dash docs](https://dash.plotly.com/installation) and **learn how to effortlessly [style](https://plotly.com/dash/design-kit/) & [deploy](https://plotly.com/dash/app-manager/) apps like this with Dash Enterprise.**\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "e00776b5", + "metadata": { + "hide_code": true + }, + "outputs": [], + "source": [ + "from IPython.display import IFrame\n", + "snippet_url = 'https://python-docs-dash-snippets.herokuapp.com/python-docs-dash-snippets/'\n", + "IFrame(snippet_url + 'subplots', width='100%', height=1200)" + ] + }, + { + "cell_type": "markdown", + "id": "1f0a3a7e", + "metadata": {}, + "source": [ + "

Sign up for Dash Club → Free cheat sheets plus updates from Chris Parmer and Adam Schroeder delivered to your inbox every two months. Includes tips and tricks, community apps, and deep dives into the Dash architecture.\n", + "Join now.

" + ] + }, + { + "cell_type": "markdown", + "id": "462c4cb7", + "metadata": {}, + "source": [ + "#### Customizing Subplot Axes\n", + "After a figure with subplots is created using the `make_subplots` function, its axis properties (title, font, range, grid style, etc.) can be customized using the `update_xaxes` and `update_yaxes` graph object figure methods. By default, these methods apply to all of the x axes or y axes in the figure. The `row` and `col` arguments can be used to control which axes are targeted by the update.\n", + "\n", + "Here is an example that creates a figure with a 2 x 2 subplot grid, populates each subplot with a scatter trace, and then updates the x and y axis titles for each subplot individually." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "f33fdc11", + "metadata": {}, + "outputs": [], + "source": [ + "from plotly.subplots import make_subplots\n", + "import plotly.graph_objects as go\n", + "\n", + "# Initialize figure with subplots\n", + "fig = make_subplots(\n", + " rows=2, cols=2, subplot_titles=(\"Plot 1\", \"Plot 2\", \"Plot 3\", \"Plot 4\")\n", + ")\n", + "\n", + "# Add traces\n", + "fig.add_trace(go.Scatter(x=[1, 2, 3], y=[4, 5, 6]), row=1, col=1)\n", + "fig.add_trace(go.Scatter(x=[20, 30, 40], y=[50, 60, 70]), row=1, col=2)\n", + "fig.add_trace(go.Scatter(x=[300, 400, 500], y=[600, 700, 800]), row=2, col=1)\n", + "fig.add_trace(go.Scatter(x=[4000, 5000, 6000], y=[7000, 8000, 9000]), row=2, col=2)\n", + "\n", + "# Update xaxis properties\n", + "fig.update_xaxes(title_text=\"xaxis 1 title\", row=1, col=1)\n", + "fig.update_xaxes(title_text=\"xaxis 2 title\", range=[10, 50], row=1, col=2)\n", + "fig.update_xaxes(title_text=\"xaxis 3 title\", showgrid=False, row=2, col=1)\n", + "fig.update_xaxes(title_text=\"xaxis 4 title\", type=\"log\", row=2, col=2)\n", + "\n", + "# Update yaxis properties\n", + "fig.update_yaxes(title_text=\"yaxis 1 title\", row=1, col=1)\n", + "fig.update_yaxes(title_text=\"yaxis 2 title\", range=[40, 80], row=1, col=2)\n", + "fig.update_yaxes(title_text=\"yaxis 3 title\", showgrid=False, row=2, col=1)\n", + "fig.update_yaxes(title_text=\"yaxis 4 title\", row=2, col=2)\n", + "\n", + "# Update title and height\n", + "fig.update_layout(title_text=\"Customizing Subplot Axes\", height=700)\n", + "\n", + "fig.show()" + ] + }, + { + "cell_type": "markdown", + "id": "c5244730", + "metadata": {}, + "source": [ + "#### Subplots with Shared X-Axes\n", + "The `shared_xaxes` argument to `make_subplots` can be used to link the x axes of subplots in the resulting figure. The `vertical_spacing` argument is used to control the vertical spacing between rows in the subplot grid.\n", + "\n", + "Here is an example that creates a figure with 3 vertically stacked subplots with linked x axes. A small vertical spacing value is used to reduce the spacing between subplot rows." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "16871843", + "metadata": {}, + "outputs": [], + "source": [ + "from plotly.subplots import make_subplots\n", + "import plotly.graph_objects as go\n", + "\n", + "fig = make_subplots(rows=3, cols=1,\n", + " shared_xaxes=True,\n", + " vertical_spacing=0.02)\n", + "\n", + "fig.add_trace(go.Scatter(x=[0, 1, 2], y=[10, 11, 12]),\n", + " row=3, col=1)\n", + "\n", + "fig.add_trace(go.Scatter(x=[2, 3, 4], y=[100, 110, 120]),\n", + " row=2, col=1)\n", + "\n", + "fig.add_trace(go.Scatter(x=[3, 4, 5], y=[1000, 1100, 1200]),\n", + " row=1, col=1)\n", + "\n", + "fig.update_layout(height=600, width=600,\n", + " title_text=\"Stacked Subplots with Shared X-Axes\")\n", + "fig.show()" + ] + }, + { + "cell_type": "markdown", + "id": "2675b651", + "metadata": {}, + "source": [ + "#### Subplots with Shared Y-Axes\n", + "The `shared_yaxes` argument to `make_subplots` can be used to link the y axes of subplots in the resulting figure.\n", + "\n", + "Here is an example that creates a figure with a 2 x 2 subplot grid, where the y axes of each row are linked.\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "1f6b2d42", + "metadata": {}, + "outputs": [], + "source": [ + "from plotly.subplots import make_subplots\n", + "import plotly.graph_objects as go\n", + "\n", + "fig = make_subplots(rows=2, cols=2, shared_yaxes=True)\n", + "\n", + "fig.add_trace(go.Scatter(x=[1, 2, 3], y=[2, 3, 4]),\n", + " row=1, col=1)\n", + "\n", + "fig.add_trace(go.Scatter(x=[20, 30, 40], y=[5, 5, 5]),\n", + " row=1, col=2)\n", + "\n", + "fig.add_trace(go.Scatter(x=[2, 3, 4], y=[600, 700, 800]),\n", + " row=2, col=1)\n", + "\n", + "fig.add_trace(go.Scatter(x=[4000, 5000, 6000], y=[7000, 8000, 9000]),\n", + " row=2, col=2)\n", + "\n", + "fig.update_layout(height=600, width=600,\n", + " title_text=\"Multiple Subplots with Shared Y-Axes\")\n", + "fig.show()" + ] + }, + { + "cell_type": "markdown", + "id": "f7ac27e9", + "metadata": {}, + "source": [ + "### Subplots with Shared Colorscale\n", + "\n", + "To share colorscale information in multiple subplots, you can use [coloraxis](https://plotly.com/javascript/reference/scatter/#scatter-marker-line-coloraxis)." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "46839f7a", + "metadata": {}, + "outputs": [], + "source": [ + "from plotly.subplots import make_subplots\n", + "import plotly.graph_objects as go\n", + "\n", + "fig = make_subplots(rows=1, cols=2, shared_yaxes=True)\n", + "\n", + "fig.add_trace(go.Bar(x=[1, 2, 3], y=[4, 5, 6],\n", + " marker=dict(color=[4, 5, 6], coloraxis=\"coloraxis\")),\n", + " 1, 1)\n", + "\n", + "fig.add_trace(go.Bar(x=[1, 2, 3], y=[2, 3, 5],\n", + " marker=dict(color=[2, 3, 5], coloraxis=\"coloraxis\")),\n", + " 1, 2)\n", + "\n", + "fig.update_layout(coloraxis=dict(colorscale='Bluered_r'), showlegend=False)\n", + "fig.show()" + ] + }, + { + "cell_type": "markdown", + "id": "01bb57db", + "metadata": {}, + "source": [ + "#### Custom Sized Subplot with Subplot Titles\n", + "The `specs` argument to `make_subplots` is used to configure per-subplot options. `specs` must be a 2-dimension list with dimensions that match those provided as the `rows` and `cols` arguments. The elements of `specs` may either be `None`, indicating no subplot should be initialized starting with this grid cell, or a dictionary containing subplot options. The `colspan` subplot option specifies the number of grid columns that the subplot starting in the given cell should occupy. If unspecified, `colspan` defaults to 1.\n", + "\n", + "Here is an example that creates a 2 by 2 subplot grid containing 3 subplots. The subplot `specs` element for position (2, 1) has a `colspan` value of 2, causing it to span the full figure width. The subplot `specs` element for position (2, 2) is `None` because no subplot begins at this location in the grid." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "a4214d6b", + "metadata": {}, + "outputs": [], + "source": [ + "from plotly.subplots import make_subplots\n", + "import plotly.graph_objects as go\n", + "\n", + "fig = make_subplots(\n", + " rows=2, cols=2,\n", + " specs=[[{}, {}],\n", + " [{\"colspan\": 2}, None]],\n", + " subplot_titles=(\"First Subplot\",\"Second Subplot\", \"Third Subplot\"))\n", + "\n", + "fig.add_trace(go.Scatter(x=[1, 2], y=[1, 2]),\n", + " row=1, col=1)\n", + "\n", + "fig.add_trace(go.Scatter(x=[1, 2], y=[1, 2]),\n", + " row=1, col=2)\n", + "fig.add_trace(go.Scatter(x=[1, 2, 3], y=[2, 1, 2]),\n", + " row=2, col=1)\n", + "\n", + "fig.update_layout(showlegend=False, title_text=\"Specs with Subplot Title\")\n", + "fig.show()" + ] + }, + { + "cell_type": "markdown", + "id": "9424a442", + "metadata": {}, + "source": [ + "#### Multiple Custom Sized Subplots\n", + "If the `print_grid` argument to `make_subplots` is set to `True`, then a text representation of the subplot grid will be printed.\n", + "\n", + "Here is an example that uses the `rowspan` and `colspan` subplot options to create a custom subplot layout with subplots of mixed sizes. The `print_grid` argument is set to `True` so that the subplot grid is printed to the screen." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "67cd4444", + "metadata": {}, + "outputs": [], + "source": [ + "from plotly.subplots import make_subplots\n", + "import plotly.graph_objects as go\n", + "\n", + "fig = make_subplots(\n", + " rows=5, cols=2,\n", + " specs=[[{}, {\"rowspan\": 2}],\n", + " [{}, None],\n", + " [{\"rowspan\": 2, \"colspan\": 2}, None],\n", + " [None, None],\n", + " [{}, {}]],\n", + " print_grid=True)\n", + "\n", + "fig.add_trace(go.Scatter(x=[1, 2], y=[1, 2], name=\"(1,1)\"), row=1, col=1)\n", + "fig.add_trace(go.Scatter(x=[1, 2], y=[1, 2], name=\"(1,2)\"), row=1, col=2)\n", + "fig.add_trace(go.Scatter(x=[1, 2], y=[1, 2], name=\"(2,1)\"), row=2, col=1)\n", + "fig.add_trace(go.Scatter(x=[1, 2], y=[1, 2], name=\"(3,1)\"), row=3, col=1)\n", + "fig.add_trace(go.Scatter(x=[1, 2], y=[1, 2], name=\"(5,1)\"), row=5, col=1)\n", + "fig.add_trace(go.Scatter(x=[1, 2], y=[1, 2], name=\"(5,2)\"), row=5, col=2)\n", + "\n", + "fig.update_layout(height=600, width=600, title_text=\"specs examples\")\n", + "fig.show()" + ] + }, + { + "cell_type": "markdown", + "id": "4767f5ae", + "metadata": {}, + "source": [ + "#### Subplots Types\n", + "By default, the `make_subplots` function assumes that the traces that will be added to all subplots are 2-dimensional cartesian traces (e.g. `scatter`, `bar`, `histogram`, `violin`, etc.). Traces with other subplot types (e.g. `scatterpolar`, `scattergeo`, `parcoords`, etc.) are supported by specifying the `type` subplot option in the `specs` argument to `make_subplots`.\n", + "\n", + "Here are the possible values for the `type` option:\n", + "\n", + " - `\"xy\"`: 2D Cartesian subplot type for scatter, bar, etc. This is the default if no `type` is specified.\n", + " - `\"scene\"`: 3D Cartesian subplot for scatter3d, cone, etc.\n", + " - `\"polar\"`: Polar subplot for scatterpolar, barpolar, etc.\n", + " - `\"ternary\"`: Ternary subplot for scatterternary.\n", + " - `\"mapbox\"`: Mapbox subplot for scattermapbox.\n", + " - `\"domain\"`: Subplot type for traces that are individually positioned. pie, parcoords, parcats, etc.\n", + " - trace type: A trace type name (e.g. `\"bar\"`, `\"scattergeo\"`, `\"carpet\"`, `\"mesh\"`, etc.) which will be used to determine the appropriate subplot type for that trace.\n", + "\n", + "Here is an example that creates and populates a 2 x 2 subplot grid containing 4 different subplot types." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "d2237d1f", + "metadata": {}, + "outputs": [], + "source": [ + "from plotly.subplots import make_subplots\n", + "import plotly.graph_objects as go\n", + "\n", + "fig = make_subplots(\n", + " rows=2, cols=2,\n", + " specs=[[{\"type\": \"xy\"}, {\"type\": \"polar\"}],\n", + " [{\"type\": \"domain\"}, {\"type\": \"scene\"}]],\n", + ")\n", + "\n", + "fig.add_trace(go.Bar(y=[2, 3, 1]),\n", + " row=1, col=1)\n", + "\n", + "fig.add_trace(go.Barpolar(theta=[0, 45, 90], r=[2, 3, 1]),\n", + " row=1, col=2)\n", + "\n", + "fig.add_trace(go.Pie(values=[2, 3, 1]),\n", + " row=2, col=1)\n", + "\n", + "fig.add_trace(go.Scatter3d(x=[2, 3, 1], y=[0, 0, 0],\n", + " z=[0.5, 1, 2], mode=\"lines\"),\n", + " row=2, col=2)\n", + "\n", + "fig.update_layout(height=700, showlegend=False)\n", + "\n", + "fig.show()" + ] + }, + { + "cell_type": "markdown", + "id": "e085f9a4", + "metadata": {}, + "source": [ + "As an alternative to providing the name of a subplot type (e.g. `\"xy\"`, `\"polar\"`, `\"domain\"`, `\"scene\"`, etc), the `type` option may also be set to a string containing the name of a trace type (e.g. `\"bar\"`, `\"barpolar\"`, `\"pie\"`, `\"scatter3d\"`, etc.), which will be used to determine the subplot type that is compatible with that trace.\n", + "\n", + "Here is the example above, modified to specify the subplot types using trace type names." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "e24c4551", + "metadata": {}, + "outputs": [], + "source": [ + "from plotly.subplots import make_subplots\n", + "import plotly.graph_objects as go\n", + "\n", + "fig = make_subplots(\n", + " rows=2, cols=2,\n", + " specs=[[{\"type\": \"bar\"}, {\"type\": \"barpolar\"}],\n", + " [{\"type\": \"pie\"}, {\"type\": \"scatter3d\"}]],\n", + ")\n", + "\n", + "fig.add_trace(go.Bar(y=[2, 3, 1]),\n", + " row=1, col=1)\n", + "\n", + "fig.add_trace(go.Barpolar(theta=[0, 45, 90], r=[2, 3, 1]),\n", + " row=1, col=2)\n", + "\n", + "fig.add_trace(go.Pie(values=[2, 3, 1]),\n", + " row=2, col=1)\n", + "\n", + "fig.add_trace(go.Scatter3d(x=[2, 3, 1], y=[0, 0, 0],\n", + " z=[0.5, 1, 2], mode=\"lines\"),\n", + " row=2, col=2)\n", + "\n", + "fig.update_layout(height=700, showlegend=False)\n", + "\n", + "fig.show()" + ] + }, + { + "cell_type": "markdown", + "id": "60425fe3", + "metadata": {}, + "source": [ + "#### Side by Side Subplot (low-level API)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "c54da951", + "metadata": {}, + "outputs": [], + "source": [ + "import plotly.graph_objects as go\n", + "\n", + "trace1 = go.Scatter(\n", + " x=[1, 2, 3],\n", + " y=[4, 5, 6]\n", + ")\n", + "trace2 = go.Scatter(\n", + " x=[20, 30, 40],\n", + " y=[50, 60, 70],\n", + " xaxis=\"x2\",\n", + " yaxis=\"y2\"\n", + ")\n", + "data = [trace1, trace2]\n", + "layout = go.Layout(\n", + " xaxis=dict(\n", + " domain=[0, 0.7]\n", + " ),\n", + " xaxis2=dict(\n", + " domain=[0.8, 1]\n", + " ),\n", + " yaxis2=dict(\n", + " anchor=\"x2\"\n", + " )\n", + ")\n", + "fig = go.Figure(data=data, layout=layout)\n", + "fig.show()" + ] + }, + { + "cell_type": "markdown", + "id": "5582cab2", + "metadata": {}, + "source": [ + "#### Subplots with shared axes (low-level API)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "17b8d244", + "metadata": {}, + "outputs": [], + "source": [ + "import plotly.graph_objects as go\n", + "\n", + "trace1 = go.Scatter(\n", + " x=[1, 2, 3],\n", + " y=[2, 3, 4]\n", + ")\n", + "trace2 = go.Scatter(\n", + " x=[20, 30, 40],\n", + " y=[5, 5, 5],\n", + " xaxis=\"x2\",\n", + " yaxis=\"y\"\n", + ")\n", + "trace3 = go.Scatter(\n", + " x=[2, 3, 4],\n", + " y=[600, 700, 800],\n", + " xaxis=\"x\",\n", + " yaxis=\"y3\"\n", + ")\n", + "trace4 = go.Scatter(\n", + " x=[4000, 5000, 6000],\n", + " y=[7000, 8000, 9000],\n", + " xaxis=\"x4\",\n", + " yaxis=\"y4\"\n", + ")\n", + "data = [trace1, trace2, trace3, trace4]\n", + "layout = go.Layout(\n", + " xaxis=dict(\n", + " domain=[0, 0.45]\n", + " ),\n", + " yaxis=dict(\n", + " domain=[0, 0.45]\n", + " ),\n", + " xaxis2=dict(\n", + " domain=[0.55, 1]\n", + " ),\n", + " xaxis4=dict(\n", + " domain=[0.55, 1],\n", + " anchor=\"y4\"\n", + " ),\n", + " yaxis3=dict(\n", + " domain=[0.55, 1]\n", + " ),\n", + " yaxis4=dict(\n", + " domain=[0.55, 1],\n", + " anchor=\"x4\"\n", + " )\n", + ")\n", + "fig = go.Figure(data=data, layout=layout)\n", + "fig.show()" + ] + }, + { + "cell_type": "markdown", + "id": "dcb2830d", + "metadata": {}, + "source": [ + "#### Stacked Subplots with a Shared X-Axis (low-level API)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "78bcccde", + "metadata": {}, + "outputs": [], + "source": [ + "import plotly.graph_objects as go\n", + "\n", + "trace1 = go.Scatter(\n", + " x=[0, 1, 2],\n", + " y=[10, 11, 12]\n", + ")\n", + "trace2 = go.Scatter(\n", + " x=[2, 3, 4],\n", + " y=[100, 110, 120],\n", + " yaxis=\"y2\"\n", + ")\n", + "trace3 = go.Scatter(\n", + " x=[3, 4, 5],\n", + " y=[1000, 1100, 1200],\n", + " yaxis=\"y3\"\n", + ")\n", + "data = [trace1, trace2, trace3]\n", + "layout = go.Layout(\n", + " yaxis=dict(\n", + " domain=[0, 0.33]\n", + " ),\n", + " legend=dict(\n", + " traceorder=\"reversed\"\n", + " ),\n", + " yaxis2=dict(\n", + " domain=[0.33, 0.66]\n", + " ),\n", + " yaxis3=dict(\n", + " domain=[0.66, 1]\n", + " )\n", + ")\n", + "fig = go.Figure(data=data, layout=layout)\n", + "fig.show()" + ] + }, + { + "cell_type": "markdown", + "id": "0fd6ed81", + "metadata": {}, + "source": [ + "#### Setting Subplots on a Figure Directly\n", + "\n", + "_new in 4.13_\n", + "\n", + "Subplots can be added to an already existing figure, provided it doesn't already\n", + "have subplots. `go.Figure.set_subplots` accepts all the same arguments as\n", + "`plotly.subplots.make_subplots`." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "ab390665", + "metadata": {}, + "outputs": [], + "source": [ + "import plotly.graph_objects as go\n", + "fig = go.Figure().set_subplots(2, 3, horizontal_spacing=0.1)" + ] + }, + { + "cell_type": "markdown", + "id": "2d27236d", + "metadata": {}, + "source": [ + "is equivalent to:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "eb7ea0fb", + "metadata": {}, + "outputs": [], + "source": [ + "from plotly.subplots import make_subplots\n", + "fig = make_subplots(2, 3, horizontal_spacing=0.1)" + ] + }, + { + "cell_type": "markdown", + "id": "4f2fb9ec", + "metadata": {}, + "source": [ + "#### Reference\n", + "All of the x-axis properties are found here: https://plotly.com/python/reference/layout/xaxis/\n", + "All of the y-axis properties are found here: https://plotly.com/python/reference/layout/yaxis/" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "72484417", + "metadata": { + "lines_to_next_cell": 2 + }, + "outputs": [], + "source": [ + "from plotly.subplots import make_subplots\n", + "help(make_subplots)" + ] + }, + { + "cell_type": "markdown", + "id": "4fb464b9", + "metadata": {}, + "source": [ + "### What About Dash?\n", + "\n", + "[Dash](https://dash.plot.ly/) is an open-source framework for building analytical applications, with no Javascript required, and it is tightly integrated with the Plotly graphing library.\n", + "\n", + "Learn about how to install Dash at https://dash.plot.ly/installation.\n", + "\n", + "Everywhere in this page that you see `fig.show()`, you can display the same figure in a Dash application by passing it to the `figure` argument of the [`Graph` component](https://dash.plot.ly/dash-core-components/graph) from the built-in `dash_core_components` package like this:\n", + "\n", + "```python\n", + "import plotly.graph_objects as go # or plotly.express as px\n", + "fig = go.Figure() # or any Plotly Express function e.g. px.bar(...)\n", + "# fig.add_trace( ... )\n", + "# fig.update_layout( ... )\n", + "\n", + "from dash import Dash, dcc, html\n", + "\n", + "app = Dash()\n", + "app.layout = html.Div([\n", + " dcc.Graph(figure=fig)\n", + "])\n", + "\n", + "app.run_server(debug=True, use_reloader=False) # Turn off reloader if inside Jupyter\n", + "```" + ] + } + ], + "metadata": { + "jupytext": { + "notebook_metadata_filter": "all" + }, + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "language": "python", + "name": "python3" + }, + "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.11.10" + }, + "plotly": { + "description": "How to make subplots in with Plotly's Python graphing library. Examples of stacked, custom-sized, gridded, and annotated subplots.", + "display_as": "file_settings", + "language": "python", + "layout": "base", + "name": "Subplots", + "order": 17, + "page_type": "u-guide", + "permalink": "python/subplots/", + "redirect_from": "ipython-notebooks/subplots/", + "thumbnail": "thumbnail/subplots.jpg" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/sunburst-charts.ipynb b/sunburst-charts.ipynb new file mode 100644 index 000000000..ca91093b4 --- /dev/null +++ b/sunburst-charts.ipynb @@ -0,0 +1,636 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "f93dbfa4", + "metadata": {}, + "source": [ + "Sunburst plots visualize hierarchical data spanning outwards radially from root to leaves. Similar to [Icicle charts](https://plotly.com/python/icicle-charts/) and [Treemaps](https://plotly.com/python/treemaps/), the hierarchy is defined by `labels` (`names` for `px.icicle`) and `parents` attributes. The root starts from the center and children are added to the outer rings.\n", + "\n", + "### Basic Sunburst Plot with plotly.express\n", + "\n", + "[Plotly Express](/python/plotly-express/) is the easy-to-use, high-level interface to Plotly, which [operates on a variety of types of data](/python/px-arguments/) and produces [easy-to-style figures](/python/styling-plotly-express/).\n", + "\n", + "With `px.sunburst`, each row of the DataFrame is represented as a sector of the sunburst." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "056287d5", + "metadata": {}, + "outputs": [], + "source": [ + "import plotly.express as px\n", + "data = dict(\n", + " character=[\"Eve\", \"Cain\", \"Seth\", \"Enos\", \"Noam\", \"Abel\", \"Awan\", \"Enoch\", \"Azura\"],\n", + " parent=[\"\", \"Eve\", \"Eve\", \"Seth\", \"Seth\", \"Eve\", \"Eve\", \"Awan\", \"Eve\" ],\n", + " value=[10, 14, 12, 10, 2, 6, 6, 4, 4])\n", + "\n", + "fig = px.sunburst(\n", + " data,\n", + " names='character',\n", + " parents='parent',\n", + " values='value',\n", + ")\n", + "fig.show()" + ] + }, + { + "cell_type": "markdown", + "id": "167df391", + "metadata": {}, + "source": [ + "### Sunburst of a rectangular DataFrame with plotly.express\n", + "\n", + "Hierarchical data are often stored as a rectangular dataframe, with different columns corresponding to different levels of the hierarchy. `px.sunburst` can take a `path` parameter corresponding to a list of columns. Note that `id` and `parent` should not be provided if `path` is given." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "7c3696fb", + "metadata": {}, + "outputs": [], + "source": [ + "import plotly.express as px\n", + "df = px.data.tips()\n", + "fig = px.sunburst(df, path=['day', 'time', 'sex'], values='total_bill')\n", + "fig.show()" + ] + }, + { + "cell_type": "markdown", + "id": "0a899fa2", + "metadata": {}, + "source": [ + "### Sunburst of a rectangular DataFrame with continuous color argument in px.sunburst\n", + "\n", + "If a `color` argument is passed, the color of a node is computed as the average of the color values of its children, weighted by their values." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "3b99d5a6", + "metadata": {}, + "outputs": [], + "source": [ + "import plotly.express as px\n", + "import numpy as np\n", + "df = px.data.gapminder().query(\"year == 2007\")\n", + "fig = px.sunburst(df, path=['continent', 'country'], values='pop',\n", + " color='lifeExp', hover_data=['iso_alpha'],\n", + " color_continuous_scale='RdBu',\n", + " color_continuous_midpoint=np.average(df['lifeExp'], weights=df['pop']))\n", + "fig.show()" + ] + }, + { + "cell_type": "markdown", + "id": "04e03fd3", + "metadata": {}, + "source": [ + "### Sunburst of a rectangular DataFrame with discrete color argument in px.sunburst\n", + "\n", + "When the argument of `color` corresponds to non-numerical data, discrete colors are used. If a sector has the same value of the `color` column for all its children, then the corresponding color is used, otherwise the first color of the discrete color sequence is used." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "d93c421e", + "metadata": {}, + "outputs": [], + "source": [ + "import plotly.express as px\n", + "df = px.data.tips()\n", + "fig = px.sunburst(df, path=['sex', 'day', 'time'], values='total_bill', color='day')\n", + "fig.show()" + ] + }, + { + "cell_type": "markdown", + "id": "fe58d9d0", + "metadata": {}, + "source": [ + "In the example below the color of `Saturday` and `Sunday` sectors is the same as `Dinner` because there are only Dinner entries for Saturday and Sunday. However, for Female -> Friday there are both lunches and dinners, hence the \"mixed\" color (blue here) is used." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "efe5ba0d", + "metadata": {}, + "outputs": [], + "source": [ + "import plotly.express as px\n", + "df = px.data.tips()\n", + "fig = px.sunburst(df, path=['sex', 'day', 'time'], values='total_bill', color='time')\n", + "fig.show()" + ] + }, + { + "cell_type": "markdown", + "id": "87eeca6c", + "metadata": {}, + "source": [ + "### Using an explicit mapping for discrete colors\n", + "\n", + "For more information about discrete colors, see the [dedicated page](/python/discrete-color)." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "5178cb01", + "metadata": {}, + "outputs": [], + "source": [ + "import plotly.express as px\n", + "df = px.data.tips()\n", + "fig = px.sunburst(df, path=['sex', 'day', 'time'], values='total_bill', color='time',\n", + " color_discrete_map={'(?)':'black', 'Lunch':'gold', 'Dinner':'darkblue'})\n", + "fig.show()" + ] + }, + { + "cell_type": "markdown", + "id": "c1d23065", + "metadata": {}, + "source": [ + "### Rectangular data with missing values\n", + "\n", + "If the dataset is not fully rectangular, missing values should be supplied as `None`. Note that the parents of `None` entries must be a leaf, i.e. it cannot have other children than `None` (otherwise a `ValueError` is raised)." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "beba1118", + "metadata": {}, + "outputs": [], + "source": [ + "import plotly.express as px\n", + "import pandas as pd\n", + "vendors = [\"A\", \"B\", \"C\", \"D\", None, \"E\", \"F\", \"G\", \"H\", None]\n", + "sectors = [\"Tech\", \"Tech\", \"Finance\", \"Finance\", \"Other\",\n", + " \"Tech\", \"Tech\", \"Finance\", \"Finance\", \"Other\"]\n", + "regions = [\"North\", \"North\", \"North\", \"North\", \"North\",\n", + " \"South\", \"South\", \"South\", \"South\", \"South\"]\n", + "sales = [1, 3, 2, 4, 1, 2, 2, 1, 4, 1]\n", + "df = pd.DataFrame(\n", + " dict(vendors=vendors, sectors=sectors, regions=regions, sales=sales)\n", + ")\n", + "print(df)\n", + "fig = px.sunburst(df, path=['regions', 'sectors', 'vendors'], values='sales')\n", + "fig.show()" + ] + }, + { + "cell_type": "markdown", + "id": "c4d52f7e", + "metadata": {}, + "source": [ + "### Basic Sunburst Plot with go.Sunburst\n", + "\n", + "If Plotly Express does not provide a good starting point, it is also possible to use [the more generic `go.Sunburst` class from `plotly.graph_objects`](/python/graph-objects/).\n", + "\n", + "Main arguments:\n", + "\n", + "1. `labels` (`names` in `px.sunburst` since `labels` is reserved for overriding columns names): sets the labels of sunburst sectors.\n", + "2. `parents`: sets the parent sectors of sunburst sectors. An empty string `''` is used for the root node in the hierarchy. In this example, the root is \"Eve\".\n", + "3. `values`: sets the values associated with sunburst sectors, determining their width (See the `branchvalues` section below for different modes for setting the width)." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "d07fe92d", + "metadata": {}, + "outputs": [], + "source": [ + "import plotly.graph_objects as go\n", + "\n", + "fig =go.Figure(go.Sunburst(\n", + " labels=[\"Eve\", \"Cain\", \"Seth\", \"Enos\", \"Noam\", \"Abel\", \"Awan\", \"Enoch\", \"Azura\"],\n", + " parents=[\"\", \"Eve\", \"Eve\", \"Seth\", \"Seth\", \"Eve\", \"Eve\", \"Awan\", \"Eve\" ],\n", + " values=[10, 14, 12, 10, 2, 6, 6, 4, 4],\n", + "))\n", + "# Update layout for tight margin\n", + "# See https://plotly.com/python/creating-and-updating-figures/\n", + "fig.update_layout(margin = dict(t=0, l=0, r=0, b=0))\n", + "\n", + "fig.show()" + ] + }, + { + "cell_type": "markdown", + "id": "3ecd8860", + "metadata": {}, + "source": [ + "### Sunburst with Repeated Labels" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "051aa872", + "metadata": {}, + "outputs": [], + "source": [ + "import plotly.graph_objects as go\n", + "\n", + "fig =go.Figure(go.Sunburst(\n", + " ids=[\n", + " \"North America\", \"Europe\", \"Australia\", \"North America - Football\", \"Soccer\",\n", + " \"North America - Rugby\", \"Europe - Football\", \"Rugby\",\n", + " \"Europe - American Football\",\"Australia - Football\", \"Association\",\n", + " \"Australian Rules\", \"Autstralia - American Football\", \"Australia - Rugby\",\n", + " \"Rugby League\", \"Rugby Union\"\n", + " ],\n", + " labels= [\n", + " \"North
America\", \"Europe\", \"Australia\", \"Football\", \"Soccer\", \"Rugby\",\n", + " \"Football\", \"Rugby\", \"American
Football\", \"Football\", \"Association\",\n", + " \"Australian
Rules\", \"American
Football\", \"Rugby\", \"Rugby
League\",\n", + " \"Rugby
Union\"\n", + " ],\n", + " parents=[\n", + " \"\", \"\", \"\", \"North America\", \"North America\", \"North America\", \"Europe\",\n", + " \"Europe\", \"Europe\",\"Australia\", \"Australia - Football\", \"Australia - Football\",\n", + " \"Australia - Football\", \"Australia - Football\", \"Australia - Rugby\",\n", + " \"Australia - Rugby\"\n", + " ],\n", + "))\n", + "fig.update_layout(margin = dict(t=0, l=0, r=0, b=0))\n", + "\n", + "fig.show()" + ] + }, + { + "cell_type": "markdown", + "id": "aeffc719", + "metadata": {}, + "source": [ + "### Branchvalues\n", + "\n", + "With branchvalues \"total\", the value of the parent represents the width of its wedge. In the example below, \"Enoch\" is 4 and \"Awan\" is 6 and so Enoch's width is 4/6ths of Awans. With branchvalues \"remainder\", the parent's width is determined by its own value plus those of its children. So, Enoch's width is 4/10ths of Awan's (4 / (6 + 4)).\n", + "\n", + "Note that this means that the sum of the values of the children cannot exceed the value of their parent when branchvalues is set to \"total\". When branchvalues is set to \"remainder\" (the default), children will not take up all of the space below their parent (unless the parent is the root and it has a value of 0)." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "ea413a72", + "metadata": {}, + "outputs": [], + "source": [ + "import plotly.graph_objects as go\n", + "\n", + "fig =go.Figure(go.Sunburst(\n", + " labels=[ \"Eve\", \"Cain\", \"Seth\", \"Enos\", \"Noam\", \"Abel\", \"Awan\", \"Enoch\", \"Azura\"],\n", + " parents=[\"\", \"Eve\", \"Eve\", \"Seth\", \"Seth\", \"Eve\", \"Eve\", \"Awan\", \"Eve\" ],\n", + " values=[ 65, 14, 12, 10, 2, 6, 6, 4, 4],\n", + " branchvalues=\"total\",\n", + "))\n", + "fig.update_layout(margin = dict(t=0, l=0, r=0, b=0))\n", + "\n", + "fig.show()" + ] + }, + { + "cell_type": "markdown", + "id": "214f7ce4", + "metadata": {}, + "source": [ + "### Large Number of Slices\n", + "\n", + "This example uses a [plotly grid attribute](https://plotly.com/python/reference/layout/#layout-grid) for the subplots. Reference the row and column destination using the [domain](https://plotly.com/python/reference/sunburst/#sunburst-domain) attribute." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "0ec1a3c2", + "metadata": {}, + "outputs": [], + "source": [ + "import plotly.graph_objects as go\n", + "\n", + "import pandas as pd\n", + "\n", + "df1 = pd.read_csv('https://raw.githubusercontent.com/plotly/datasets/718417069ead87650b90472464c7565dc8c2cb1c/sunburst-coffee-flavors-complete.csv')\n", + "df2 = pd.read_csv('https://raw.githubusercontent.com/plotly/datasets/718417069ead87650b90472464c7565dc8c2cb1c/coffee-flavors.csv')\n", + "\n", + "fig = go.Figure()\n", + "\n", + "fig.add_trace(go.Sunburst(\n", + " ids=df1.ids,\n", + " labels=df1.labels,\n", + " parents=df1.parents,\n", + " domain=dict(column=0)\n", + "))\n", + "\n", + "fig.add_trace(go.Sunburst(\n", + " ids=df2.ids,\n", + " labels=df2.labels,\n", + " parents=df2.parents,\n", + " domain=dict(column=1),\n", + " maxdepth=2\n", + "))\n", + "\n", + "fig.update_layout(\n", + " grid= dict(columns=2, rows=1),\n", + " margin = dict(t=0, l=0, r=0, b=0)\n", + ")\n", + "\n", + "fig.show()" + ] + }, + { + "cell_type": "markdown", + "id": "2fea5715", + "metadata": {}, + "source": [ + "#### Controlling text orientation inside sunburst sectors\n", + "\n", + "The `insidetextorientation` attribute controls the orientation of text inside sectors. With\n", + "\"auto\" the texts may automatically be rotated to fit with the maximum size inside the slice. Using \"horizontal\" (resp. \"radial\", \"tangential\") forces text to be horizontal (resp. radial or tangential). Note that `plotly` may reduce the font size in order to fit the text with the requested orientation.\n", + "\n", + "For a figure `fig` created with plotly express, use `fig.update_traces(insidetextorientation='...')` to change the text orientation." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "921ad078", + "metadata": {}, + "outputs": [], + "source": [ + "import plotly.graph_objects as go\n", + "import pandas as pd\n", + "\n", + "df = pd.read_csv('https://raw.githubusercontent.com/plotly/datasets/718417069ead87650b90472464c7565dc8c2cb1c/coffee-flavors.csv')\n", + "\n", + "fig = go.Figure()\n", + "\n", + "fig.add_trace(go.Sunburst(\n", + " ids=df.ids,\n", + " labels=df.labels,\n", + " parents=df.parents,\n", + " domain=dict(column=1),\n", + " maxdepth=2,\n", + " insidetextorientation='radial'\n", + "))\n", + "\n", + "fig.update_layout(\n", + " margin = dict(t=10, l=10, r=10, b=10)\n", + ")\n", + "\n", + "fig.show()" + ] + }, + { + "cell_type": "markdown", + "id": "47c4ae70", + "metadata": {}, + "source": [ + "### Controlling text fontsize with uniformtext\n", + "\n", + "If you want all the text labels to have the same size, you can use the `uniformtext` layout parameter. The `minsize` attribute sets the font size, and the `mode` attribute sets what happens for labels which cannot fit with the desired fontsize: either `hide` them or `show` them with overflow.\n", + "\n", + "*Note: animated transitions are currently not implemented when `uniformtext` is used.*" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "545a454f", + "metadata": {}, + "outputs": [], + "source": [ + "import plotly.graph_objects as go\n", + "import pandas as pd\n", + "\n", + "df = pd.read_csv('https://raw.githubusercontent.com/plotly/datasets/718417069ead87650b90472464c7565dc8c2cb1c/sunburst-coffee-flavors-complete.csv')\n", + "\n", + "fig = go.Figure(go.Sunburst(\n", + " ids = df.ids,\n", + " labels = df.labels,\n", + " parents = df.parents))\n", + "fig.update_layout(uniformtext=dict(minsize=10, mode='hide'))\n", + "fig.show()" + ] + }, + { + "cell_type": "markdown", + "id": "259489c3", + "metadata": {}, + "source": [ + "### Pattern Fills\n", + "\n", + "*New in 5.15*\n", + "\n", + "Sunburst charts support [patterns](/python/pattern-hatching-texture/) (also known as hatching or texture) in addition to color. In this example, we add a different pattern to each level of the hierarchy. We also specify the `solidity` of the pattern." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "83e2426c", + "metadata": {}, + "outputs": [], + "source": [ + "import plotly.graph_objects as go\n", + "\n", + "fig = go.Figure(\n", + " go.Sunburst(\n", + " labels=[\"Eve\", \"Cain\", \"Seth\", \"Enos\", \"Noam\", \"Abel\", \"Awan\", \"Enoch\", \"Azura\"],\n", + " parents=[\"\", \"Eve\", \"Eve\", \"Seth\", \"Seth\", \"Eve\", \"Eve\", \"Awan\", \"Eve\"],\n", + " values=[65, 14, 12, 10, 2, 6, 6, 4, 4],\n", + " branchvalues=\"total\",\n", + " textfont_size=16,\n", + " marker=dict(\n", + " pattern=dict(\n", + " shape=[\"\", \"/\", \"/\", \".\", \".\", \"/\", \"/\", \".\", \"/\"], solidity=0.9\n", + " )\n", + " ),\n", + " )\n", + ")\n", + "\n", + "fig.update_layout(margin=dict(t=0, l=0, r=0, b=0))\n", + "\n", + "fig.show()\n" + ] + }, + { + "cell_type": "markdown", + "id": "63ecff0a", + "metadata": {}, + "source": [ + "### Sunburst chart with a continuous colorscale\n", + "\n", + "The example below visualizes a breakdown of sales (corresponding to sector width) and call success rate (corresponding to sector color) by region, county and salesperson level. For example, when exploring the data you can see that although the East region is behaving poorly, the Tyler county is still above average -- however, its performance is reduced by the poor success rate of salesperson GT.\n", + "\n", + "In the right subplot which has a `maxdepth` of two levels, click on a sector to see its breakdown to lower levels." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "717391e8", + "metadata": {}, + "outputs": [], + "source": [ + "import plotly.graph_objects as go\n", + "from plotly.subplots import make_subplots\n", + "import pandas as pd\n", + "\n", + "df = pd.read_csv('https://raw.githubusercontent.com/plotly/datasets/master/sales_success.csv')\n", + "print(df.head())\n", + "\n", + "levels = ['salesperson', 'county', 'region'] # levels used for the hierarchical chart\n", + "color_columns = ['sales', 'calls']\n", + "value_column = 'calls'\n", + "\n", + "def build_hierarchical_dataframe(df, levels, value_column, color_columns=None):\n", + " \"\"\"\n", + " Build a hierarchy of levels for Sunburst or Treemap charts.\n", + "\n", + " Levels are given starting from the bottom to the top of the hierarchy,\n", + " ie the last level corresponds to the root.\n", + " \"\"\"\n", + " df_list = []\n", + " for i, level in enumerate(levels):\n", + " df_tree = pd.DataFrame(columns=['id', 'parent', 'value', 'color'])\n", + " dfg = df.groupby(levels[i:]).sum()\n", + " dfg = dfg.reset_index()\n", + " df_tree['id'] = dfg[level].copy()\n", + " if i < len(levels) - 1:\n", + " df_tree['parent'] = dfg[levels[i+1]].copy()\n", + " else:\n", + " df_tree['parent'] = 'total'\n", + " df_tree['value'] = dfg[value_column]\n", + " df_tree['color'] = dfg[color_columns[0]] / dfg[color_columns[1]]\n", + " df_list.append(df_tree)\n", + " total = pd.Series(dict(id='total', parent='',\n", + " value=df[value_column].sum(),\n", + " color=df[color_columns[0]].sum() / df[color_columns[1]].sum()), name=0)\n", + " df_list.append(total)\n", + " df_all_trees = pd.concat(df_list, ignore_index=True)\n", + " return df_all_trees\n", + "\n", + "\n", + "df_all_trees = build_hierarchical_dataframe(df, levels, value_column, color_columns)\n", + "average_score = df['sales'].sum() / df['calls'].sum()\n", + "\n", + "fig = make_subplots(1, 2, specs=[[{\"type\": \"domain\"}, {\"type\": \"domain\"}]],)\n", + "\n", + "fig.add_trace(go.Sunburst(\n", + " labels=df_all_trees['id'],\n", + " parents=df_all_trees['parent'],\n", + " values=df_all_trees['value'],\n", + " branchvalues='total',\n", + " marker=dict(\n", + " colors=df_all_trees['color'],\n", + " colorscale='RdBu',\n", + " cmid=average_score),\n", + " hovertemplate='%{label}
Sales: %{value}
Success rate: %{color:.2f}',\n", + " name=''\n", + " ), 1, 1)\n", + "\n", + "fig.add_trace(go.Sunburst(\n", + " labels=df_all_trees['id'],\n", + " parents=df_all_trees['parent'],\n", + " values=df_all_trees['value'],\n", + " branchvalues='total',\n", + " marker=dict(\n", + " colors=df_all_trees['color'],\n", + " colorscale='RdBu',\n", + " cmid=average_score),\n", + " hovertemplate='%{label}
Sales: %{value}
Success rate: %{color:.2f}',\n", + " maxdepth=2\n", + " ), 1, 2)\n", + "\n", + "fig.update_layout(margin=dict(t=10, b=10, r=10, l=10))\n", + "fig.show()" + ] + }, + { + "cell_type": "markdown", + "id": "14516d43", + "metadata": {}, + "source": [ + "#### Reference\n", + "\n", + "See [function reference for `px.sunburst()`](https://plotly.com/python-api-reference/generated/plotly.express.sunburst) or https://plotly.com/python/reference/sunburst/ for more information and chart attribute options!\n" + ] + }, + { + "cell_type": "markdown", + "id": "49c3e2e7", + "metadata": {}, + "source": [ + "### What About Dash?\n", + "\n", + "[Dash](https://dash.plot.ly/) is an open-source framework for building analytical applications, with no Javascript required, and it is tightly integrated with the Plotly graphing library.\n", + "\n", + "Learn about how to install Dash at https://dash.plot.ly/installation.\n", + "\n", + "Everywhere in this page that you see `fig.show()`, you can display the same figure in a Dash application by passing it to the `figure` argument of the [`Graph` component](https://dash.plot.ly/dash-core-components/graph) from the built-in `dash_core_components` package like this:\n", + "\n", + "```python\n", + "import plotly.graph_objects as go # or plotly.express as px\n", + "fig = go.Figure() # or any Plotly Express function e.g. px.bar(...)\n", + "# fig.add_trace( ... )\n", + "# fig.update_layout( ... )\n", + "\n", + "from dash import Dash, dcc, html\n", + "\n", + "app = Dash()\n", + "app.layout = html.Div([\n", + " dcc.Graph(figure=fig)\n", + "])\n", + "\n", + "app.run_server(debug=True, use_reloader=False) # Turn off reloader if inside Jupyter\n", + "```" + ] + } + ], + "metadata": { + "jupytext": { + "notebook_metadata_filter": "all" + }, + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "language": "python", + "name": "python3" + }, + "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.11" + }, + "plotly": { + "description": "How to make Sunburst Charts.", + "display_as": "basic", + "language": "python", + "layout": "base", + "name": "Sunburst Charts", + "order": 10, + "page_type": "u-guide", + "permalink": "python/sunburst-charts/", + "thumbnail": "thumbnail/sunburst.gif" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/supported-colors.ipynb b/supported-colors.ipynb new file mode 100644 index 000000000..4dd24a94f --- /dev/null +++ b/supported-colors.ipynb @@ -0,0 +1,204 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "bd639f4e", + "metadata": {}, + "source": [ + "# Supported CSS Colors\n", + "\n", + "Many properties in Plotly.py for configuring colors support named CSS colors. For example, marker colors:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "2b04625a", + "metadata": {}, + "outputs": [], + "source": [ + "import plotly.graph_objects as go\n", + "\n", + "fig = go.Figure([\n", + " go.Bar(\n", + " x=['Jan', 'Feb', 'Mar', 'Apr'],\n", + " y=[20, 14, 25, 16],\n", + " name='Primary Product',\n", + " # Named CSS color\n", + " marker_color='royalblue'\n", + " )\n", + "])\n", + " \n", + "fig.show()" + ] + }, + { + "cell_type": "markdown", + "id": "4adf4cff", + "metadata": {}, + "source": [ + "These colors are supported in Plotly.py when a property accepts a [named CSS color](https://developer.mozilla.org/en-US/docs/Web/CSS/named-color)." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "91d02ac4", + "metadata": { + "hide_code": true, + "lines_to_next_cell": 2 + }, + "outputs": [], + "source": [ + "import plotly.graph_objects as go\n", + "import pandas as pd\n", + "\n", + "supported_colors = [\"aliceblue\", \"antiquewhite\", \"aqua\", \"aquamarine\", \"azure\",\n", + " \"beige\", \"bisque\", \"black\", \"blanchedalmond\", \"blue\",\n", + " \"blueviolet\", \"brown\", \"burlywood\", \"cadetblue\",\n", + " \"chartreuse\", \"chocolate\", \"coral\", \"cornflowerblue\",\n", + " \"cornsilk\", \"crimson\", \"cyan\", \"darkblue\", \"darkcyan\",\n", + " \"darkgoldenrod\", \"darkgray\", \"darkgrey\", \"darkgreen\",\n", + " \"darkkhaki\", \"darkmagenta\", \"darkolivegreen\", \"darkorange\",\n", + " \"darkorchid\", \"darkred\", \"darksalmon\", \"darkseagreen\",\n", + " \"darkslateblue\", \"darkslategray\", \"darkslategrey\",\n", + " \"darkturquoise\", \"darkviolet\", \"deeppink\", \"deepskyblue\",\n", + " \"dimgray\", \"dimgrey\", \"dodgerblue\", \"firebrick\",\n", + " \"floralwhite\", \"forestgreen\", \"fuchsia\", \"gainsboro\",\n", + " \"ghostwhite\", \"gold\", \"goldenrod\", \"gray\", \"grey\", \"green\",\n", + " \"greenyellow\", \"honeydew\", \"hotpink\", \"indianred\", \"indigo\",\n", + " \"ivory\", \"khaki\", \"lavender\", \"lavenderblush\", \"lawngreen\",\n", + " \"lemonchiffon\", \"lightblue\", \"lightcoral\", \"lightcyan\",\n", + " \"lightgoldenrodyellow\", \"lightgray\", \"lightgrey\",\n", + " \"lightgreen\", \"lightpink\", \"lightsalmon\", \"lightseagreen\",\n", + " \"lightskyblue\", \"lightslategray\", \"lightslategrey\",\n", + " \"lightsteelblue\", \"lightyellow\", \"lime\", \"limegreen\",\n", + " \"linen\", \"magenta\", \"maroon\", \"mediumaquamarine\",\n", + " \"mediumblue\", \"mediumorchid\", \"mediumpurple\",\n", + " \"mediumseagreen\", \"mediumslateblue\", \"mediumspringgreen\",\n", + " \"mediumturquoise\", \"mediumvioletred\", \"midnightblue\",\n", + " \"mintcream\", \"mistyrose\", \"moccasin\", \"navajowhite\", \"navy\",\n", + " \"oldlace\", \"olive\", \"olivedrab\", \"orange\", \"orangered\",\n", + " \"orchid\", \"palegoldenrod\", \"palegreen\", \"paleturquoise\",\n", + " \"palevioletred\", \"papayawhip\", \"peachpuff\", \"peru\", \"pink\",\n", + " \"plum\", \"powderblue\", \"purple\", \"red\", \"rosybrown\",\n", + " \"royalblue\", \"rebeccapurple\", \"saddlebrown\", \"salmon\",\n", + " \"sandybrown\", \"seagreen\", \"seashell\", \"sienna\", \"silver\",\n", + " \"skyblue\", \"slateblue\", \"slategray\", \"slategrey\", \"snow\",\n", + " \"springgreen\", \"steelblue\", \"tan\", \"teal\", \"thistle\", \"tomato\",\n", + " \"turquoise\", \"violet\", \"wheat\", \"white\", \"whitesmoke\",\n", + " \"yellow\", \"yellowgreen\"]\n", + "\n", + "fig = go.Figure(layout=dict(title=\"Supported Named CSS Colors\"))\n", + "\n", + "for i, color in enumerate(supported_colors):\n", + " row, col = i // 5, i % 5\n", + " x0, y0 = col * 1.2, -row * 1.2\n", + " \n", + " fig.add_shape(\n", + " type=\"rect\",\n", + " x0=x0, y0=y0,\n", + " x1=x0+1, y1=y0+1,\n", + " fillcolor=color,\n", + " line=dict(color=\"black\", width=0.2),\n", + " )\n", + " \n", + " fig.add_annotation(\n", + " x=x0+0.5, y=y0-0.1,\n", + " text=color,\n", + " showarrow=False,\n", + " font=dict(size=10)\n", + " )\n", + "\n", + "fig.update_layout(\n", + " height=((len(supported_colors) // 5) + (1 if len(supported_colors) % 5 else 0)) * 120,\n", + " width=800,\n", + " showlegend=False,\n", + " plot_bgcolor='rgba(0,0,0,0)',\n", + " margin=dict(l=50, r=50, t=50, b=50),\n", + " xaxis=dict(\n", + " showgrid=False,\n", + " zeroline=False,\n", + " showticklabels=False,\n", + " range=[-0.5, 6]\n", + " ),\n", + " yaxis=dict(\n", + " showgrid=False,\n", + " zeroline=False,\n", + " showticklabels=False,\n", + " scaleanchor=\"x\",\n", + " scaleratio=1,\n", + " range=[-((len(supported_colors) // 5) + 1) * 1.2, 1.5]\n", + " )\n", + ")\n", + "\n", + "fig.show()" + ] + }, + { + "cell_type": "markdown", + "id": "74306f01", + "metadata": {}, + "source": [ + "### What About Dash?\n", + "\n", + "[Dash](https://dash.plot.ly/) is an open-source framework for building analytical applications, with no Javascript required, and it is tightly integrated with the Plotly graphing library.\n", + "\n", + "Learn about how to install Dash at https://dash.plot.ly/installation.\n", + "\n", + "Everywhere in this page that you see `fig.show()`, you can display the same figure in a Dash application by passing it to the `figure` argument of the [`Graph` component](https://dash.plot.ly/dash-core-components/graph) from the built-in `dash_core_components` package like this:\n", + "\n", + "```python\n", + "import plotly.graph_objects as go # or plotly.express as px\n", + "fig = go.Figure() # or any Plotly Express function e.g. px.bar(...)\n", + "# fig.add_trace( ... )\n", + "# fig.update_layout( ... )\n", + "\n", + "from dash import Dash, dcc, html\n", + "\n", + "app = Dash()\n", + "app.layout = html.Div([\n", + " dcc.Graph(figure=fig)\n", + "])\n", + "\n", + "app.run_server(debug=True, use_reloader=False) # Turn off reloader if inside Jupyter\n", + "```" + ] + } + ], + "metadata": { + "jupytext": { + "notebook_metadata_filter": "all" + }, + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "language": "python", + "name": "python3" + }, + "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.13.2" + }, + "plotly": { + "description": "A list of supported named CSS Colors", + "display_as": "file_settings", + "language": "python", + "layout": "base", + "name": "Supported CSS Colors", + "order": 40, + "page_type": "example_index", + "permalink": "python/css-colors/", + "thumbnail": "thumbnail/shape.jpg" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/table-subplots.ipynb b/table-subplots.ipynb new file mode 100644 index 000000000..7a1e06103 --- /dev/null +++ b/table-subplots.ipynb @@ -0,0 +1,160 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "eac20c7b", + "metadata": {}, + "source": [ + "#### Import CSV Data" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "d932c063", + "metadata": {}, + "outputs": [], + "source": [ + "import plotly.graph_objects as go\n", + "from plotly.subplots import make_subplots\n", + "\n", + "import pandas as pd\n", + "import re\n", + "\n", + "df = pd.read_csv(\"https://raw.githubusercontent.com/plotly/datasets/master/Mining-BTC-180.csv\")\n", + "\n", + "for i, row in enumerate(df[\"Date\"]):\n", + " p = re.compile(\" 00:00:00\")\n", + " datetime = p.split(df[\"Date\"][i])[0]\n", + " df.iloc[i, 1] = datetime\n", + "\n", + "fig = make_subplots(\n", + " rows=3, cols=1,\n", + " shared_xaxes=True,\n", + " vertical_spacing=0.03,\n", + " specs=[[{\"type\": \"table\"}],\n", + " [{\"type\": \"scatter\"}],\n", + " [{\"type\": \"scatter\"}]]\n", + ")\n", + "\n", + "fig.add_trace(\n", + " go.Scatter(\n", + " x=df[\"Date\"],\n", + " y=df[\"Mining-revenue-USD\"],\n", + " mode=\"lines\",\n", + " name=\"mining revenue\"\n", + " ),\n", + " row=3, col=1\n", + ")\n", + "\n", + "fig.add_trace(\n", + " go.Scatter(\n", + " x=df[\"Date\"],\n", + " y=df[\"Hash-rate\"],\n", + " mode=\"lines\",\n", + " name=\"hash-rate-TH/s\"\n", + " ),\n", + " row=2, col=1\n", + ")\n", + "\n", + "fig.add_trace(\n", + " go.Table(\n", + " header=dict(\n", + " values=[\"Date\", \"Number
Transactions\", \"Output
Volume (BTC)\",\n", + " \"Market
Price\", \"Hash
Rate\", \"Cost per
trans-USD\",\n", + " \"Mining
Revenue-USD\", \"Trasaction
fees-BTC\"],\n", + " font=dict(size=10),\n", + " align=\"left\"\n", + " ),\n", + " cells=dict(\n", + " values=[df[k].tolist() for k in df.columns[1:]],\n", + " align = \"left\")\n", + " ),\n", + " row=1, col=1\n", + ")\n", + "fig.update_layout(\n", + " height=800,\n", + " showlegend=False,\n", + " title_text=\"Bitcoin mining stats for 180 days\",\n", + ")\n", + "\n", + "fig.show()" + ] + }, + { + "cell_type": "markdown", + "id": "8ee737bf", + "metadata": {}, + "source": [ + "#### Reference\n", + "See https://plotly.com/python/reference/table/ for more information regarding chart attributes!
\n", + "For examples of Plotly Tables, see: https://plotly.com/python/table/\n" + ] + }, + { + "cell_type": "markdown", + "id": "3c794a16", + "metadata": {}, + "source": [ + "### What About Dash?\n", + "\n", + "[Dash](https://dash.plot.ly/) is an open-source framework for building analytical applications, with no Javascript required, and it is tightly integrated with the Plotly graphing library.\n", + "\n", + "Learn about how to install Dash at https://dash.plot.ly/installation.\n", + "\n", + "Everywhere in this page that you see `fig.show()`, you can display the same figure in a Dash application by passing it to the `figure` argument of the [`Graph` component](https://dash.plot.ly/dash-core-components/graph) from the built-in `dash_core_components` package like this:\n", + "\n", + "```python\n", + "import plotly.graph_objects as go # or plotly.express as px\n", + "fig = go.Figure() # or any Plotly Express function e.g. px.bar(...)\n", + "# fig.add_trace( ... )\n", + "# fig.update_layout( ... )\n", + "\n", + "from dash import Dash, dcc, html\n", + "\n", + "app = Dash()\n", + "app.layout = html.Div([\n", + " dcc.Graph(figure=fig)\n", + "])\n", + "\n", + "app.run_server(debug=True, use_reloader=False) # Turn off reloader if inside Jupyter\n", + "```" + ] + } + ], + "metadata": { + "jupytext": { + "notebook_metadata_filter": "all" + }, + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "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.7.3" + }, + "plotly": { + "description": "How to create a subplot with tables and charts in Python with Plotly.", + "display_as": "multiple_axes", + "language": "python", + "layout": "base", + "name": "Table and Chart Subplots", + "order": 3, + "page_type": "example_index", + "permalink": "python/table-subplots/", + "thumbnail": "thumbnail/table_subplots.jpg" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/table.ipynb b/table.ipynb new file mode 100644 index 000000000..9cc25df47 --- /dev/null +++ b/table.ipynb @@ -0,0 +1,384 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "5ce47347", + "metadata": {}, + "source": [ + "`go.Table` provides a Table object for detailed data viewing. The data are arranged in\n", + "a grid of rows and columns. Most styling can be specified for header, columns, rows or individual cells. Table is using a column-major order, ie. the grid is represented as a vector of column vectors.\n", + "\n", + "Note that [Dash](https://dash.plotly.com/) provides a different type of [DataTable](https://dash.plotly.com/datatable).\n", + "\n", + "#### Basic Table" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "16786e5f", + "metadata": {}, + "outputs": [], + "source": [ + "import plotly.graph_objects as go\n", + "\n", + "fig = go.Figure(data=[go.Table(header=dict(values=['A Scores', 'B Scores']),\n", + " cells=dict(values=[[100, 90, 80, 90], [95, 85, 75, 95]]))\n", + " ])\n", + "fig.show()" + ] + }, + { + "cell_type": "markdown", + "id": "d90552d0", + "metadata": {}, + "source": [ + "#### Styled Table" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "a9833457", + "metadata": {}, + "outputs": [], + "source": [ + "import plotly.graph_objects as go\n", + "\n", + "fig = go.Figure(data=[go.Table(\n", + " header=dict(values=['A Scores', 'B Scores'],\n", + " line_color='darkslategray',\n", + " fill_color='lightskyblue',\n", + " align='left'),\n", + " cells=dict(values=[[100, 90, 80, 90], # 1st column\n", + " [95, 85, 75, 95]], # 2nd column\n", + " line_color='darkslategray',\n", + " fill_color='lightcyan',\n", + " align='left'))\n", + "])\n", + "\n", + "fig.update_layout(width=500, height=300)\n", + "fig.show()" + ] + }, + { + "cell_type": "markdown", + "id": "043e80c1", + "metadata": {}, + "source": [ + "#### Use a Pandas Dataframe" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "76f4cc80", + "metadata": {}, + "outputs": [], + "source": [ + "import plotly.graph_objects as go\n", + "import pandas as pd\n", + "\n", + "df = pd.read_csv('https://raw.githubusercontent.com/plotly/datasets/master/2014_usa_states.csv')\n", + "\n", + "fig = go.Figure(data=[go.Table(\n", + " header=dict(values=list(df.columns),\n", + " fill_color='paleturquoise',\n", + " align='left'),\n", + " cells=dict(values=[df.Rank, df.State, df.Postal, df.Population],\n", + " fill_color='lavender',\n", + " align='left'))\n", + "])\n", + "\n", + "fig.show()" + ] + }, + { + "cell_type": "markdown", + "id": "0aeba5ab", + "metadata": {}, + "source": [ + "#### Tables in Dash\n", + "\n", + "[Dash](https://plotly.com/dash/) is the best way to build analytical apps in Python using Plotly figures. To run the app below, run `pip install dash`, click \"Download\" to get the code and run `python app.py`.\n", + "\n", + "Get started with [the official Dash docs](https://dash.plotly.com/installation) and **learn how to effortlessly [style](https://plotly.com/dash/design-kit/) & [deploy](https://plotly.com/dash/app-manager/) apps like this with Dash Enterprise.**" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "1398522d", + "metadata": { + "hide_code": true + }, + "outputs": [], + "source": [ + "from IPython.display import IFrame\n", + "snippet_url = 'https://python-docs-dash-snippets.herokuapp.com/python-docs-dash-snippets/'\n", + "IFrame(snippet_url + 'table', width='100%', height=1200)" + ] + }, + { + "cell_type": "markdown", + "id": "609428c1", + "metadata": {}, + "source": [ + "

Sign up for Dash Club → Free cheat sheets plus updates from Chris Parmer and Adam Schroeder delivered to your inbox every two months. Includes tips and tricks, community apps, and deep dives into the Dash architecture.\n", + "Join now.

" + ] + }, + { + "cell_type": "markdown", + "id": "b47be221", + "metadata": {}, + "source": [ + "#### Changing Row and Column Size" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "93443238", + "metadata": {}, + "outputs": [], + "source": [ + "import plotly.graph_objects as go\n", + "\n", + "values = [['Salaries', 'Office', 'Merchandise', 'Legal', 'TOTAL
EXPENSES
'], #1st col\n", + " [\"Lorem ipsum dolor sit amet, tollit discere inermis pri ut. Eos ea iusto timeam, an prima laboramus vim. Id usu aeterno adversarium, summo mollis timeam vel ad\",\n", + " \"Lorem ipsum dolor sit amet, tollit discere inermis pri ut. Eos ea iusto timeam, an prima laboramus vim. Id usu aeterno adversarium, summo mollis timeam vel ad\",\n", + " \"Lorem ipsum dolor sit amet, tollit discere inermis pri ut. Eos ea iusto timeam, an prima laboramus vim. Id usu aeterno adversarium, summo mollis timeam vel ad\",\n", + " \"Lorem ipsum dolor sit amet, tollit discere inermis pri ut. Eos ea iusto timeam, an prima laboramus vim. Id usu aeterno adversarium, summo mollis timeam vel ad\",\n", + " \"Lorem ipsum dolor sit amet, tollit discere inermis pri ut. Eos ea iusto timeam, an prima laboramus vim. Id usu aeterno adversarium, summo mollis timeam vel ad\"]]\n", + "\n", + "\n", + "fig = go.Figure(data=[go.Table(\n", + " columnorder = [1,2],\n", + " columnwidth = [80,400],\n", + " header = dict(\n", + " values = [['EXPENSES
as of July 2017'],\n", + " ['DESCRIPTION']],\n", + " line_color='darkslategray',\n", + " fill_color='royalblue',\n", + " align=['left','center'],\n", + " font=dict(color='white', size=12),\n", + " height=40\n", + " ),\n", + " cells=dict(\n", + " values=values,\n", + " line_color='darkslategray',\n", + " fill=dict(color=['paleturquoise', 'white']),\n", + " align=['left', 'center'],\n", + " font_size=12,\n", + " height=30)\n", + " )\n", + "])\n", + "fig.show()" + ] + }, + { + "cell_type": "markdown", + "id": "279c5ee7", + "metadata": {}, + "source": [ + "#### Alternating Row Colors" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "94bd92cd", + "metadata": {}, + "outputs": [], + "source": [ + "import plotly.graph_objects as go\n", + "\n", + "headerColor = 'grey'\n", + "rowEvenColor = 'lightgrey'\n", + "rowOddColor = 'white'\n", + "\n", + "fig = go.Figure(data=[go.Table(\n", + " header=dict(\n", + " values=['EXPENSES','Q1','Q2','Q3','Q4'],\n", + " line_color='darkslategray',\n", + " fill_color=headerColor,\n", + " align=['left','center'],\n", + " font=dict(color='white', size=12)\n", + " ),\n", + " cells=dict(\n", + " values=[\n", + " ['Salaries', 'Office', 'Merchandise', 'Legal', 'TOTAL'],\n", + " [1200000, 20000, 80000, 2000, 12120000],\n", + " [1300000, 20000, 70000, 2000, 130902000],\n", + " [1300000, 20000, 120000, 2000, 131222000],\n", + " [1400000, 20000, 90000, 2000, 14102000]],\n", + " line_color='darkslategray',\n", + " # 2-D list of colors for alternating rows\n", + " fill_color = [[rowOddColor,rowEvenColor,rowOddColor, rowEvenColor,rowOddColor]*5],\n", + " align = ['left', 'center'],\n", + " font = dict(color = 'darkslategray', size = 11)\n", + " ))\n", + "])\n", + "\n", + "fig.show()" + ] + }, + { + "cell_type": "markdown", + "id": "dca014cc", + "metadata": {}, + "source": [ + "#### Row Color Based on Variable" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "84f0c506", + "metadata": {}, + "outputs": [], + "source": [ + "import plotly.graph_objects as go\n", + "\n", + "import pandas as pd\n", + "\n", + "colors = ['rgb(239, 243, 255)', 'rgb(189, 215, 231)', 'rgb(107, 174, 214)',\n", + " 'rgb(49, 130, 189)', 'rgb(8, 81, 156)']\n", + "data = {'Year' : [2010, 2011, 2012, 2013, 2014], 'Color' : colors}\n", + "df = pd.DataFrame(data)\n", + "\n", + "fig = go.Figure(data=[go.Table(\n", + " header=dict(\n", + " values=[\"Color\", \"YEAR\"],\n", + " line_color='white', fill_color='white',\n", + " align='center', font=dict(color='black', size=12)\n", + " ),\n", + " cells=dict(\n", + " values=[df.Color, df.Year],\n", + " line_color=[df.Color], fill_color=[df.Color],\n", + " align='center', font=dict(color='black', size=11)\n", + " ))\n", + "])\n", + "\n", + "fig.show()" + ] + }, + { + "cell_type": "markdown", + "id": "dcfd6190", + "metadata": {}, + "source": [ + "#### Cell Color Based on Variable" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "94564d34", + "metadata": {}, + "outputs": [], + "source": [ + "import plotly.graph_objects as go\n", + "from plotly.colors import n_colors\n", + "import numpy as np\n", + "np.random.seed(1)\n", + "\n", + "colors = n_colors('rgb(255, 200, 200)', 'rgb(200, 0, 0)', 9, colortype='rgb')\n", + "a = np.random.randint(low=0, high=9, size=10)\n", + "b = np.random.randint(low=0, high=9, size=10)\n", + "c = np.random.randint(low=0, high=9, size=10)\n", + "\n", + "fig = go.Figure(data=[go.Table(\n", + " header=dict(\n", + " values=['Column A', 'Column B', 'Column C'],\n", + " line_color='white', fill_color='white',\n", + " align='center',font=dict(color='black', size=12)\n", + " ),\n", + " cells=dict(\n", + " values=[a, b, c],\n", + " line_color=[np.array(colors)[a],np.array(colors)[b], np.array(colors)[c]],\n", + " fill_color=[np.array(colors)[a],np.array(colors)[b], np.array(colors)[c]],\n", + " align='center', font=dict(color='white', size=11)\n", + " ))\n", + "])\n", + "\n", + "fig.show()" + ] + }, + { + "cell_type": "markdown", + "id": "ab094eb4", + "metadata": {}, + "source": [ + "#### Reference\n", + "For more information on tables and table attributes see: https://plotly.com/python/reference/table/.\n" + ] + }, + { + "cell_type": "markdown", + "id": "487d0136", + "metadata": {}, + "source": [ + "### What About Dash?\n", + "\n", + "[Dash](https://dash.plot.ly/) is an open-source framework for building analytical applications, with no Javascript required, and it is tightly integrated with the Plotly graphing library.\n", + "\n", + "Learn about how to install Dash at https://dash.plot.ly/installation.\n", + "\n", + "Everywhere in this page that you see `fig.show()`, you can display the same figure in a Dash application by passing it to the `figure` argument of the [`Graph` component](https://dash.plot.ly/dash-core-components/graph) from the built-in `dash_core_components` package like this:\n", + "\n", + "```python\n", + "import plotly.graph_objects as go # or plotly.express as px\n", + "fig = go.Figure() # or any Plotly Express function e.g. px.bar(...)\n", + "# fig.add_trace( ... )\n", + "# fig.update_layout( ... )\n", + "\n", + "from dash import Dash, dcc, html\n", + "\n", + "app = Dash()\n", + "app.layout = html.Div([\n", + " dcc.Graph(figure=fig)\n", + "])\n", + "\n", + "app.run_server(debug=True, use_reloader=False) # Turn off reloader if inside Jupyter\n", + "```" + ] + } + ], + "metadata": { + "jupytext": { + "notebook_metadata_filter": "all" + }, + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "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.8.8" + }, + "plotly": { + "description": "How to make tables in Python with Plotly.", + "display_as": "basic", + "language": "python", + "layout": "base", + "name": "Tables", + "order": 11, + "page_type": "u-guide", + "permalink": "python/table/", + "thumbnail": "thumbnail/table.gif" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/templates.ipynb b/templates.ipynb new file mode 100644 index 000000000..e0cad624a --- /dev/null +++ b/templates.ipynb @@ -0,0 +1,707 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "0d23fff3", + "metadata": {}, + "source": [ + "### Theming and templates\n", + "\n", + "The Plotly Python library comes pre-loaded with several themes that you can get started using right away, and it also provides support for creating and registering your own themes.\n", + "\n", + "> Note on terminology: Theming generally refers to the process of defining default styles for visual elements. Themes in plotly are implemented using objects called templates. Templates are slightly more general than traditional themes because in addition to defining default styles, templates can pre-populate a figure with visual elements like annotations, shapes, images, and more. In the documentation we will refer to the overall process of defining default styles as theming, and when in comes to the plotly API we will talk about how themes are implemented using templates.\n", + "\n", + "### Using built-in themes\n", + "\n", + "#### View available themes\n", + "\n", + "To see information about the available themes and the current default theme, display the `plotly.io.templates` configuration object like this." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "5a121c80", + "metadata": {}, + "outputs": [], + "source": [ + "import plotly.io as pio\n", + "pio.templates" + ] + }, + { + "cell_type": "markdown", + "id": "22917062", + "metadata": {}, + "source": [ + "From this, we can see that the default theme is `\"plotly\"`, and we can see the names of several additional themes that we can choose from.\n", + "\n", + "#### Specifying themes in Plotly Express\n", + "\n", + "All Plotly Express functions accept a `template` argument that can be set to the name of a registered theme (or to a `Template` object as discussed later in this section). Here is an example of using Plotly Express to build and display the same scatter plot with six different themes." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "db94e7bf", + "metadata": {}, + "outputs": [], + "source": [ + "import plotly.express as px\n", + "\n", + "df = px.data.gapminder()\n", + "df_2007 = df.query(\"year==2007\")\n", + "\n", + "for template in [\"plotly\", \"plotly_white\", \"plotly_dark\", \"ggplot2\", \"seaborn\", \"simple_white\", \"none\"]:\n", + " fig = px.scatter(df_2007,\n", + " x=\"gdpPercap\", y=\"lifeExp\", size=\"pop\", color=\"continent\",\n", + " log_x=True, size_max=60,\n", + " template=template, title=\"Gapminder 2007: '%s' theme\" % template)\n", + " fig.show()" + ] + }, + { + "cell_type": "markdown", + "id": "d7331193", + "metadata": {}, + "source": [ + "#### Specifying themes in graph object figures\n", + "\n", + "The theme for a particular graph object figure can be specified by setting the `template` property of the figure's `layout` to the name of a registered theme (or to a `Template` object as discussed later in this section). Here is an example of constructing a surface plot and then displaying it with each of six themes." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "fa53cde3", + "metadata": {}, + "outputs": [], + "source": [ + "import plotly.graph_objects as go\n", + "import pandas as pd\n", + "\n", + "z_data = pd.read_csv(\"https://raw.githubusercontent.com/plotly/datasets/master/api_docs/mt_bruno_elevation.csv\")\n", + "\n", + "fig = go.Figure(\n", + " data=go.Surface(z=z_data.values),\n", + " layout=go.Layout(\n", + " title=dict(text=\"Mt Bruno Elevation\"),\n", + " width=500,\n", + " height=500,\n", + " ))\n", + "\n", + "for template in [\"plotly\", \"plotly_white\", \"plotly_dark\", \"ggplot2\", \"seaborn\", \"simple_white\", \"none\"]:\n", + " fig.update_layout(template=template, title=dict(text=\"Mt Bruno Elevation: '%s' theme\" % template))\n", + " fig.show()" + ] + }, + { + "cell_type": "markdown", + "id": "9a968d31", + "metadata": {}, + "source": [ + "#### Specifying a default themes\n", + "\n", + "If a theme is not provided to a Plotly Express function or to a graph object figure, then the default theme is used. The default theme starts out as `\"plotly\"`, but it can be changed by setting the `plotly.io.templates.default` property to the name of a registered theme.\n", + "\n", + "Here is an example of changing to default theme to `\"plotly_white\"` and then constructing a scatter plot with Plotly Express without providing a template.\n", + "\n", + "> Note: Default themes persist for the duration of a single session, but they do not persist across sessions. If you are working in an IPython kernel, this means that default themes will persist for the life of the kernel, but they will not persist across kernel restarts." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "0e7b593a", + "metadata": {}, + "outputs": [], + "source": [ + "import plotly.io as pio\n", + "import plotly.express as px\n", + "\n", + "pio.templates.default = \"plotly_white\"\n", + "\n", + "df = px.data.gapminder()\n", + "df_2007 = df.query(\"year==2007\")\n", + "\n", + "fig = px.scatter(df_2007,\n", + " x=\"gdpPercap\", y=\"lifeExp\", size=\"pop\", color=\"continent\",\n", + " log_x=True, size_max=60,\n", + " title=\"Gapminder 2007: current default theme\")\n", + "fig.show()" + ] + }, + { + "cell_type": "markdown", + "id": "6b7d0c0f", + "metadata": {}, + "source": [ + "#### Disable default theming\n", + "\n", + "If you do not wish to use any of the new themes by default, or you want your figures to look exactly the way they did prior to plotly.py version 4, you can disable default theming by setting the default theme to `\"none\"`." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "9d09da4d", + "metadata": {}, + "outputs": [], + "source": [ + "import plotly.io as pio\n", + "pio.templates.default = \"none\"" + ] + }, + { + "cell_type": "markdown", + "id": "ea669d81", + "metadata": {}, + "source": [ + "### Creating themes\n", + "\n", + "#### Representing themes with Template objects\n", + "\n", + "Themes in plotly.py are represented by instances of the `Template` class from the `plotly.graph_objects.layout` module. A `Template` is a graph object that contains two top-level properties: `layout` and `data`. These template properties are described in their own sections below.\n", + "\n", + "#### The template layout property\n", + "\n", + "The `layout` property of a template is a graph object with the exact same structure as the `layout` property of a figure. When you provide values for properties of the template's `layout`, these values will be used as the defaults in any figure that this template is applied to.\n", + "\n", + "Here is an example that creates a template that sets the default title font to size 24 Rockwell, and then constructs a graph object figure with this template." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "44865dfa", + "metadata": {}, + "outputs": [], + "source": [ + "import plotly.graph_objects as go\n", + "\n", + "large_rockwell_template = dict(\n", + " layout=go.Layout(title_font=dict(family=\"Rockwell\", size=24))\n", + ")\n", + "\n", + "fig = go.Figure()\n", + "fig.update_layout(title=dict(text=\"Figure Title\"),\n", + " template=large_rockwell_template)\n", + "fig.show()" + ] + }, + { + "cell_type": "markdown", + "id": "88688317", + "metadata": {}, + "source": [ + "> Note: this example uses magic underscore notation to write `go.Layout(title=dict(font=dict(...)))` as `go.Layout(title_font=dict(...))`\n", + "\n", + "#### The template data property\n", + "\n", + "The `data` property of a template is used to customize the default values of the properties of traces that are added to a figure that the template is applied to. This `data` property holds a graph object, with type `go.layout.template.Data`, that has a property named after each supported trace type. These trace type properties are then assigned lists or tuples of graph object traces of the corresponding type.\n", + "\n", + "Here is an example that creates a template that sets the default scatter trace markers to be size 20 diamonds, and then constructs a graph object figure with this template." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "4ccca4ed", + "metadata": {}, + "outputs": [], + "source": [ + "import plotly.graph_objects as go\n", + "\n", + "diamond_template = go.layout.Template()\n", + "diamond_template.data.scatter = [go.Scatter(marker=dict(symbol=\"diamond\", size=20))]\n", + "\n", + "fig = go.Figure()\n", + "fig.update_layout(template=diamond_template)\n", + "fig.add_scatter(y=[2, 1, 3], mode=\"markers\")\n", + "fig.show()" + ] + }, + { + "cell_type": "markdown", + "id": "c5146517", + "metadata": {}, + "source": [ + "If a trace type property is set to a list of more than one trace, then the default properties are cycled as more traces are added to the figure. Here is an example that creates a template that cycles the default marker symbol for scatter traces, and then constructs a figure that uses this template." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "0d64de96", + "metadata": {}, + "outputs": [], + "source": [ + "import plotly.graph_objects as go\n", + "\n", + "symbol_template = go.layout.Template()\n", + "symbol_template.data.scatter = [\n", + " go.Scatter(marker=dict(symbol=\"diamond\", size=10)),\n", + " go.Scatter(marker=dict(symbol=\"square\", size=10)),\n", + " go.Scatter(marker=dict(symbol=\"circle\", size=10)),\n", + "]\n", + "\n", + "fig = go.Figure()\n", + "fig.update_layout(template=symbol_template)\n", + "fig.add_scatter(y=[1, 2, 3], mode=\"markers\", name=\"first\")\n", + "fig.add_scatter(y=[2, 3, 4], mode=\"markers\", name=\"second\")\n", + "fig.add_scatter(y=[3, 4, 5], mode=\"markers\", name=\"third\")\n", + "fig.add_scatter(y=[4, 5, 6], mode=\"markers\", name=\"forth\")\n", + "fig.show()" + ] + }, + { + "cell_type": "markdown", + "id": "33c350d2", + "metadata": {}, + "source": [ + "Note that because we built the template with a list of 3 scatter trace [graph objects](/python/graph-objects/) (one each for the diamond, square, and circle symbols), the forth scatter trace in the figure cycles around and takes on the defaults specified in the first template trace (The diamond symbol).\n", + "\n", + "#### Theming object tuple properties\n", + "\n", + "Some properties in the figure hierarchy are specified as tuples of objects. For example, the text annotations for a graph object figure can be stored as a tuple of `go.layout.Annotation` objects in the `annotations` property of the figure's layout.\n", + "\n", + "To use a template to configure the default properties of all of the elements in an object tuple property (e.g. `layout.annotations`), use the `*defaults` property in the template that corresponds to the tuple property (e.g. `layout.template.layout.annotationdefaults`). The `*defaults` template property should be set to a single graph object that matches the type of the elements of the corresponding tuple. The properties of this `*defaults` object in the template will be applied to all elements of the object tuple in the figure that the template is applied to.\n", + "\n", + "Here is an example that creates a template that specifies the default annotation text color, and then constructs a figure that uses this template." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "b518a73b", + "metadata": {}, + "outputs": [], + "source": [ + "import plotly.graph_objects as go\n", + "\n", + "annotation_template = go.layout.Template()\n", + "annotation_template.layout.annotationdefaults = dict(font=dict(color=\"crimson\"))\n", + "\n", + "fig = go.Figure()\n", + "fig.update_layout(\n", + " template=annotation_template,\n", + " annotations=[\n", + " dict(text=\"Look Here\", x=1, y=1),\n", + " dict(text=\"Look There\", x=2, y=2)\n", + " ]\n", + " )\n", + "fig.show()" + ] + }, + { + "cell_type": "markdown", + "id": "76b6bb71", + "metadata": {}, + "source": [ + "#### Including tuple elements in a theme\n", + "\n", + "The previous section described how to use a template to customize the default properties of tuple elements that are added to a figure that the template is applied to. This is useful for styling, for example, any annotations, shapes, or images that will eventually be added to the figure.\n", + "\n", + "It is also possible for a template to define tuple elements that should be included, as is, in any figure that the template is applied to. This is done by specifying a list of one or more tuple element graph objects (e.g. `go.layout.Annotation` objects) as the value of the corresponding tuple property in the template (e.g. at `template.layout.annotations`). Note that the `name` property of these tuple element graph objects must be set to a unique non-empty string.\n", + "\n", + "Here is an example that creates a template that adds a large semi-transparent \"DRAFT\" watermark to the middle of the figure, and constructs a figure using this template." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "23acc951", + "metadata": {}, + "outputs": [], + "source": [ + "import plotly.graph_objects as go\n", + "\n", + "draft_template = go.layout.Template()\n", + "draft_template.layout.annotations = [\n", + " dict(\n", + " name=\"draft watermark\",\n", + " text=\"DRAFT\",\n", + " textangle=-30,\n", + " opacity=0.1,\n", + " font=dict(color=\"black\", size=100),\n", + " xref=\"paper\",\n", + " yref=\"paper\",\n", + " x=0.5,\n", + " y=0.5,\n", + " showarrow=False,\n", + " )\n", + "]\n", + "\n", + "fig=go.Figure()\n", + "fig.update_layout(template=draft_template)\n", + "fig.show()" + ] + }, + { + "cell_type": "markdown", + "id": "aec20833", + "metadata": {}, + "source": [ + "#### Customizing theme tuple elements in a figure\n", + "\n", + "The previous section described how a template can be used to add default tuple element graph objects (e.g. annotations, shapes, or images) to a figure. The properties of these default tuple elements can be customized from within the figure by adding an tuple element with a `templateitemname` property that matches the `name` property of the template object.\n", + "\n", + "Here is an example, using the same `draft_template` defined above, that customizes the watermark from within the figure to read \"CONFIDENTIAL\" rather than \"DRAFT\"." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "745d32e4", + "metadata": {}, + "outputs": [], + "source": [ + "import plotly.graph_objects as go\n", + "\n", + "draft_template = go.layout.Template()\n", + "draft_template.layout.annotations = [\n", + " dict(\n", + " name=\"draft watermark\",\n", + " text=\"DRAFT\",\n", + " textangle=-30,\n", + " opacity=0.1,\n", + " font=dict(color=\"black\", size=100),\n", + " xref=\"paper\",\n", + " yref=\"paper\",\n", + " x=0.5,\n", + " y=0.5,\n", + " showarrow=False,\n", + " )\n", + "]\n", + "\n", + "fig = go.Figure()\n", + "fig.update_layout(\n", + " template=draft_template,\n", + " annotations=[\n", + " dict(\n", + " templateitemname=\"draft watermark\",\n", + " text=\"CONFIDENTIAL\",\n", + " )\n", + " ]\n", + ")\n", + "fig.show()" + ] + }, + { + "cell_type": "markdown", + "id": "64a96d18", + "metadata": {}, + "source": [ + "#### Registering themes as named templates\n", + "\n", + "The examples above construct and configure a `Template` object and then pass that object as the template specification to graph object figures (as the `layout.template` property) or Plotly Express functions (as the `template` keyword argument). It is also possible to register custom templates by name so that the name itself can be used to refer to the template. To register a template, use dictionary-style assignment to associate the template object with a name in the `plotly.io.templates` configuration object.\n", + "\n", + "Here is an example of registering the draft watermark template from the previous sections as a template named `\"draft\"`. Then a graph object figure is created with the draft template specified by name." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "d4242bb9", + "metadata": {}, + "outputs": [], + "source": [ + "import plotly.graph_objects as go\n", + "import plotly.io as pio\n", + "\n", + "pio.templates[\"draft\"] = go.layout.Template(\n", + " layout_annotations=[\n", + " dict(\n", + " name=\"draft watermark\",\n", + " text=\"DRAFT\",\n", + " textangle=-30,\n", + " opacity=0.1,\n", + " font=dict(color=\"black\", size=100),\n", + " xref=\"paper\",\n", + " yref=\"paper\",\n", + " x=0.5,\n", + " y=0.5,\n", + " showarrow=False,\n", + " )\n", + " ]\n", + ")\n", + "\n", + "fig = go.Figure()\n", + "fig.update_layout(template=\"draft\")\n", + "fig.show()" + ] + }, + { + "cell_type": "markdown", + "id": "5392a1f2", + "metadata": {}, + "source": [ + "> Note: this example uses magic underscore notation to write `go.layout.Template(layout=dict(annotations=[...]))` as ``go.layout.Template(layout_annotations=[...])`\n", + "\n", + "It is also possible to set your own custom template as the default so that you do not need to pass it by name when constructing graph object figures or calling Plotly Express functions." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "91ef7d0c", + "metadata": {}, + "outputs": [], + "source": [ + "import plotly.graph_objects as go\n", + "import plotly.io as pio\n", + "\n", + "pio.templates[\"draft\"] = go.layout.Template(\n", + " layout_annotations=[\n", + " dict(\n", + " name=\"draft watermark\",\n", + " text=\"DRAFT\",\n", + " textangle=-30,\n", + " opacity=0.1,\n", + " font=dict(color=\"black\", size=100),\n", + " xref=\"paper\",\n", + " yref=\"paper\",\n", + " x=0.5,\n", + " y=0.5,\n", + " showarrow=False,\n", + " )\n", + " ]\n", + ")\n", + "pio.templates.default = \"draft\"\n", + "\n", + "fig = go.Figure()\n", + "fig.show()" + ] + }, + { + "cell_type": "markdown", + "id": "e36fc417", + "metadata": {}, + "source": [ + "#### Combining themes\n", + "\n", + "You may have noticed that figures displayed with the custom templates defined above do not have the gray background and white gridlines that are part of the default styling of figures created with plotly.py. The reason for this is that the default styling is specified in a template named `\"plotly\"`, and specifying a custom template overrides the default `\"plotly\"` template.\n", + "\n", + "If you want the styling of a custom template to be applied on top of the default styling of the `\"plotly\"` template, then you will need to combine the custom template with the `\"plotly\"` template. Multiple registered templates (whether built-in or user-defined) can be combined by specifying a template string that contains multiple template names joined on `\"+\"` characters.\n", + "\n", + "Here is an example of setting the default template to be a combination of the built-in `\"plotly\"` template and the custom `\"draft\"` template from the previous example." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "a751edcc", + "metadata": {}, + "outputs": [], + "source": [ + "import plotly.graph_objects as go\n", + "import plotly.io as pio\n", + "\n", + "pio.templates[\"draft\"] = go.layout.Template(\n", + " layout_annotations=[\n", + " dict(\n", + " name=\"draft watermark\",\n", + " text=\"DRAFT\",\n", + " textangle=-30,\n", + " opacity=0.1,\n", + " font=dict(color=\"black\", size=100),\n", + " xref=\"paper\",\n", + " yref=\"paper\",\n", + " x=0.5,\n", + " y=0.5,\n", + " showarrow=False,\n", + " )\n", + " ]\n", + ")\n", + "pio.templates.default = \"plotly+draft\"\n", + "\n", + "fig = go.Figure()\n", + "fig.show()" + ] + }, + { + "cell_type": "markdown", + "id": "8801593b", + "metadata": {}, + "source": [ + "Combining themes is also supported by Plotly Express" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "9325a50a", + "metadata": {}, + "outputs": [], + "source": [ + "import plotly.io as pio\n", + "import plotly.express as px\n", + "\n", + "pio.templates[\"draft\"] = go.layout.Template(\n", + " layout_annotations=[\n", + " dict(\n", + " name=\"draft watermark\",\n", + " text=\"DRAFT\",\n", + " textangle=-30,\n", + " opacity=0.1,\n", + " font=dict(color=\"black\", size=100),\n", + " xref=\"paper\",\n", + " yref=\"paper\",\n", + " x=0.5,\n", + " y=0.5,\n", + " showarrow=False,\n", + " )\n", + " ]\n", + ")\n", + "pio.templates.default = \"plotly+draft\"\n", + "\n", + "df = px.data.gapminder()\n", + "df_2007 = df.query(\"year==2007\")\n", + "\n", + "fig = px.scatter(df_2007,\n", + " x=\"gdpPercap\", y=\"lifeExp\", size=\"pop\", color=\"continent\",\n", + " log_x=True, size_max=60,\n", + " title=\"Gapminder 2007: current default theme\")\n", + "fig.show()" + ] + }, + { + "cell_type": "markdown", + "id": "cbc15f81", + "metadata": {}, + "source": [ + "\n", + "#### Saving and distributing custom themes\n", + "\n", + "The easiest way to save and distribute a custom template is to make a `*.py` file that creates and registers the template when it is imported. Here is an example of the contents of a file called `my_themes.py` that creates and registers the `\"draft\"` template when it is imported\n", + "\n", + "**my_themes.py**\n", + "\n", + "---\n", + "\n", + "```python\n", + "import plotly.graph_objects as go\n", + "import plotly.io as pio\n", + "\n", + "pio.templates[\"draft\"] = go.layout.Template(\n", + " layout_annotations=[\n", + " dict(\n", + " name=\"draft watermark\",\n", + " text=\"DRAFT\",\n", + " textangle=-30,\n", + " opacity=0.1,\n", + " font=dict(color=\"black\", size=100),\n", + " xref=\"paper\",\n", + " yref=\"paper\",\n", + " x=0.5,\n", + " y=0.5,\n", + " showarrow=False,\n", + " )\n", + " ]\n", + ")\n", + "```\n", + "\n", + "---\n", + "\n", + "To get access to the `\"draft\"` template, import the `my_themes` module.\n", + "\n", + "```python\n", + "import my_themes\n", + "import plotly.io as pio\n", + "pio.templates.default = \"draft\"\n", + "...\n", + "```\n", + "\n", + "> Note: In order for the import to succeed, the `my_themes.py` file must be on Python's module search path. See https://docs.python.org/3/tutorial/modules.html#the-module-search-path for more information.\n" + ] + }, + { + "cell_type": "markdown", + "id": "61edb320", + "metadata": {}, + "source": [ + "#### Examining built-in themes\n", + "\n", + "It may be useful to examine the contents and structure of the built-in templates when creating your own custom templates. The `Template` graph object for a registered template can be loaded using dictionary-style key access on the `plotly.io.templates` configuration object. Here is an example of loading the `Template` graph object for the `\"plotly\"` template, and then displaying the value of the template's `layout` property." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "54a7e41b", + "metadata": {}, + "outputs": [], + "source": [ + "import plotly.io as pio\n", + "plotly_template = pio.templates[\"plotly\"]\n", + "plotly_template.layout" + ] + }, + { + "cell_type": "markdown", + "id": "8bea7fee", + "metadata": {}, + "source": [ + "### What About Dash?\n", + "\n", + "[Dash](https://dash.plot.ly/) is an open-source framework for building analytical applications, with no Javascript required, and it is tightly integrated with the Plotly graphing library.\n", + "\n", + "Learn about how to install Dash at https://dash.plot.ly/installation.\n", + "\n", + "Everywhere in this page that you see `fig.show()`, you can display the same figure in a Dash application by passing it to the `figure` argument of the [`Graph` component](https://dash.plot.ly/dash-core-components/graph) from the built-in `dash_core_components` package like this:\n", + "\n", + "```python\n", + "import plotly.graph_objects as go # or plotly.express as px\n", + "fig = go.Figure() # or any Plotly Express function e.g. px.bar(...)\n", + "# fig.add_trace( ... )\n", + "# fig.update_layout( ... )\n", + "\n", + "from dash import Dash, dcc, html\n", + "\n", + "app = Dash()\n", + "app.layout = html.Div([\n", + " dcc.Graph(figure=fig)\n", + "])\n", + "\n", + "app.run_server(debug=True, use_reloader=False) # Turn off reloader if inside Jupyter\n", + "```" + ] + } + ], + "metadata": { + "jupytext": { + "notebook_metadata_filter": "all" + }, + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "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.7.3" + }, + "plotly": { + "description": "Theming and templates with plotly with Python", + "display_as": "file_settings", + "language": "python", + "layout": "base", + "name": "Theming and templates", + "order": 7, + "page_type": "u-guide", + "permalink": "python/templates/", + "thumbnail": "thumbnail/theming-and-templates.png" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/ternary-contour.ipynb b/ternary-contour.ipynb new file mode 100644 index 000000000..8bb8397d8 --- /dev/null +++ b/ternary-contour.ipynb @@ -0,0 +1,265 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "3f040386", + "metadata": {}, + "source": [ + "## Ternary contour plots" + ] + }, + { + "cell_type": "markdown", + "id": "e2d864ed", + "metadata": {}, + "source": [ + "A ternary contour plots represents isovalue lines of a quantity defined inside a [ternary diagram](https://en.wikipedia.org/wiki/Ternary_plot), i.e. as a function of three variables which sum is constant. Coordinates of the ternary plot often correspond to concentrations of three species, and the quantity represented as contours is some property (e.g., physical, chemical, thermodynamical) varying with the composition.\n", + "\n", + "For ternary contour plots, use the [figure factory](/python/figure-factories/) called ``create_ternary_contour``. The figure factory interpolates between given data points in order to compute the contours.\n", + "\n", + "Below we represent an example from metallurgy, where the mixing enthalpy is represented as a contour plot for aluminum-copper-yttrium (Al-Cu-Y) alloys.\n", + "\n", + "#### Simple ternary contour plot with plotly" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "be019187", + "metadata": {}, + "outputs": [], + "source": [ + "import plotly.figure_factory as ff\n", + "import numpy as np\n", + "Al = np.array([0. , 0. , 0., 0., 1./3, 1./3, 1./3, 2./3, 2./3, 1.])\n", + "Cu = np.array([0., 1./3, 2./3, 1., 0., 1./3, 2./3, 0., 1./3, 0.])\n", + "Y = 1 - Al - Cu\n", + "# synthetic data for mixing enthalpy\n", + "# See https://pycalphad.org/docs/latest/examples/TernaryExamples.html\n", + "enthalpy = (Al - 0.01) * Cu * (Al - 0.52) * (Cu - 0.48) * (Y - 1)**2\n", + "fig = ff.create_ternary_contour(np.array([Al, Y, Cu]), enthalpy,\n", + " pole_labels=['Al', 'Y', 'Cu'],\n", + " interp_mode='cartesian')\n", + "fig.show()" + ] + }, + { + "cell_type": "markdown", + "id": "f109e59b", + "metadata": {}, + "source": [ + "#### Customized ternary contour plot" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "ff8f134e", + "metadata": {}, + "outputs": [], + "source": [ + "import plotly.figure_factory as ff\n", + "import numpy as np\n", + "Al = np.array([0. , 0. , 0., 0., 1./3, 1./3, 1./3, 2./3, 2./3, 1.])\n", + "Cu = np.array([0., 1./3, 2./3, 1., 0., 1./3, 2./3, 0., 1./3, 0.])\n", + "Y = 1 - Al - Cu\n", + "# synthetic data for mixing enthalpy\n", + "# See https://pycalphad.org/docs/latest/examples/TernaryExamples.html\n", + "enthalpy = 2.e6 * (Al - 0.01) * Cu * (Al - 0.52) * (Cu - 0.48) * (Y - 1)**2 - 5000\n", + "fig = ff.create_ternary_contour(np.array([Al, Y, Cu]), enthalpy,\n", + " pole_labels=['Al', 'Y', 'Cu'],\n", + " interp_mode='cartesian',\n", + " ncontours=20,\n", + " colorscale='Viridis',\n", + " showscale=True,\n", + " title=dict(\n", + " text='Mixing enthalpy of ternary alloy'\n", + " ))\n", + "fig.show()" + ] + }, + { + "cell_type": "markdown", + "id": "02e3470b", + "metadata": {}, + "source": [ + "#### Ternary contour plot with lines only" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "bc7d2bff", + "metadata": {}, + "outputs": [], + "source": [ + "import plotly.figure_factory as ff\n", + "import numpy as np\n", + "Al = np.array([0. , 0. , 0., 0., 1./3, 1./3, 1./3, 2./3, 2./3, 1.])\n", + "Cu = np.array([0., 1./3, 2./3, 1., 0., 1./3, 2./3, 0., 1./3, 0.])\n", + "Y = 1 - Al - Cu\n", + "# synthetic data for mixing enthalpy\n", + "# See https://pycalphad.org/docs/latest/examples/TernaryExamples.html\n", + "enthalpy = 2.e6 * (Al - 0.01) * Cu * (Al - 0.52) * (Cu - 0.48) * (Y - 1)**2 - 5000\n", + "fig = ff.create_ternary_contour(np.array([Al, Y, Cu]), enthalpy,\n", + " pole_labels=['Al', 'Y', 'Cu'],\n", + " interp_mode='cartesian',\n", + " ncontours=20,\n", + " coloring='lines')\n", + "fig.show()" + ] + }, + { + "cell_type": "markdown", + "id": "9c38c440", + "metadata": {}, + "source": [ + "#### Ternary contour plot with data points\n", + "\n", + "With `showmarkers=True`, data points used to compute the contours are also displayed. They are best visualized for contour lines (no solid coloring). At the moment data points lying on the edges of the diagram are not displayed, this will be improved in future versions." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "4505034d", + "metadata": {}, + "outputs": [], + "source": [ + "import plotly.figure_factory as ff\n", + "import numpy as np\n", + "Al, Cu = np.mgrid[0:1:7j, 0:1:7j]\n", + "Al, Cu = Al.ravel(), Cu.ravel()\n", + "mask = Al + Cu <= 1\n", + "Al, Cu = Al[mask], Cu[mask]\n", + "Y = 1 - Al - Cu\n", + "\n", + "enthalpy = (Al - 0.5) * (Cu - 0.5) * (Y - 1)**2\n", + "fig = ff.create_ternary_contour(np.array([Al, Y, Cu]), enthalpy,\n", + " pole_labels=['Al', 'Y', 'Cu'],\n", + " ncontours=20,\n", + " coloring='lines',\n", + " showmarkers=True)\n", + "fig.show()" + ] + }, + { + "cell_type": "markdown", + "id": "53008e2f", + "metadata": {}, + "source": [ + "#### Interpolation mode\n", + "\n", + "Two modes are available in order to interpolate between data points: interpolation in Cartesian space (`interp_mode='cartesian'`) or interpolation using the [isometric log-ratio transformation](https://link.springer.com/article/10.1023/A:1023818214614) (see also [preprint](https://www.researchgate.net/profile/Leon_Parent2/post/What_is_the_best_approach_for_diagnosing_nutrient_disorders_and_formulating_fertilizer_recommendations/attachment/59d62a69c49f478072e9cf3f/AS%3A272541220835360%401441990298625/download/Egozcue+et+al+2003.pdf)), `interp_mode='ilr'`. The `ilr` transformation preserves metrics in the [simplex](https://en.wikipedia.org/wiki/Simplex) but is not defined on its edges." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "4321c11c", + "metadata": {}, + "outputs": [], + "source": [ + "a, b = np.mgrid[0:1:20j, 0:1:20j]\n", + "mask = a + b <= 1\n", + "a, b = a[mask], b[mask]\n", + "coords = np.stack((a, b, 1 - a - b))\n", + "value = np.sin(3.2 * np.pi * (a + b)) + np.sin(3 * np.pi * (a - b))\n", + "fig = ff.create_ternary_contour(coords, value, ncontours=9)\n", + "fig.show()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "81efb67e", + "metadata": {}, + "outputs": [], + "source": [ + "a, b = np.mgrid[0:1:20j, 0:1:20j]\n", + "mask = a + b <= 1\n", + "a, b = a[mask], b[mask]\n", + "coords = np.stack((a, b, 1 - a - b))\n", + "value = np.sin(3.2 * np.pi * (a + b)) + np.sin(3 * np.pi * (a - b))\n", + "fig = ff.create_ternary_contour(coords, value, interp_mode='cartesian',\n", + " ncontours=9)\n", + "fig.show()" + ] + }, + { + "cell_type": "markdown", + "id": "71696495", + "metadata": {}, + "source": [ + "#### Reference\n", + "\n", + "For more info on `ff.create_ternary_contour()`, see the [full function reference](https://plotly.com/python-api-reference/generated/plotly.figure_factory.create_ternary_contour.html)" + ] + }, + { + "cell_type": "markdown", + "id": "0195c23d", + "metadata": {}, + "source": [ + "### What About Dash?\n", + "\n", + "[Dash](https://dash.plot.ly/) is an open-source framework for building analytical applications, with no Javascript required, and it is tightly integrated with the Plotly graphing library.\n", + "\n", + "Learn about how to install Dash at https://dash.plot.ly/installation.\n", + "\n", + "Everywhere in this page that you see `fig.show()`, you can display the same figure in a Dash application by passing it to the `figure` argument of the [`Graph` component](https://dash.plot.ly/dash-core-components/graph) from the built-in `dash_core_components` package like this:\n", + "\n", + "```python\n", + "import plotly.graph_objects as go # or plotly.express as px\n", + "fig = go.Figure() # or any Plotly Express function e.g. px.bar(...)\n", + "# fig.add_trace( ... )\n", + "# fig.update_layout( ... )\n", + "\n", + "from dash import Dash, dcc, html\n", + "\n", + "app = Dash()\n", + "app.layout = html.Div([\n", + " dcc.Graph(figure=fig)\n", + "])\n", + "\n", + "app.run_server(debug=True, use_reloader=False) # Turn off reloader if inside Jupyter\n", + "```" + ] + } + ], + "metadata": { + "jupytext": { + "notebook_metadata_filter": "all" + }, + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "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.6.7" + }, + "plotly": { + "description": "How to make Ternary Contour Plots in Python with plotly", + "display_as": "scientific", + "language": "python", + "layout": "base", + "name": "Ternary contours", + "order": 18, + "page_type": "u-guide", + "permalink": "python/ternary-contour/", + "thumbnail": "thumbnail/ternary-contour.jpg" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/ternary-plots.ipynb b/ternary-plots.ipynb new file mode 100644 index 000000000..0d689dbee --- /dev/null +++ b/ternary-plots.ipynb @@ -0,0 +1,205 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "864af5b9", + "metadata": {}, + "source": [ + "## Ternary Plots\n", + "\n", + "A ternary plot depicts the ratios of three variables as positions in an equilateral triangle.\n", + "\n", + "## Ternary scatter plot with Plotly Express\n", + "\n", + "[Plotly Express](/python/plotly-express/) is the easy-to-use, high-level interface to Plotly, which [operates on a variety of types of data](/python/px-arguments/) and produces [easy-to-style figures](/python/styling-plotly-express/).\n", + "\n", + "Here we use `px.scatter_ternary` to visualize the three-way split between the three major candidates in a municipal election." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "4610ce7b", + "metadata": {}, + "outputs": [], + "source": [ + "import plotly.express as px\n", + "df = px.data.election()\n", + "fig = px.scatter_ternary(df, a=\"Joly\", b=\"Coderre\", c=\"Bergeron\")\n", + "fig.show()" + ] + }, + { + "cell_type": "markdown", + "id": "838d2c5e", + "metadata": {}, + "source": [ + "We can scale and color the markers to produce a ternary bubble chart." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "80b37fa5", + "metadata": {}, + "outputs": [], + "source": [ + "import plotly.express as px\n", + "df = px.data.election()\n", + "fig = px.scatter_ternary(df, a=\"Joly\", b=\"Coderre\", c=\"Bergeron\", hover_name=\"district\",\n", + " color=\"winner\", size=\"total\", size_max=15,\n", + " color_discrete_map = {\"Joly\": \"blue\", \"Bergeron\": \"green\", \"Coderre\":\"red\"} )\n", + "fig.show()" + ] + }, + { + "cell_type": "markdown", + "id": "c99cdd2b", + "metadata": {}, + "source": [ + "### Ternary scatter plot with Plotly Graph Objects" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "f3f762a7", + "metadata": {}, + "outputs": [], + "source": [ + "import plotly.graph_objects as go\n", + "\n", + "rawData = [\n", + " {'journalist':75,'developer':25,'designer':0,'label':'point 1'},\n", + " {'journalist':70,'developer':10,'designer':20,'label':'point 2'},\n", + " {'journalist':75,'developer':20,'designer':5,'label':'point 3'},\n", + " {'journalist':5,'developer':60,'designer':35,'label':'point 4'},\n", + " {'journalist':10,'developer':80,'designer':10,'label':'point 5'},\n", + " {'journalist':10,'developer':90,'designer':0,'label':'point 6'},\n", + " {'journalist':20,'developer':70,'designer':10,'label':'point 7'},\n", + " {'journalist':10,'developer':20,'designer':70,'label':'point 8'},\n", + " {'journalist':15,'developer':5,'designer':80,'label':'point 9'},\n", + " {'journalist':10,'developer':10,'designer':80,'label':'point 10'},\n", + " {'journalist':20,'developer':10,'designer':70,'label':'point 11'},\n", + "];\n", + "\n", + "def makeAxis(title, tickangle):\n", + " return {\n", + " 'title': {'text': title, 'font': { 'size': 20}},\n", + " 'tickangle': tickangle,\n", + " 'tickfont': { 'size': 15 },\n", + " 'tickcolor': 'rgba(0,0,0,0)',\n", + " 'ticklen': 5,\n", + " 'showline': True,\n", + " 'showgrid': True\n", + " }\n", + "\n", + "fig = go.Figure(go.Scatterternary({\n", + " 'mode': 'markers',\n", + " 'a': [i for i in map(lambda x: x['journalist'], rawData)],\n", + " 'b': [i for i in map(lambda x: x['developer'], rawData)],\n", + " 'c': [i for i in map(lambda x: x['designer'], rawData)],\n", + " 'text': [i for i in map(lambda x: x['label'], rawData)],\n", + " 'marker': {\n", + " 'symbol': 100,\n", + " 'color': '#DB7365',\n", + " 'size': 14,\n", + " 'line': { 'width': 2 }\n", + " }\n", + "}))\n", + "\n", + "fig.update_layout({\n", + " 'ternary': {\n", + " 'sum': 100,\n", + " 'aaxis': makeAxis('Journalist', 0),\n", + " 'baxis': makeAxis('
Developer', 45),\n", + " 'caxis': makeAxis('
Designer', -45)\n", + " },\n", + " 'annotations': [{\n", + " 'showarrow': False,\n", + " 'text': 'Simple Ternary Plot with Markers',\n", + " 'x': 0.5,\n", + " 'y': 1.3,\n", + " 'font': { 'size': 15 }\n", + " }]\n", + "})\n", + "\n", + "fig.show()" + ] + }, + { + "cell_type": "markdown", + "id": "6f082a32", + "metadata": {}, + "source": [ + "#### Reference\n", + "See [function reference for `px.(scatter_ternary)`](https://plotly.com/python-api-reference/generated/plotly.express.scatter_ternary) or https://plotly.com/python/reference/scatterternary/ for more information and chart attribute options!\n" + ] + }, + { + "cell_type": "markdown", + "id": "2faabbc2", + "metadata": {}, + "source": [ + "### What About Dash?\n", + "\n", + "[Dash](https://dash.plot.ly/) is an open-source framework for building analytical applications, with no Javascript required, and it is tightly integrated with the Plotly graphing library.\n", + "\n", + "Learn about how to install Dash at https://dash.plot.ly/installation.\n", + "\n", + "Everywhere in this page that you see `fig.show()`, you can display the same figure in a Dash application by passing it to the `figure` argument of the [`Graph` component](https://dash.plot.ly/dash-core-components/graph) from the built-in `dash_core_components` package like this:\n", + "\n", + "```python\n", + "import plotly.graph_objects as go # or plotly.express as px\n", + "fig = go.Figure() # or any Plotly Express function e.g. px.bar(...)\n", + "# fig.add_trace( ... )\n", + "# fig.update_layout( ... )\n", + "\n", + "from dash import Dash, dcc, html\n", + "\n", + "app = Dash()\n", + "app.layout = html.Div([\n", + " dcc.Graph(figure=fig)\n", + "])\n", + "\n", + "app.run_server(debug=True, use_reloader=False) # Turn off reloader if inside Jupyter\n", + "```" + ] + } + ], + "metadata": { + "jupytext": { + "notebook_metadata_filter": "all" + }, + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "language": "python", + "name": "python3" + }, + "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.11.10" + }, + "plotly": { + "description": "How to make Ternary plots in Python with Plotly.", + "display_as": "scientific", + "language": "python", + "layout": "base", + "name": "Ternary Plots", + "order": 4, + "page_type": "example_index", + "permalink": "python/ternary-plots/", + "thumbnail": "thumbnail/v4-migration.png" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/ternary-scatter-contour.ipynb b/ternary-scatter-contour.ipynb new file mode 100644 index 000000000..4175606a9 --- /dev/null +++ b/ternary-scatter-contour.ipynb @@ -0,0 +1,229 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "d1a4ad4e", + "metadata": {}, + "source": [ + "#### Load and Process Data Files" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "54e665fa", + "metadata": {}, + "outputs": [], + "source": [ + "import json\n", + "import pandas as pd\n", + "\n", + "contour_raw_data = pd.read_json('https://raw.githubusercontent.com/plotly/datasets/master/contour_data.json')\n", + "scatter_raw_data = pd.read_json('https://raw.githubusercontent.com/plotly/datasets/master/scatter_data.json')\n", + "\n", + "scatter_data = scatter_raw_data['Data']\n", + "\n", + "def clean_data(data_in):\n", + " \"\"\"\n", + " Cleans data in a format which can be conveniently\n", + " used for drawing traces. Takes a dictionary as the\n", + " input, and returns a list in the following format:\n", + "\n", + " input = {'key': ['a b c']}\n", + " output = [key, [a, b, c]]\n", + " \"\"\"\n", + " key = list(data_in.keys())[0]\n", + " data_out = [key]\n", + " for i in data_in[key]:\n", + " data_out.append(list(map(float, i.split(' '))))\n", + "\n", + " return data_out\n", + "\n", + "\n", + "#Example:\n", + "print(clean_data({'L1': ['.03 0.5 0.47','0.4 0.5 0.1']}))" + ] + }, + { + "cell_type": "markdown", + "id": "7519c2ce", + "metadata": {}, + "source": [ + "#### Create Ternary Scatter Plot:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "fe8457bc", + "metadata": {}, + "outputs": [], + "source": [ + "import plotly.graph_objects as go\n", + "\n", + "a_list = []\n", + "b_list = []\n", + "c_list = []\n", + "text = []\n", + "\n", + "for raw_data in scatter_data:\n", + " data = clean_data(raw_data)\n", + " text.append(data[0])\n", + " c_list.append(data[1][0])\n", + " a_list.append(data[1][1])\n", + " b_list.append(data[1][2])\n", + "\n", + "fig = go.Figure(go.Scatterternary(\n", + " text=text,\n", + " a=a_list,\n", + " b=b_list,\n", + " c=c_list,\n", + " mode='markers',\n", + " marker={'symbol': 100,\n", + " 'color': 'green',\n", + " 'size': 10},\n", + "))\n", + "\n", + "fig.update_layout({\n", + " 'title': 'Ternary Scatter Plot',\n", + " 'ternary':\n", + " {\n", + " 'sum':1,\n", + " 'aaxis':{'title': 'X', 'min': 0.01, 'linewidth':2, 'ticks':'outside' },\n", + " 'baxis':{'title': 'W', 'min': 0.01, 'linewidth':2, 'ticks':'outside' },\n", + " 'caxis':{'title': 'S', 'min': 0.01, 'linewidth':2, 'ticks':'outside' }\n", + " },\n", + " 'showlegend': False\n", + "})\n", + "\n", + "fig.show()" + ] + }, + { + "cell_type": "markdown", + "id": "6748e53e", + "metadata": {}, + "source": [ + "#### Create Ternary Contour Plot:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "26ca4e1b", + "metadata": {}, + "outputs": [], + "source": [ + "import plotly.graph_objects as go\n", + "\n", + "\n", + "contour_dict = contour_raw_data['Data']\n", + "\n", + "# Defining a colormap:\n", + "colors = ['#8dd3c7','#ffffb3','#bebada',\n", + " '#fb8072','#80b1d3','#fdb462',\n", + " '#b3de69','#fccde5','#d9d9d9',\n", + " '#bc80bd']\n", + "colors_iterator = iter(colors)\n", + "\n", + "fig = go.Figure()\n", + "\n", + "for raw_data in contour_dict:\n", + " data = clean_data(raw_data)\n", + "\n", + " a = [inner_data[0] for inner_data in data[1:]]\n", + " a.append(data[1][0]) # Closing the loop\n", + "\n", + " b = [inner_data[1] for inner_data in data[1:]]\n", + " b.append(data[1][1]) # Closing the loop\n", + "\n", + " c = [inner_data[2] for inner_data in data[1:]]\n", + " c.append(data[1][2]) # Closing the loop\n", + "\n", + " fig.add_trace(go.Scatterternary(\n", + " text = data[0],\n", + " a=a, b=b, c=c, mode='lines',\n", + " line=dict(color='#444', shape='spline'),\n", + " fill='toself',\n", + " fillcolor = colors_iterator.__next__()\n", + " ))\n", + "\n", + "fig.update_layout(title = 'Ternary Contour Plot')\n", + "fig.show()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "20da1b5c", + "metadata": {}, + "outputs": [], + "source": [] + }, + { + "cell_type": "markdown", + "id": "72c1b4b0", + "metadata": {}, + "source": [ + "### What About Dash?\n", + "\n", + "[Dash](https://dash.plot.ly/) is an open-source framework for building analytical applications, with no Javascript required, and it is tightly integrated with the Plotly graphing library.\n", + "\n", + "Learn about how to install Dash at https://dash.plot.ly/installation.\n", + "\n", + "Everywhere in this page that you see `fig.show()`, you can display the same figure in a Dash application by passing it to the `figure` argument of the [`Graph` component](https://dash.plot.ly/dash-core-components/graph) from the built-in `dash_core_components` package like this:\n", + "\n", + "```python\n", + "import plotly.graph_objects as go # or plotly.express as px\n", + "fig = go.Figure() # or any Plotly Express function e.g. px.bar(...)\n", + "# fig.add_trace( ... )\n", + "# fig.update_layout( ... )\n", + "\n", + "from dash import Dash, dcc, html\n", + "\n", + "app = Dash()\n", + "app.layout = html.Div([\n", + " dcc.Graph(figure=fig)\n", + "])\n", + "\n", + "app.run_server(debug=True, use_reloader=False) # Turn off reloader if inside Jupyter\n", + "```" + ] + } + ], + "metadata": { + "jupytext": { + "notebook_metadata_filter": "all" + }, + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "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.6.7" + }, + "plotly": { + "description": "How to make a scatter plot overlaid on ternary contour in Python with Plotly.", + "display_as": "scientific", + "language": "python", + "layout": "base", + "name": "Ternary Overlay", + "order": 8, + "page_type": "u-guide", + "permalink": "python/ternary-scatter-contour/", + "thumbnail": "thumbnail/ternary-scatter-contour.jpg" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/text-and-annotations.ipynb b/text-and-annotations.ipynb new file mode 100644 index 000000000..ea691e787 --- /dev/null +++ b/text-and-annotations.ipynb @@ -0,0 +1,1287 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "392fa1e0", + "metadata": {}, + "source": [ + "### Adding Text to Figures\n", + "\n", + "As a general rule, there are two ways to add text labels to figures:\n", + "1. Certain trace types, notably in the `scatter` family (e.g. `scatter`, `scatter3d`, `scattergeo` etc), support a `text` attribute, and can be displayed with or without markers.\n", + "2. Standalone text annotations can be added to figures using `fig.add_annotation()`, with or without arrows, and they can be positioned absolutely within the figure, or they can be positioned relative to the axes of 2d or 3d cartesian subplots i.e. in data coordinates.\n", + "\n", + "The differences between these two approaches are that:\n", + "* Traces can optionally support hover labels and can appear in legends.\n", + "* Text annotations can be positioned absolutely or relative to data coordinates in 2d/3d cartesian subplots only.\n", + "* Traces cannot be positioned absolutely but can be positioned relative to data coordinates in any subplot type.\n", + "* Traces also be used to [draw shapes](/python/shapes/), although there is a [shape equivalent to text annotations](/python/shapes/)." + ] + }, + { + "cell_type": "markdown", + "id": "e7cb1d1f", + "metadata": {}, + "source": [ + "### Text on scatter plots with Plotly Express\n", + "\n", + "Here is an example that creates a scatter plot with text labels using Plotly Express." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "ae03475e", + "metadata": {}, + "outputs": [], + "source": [ + "import plotly.express as px\n", + "\n", + "df = px.data.gapminder().query(\"year==2007 and continent=='Americas'\")\n", + "\n", + "fig = px.scatter(df, x=\"gdpPercap\", y=\"lifeExp\", text=\"country\", log_x=True, size_max=60)\n", + "\n", + "fig.update_traces(textposition='top center')\n", + "\n", + "fig.update_layout(\n", + " height=800,\n", + " title_text='GDP and Life Expectancy (Americas, 2007)'\n", + ")\n", + "\n", + "fig.show()" + ] + }, + { + "cell_type": "markdown", + "id": "72848d2d", + "metadata": {}, + "source": [ + "### Text on scatter plots with Graph Objects" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "3c957267", + "metadata": {}, + "outputs": [], + "source": [ + "import plotly.graph_objects as go\n", + "\n", + "fig = go.Figure()\n", + "\n", + "fig.add_trace(go.Scatter(\n", + " x=[0, 1, 2],\n", + " y=[1, 1, 1],\n", + " mode=\"lines+markers+text\",\n", + " name=\"Lines, Markers and Text\",\n", + " text=[\"Text A\", \"Text B\", \"Text C\"],\n", + " textposition=\"top center\"\n", + "))\n", + "\n", + "fig.add_trace(go.Scatter(\n", + " x=[0, 1, 2],\n", + " y=[2, 2, 2],\n", + " mode=\"markers+text\",\n", + " name=\"Markers and Text\",\n", + " text=[\"Text D\", \"Text E\", \"Text F\"],\n", + " textposition=\"bottom center\"\n", + "))\n", + "\n", + "fig.add_trace(go.Scatter(\n", + " x=[0, 1, 2],\n", + " y=[3, 3, 3],\n", + " mode=\"lines+text\",\n", + " name=\"Lines and Text\",\n", + " text=[\"Text G\", \"Text H\", \"Text I\"],\n", + " textposition=\"bottom center\"\n", + "))\n", + "\n", + "fig.show()" + ] + }, + { + "cell_type": "markdown", + "id": "37ff1ea4", + "metadata": {}, + "source": [ + "### Text positioning in Dash\n", + "\n", + "[Dash](https://plotly.com/dash/) is the best way to build analytical apps in Python using Plotly figures. To run the app below, run `pip install dash`, click \"Download\" to get the code and run `python app.py`.\n", + "\n", + "Get started with [the official Dash docs](https://dash.plotly.com/installation) and **learn how to effortlessly [style](https://plotly.com/dash/design-kit/) & [deploy](https://plotly.com/dash/app-manager/) apps like this with Dash Enterprise.**\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "0940c2ea", + "metadata": { + "hide_code": true + }, + "outputs": [], + "source": [ + "from IPython.display import IFrame\n", + "snippet_url = 'https://python-docs-dash-snippets.herokuapp.com/python-docs-dash-snippets/'\n", + "IFrame(snippet_url + 'text-and-annotations', width='100%', height=1200)" + ] + }, + { + "cell_type": "markdown", + "id": "9cda0691", + "metadata": {}, + "source": [ + "

Sign up for Dash Club → Free cheat sheets plus updates from Chris Parmer and Adam Schroeder delivered to your inbox every two months. Includes tips and tricks, community apps, and deep dives into the Dash architecture.\n", + "Join now.

" + ] + }, + { + "cell_type": "markdown", + "id": "850f2c98", + "metadata": {}, + "source": [ + "### Controlling Text Size with `uniformtext`\n", + "\n", + "For the [pie](/python/pie-charts), [bar](/python/bar-charts)-like, [sunburst](/python/sunburst-charts) and [treemap](/python/treemaps) traces, it is possible to force all the text labels to have the same size thanks to the `uniformtext` layout parameter. The `minsize` attribute sets the font size, and the `mode` attribute sets what happens for labels which cannot fit with the desired fontsize: either `hide` them or `show` them with overflow." + ] + }, + { + "cell_type": "markdown", + "id": "1b677e08", + "metadata": {}, + "source": [ + "Here is a bar chart with the default behavior which will scale down text to fit." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "b226d216", + "metadata": {}, + "outputs": [], + "source": [ + "import plotly.express as px\n", + "\n", + "df = px.data.gapminder(year=2007)\n", + "fig = px.bar(df, x='continent', y='pop', color=\"lifeExp\", text='country',\n", + " title=\"Default behavior: some text is tiny\")\n", + "fig.update_traces(textposition='inside')\n", + "fig.show()" + ] + }, + { + "cell_type": "markdown", + "id": "9eca83c0", + "metadata": {}, + "source": [ + "Here is the same figure with uniform text applied: the text for all bars is the same size, with a minimum size of 8. Any text at the minimum size which does not fit in the bar is hidden." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "9484603a", + "metadata": {}, + "outputs": [], + "source": [ + "import plotly.express as px\n", + "\n", + "df = px.data.gapminder(year=2007)\n", + "fig = px.bar(df, x='continent', y='pop', color=\"lifeExp\", text='country',\n", + " title=\"Uniform Text: min size is 8, hidden if can't fit\")\n", + "fig.update_traces(textposition='inside')\n", + "fig.update_layout(uniformtext_minsize=8, uniformtext_mode='hide')\n", + "fig.show()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "161ff590", + "metadata": {}, + "outputs": [], + "source": [ + "import plotly.express as px\n", + "\n", + "df = px.data.gapminder().query(\"continent == 'Asia' and year == 2007\")\n", + "fig = px.pie(df, values='pop', names='country')\n", + "fig.update_traces(textposition='inside')\n", + "fig.update_layout(uniformtext_minsize=12, uniformtext_mode='hide')\n", + "fig.show()" + ] + }, + { + "cell_type": "markdown", + "id": "44fb7f5f", + "metadata": {}, + "source": [ + "### Controlling Maximum Text Size\n", + "\n", + "The `textfont_size` parameter of the the [pie](/python/pie-charts), [bar](/python/bar-charts)-like, [sunburst](/python/sunburst-charts) and [treemap](/python/treemaps) traces can be used to set the **maximum font size** used in the chart. Note that the `textfont` parameter sets the `insidetextfont` and `outsidetextfont` parameter, which can also be set independently." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "584bb40e", + "metadata": {}, + "outputs": [], + "source": [ + "import plotly.express as px\n", + "\n", + "df = px.data.gapminder().query(\"continent == 'Asia' and year == 2007\")\n", + "fig = px.pie(df, values='pop', names='country')\n", + "fig.update_traces(textposition='inside', textfont_size=14)\n", + "fig.show()" + ] + }, + { + "cell_type": "markdown", + "id": "835d8c2e", + "metadata": {}, + "source": [ + "### Text Annotations\n", + "\n", + "Annotations can be added to a figure using `fig.add_annotation()`." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "7df77a80", + "metadata": {}, + "outputs": [], + "source": [ + "import plotly.graph_objects as go\n", + "\n", + "fig = go.Figure()\n", + "\n", + "fig.add_trace(go.Scatter(\n", + " x=[0, 1, 2, 3, 4, 5, 6, 7, 8],\n", + " y=[0, 1, 3, 2, 4, 3, 4, 6, 5]\n", + "))\n", + "\n", + "\n", + "fig.add_trace(go.Scatter(\n", + " x=[0, 1, 2, 3, 4, 5, 6, 7, 8],\n", + " y=[0, 4, 5, 1, 2, 2, 3, 4, 2]\n", + "))\n", + "\n", + "fig.add_annotation(x=2, y=5,\n", + " text=\"Text annotation with arrow\",\n", + " showarrow=True,\n", + " arrowhead=1)\n", + "fig.add_annotation(x=4, y=4,\n", + " text=\"Text annotation without arrow\",\n", + " showarrow=False,\n", + " yshift=10)\n", + "\n", + "fig.update_layout(showlegend=False)\n", + "\n", + "fig.show()" + ] + }, + { + "cell_type": "markdown", + "id": "85e10aad", + "metadata": {}, + "source": [ + "#### Text Annotations with Log Axes\n", + "\n", + "If the `x` or `y` positions of an annotation reference a log axis, you need to provide that position as a `log10` value when adding the annotation. In this example, the `yaxis` is a log axis so we pass the `log10` value of `1000` to the annotation's `y` position." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "0a40c4bd", + "metadata": {}, + "outputs": [], + "source": [ + "import plotly.graph_objects as go\n", + "import math\n", + "\n", + "dates = [\n", + " \"2024-01-01\",\n", + " \"2024-01-02\",\n", + " \"2024-01-03\",\n", + " \"2024-01-04\",\n", + " \"2024-01-05\",\n", + " \"2024-01-06\",\n", + "]\n", + "y_values = [1, 30, 70, 100, 1000, 10000000]\n", + "\n", + "fig = go.Figure(\n", + " data=[go.Scatter(x=dates, y=y_values, mode=\"lines+markers\")],\n", + " layout=go.Layout(\n", + " yaxis=dict(\n", + " type=\"log\",\n", + " )\n", + " ),\n", + ")\n", + "\n", + "fig.add_annotation(\n", + " x=\"2024-01-05\",\n", + " y=math.log10(1000),\n", + " text=\"Log axis annotation\",\n", + " showarrow=True,\n", + " xanchor=\"right\",\n", + ")\n", + "\n", + "fig.show()" + ] + }, + { + "cell_type": "markdown", + "id": "54ef4473", + "metadata": {}, + "source": [ + "### 3D Annotations" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "542c19ab", + "metadata": {}, + "outputs": [], + "source": [ + "import plotly.graph_objects as go\n", + "\n", + "fig = go.Figure()\n", + "\n", + "fig.add_trace(go.Scatter3d(\n", + " x=[\"2017-01-01\", \"2017-02-10\", \"2017-03-20\"],\n", + " y=[\"A\", \"B\", \"C\"],\n", + " z=[1, 1000, 100000],\n", + " name=\"z\",\n", + "))\n", + "\n", + "fig.update_layout(\n", + " scene=dict(\n", + " xaxis=dict(type=\"date\"),\n", + " yaxis=dict(type=\"category\"),\n", + " zaxis=dict(type=\"log\"),\n", + " annotations=[\n", + " dict(\n", + " showarrow=False,\n", + " x=\"2017-01-01\",\n", + " y=\"A\",\n", + " z=0,\n", + " text=\"Point 1\",\n", + " xanchor=\"left\",\n", + " xshift=10,\n", + " opacity=0.7),\n", + " dict(\n", + " x=\"2017-02-10\",\n", + " y=\"B\",\n", + " z=4,\n", + " text=\"Point 2\",\n", + " textangle=0,\n", + " ax=0,\n", + " ay=-75,\n", + " font=dict(\n", + " color=\"black\",\n", + " size=12\n", + " ),\n", + " arrowcolor=\"black\",\n", + " arrowsize=3,\n", + " arrowwidth=1,\n", + " arrowhead=1),\n", + " dict(\n", + " x=\"2017-03-20\",\n", + " y=\"C\",\n", + " z=5,\n", + " ax=50,\n", + " ay=0,\n", + " text=\"Point 3\",\n", + " arrowhead=1,\n", + " xanchor=\"left\",\n", + " yanchor=\"bottom\"\n", + " )]\n", + " ),\n", + ")\n", + "\n", + "fig.show()" + ] + }, + { + "cell_type": "markdown", + "id": "a7f005db", + "metadata": {}, + "source": [ + "### Font Color, Size, and Familiy\n", + "\n", + "Use `textfont` to specify a font `family`, `size`, or `color`." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "705c84d7", + "metadata": {}, + "outputs": [], + "source": [ + "import plotly.graph_objects as go\n", + "\n", + "fig = go.Figure()\n", + "\n", + "fig.add_trace(go.Scatter(\n", + " x=[0, 1, 2],\n", + " y=[1, 1, 1],\n", + " mode=\"lines+markers+text\",\n", + " name=\"Lines, Markers and Text\",\n", + " text=[\"Text A\", \"Text B\", \"Text C\"],\n", + " textposition=\"top right\",\n", + " textfont=dict(\n", + " family=\"sans serif\",\n", + " size=18,\n", + " color=\"crimson\"\n", + " )\n", + "))\n", + "\n", + "fig.add_trace(go.Scatter(\n", + " x=[0, 1, 2],\n", + " y=[2, 2, 2],\n", + " mode=\"lines+markers+text\",\n", + " name=\"Lines and Text\",\n", + " text=[\"Text G\", \"Text H\", \"Text I\"],\n", + " textposition=\"bottom center\",\n", + " textfont=dict(\n", + " family=\"sans serif\",\n", + " size=18,\n", + " color=\"LightSeaGreen\"\n", + " )\n", + "))\n", + "\n", + "fig.update_layout(showlegend=False)\n", + "\n", + "fig.show()" + ] + }, + { + "cell_type": "markdown", + "id": "82a1aed9", + "metadata": {}, + "source": [ + "### Font Style, Variant, and Weight\n", + "\n", + "*New in 5.22*\n", + "\n", + "You can also configure a font's `variant`, `style`, and `weight` on `textfont`. Here, we configure an `italic` style on the first bar, `bold` weight on the second, and `small-caps` as the font variant on the third." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "bcaed730", + "metadata": {}, + "outputs": [], + "source": [ + "import plotly.graph_objects as go\n", + "from plotly import data\n", + "\n", + "df = data.medals_wide()\n", + "\n", + "fig = go.Figure(\n", + " data=[\n", + " go.Bar(\n", + " x=df.nation,\n", + " y=df.gold,\n", + " name=\"Gold\",\n", + " marker=dict(color=\"Gold\"),\n", + " text=\"Gold\",\n", + " textfont=dict(style=\"italic\"),\n", + " ),\n", + " go.Bar(\n", + " x=df.nation,\n", + " y=df.silver,\n", + " name=\"Silver\",\n", + " marker=dict(color=\"MediumTurquoise\"),\n", + " text=\"Silver\",\n", + " textfont=dict(weight=\"bold\"),\n", + " ),\n", + " go.Bar(\n", + " x=df.nation,\n", + " y=df.bronze,\n", + " name=\"Bronze\",\n", + " marker=dict(color=\"LightGreen\"),\n", + " text=\"Bronze\",\n", + " textfont=dict(variant=\"small-caps\"),\n", + " ),\n", + " ],\n", + " layout=dict(barcornerradius=15, showlegend=False),\n", + ")\n", + "\n", + "fig.show()\n" + ] + }, + { + "cell_type": "markdown", + "id": "f1c0f83f", + "metadata": {}, + "source": [ + "## Numeric Font Weight\n", + "\n", + "*New in 5.23*\n", + "\n", + "In the previous example, we set a font `weight` using a keyword value. You can also set font `weight` using a numeric value.\n", + "\n", + "The font weights that are available depend on the font family that is set. If you set a font `weight` that isn't available for a particular font family, the weight will be rounded to the nearest available value.\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "1a83aa14", + "metadata": {}, + "outputs": [], + "source": [ + "import plotly.graph_objects as go\n", + "from plotly import data\n", + "\n", + "df = data.medals_wide()\n", + "\n", + "fig = go.Figure(\n", + " data=[\n", + " go.Bar(\n", + " x=df.nation,\n", + " y=df.gold,\n", + " name=\"Gold\",\n", + " marker=dict(color=\"Gold\"),\n", + " text=\"Gold\",\n", + " textfont=dict(weight=900, size=17),\n", + " ),\n", + " go.Bar(\n", + " x=df.nation,\n", + " y=df.silver,\n", + " name=\"Silver\",\n", + " marker=dict(color=\"MediumTurquoise\"),\n", + " text=\"Silver\",\n", + " textfont=dict(size=17),\n", + " ),\n", + " go.Bar(\n", + " x=df.nation,\n", + " y=df.bronze,\n", + " name=\"Bronze\",\n", + " marker=dict(color=\"LightGreen\"),\n", + " text=\"Bronze\",\n", + " textfont=dict(size=17),\n", + " ),\n", + " ],\n", + " layout=dict(barcornerradius=15, showlegend=False),\n", + ")\n", + "\n", + "fig.show()" + ] + }, + { + "cell_type": "markdown", + "id": "0ecab2cb", + "metadata": {}, + "source": [ + "[scattergl](https://plotly.com/python/reference/scattergl) traces do not support all numeric font weights. When you specify a numeric font weight on `scattergl`, weights up to 500 are mapped to the keyword font weight \"normal\", while weights above 500 are mapped to \"bold\"." + ] + }, + { + "cell_type": "markdown", + "id": "5df64ae2", + "metadata": {}, + "source": [ + "## Text Case\n", + "\n", + "*New in 5.23*\n", + "\n", + "You can configure text case using the `textfont.textcase` property. In this example, we set `textfont.textcase=\"upper\"` to transform the text on all bars to uppercase." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "ae989226", + "metadata": {}, + "outputs": [], + "source": [ + "import plotly.graph_objects as go\n", + "from plotly import data\n", + "\n", + "df = data.gapminder()\n", + "\n", + "grouped = df[df.year == 2007].loc[df[df.year == 2007].groupby('continent')['lifeExp'].idxmax()]\n", + "\n", + "fig = go.Figure(\n", + " data=go.Bar(\n", + " x=grouped['lifeExp'],\n", + " y=grouped['continent'],\n", + " text=grouped['country'],\n", + " orientation='h',\n", + " textfont=dict(\n", + " family=\"sans serif\",\n", + " size=14,\n", + " # Here we set textcase to \"upper.\n", + " # Set to lower\" for lowercase text, or \"word caps\" to capitalize the first letter of each word\n", + " textcase=\"upper\"\n", + "\n", + " )\n", + " ),\n", + " layout=go.Layout(\n", + " title_text='Country with Highest Life Expectancy per Continent, 2007',\n", + " yaxis=dict(showticklabels=False)\n", + " )\n", + ")\n", + "\n", + "fig.show()" + ] + }, + { + "cell_type": "markdown", + "id": "a267a023", + "metadata": {}, + "source": [ + "## Text Lines\n", + "\n", + "*New in 5.23*\n", + "\n", + "You can add decoration lines to text using the `textfont.lineposition` property. This property accepts `\"under\"`, `\"over\"`, and `\"through\"`, or a combination of these separated by a `+`." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "cbf4a120", + "metadata": {}, + "outputs": [], + "source": [ + "import plotly.graph_objects as go\n", + "from plotly import data\n", + "\n", + "df = data.gapminder()\n", + "\n", + "grouped = df[df.year == 2002].loc[df[df.year == 2002].groupby('continent')['lifeExp'].idxmax()]\n", + "\n", + "fig = go.Figure(\n", + " data=go.Bar(\n", + " x=grouped['lifeExp'],\n", + " y=grouped['continent'],\n", + " text=grouped['country'],\n", + " orientation='h',\n", + " marker_color='MediumSlateBlue',\n", + " textfont=dict(\n", + " lineposition=\"under\" # combine different line positions with a \"+\" to add more than one: \"under+over\"\n", + " )\n", + " ),\n", + " layout=go.Layout(\n", + " title_text='Country with Highest Life Expectancy per Continent, 2002',\n", + " yaxis=dict(showticklabels=False)\n", + " )\n", + ")\n", + "\n", + "fig.show()" + ] + }, + { + "cell_type": "markdown", + "id": "9a89aece", + "metadata": {}, + "source": [ + "## Text Shadow\n", + "\n", + "*New in 5.23*\n", + "\n", + "You can apply a shadow effect to text using the `textfont.shadow` property. This property accepts shadow specifications in the same format as the [text-shadow CSS property](https://developer.mozilla.org/en-US/docs/Web/CSS/text-shadow)." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "5d14afce", + "metadata": {}, + "outputs": [], + "source": [ + "import plotly.graph_objects as go\n", + "from plotly import data\n", + "\n", + "df = data.gapminder()\n", + "\n", + "grouped = df[df.year == 1997].loc[df[df.year == 1997].groupby('continent')['lifeExp'].idxmax()]\n", + "\n", + "fig = go.Figure(\n", + " data=go.Bar(\n", + " x=grouped['lifeExp'],\n", + " y=grouped['continent'],\n", + " text=grouped['country'],\n", + " orientation='h',\n", + " textfont=dict(\n", + " shadow=\"1px 1px 2px pink\"\n", + " )\n", + " ),\n", + " layout=go.Layout(\n", + " title_text='Country with Highest Life Expectancy per Continent, 1997',\n", + " yaxis=dict(showticklabels=False)\n", + " )\n", + ")\n", + "\n", + "fig.show()" + ] + }, + { + "cell_type": "markdown", + "id": "2623f172", + "metadata": {}, + "source": [ + "### Styling and Coloring Annotations" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "f2c99f55", + "metadata": {}, + "outputs": [], + "source": [ + "import plotly.graph_objects as go\n", + "\n", + "fig = go.Figure()\n", + "\n", + "fig.add_trace(go.Scatter(\n", + " x=[0, 1, 2, 3, 4, 5, 6, 7, 8],\n", + " y=[0, 1, 3, 2, 4, 3, 4, 6, 5]\n", + "))\n", + "\n", + "fig.add_trace(go.Scatter(\n", + " x=[0, 1, 2, 3, 4, 5, 6, 7, 8],\n", + " y=[0, 4, 5, 1, 2, 2, 3, 4, 2]\n", + "))\n", + "\n", + "fig.add_annotation(\n", + " x=2,\n", + " y=5,\n", + " xref=\"x\",\n", + " yref=\"y\",\n", + " text=\"max=5\",\n", + " showarrow=True,\n", + " font=dict(\n", + " family=\"Courier New, monospace\",\n", + " size=16,\n", + " color=\"#ffffff\"\n", + " ),\n", + " align=\"center\",\n", + " arrowhead=2,\n", + " arrowsize=1,\n", + " arrowwidth=2,\n", + " arrowcolor=\"#636363\",\n", + " ax=20,\n", + " ay=-30,\n", + " bordercolor=\"#c7c7c7\",\n", + " borderwidth=2,\n", + " borderpad=4,\n", + " bgcolor=\"#ff7f0e\",\n", + " opacity=0.8\n", + " )\n", + "\n", + "fig.update_layout(showlegend=False)\n", + "fig.show()" + ] + }, + { + "cell_type": "markdown", + "id": "f5d2db30", + "metadata": {}, + "source": [ + "### Text Font as an Array - Styling Each Text Element" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "696b2b0f", + "metadata": {}, + "outputs": [], + "source": [ + "import plotly.graph_objects as go\n", + "\n", + "fig = go.Figure()\n", + "\n", + "fig.add_trace(go.Scattergeo(\n", + " lat=[45.5, 43.4, 49.13, 51.1, 53.34, 45.24, 44.64, 48.25, 49.89, 50.45],\n", + " lon=[-73.57, -79.24, -123.06, -114.1, -113.28, -75.43, -63.57, -123.21, -97.13,\n", + " -104.6],\n", + " marker={\n", + " \"color\": [\"MidnightBlue\", \"IndianRed\", \"MediumPurple\", \"Orange\", \"Crimson\",\n", + " \"LightSeaGreen\", \"RoyalBlue\", \"LightSalmon\", \"DarkOrange\", \"MediumSlateBlue\"],\n", + " \"line\": {\n", + " \"width\": 1\n", + " },\n", + " \"size\": 10\n", + " },\n", + " mode=\"markers+text\",\n", + " name=\"\",\n", + " text=[\"Montreal\", \"Toronto\", \"Vancouver\", \"Calgary\", \"Edmonton\", \"Ottawa\",\n", + " \"Halifax\",\n", + " \"Victoria\", \"Winnepeg\", \"Regina\"],\n", + " textfont={\n", + " \"color\": [\"MidnightBlue\", \"IndianRed\", \"MediumPurple\", \"Gold\", \"Crimson\",\n", + " \"LightSeaGreen\",\n", + " \"RoyalBlue\", \"LightSalmon\", \"DarkOrange\", \"MediumSlateBlue\"],\n", + " \"family\": [\"Arial, sans-serif\", \"Balto, sans-serif\", \"Courier New, monospace\",\n", + " \"Droid Sans, sans-serif\", \"Droid Serif, serif\",\n", + " \"Droid Sans Mono, sans-serif\",\n", + " \"Gravitas One, cursive\", \"Old Standard TT, serif\",\n", + " \"Open Sans, sans-serif\",\n", + " \"PT Sans Narrow, sans-serif\", \"Raleway, sans-serif\",\n", + " \"Times New Roman, Times, serif\"],\n", + " \"size\": [22, 21, 20, 19, 18, 17, 16, 15, 14, 13]\n", + " },\n", + " textposition=[\"top center\", \"middle left\", \"top center\", \"bottom center\",\n", + " \"top right\",\n", + " \"middle left\", \"bottom right\", \"bottom left\", \"top right\",\n", + " \"top right\"]\n", + "))\n", + "\n", + "fig.update_layout(\n", + " title_text=\"Canadian cities\",\n", + " geo=dict(\n", + " lataxis=dict(range=[40, 70]),\n", + " lonaxis=dict(range=[-130, -55]),\n", + " scope=\"north america\"\n", + " )\n", + ")\n", + "\n", + "fig.show()" + ] + }, + { + "cell_type": "markdown", + "id": "e6174d0f", + "metadata": {}, + "source": [ + "### HTML Tags in Text\n", + "\n", + "The `text` attribute supports the following HTML tags: `
`,``,``, ``, `` and ``.\n", + "In version 5.23 and later, `` and ``are also supported." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "4532f984", + "metadata": {}, + "outputs": [], + "source": [ + "import plotly.graph_objects as go\n", + "\n", + "fig = go.Figure(\n", + " data=[\n", + " go.Scatter(\n", + " x=[0, 1, 2, 3, 4, 5, 6, 7, 8],\n", + " y=[0, 1, 3, 2, 4, 3, 4, 6, 5],\n", + " mode=\"lines+markers\",\n", + " name=\"Series 1\",\n", + " ),\n", + " go.Scatter(\n", + " x=[0, 1, 2, 3, 4, 5, 6, 7, 8],\n", + " y=[0, 4, 5, 1, 2, 2, 3, 4, 2],\n", + " mode=\"lines+markers\",\n", + " name=\"Series 2\",\n", + " ),\n", + " ],\n", + " layout=go.Layout(\n", + " annotations=[\n", + " dict(\n", + " x=2,\n", + " y=5,\n", + " text=\"Text annotation using bolded text, italicized text, underlined text,
and a new line\",\n", + " showarrow=True,\n", + " arrowhead=1,\n", + " ),\n", + " dict(\n", + " x=4,\n", + " y=4,\n", + " text=\"Text annotation with
a link.\",\n", + " showarrow=False,\n", + " yshift=10,\n", + " ),\n", + " ],\n", + " showlegend=False,\n", + " ),\n", + ")\n", + "\n", + "fig.show()\n" + ] + }, + { + "cell_type": "markdown", + "id": "c407ac96", + "metadata": {}, + "source": [ + "### Positioning Text Annotations Absolutely\n", + "\n", + "By default, text annotations have `xref` and `yref` set to `\"x\"` and `\"y\"`, respectively, meaning that their x/y coordinates are with respect to the axes of the plot. This means that panning the plot will cause the annotations to move. Setting `xref` and/or `yref` to `\"paper\"` will cause the `x` and `y` attributes to be interpreted in [paper coordinates](/python/figure-structure/#positioning-with-paper-container-coordinates-or-axis-domain-coordinates).\n", + "\n", + "Try panning or zooming in the following figure:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "d92e6369", + "metadata": {}, + "outputs": [], + "source": [ + "import plotly.express as px\n", + "\n", + "fig = px.scatter(x=[1, 2, 3], y=[1, 2, 3], title=\"Try panning or zooming!\")\n", + "\n", + "fig.add_annotation(text=\"Absolutely-positioned annotation\",\n", + " xref=\"paper\", yref=\"paper\",\n", + " x=0.3, y=0.3, showarrow=False)\n", + "\n", + "fig.show()" + ] + }, + { + "cell_type": "markdown", + "id": "98bee0b3", + "metadata": {}, + "source": [ + "### Adding Annotations Referenced to an Axis\n", + "\n", + "To place annotations relative to the length or height of an axis, the string\n", + "`' domain'` can be added after the axis reference in the `xref` or `yref` fields.\n", + "For example:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "b2ffdde3", + "metadata": {}, + "outputs": [], + "source": [ + "import plotly.express as px\n", + "import plotly.graph_objects as go\n", + "\n", + "df = px.data.wind()\n", + "fig = px.scatter(df, y=\"frequency\")\n", + "\n", + "# Set a custom domain to see how the ' domain' string changes the behaviour\n", + "fig.update_layout(xaxis=dict(domain=[0, 0.5]), yaxis=dict(domain=[0.25, 0.75]))\n", + "\n", + "fig.add_annotation(\n", + " xref=\"x domain\",\n", + " yref=\"y domain\",\n", + " # The arrow head will be 25% along the x axis, starting from the left\n", + " x=0.25,\n", + " # The arrow head will be 40% along the y axis, starting from the bottom\n", + " y=0.4,\n", + " text=\"An annotation referencing the axes\",\n", + " arrowhead=2,\n", + ")\n", + "\n", + "fig.show()" + ] + }, + { + "cell_type": "markdown", + "id": "0eaf9498", + "metadata": {}, + "source": [ + "### Specifying the Text's Position Absolutely\n", + "\n", + "The text coordinates / dimensions of the arrow can be specified absolutely, as\n", + "long as they use exactly the same coordinate system as the arrowhead. For\n", + "example:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "78de0ab5", + "metadata": { + "lines_to_next_cell": 0 + }, + "outputs": [], + "source": [ + "import plotly.express as px\n", + "import plotly.graph_objects as go\n", + "\n", + "df = px.data.wind()\n", + "fig = px.scatter(df, y=\"frequency\")\n", + "\n", + "fig.update_layout(xaxis=dict(domain=[0, 0.5]), yaxis=dict(domain=[0.25, 0.75]))\n", + "fig.add_annotation(\n", + " xref=\"x domain\",\n", + " yref=\"y\",\n", + " x=0.75,\n", + " y=1,\n", + " text=\"An annotation whose text and arrowhead reference the axes and the data\",\n", + " # If axref is exactly the same as xref, then the text's position is\n", + " # absolute and specified in the same coordinates as xref.\n", + " axref=\"x domain\",\n", + " # The same is the case for yref and ayref, but here the coordinates are data\n", + " # coordinates\n", + " ayref=\"y\",\n", + " ax=0.5,\n", + " ay=2,\n", + " arrowhead=2,\n", + ")\n", + "\n", + "fig.show()" + ] + }, + { + "cell_type": "markdown", + "id": "d9602587", + "metadata": {}, + "source": [ + "### Specifying Source Lines or Figure Notes on the Bottom of a Figure\n", + "\n", + "This example shows how to add a note about the data source or interpretation at the bottom of the figure. This example aligns the note in the bottom right corner using the title element and container coordinates and then uses an annotation to add a figure title. A near zero container coordinate is an easy and robust way to put text -- such as a source line or figure note -- at the bottom of a figure. It is easier to specify the bottom of the figure in container coordinates than using paper coordinates, since uncertainty about the size of legends and x-axis labels make the paper coordinate of the bottom of the figure uncertain. Making the y container coordinate very slightly positive avoids cutting off the descending strokes of letters like y, p, and q. Only the title command supports container coordinates, so this example re-purposes the title element to insert the note and re-purposes an annotation element for the title. The top of the figure is typically less cluttered and more predictable than the bottom of the figure, so an annotation with its bottom at a paper y-coordinate slightly greater than 1 is a reasonable title location on many graphs." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "18d52c96", + "metadata": { + "lines_to_next_cell": 2 + }, + "outputs": [], + "source": [ + "import plotly.express as px\n", + "df = px.data.iris()\n", + "fig.update_layout(\n", + " title=dict(text=\"Note: this is the Plotly title element.\",\n", + " # keeping this title string short avoids getting the text cut off in small windows\n", + " # if you need longer text, consider 1) embedding your graphic on a web page and\n", + " # putting the note in the HTML to use the browser's automated word wrap,\n", + " # 2) using this approach and also specifying a graph width that shows the whole title,\n", + " # or 3) using
tags to wrap the text onto multiple lines\n", + " yref=\"container\",\n", + " y=0.005,\n", + " # The \"paper\" x-coordinates lets us align this with either the right or left\n", + " # edge of the plot region. \n", + " # The code to align this flush with the right edge of the plot area is \n", + " # predictable and simple. \n", + " # Putting the title in the lower left corner, aligned with the left edge of the axis labeling would\n", + " # require graph specific coordinate adjustments.\n", + " xref=\"paper\",\n", + " xanchor=\"right\",\n", + " x=1, \n", + " font=dict(size=12)),\n", + " plot_bgcolor=\"white\",\n", + "\n", + " # We move the legend out of the right margin so the right-aligned note is \n", + " # flush with the right most element of the graph.\n", + " # Here we put the legend in a corner of the graph region\n", + " # because it has consistent coordinates at all screen resolutions.\n", + " legend=dict(\n", + " yanchor=\"top\",\n", + " y=1,\n", + " xanchor=\"right\",\n", + " x=1,\n", + " borderwidth=1)\n", + " )\n", + "\n", + "# Insert a title by repurposing an annotation \n", + "fig.add_annotation(\n", + " yref=\"paper\",\n", + " yanchor=\"bottom\",\n", + " y=1.025, # y = 1 is the top of the plot area; the top is typically uncluttered, so placing \n", + " # the bottom of the title slightly above the graph region works on a wide variety of graphs\n", + " text=\"This title is a Plotly annotation\",\n", + "\n", + " # Center the title horizontally over the plot area\n", + " xref=\"paper\",\n", + " xanchor=\"center\",\n", + " x=0.5, \n", + "\n", + " showarrow=False,\n", + " font=dict(size=18)\n", + " )\n", + "\n", + "fig.show()" + ] + }, + { + "cell_type": "markdown", + "id": "c49561c2", + "metadata": {}, + "source": [ + "### Customize Displayed Text with a Text Template\n", + "\n", + "To show an arbitrary text in your chart you can use [texttemplate](https://plotly.com/python/reference/pie/#pie-texttemplate), which is a template string used for rendering the information, and will override [textinfo](https://plotly.com/python/reference/treemap/#treemap-textinfo).\n", + "This template string can include `variables` in %{variable} format, `numbers` in [d3-format's syntax](https://github.com/d3/d3-3.x-api-reference/blob/master/Formatting.md#d3_forma), and `date` in [d3-time-format's syntax](https://github.com/d3/d3-time-format).\n", + "`texttemplate` customizes the text that appears on your plot vs. [hovertemplate](https://plotly.com/python/reference/pie/#pie-hovertemplate) that customizes the tooltip text." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "992e7a7d", + "metadata": {}, + "outputs": [], + "source": [ + "import plotly.graph_objects as go\n", + "\n", + "fig = go.Figure(go.Pie(\n", + " values = [40000000, 20000000, 30000000, 10000000],\n", + " labels = [\"Wages\", \"Operating expenses\", \"Cost of sales\", \"Insurance\"],\n", + " texttemplate = \"%{label}: %{value:$,s}
(%{percent})\",\n", + " textposition = \"inside\"))\n", + "\n", + "fig.show()" + ] + }, + { + "cell_type": "markdown", + "id": "ea772050", + "metadata": {}, + "source": [ + "### Customize Text Template\n", + "\n", + "The following example uses [textfont](https://plotly.com/python/reference/scatterternary/#scatterternary-textfont) to customize the added text." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "f8bec4cf", + "metadata": {}, + "outputs": [], + "source": [ + "import plotly.graph_objects as go\n", + "\n", + "fig = go.Figure(go.Scatterternary(\n", + " a = [3, 2, 5],\n", + " b = [2, 5, 2],\n", + " c = [5, 2, 2],\n", + " mode = \"markers+text\",\n", + " text = [\"A\", \"B\", \"C\"],\n", + " texttemplate = \"%{text}
(%{a:.2f}, %{b:.2f}, %{c:.2f})\",\n", + " textposition = \"bottom center\",\n", + " textfont = {'family': \"Times\", 'size': [18, 21, 20], 'color': [\"IndianRed\", \"MediumPurple\", \"DarkOrange\"]}\n", + "))\n", + "\n", + "fig.show()" + ] + }, + { + "cell_type": "markdown", + "id": "6e990967", + "metadata": {}, + "source": [ + "### Set Date in Text Template\n", + "\n", + "The following example shows how to show date by setting [axis.type](https://plotly.com/python/reference/layout/yaxis/#layout-yaxis-type) in [funnel charts](https://plotly.com/python/funnel-charts/).\n", + "As you can see [textinfo](https://plotly.com/python/reference/funnel/#funnel-textinfo) and [texttemplate](https://plotly.com/python/reference/funnel/#funnel-texttemplate) have the same functionality when you want to determine 'just' the trace information on the graph." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "3c630909", + "metadata": {}, + "outputs": [], + "source": [ + "from plotly import graph_objects as go\n", + "\n", + "fig = go.Figure()\n", + "\n", + "fig.add_trace(go.Funnel(\n", + " name = 'Montreal',\n", + " orientation = \"h\",\n", + " y = [\"2018-01-01\", \"2018-07-01\", \"2019-01-01\", \"2020-01-01\"],\n", + " x = [100, 60, 40, 20],\n", + " textposition = \"inside\",\n", + " texttemplate = \"%{y| %a. %_d %b %Y}\"))\n", + "\n", + "fig.add_trace(go.Funnel(\n", + " name = 'Vancouver',\n", + " orientation = \"h\",\n", + " y = [\"2018-01-01\", \"2018-07-01\", \"2019-01-01\", \"2020-01-01\"],\n", + " x = [90, 70, 50, 10],\n", + " textposition = \"inside\",\n", + " textinfo = \"label\"))\n", + "\n", + "fig.update_layout(yaxis = {'type': 'date'})\n", + "\n", + "fig.show()" + ] + }, + { + "cell_type": "markdown", + "id": "8b4f9c58", + "metadata": {}, + "source": [ + "#### Reference\n", + "\n", + "See https://plotly.com/python/reference/layout/annotations/ for more information and chart attribute options!\n" + ] + }, + { + "cell_type": "markdown", + "id": "e58ef368", + "metadata": {}, + "source": [ + "### What About Dash?\n", + "\n", + "[Dash](https://dash.plot.ly/) is an open-source framework for building analytical applications, with no Javascript required, and it is tightly integrated with the Plotly graphing library.\n", + "\n", + "Learn about how to install Dash at https://dash.plot.ly/installation.\n", + "\n", + "Everywhere in this page that you see `fig.show()`, you can display the same figure in a Dash application by passing it to the `figure` argument of the [`Graph` component](https://dash.plot.ly/dash-core-components/graph) from the built-in `dash_core_components` package like this:\n", + "\n", + "```python\n", + "import plotly.graph_objects as go # or plotly.express as px\n", + "fig = go.Figure() # or any Plotly Express function e.g. px.bar(...)\n", + "# fig.add_trace( ... )\n", + "# fig.update_layout( ... )\n", + "\n", + "from dash import Dash, dcc, html\n", + "\n", + "app = Dash()\n", + "app.layout = html.Div([\n", + " dcc.Graph(figure=fig)\n", + "])\n", + "\n", + "app.run_server(debug=True, use_reloader=False) # Turn off reloader if inside Jupyter\n", + "```" + ] + } + ], + "metadata": { + "jupytext": { + "notebook_metadata_filter": "all" + }, + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "language": "python", + "name": "python3" + }, + "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.14" + }, + "plotly": { + "description": "How to add text labels and annotations to plots in python.", + "display_as": "file_settings", + "language": "python", + "layout": "base", + "name": "Text and Annotations", + "order": 22, + "permalink": "python/text-and-annotations/", + "thumbnail": "thumbnail/text-and-annotations.png" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/tick-formatting.ipynb b/tick-formatting.ipynb new file mode 100644 index 000000000..0ac0b6085 --- /dev/null +++ b/tick-formatting.ipynb @@ -0,0 +1,377 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "7fd96e36", + "metadata": {}, + "source": [ + "#### Tickmode - Linear" + ] + }, + { + "cell_type": "markdown", + "id": "34b08663", + "metadata": {}, + "source": [ + "If `\"linear\"`, the placement of the ticks is determined by a starting position `tick0` and a tick step `dtick`" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "f5dcfc33", + "metadata": {}, + "outputs": [], + "source": [ + "import plotly.graph_objects as go\n", + "\n", + "fig = go.Figure(go.Scatter(\n", + " x = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12],\n", + " y = [28.8, 28.5, 37, 56.8, 69.7, 79.7, 78.5, 77.8, 74.1, 62.6, 45.3, 39.9]\n", + "))\n", + "\n", + "fig.update_layout(\n", + " xaxis = dict(\n", + " tickmode = 'linear',\n", + " tick0 = 0.5,\n", + " dtick = 0.75\n", + " )\n", + ")\n", + "\n", + "fig.show()" + ] + }, + { + "cell_type": "markdown", + "id": "0d32a706", + "metadata": {}, + "source": [ + "#### Tickmode - Array" + ] + }, + { + "cell_type": "markdown", + "id": "ced4c1d9", + "metadata": {}, + "source": [ + "If `\"array\"`, the placement of the ticks is set via `tickvals` and the tick text is `ticktext`." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "d4cd8662", + "metadata": {}, + "outputs": [], + "source": [ + "import plotly.graph_objects as go\n", + "\n", + "fig = go.Figure(go.Scatter(\n", + " x = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12],\n", + " y = [28.8, 28.5, 37, 56.8, 69.7, 79.7, 78.5, 77.8, 74.1, 62.6, 45.3, 39.9]\n", + "))\n", + "\n", + "fig.update_layout(\n", + " xaxis = dict(\n", + " tickmode = 'array',\n", + " tickvals = [1, 3, 5, 7, 9, 11],\n", + " ticktext = ['One', 'Three', 'Five', 'Seven', 'Nine', 'Eleven']\n", + " )\n", + ")\n", + "\n", + "fig.show()" + ] + }, + { + "cell_type": "markdown", + "id": "436fe915", + "metadata": {}, + "source": [ + "### Dynamic tickmode in Dash\n", + "\n", + "[Dash](https://plotly.com/dash/) is the best way to build analytical apps in Python using Plotly figures. To run the app below, run `pip install dash`, click \"Download\" to get the code and run `python app.py`.\n", + "\n", + "Get started with [the official Dash docs](https://dash.plotly.com/installation) and **learn how to effortlessly [style](https://plotly.com/dash/design-kit/) & [deploy](https://plotly.com/dash/app-manager/) apps like this with Dash Enterprise.**\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "593aef2c", + "metadata": { + "hide_code": true + }, + "outputs": [], + "source": [ + "from IPython.display import IFrame\n", + "snippet_url = 'https://python-docs-dash-snippets.herokuapp.com/python-docs-dash-snippets/'\n", + "IFrame(snippet_url + 'tick-formatting', width='100%', height=1200)" + ] + }, + { + "cell_type": "markdown", + "id": "60c7b2e5", + "metadata": {}, + "source": [ + "

Sign up for Dash Club → Free cheat sheets plus updates from Chris Parmer and Adam Schroeder delivered to your inbox every two months. Includes tips and tricks, community apps, and deep dives into the Dash architecture.\n", + "Join now.

" + ] + }, + { + "cell_type": "markdown", + "id": "d6217c35", + "metadata": {}, + "source": [ + "#### Using Tickformat Attribute" + ] + }, + { + "cell_type": "markdown", + "id": "32a7e027", + "metadata": {}, + "source": [ + "For more formatting types, see: https://github.com/d3/d3-format/blob/master/README.md#locale_format" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "eafc7ffe", + "metadata": {}, + "outputs": [], + "source": [ + "import plotly.graph_objects as go\n", + "\n", + "fig = go.Figure(go.Scatter(\n", + " x = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12],\n", + " y = [28.8, 28.5, 37, 56.8, 69.7, 79.7, 78.5, 77.8, 74.1, 62.6, 45.3, 39.9]\n", + "))\n", + "\n", + "fig.update_layout(yaxis_tickformat = '%')\n", + "\n", + "fig.show()" + ] + }, + { + "cell_type": "markdown", + "id": "ddc88491", + "metadata": {}, + "source": [ + "#### Using Tickformat Attribute - Date/Time" + ] + }, + { + "cell_type": "markdown", + "id": "12729aa2", + "metadata": {}, + "source": [ + "For more date/time formatting types, see: https://github.com/d3/d3-time-format/blob/master/README.md" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "88aa301d", + "metadata": {}, + "outputs": [], + "source": [ + "import plotly.graph_objects as go\n", + "\n", + "import pandas as pd\n", + "\n", + "df = pd.read_csv('https://raw.githubusercontent.com/plotly/datasets/master/finance-charts-apple.csv')\n", + "\n", + "fig = go.Figure(go.Scatter(\n", + " x = df['Date'],\n", + " y = df['AAPL.High'],\n", + "))\n", + "\n", + "fig.update_layout(\n", + " title = 'Time Series with Custom Date-Time Format',\n", + " xaxis_tickformat = '%d %B (%a)
%Y'\n", + ")\n", + "\n", + "fig.show()" + ] + }, + { + "cell_type": "markdown", + "id": "b2070c8f", + "metadata": {}, + "source": [ + "#### Using Exponentformat Attribute" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "33ecbb49", + "metadata": {}, + "outputs": [], + "source": [ + "import plotly.graph_objects as go\n", + "\n", + "fig = go.Figure(go.Scatter(\n", + " x = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12],\n", + " y = [68000, 52000, 60000, 20000, 95000, 40000, 60000, 79000, 74000, 42000, 20000, 90000]\n", + "))\n", + "\n", + "fig.update_layout(\n", + " yaxis = dict(\n", + " showexponent = 'all',\n", + " exponentformat = 'e'\n", + " )\n", + ")\n", + "\n", + "fig.show()" + ] + }, + { + "cell_type": "markdown", + "id": "97354f26", + "metadata": {}, + "source": [ + "#### Tickformatstops to customize for different zoom levels" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "b276b23d", + "metadata": {}, + "outputs": [], + "source": [ + "import plotly.graph_objects as go\n", + "\n", + "import pandas as pd\n", + "\n", + "df = pd.read_csv('https://raw.githubusercontent.com/plotly/datasets/master/finance-charts-apple.csv')\n", + "\n", + "fig = go.Figure(go.Scatter(\n", + " x = df['Date'],\n", + " y = df['mavg']\n", + "))\n", + "\n", + "fig.update_layout(\n", + " xaxis_tickformatstops = [\n", + " dict(dtickrange=[None, 1000], value=\"%H:%M:%S.%L ms\"),\n", + " dict(dtickrange=[1000, 60000], value=\"%H:%M:%S s\"),\n", + " dict(dtickrange=[60000, 3600000], value=\"%H:%M m\"),\n", + " dict(dtickrange=[3600000, 86400000], value=\"%H:%M h\"),\n", + " dict(dtickrange=[86400000, 604800000], value=\"%e. %b d\"),\n", + " dict(dtickrange=[604800000, \"M1\"], value=\"%e. %b w\"),\n", + " dict(dtickrange=[\"M1\", \"M12\"], value=\"%b '%y M\"),\n", + " dict(dtickrange=[\"M12\", None], value=\"%Y Y\")\n", + " ]\n", + ")\n", + "\n", + "fig.show()" + ] + }, + { + "cell_type": "markdown", + "id": "161db3a4", + "metadata": {}, + "source": [ + "#### Placing ticks and gridlines between categories" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "4102167e", + "metadata": {}, + "outputs": [], + "source": [ + "import plotly.graph_objects as go\n", + "\n", + "fig = go.Figure(go.Bar(\n", + " x = [\"apples\", \"oranges\", \"pears\"],\n", + " y = [1, 2, 3]\n", + "))\n", + "\n", + "fig.update_xaxes(\n", + " showgrid=True,\n", + " ticks=\"outside\",\n", + " tickson=\"boundaries\",\n", + " ticklen=20\n", + ")\n", + "\n", + "fig.show()" + ] + }, + { + "cell_type": "markdown", + "id": "30de30d6", + "metadata": {}, + "source": [ + "#### Reference\n", + "See https://plotly.com/python/reference/layout/xaxis/ for more information and chart attribute options!\n" + ] + }, + { + "cell_type": "markdown", + "id": "705d3cc2", + "metadata": {}, + "source": [ + "### What About Dash?\n", + "\n", + "[Dash](https://dash.plot.ly/) is an open-source framework for building analytical applications, with no Javascript required, and it is tightly integrated with the Plotly graphing library.\n", + "\n", + "Learn about how to install Dash at https://dash.plot.ly/installation.\n", + "\n", + "Everywhere in this page that you see `fig.show()`, you can display the same figure in a Dash application by passing it to the `figure` argument of the [`Graph` component](https://dash.plot.ly/dash-core-components/graph) from the built-in `dash_core_components` package like this:\n", + "\n", + "```python\n", + "import plotly.graph_objects as go # or plotly.express as px\n", + "fig = go.Figure() # or any Plotly Express function e.g. px.bar(...)\n", + "# fig.add_trace( ... )\n", + "# fig.update_layout( ... )\n", + "\n", + "from dash import Dash, dcc, html\n", + "\n", + "app = Dash()\n", + "app.layout = html.Div([\n", + " dcc.Graph(figure=fig)\n", + "])\n", + "\n", + "app.run_server(debug=True, use_reloader=False) # Turn off reloader if inside Jupyter\n", + "```" + ] + } + ], + "metadata": { + "jupytext": { + "notebook_metadata_filter": "all" + }, + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "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.8.8" + }, + "plotly": { + "description": "How to format axes ticks in Python with Plotly.", + "display_as": "file_settings", + "language": "python", + "layout": "base", + "name": "Formatting Ticks", + "order": 12, + "permalink": "python/tick-formatting/", + "thumbnail": "thumbnail/tick-formatting.gif" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/tile-county-choropleth.ipynb b/tile-county-choropleth.ipynb new file mode 100644 index 000000000..80b4fb011 --- /dev/null +++ b/tile-county-choropleth.ipynb @@ -0,0 +1,424 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "5984d041", + "metadata": {}, + "source": [ + "A [Choropleth Map](https://en.wikipedia.org/wiki/Choropleth_map) is a map composed of colored polygons. It is used to represent spatial variations of a quantity. This page documents how to build **tile-map** choropleth maps, but you can also build [**outline** choropleth maps](/python/choropleth-maps).\n", + "\n", + "Below we show how to create Choropleth Maps using either Plotly Express' `px.choropleth_map` function or the lower-level `go.Choroplethmap` graph object.\n", + "\n", + "### Introduction: main parameters for choropleth tile maps\n", + "\n", + "Making choropleth maps requires two main types of input:\n", + "\n", + "1. GeoJSON-formatted geometry information where each feature has either an `id` field or some identifying value in `properties`.\n", + "2. A list of values indexed by feature identifier.\n", + "\n", + "The GeoJSON data is passed to the `geojson` argument, and the data is passed into the `color` argument of `px.choropleth_map` (`z` if using `graph_objects`), in the same order as the IDs are passed into the `location` argument.\n", + "\n", + "**Note** the `geojson` attribute can also be the URL to a GeoJSON file, which can speed up map rendering in certain cases.\n", + "\n", + "#### GeoJSON with `feature.id`\n", + "\n", + "Here we load a GeoJSON file containing the geometry information for US counties, where `feature.id` is a [FIPS code](https://en.wikipedia.org/wiki/FIPS_county_code)." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "244b5732", + "metadata": {}, + "outputs": [], + "source": [ + "from urllib.request import urlopen\n", + "import json\n", + "with urlopen('https://raw.githubusercontent.com/plotly/datasets/master/geojson-counties-fips.json') as response:\n", + " counties = json.load(response)\n", + "\n", + "counties[\"features\"][0]" + ] + }, + { + "cell_type": "markdown", + "id": "aa8fc406", + "metadata": {}, + "source": [ + "#### Data indexed by `id`\n", + "\n", + "Here we load unemployment data by county, also indexed by [FIPS code](https://en.wikipedia.org/wiki/FIPS_county_code)." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "a8ab0fd8", + "metadata": {}, + "outputs": [], + "source": [ + "import pandas as pd\n", + "df = pd.read_csv(\"https://raw.githubusercontent.com/plotly/datasets/master/fips-unemp-16.csv\",\n", + " dtype={\"fips\": str})\n", + "df.head()" + ] + }, + { + "cell_type": "markdown", + "id": "9b66dd6d", + "metadata": {}, + "source": [ + "### Choropleth map using plotly.express and carto base map\n", + "\n", + "[Plotly Express](/python/plotly-express/) is the easy-to-use, high-level interface to Plotly, which [operates on a variety of types of data](/python/px-arguments/) and produces [easy-to-style figures](/python/styling-plotly-express/).\n", + "\n", + "With `px.choropleth_map`, each row of the DataFrame is represented as a region of the choropleth." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "7db49f1a", + "metadata": {}, + "outputs": [], + "source": [ + "from urllib.request import urlopen\n", + "import json\n", + "with urlopen('https://raw.githubusercontent.com/plotly/datasets/master/geojson-counties-fips.json') as response:\n", + " counties = json.load(response)\n", + "\n", + "import pandas as pd\n", + "df = pd.read_csv(\"https://raw.githubusercontent.com/plotly/datasets/master/fips-unemp-16.csv\",\n", + " dtype={\"fips\": str})\n", + "\n", + "import plotly.express as px\n", + "\n", + "fig = px.choropleth_map(df, geojson=counties, locations='fips', color='unemp',\n", + " color_continuous_scale=\"Viridis\",\n", + " range_color=(0, 12),\n", + " map_style=\"carto-positron\",\n", + " zoom=3, center = {\"lat\": 37.0902, \"lon\": -95.7129},\n", + " opacity=0.5,\n", + " labels={'unemp':'unemployment rate'}\n", + " )\n", + "fig.update_layout(margin={\"r\":0,\"t\":0,\"l\":0,\"b\":0})\n", + "fig.show()" + ] + }, + { + "cell_type": "markdown", + "id": "8ae07900", + "metadata": {}, + "source": [ + "### Choropleth maps in Dash\n", + "\n", + "[Dash](https://plotly.com/dash/) is the best way to build analytical apps in Python using Plotly figures. To run the app below, run `pip install dash`, click \"Download\" to get the code and run `python app.py`.\n", + "\n", + "Get started with [the official Dash docs](https://dash.plotly.com/installation) and **learn how to effortlessly [style](https://plotly.com/dash/design-kit/) & [deploy](https://plotly.com/dash/app-manager/) apps like this with Dash Enterprise.**\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "53ba5b5d", + "metadata": { + "hide_code": true + }, + "outputs": [], + "source": [ + "from IPython.display import IFrame\n", + "snippet_url = 'https://python-docs-dash-snippets.herokuapp.com/python-docs-dash-snippets/'\n", + "IFrame(snippet_url + 'mapbox-county-choropleth', width='100%', height=1200)" + ] + }, + { + "cell_type": "markdown", + "id": "8b34ae81", + "metadata": {}, + "source": [ + "

Sign up for Dash Club → Free cheat sheets plus updates from Chris Parmer and Adam Schroeder delivered to your inbox every two months. Includes tips and tricks, community apps, and deep dives into the Dash architecture.\n", + "Join now.

" + ] + }, + { + "cell_type": "markdown", + "id": "f33929e5", + "metadata": {}, + "source": [ + "### Indexing by GeoJSON Properties\n", + "\n", + "If the GeoJSON you are using either does not have an `id` field or you wish you use one of the keys in the `properties` field, you may use the `featureidkey` parameter to specify where to match the values of `locations`.\n", + "\n", + "In the following GeoJSON object/data-file pairing, the values of `properties.district` match the values of the `district` column:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "bae6a1b4", + "metadata": {}, + "outputs": [], + "source": [ + "import plotly.express as px\n", + "\n", + "df = px.data.election()\n", + "geojson = px.data.election_geojson()\n", + "\n", + "print(df[\"district\"][2])\n", + "print(geojson[\"features\"][0][\"properties\"])" + ] + }, + { + "cell_type": "markdown", + "id": "60d03aab", + "metadata": {}, + "source": [ + "To use them together, we set `locations` to `district` and `featureidkey` to `\"properties.district\"`. The `color` is set to the number of votes by the candidate named Bergeron." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "a287f5a1", + "metadata": {}, + "outputs": [], + "source": [ + "import plotly.express as px\n", + "\n", + "df = px.data.election()\n", + "geojson = px.data.election_geojson()\n", + "\n", + "fig = px.choropleth_map(df, geojson=geojson, color=\"Bergeron\",\n", + " locations=\"district\", featureidkey=\"properties.district\",\n", + " center={\"lat\": 45.5517, \"lon\": -73.7073},\n", + " map_style=\"carto-positron\", zoom=9)\n", + "fig.update_layout(margin={\"r\":0,\"t\":0,\"l\":0,\"b\":0})\n", + "fig.show()" + ] + }, + { + "cell_type": "markdown", + "id": "4408096b", + "metadata": {}, + "source": [ + "### Discrete Colors\n", + "\n", + "In addition to [continuous colors](/python/colorscales/), we can [discretely-color](/python/discrete-color/) our choropleth maps by setting `color` to a non-numerical column, like the name of the winner of an election." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "0a29bf6f", + "metadata": {}, + "outputs": [], + "source": [ + "import plotly.express as px\n", + "\n", + "df = px.data.election()\n", + "geojson = px.data.election_geojson()\n", + "\n", + "fig = px.choropleth_map(df, geojson=geojson, color=\"winner\",\n", + " locations=\"district\", featureidkey=\"properties.district\",\n", + " center={\"lat\": 45.5517, \"lon\": -73.7073},\n", + " map_style=\"carto-positron\", zoom=9)\n", + "fig.update_layout(margin={\"r\":0,\"t\":0,\"l\":0,\"b\":0})\n", + "fig.show()" + ] + }, + { + "cell_type": "markdown", + "id": "d9179225", + "metadata": {}, + "source": [ + "### Using GeoPandas Data Frames\n", + "\n", + "`px.choropleth_map` accepts the `geometry` of a [GeoPandas](https://geopandas.org/) data frame as the input to `geojson` if the `geometry` contains polygons." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "40ce96a3", + "metadata": {}, + "outputs": [], + "source": [ + "import plotly.express as px\n", + "import geopandas as gpd\n", + "\n", + "df = px.data.election()\n", + "geo_df = gpd.GeoDataFrame.from_features(\n", + " px.data.election_geojson()[\"features\"]\n", + ").merge(df, on=\"district\").set_index(\"district\")\n", + "\n", + "fig = px.choropleth_map(geo_df,\n", + " geojson=geo_df.geometry,\n", + " locations=geo_df.index,\n", + " color=\"Joly\",\n", + " center={\"lat\": 45.5517, \"lon\": -73.7073},\n", + " map_style=\"open-street-map\",\n", + " zoom=8.5)\n", + "fig.show()" + ] + }, + { + "cell_type": "markdown", + "id": "aa408ad5", + "metadata": {}, + "source": [ + "### Choropleth map using plotly.graph_objects and carto base map\n", + "\n", + "If Plotly Express does not provide a good starting point, it is also possible to use [the more generic `go.Choroplethmap` class from `plotly.graph_objects`](/python/graph-objects/)." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "fce5d863", + "metadata": {}, + "outputs": [], + "source": [ + "from urllib.request import urlopen\n", + "import json\n", + "with urlopen('https://raw.githubusercontent.com/plotly/datasets/master/geojson-counties-fips.json') as response:\n", + " counties = json.load(response)\n", + "\n", + "import pandas as pd\n", + "df = pd.read_csv(\"https://raw.githubusercontent.com/plotly/datasets/master/fips-unemp-16.csv\",\n", + " dtype={\"fips\": str})\n", + "\n", + "import plotly.graph_objects as go\n", + "\n", + "fig = go.Figure(go.Choroplethmap(geojson=counties, locations=df.fips, z=df.unemp,\n", + " colorscale=\"Viridis\", zmin=0, zmax=12,\n", + " marker_opacity=0.5, marker_line_width=0))\n", + "fig.update_layout(map_style=\"carto-positron\",\n", + " map_zoom=3, map_center = {\"lat\": 37.0902, \"lon\": -95.7129})\n", + "fig.update_layout(margin={\"r\":0,\"t\":0,\"l\":0,\"b\":0})\n", + "fig.show()" + ] + }, + { + "cell_type": "markdown", + "id": "720d8ac9", + "metadata": {}, + "source": [ + "### Mapbox Maps\n", + "\n", + "> Mapbox traces are deprecated and may be removed in a future version of Plotly.py.\n", + "\n", + "The earlier examples using `px.choropleth_map` and `go.Choroplethmap` use [Maplibre](https://maplibre.org/maplibre-gl-js/docs/) for rendering. These traces were introduced in Plotly.py 5.24 and are now the recommended way to create tile-based choropleth maps. There are also choropleth traces that use [Mapbox](https://docs.mapbox.com): `px.choropleth_mapbox` and `go.Choroplethmapbox`\n", + "\n", + "To plot on Mapbox maps with Plotly you _may_ need a Mapbox account and a public [Mapbox Access Token](https://www.mapbox.com/studio). See our [Mapbox Map Layers](/python/mapbox-layers/) documentation for more information.\n", + "\n", + "Here's an exmaple of using the Mapbox Light base map, which requires a free token." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "a5163a52", + "metadata": {}, + "outputs": [], + "source": [ + "token = open(\".mapbox_token\").read() # you will need your own token\n", + "\n", + "from urllib.request import urlopen\n", + "import json\n", + "with urlopen('https://raw.githubusercontent.com/plotly/datasets/master/geojson-counties-fips.json') as response:\n", + " counties = json.load(response)\n", + "\n", + "import pandas as pd\n", + "df = pd.read_csv(\"https://raw.githubusercontent.com/plotly/datasets/master/fips-unemp-16.csv\",\n", + " dtype={\"fips\": str})\n", + "\n", + "import plotly.graph_objects as go\n", + "\n", + "fig = go.Figure(go.Choroplethmapbox(geojson=counties, locations=df.fips, z=df.unemp,\n", + " colorscale=\"Viridis\", zmin=0, zmax=12, marker_line_width=0))\n", + "fig.update_layout(mapbox_style=\"light\", mapbox_accesstoken=token,\n", + " mapbox_zoom=3, mapbox_center = {\"lat\": 37.0902, \"lon\": -95.7129})\n", + "fig.update_layout(margin={\"r\":0,\"t\":0,\"l\":0,\"b\":0})\n", + "fig.show()" + ] + }, + { + "cell_type": "markdown", + "id": "2693ab0a", + "metadata": {}, + "source": [ + "#### Reference\n", + "\n", + "See [function reference for `px.choropleth_map`](https://plotly.com/python-api-reference/generated/plotly.express.choropleth_map) or https://plotly.com/python/reference/choroplethmap/ for more information about the attributes available.\n", + "\n", + "For Mapbox-based tile maps, see [function reference for `px.choropleth_mapbox`](https://plotly.com/python-api-reference/generated/plotly.express.choropleth_mapbox) or https://plotly.com/python/reference/choroplethmapbox/.\n" + ] + }, + { + "cell_type": "markdown", + "id": "1a6d4d5b", + "metadata": {}, + "source": [ + "### What About Dash?\n", + "\n", + "[Dash](https://dash.plot.ly/) is an open-source framework for building analytical applications, with no Javascript required, and it is tightly integrated with the Plotly graphing library.\n", + "\n", + "Learn about how to install Dash at https://dash.plot.ly/installation.\n", + "\n", + "Everywhere in this page that you see `fig.show()`, you can display the same figure in a Dash application by passing it to the `figure` argument of the [`Graph` component](https://dash.plot.ly/dash-core-components/graph) from the built-in `dash_core_components` package like this:\n", + "\n", + "```python\n", + "import plotly.graph_objects as go # or plotly.express as px\n", + "fig = go.Figure() # or any Plotly Express function e.g. px.bar(...)\n", + "# fig.add_trace( ... )\n", + "# fig.update_layout( ... )\n", + "\n", + "from dash import Dash, dcc, html\n", + "\n", + "app = Dash()\n", + "app.layout = html.Div([\n", + " dcc.Graph(figure=fig)\n", + "])\n", + "\n", + "app.run_server(debug=True, use_reloader=False) # Turn off reloader if inside Jupyter\n", + "```" + ] + } + ], + "metadata": { + "jupytext": { + "notebook_metadata_filter": "all" + }, + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "language": "python", + "name": "python3" + }, + "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" + }, + "plotly": { + "description": "How to make a choropleth map of US counties in Python with Plotly.", + "display_as": "maps", + "language": "python", + "layout": "base", + "name": "Tile Choropleth Maps", + "order": 2, + "page_type": "example_index", + "permalink": "python/tile-county-choropleth/", + "redirect_from": "python/mapbox-county-choropleth/", + "thumbnail": "thumbnail/mapbox-choropleth.png" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/tile-map-layers.ipynb b/tile-map-layers.ipynb new file mode 100644 index 000000000..5f117513b --- /dev/null +++ b/tile-map-layers.ipynb @@ -0,0 +1,496 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "35221fc5", + "metadata": {}, + "source": [ + "\n", + "## Tile Maps vs Outline Maps\n", + "\n", + "Plotly supports two different kinds of maps:\n", + "\n", + "- **[Tile-based maps](https://en.wikipedia.org/wiki/Tiled_web_map)**\n", + "\n", + "If your figure is created with a `px.scatter_map`, `px_scatter_mapbox`, `px.line_map`, `px.line_mapbox`, `px.choropleth_map`, `px.choropleth_mapbox`, `px.density_map`, or `px.density_mapbox` function or otherwise contains one or more traces of type `go.Scattermap`, `go.Scattermapbox`, `go.Choroplethmap`, `go.Choroplethmapbox`, `go.Densitymap`, or `go.Densitymapbox`, the `layout.map` or `layout.mapbox` object in your figure contains configuration information for the map itself.\n", + "\n", + "- **Outline-based maps**\n", + "\n", + "Geo maps are outline-based maps. If your figure is created with a `px.scatter_geo`, `px.line_geo` or `px.choropleth` function or otherwise contains one or more traces of type `go.Scattergeo` or `go.Choropleth`, the `layout.geo` object in your figure contains configuration information for the map itself.\n", + "\n", + "> This page documents tile-based maps, and the [Geo map documentation](/python/map-configuration/) describes how to configure outline-based maps.\n", + "\n", + "## Tile Map Renderers\n", + "\n", + "Tile-based traces in Plotly use Maplibre or Mapbox.\n", + "\n", + "Maplibre-based traces (new in 5.24) are ones generated in Plotly Express using `px.scatter_map`, `px.line_map`, `px.choropleth_map`, `px.density_map`, or Graph Objects using `go.Scattermap`, `go.Choroplethmap`, or `go.Densitymap`.\n", + "\n", + "Mapbox-based traces are suffixed with `mapbox`, for example `go.Scattermapbox`. These are deprecated as of version 5.24 and we recommend using the Maplibre-based traces.\n", + "\n", + "### Maplibre\n", + "\n", + "*New in 5.24*\n", + "\n", + "Maplibre-based tile maps have three different types of layers:\n", + "\n", + "- `layout.map.style` defines the lowest layers of the map, also known as the \"base map\".\n", + "- The various traces in `data` are by default rendered above the base map (although this can be controlled via the `below` attribute).\n", + "- `layout.map.layers` is an array that defines more layers that are by default rendered above the traces in `data` (although this can also be controlled via the `below` attribute.\n", + "\n", + "\n", + "#### Base Maps in `layout.map.style`.\n", + "\n", + "The accepted values for `layout.map.style` are one of:\n", + "\n", + "- \"basic\"\n", + "- \"carto-darkmatter\"\n", + "- \"carto-darkmatter-nolabels\"\n", + "- \"carto-positron\"\n", + "- \"carto-positron-nolabels\"\n", + "- \"carto-voyager\"\n", + "- \"carto-voyager-nolabels\"\n", + "- \"dark\"\n", + "- \"light\"\n", + "- \"open-street-map\"\n", + "- \"outdoors\"\n", + "- \"satellite\"\n", + "- \"satellite-streets\"\n", + "- \"streets\"\n", + "- \"white-bg\" - an empty white canvas which results in no external HTTP requests\n", + "\n", + "- A custom style URL. For example: https://tiles.stadiamaps.com/styles/stamen_watercolor.json?api_key=YOUR-API-KEY\n", + "\n", + "- A Map Style object as defined at https://maplibre.org/maplibre-style-spec/\n", + "\n", + "\n", + "#### OpenStreetMap tiles\n", + "\n", + "Here is a simple map rendered with OpenStreetMaps tiles." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "166ba979", + "metadata": {}, + "outputs": [], + "source": [ + "import pandas as pd\n", + "us_cities = pd.read_csv(\"https://raw.githubusercontent.com/plotly/datasets/master/us-cities-top-1k.csv\")\n", + "\n", + "import plotly.express as px\n", + "\n", + "fig = px.scatter_map(us_cities, lat=\"lat\", lon=\"lon\", hover_name=\"City\", hover_data=[\"State\", \"Population\"],\n", + " color_discrete_sequence=[\"fuchsia\"], zoom=3, height=300)\n", + "fig.update_layout(map_style=\"open-street-map\")\n", + "fig.update_layout(margin={\"r\":0,\"t\":0,\"l\":0,\"b\":0})\n", + "fig.show()" + ] + }, + { + "cell_type": "markdown", + "id": "7477d581", + "metadata": {}, + "source": [ + "#### Using `layout.map.layers` to Specify a Base Map\n", + "\n", + "If you have access to your own private tile servers, or wish to use a tile server not included in the list above, the recommended approach is to set `layout.map.style` to `\"white-bg\"` and to use `layout.map.layers` with `below` to specify a custom base map.\n", + "\n", + "> If you omit the `below` attribute when using this approach, your data will likely be hidden by fully-opaque raster tiles!\n", + "\n", + "#### Base Tiles from the USGS: no token needed\n", + "\n", + "Here is an example of a map which uses a public USGS imagery map, specified in `layout.map.layers`, and which is rendered _below_ the `data` layer." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "5cfd71e1", + "metadata": { + "lines_to_next_cell": 2 + }, + "outputs": [], + "source": [ + "import pandas as pd\n", + "us_cities = pd.read_csv(\"https://raw.githubusercontent.com/plotly/datasets/master/us-cities-top-1k.csv\")\n", + "\n", + "import plotly.express as px\n", + "\n", + "fig = px.scatter_map(us_cities, lat=\"lat\", lon=\"lon\", hover_name=\"City\", hover_data=[\"State\", \"Population\"],\n", + " color_discrete_sequence=[\"fuchsia\"], zoom=3, height=300)\n", + "fig.update_layout(\n", + " map_style=\"white-bg\",\n", + " map_layers=[\n", + " {\n", + " \"below\": 'traces',\n", + " \"sourcetype\": \"raster\",\n", + " \"sourceattribution\": \"United States Geological Survey\",\n", + " \"source\": [\n", + " \"https://basemap.nationalmap.gov/arcgis/rest/services/USGSImageryOnly/MapServer/tile/{z}/{y}/{x}\"\n", + " ]\n", + " }\n", + " ])\n", + "fig.update_layout(margin={\"r\":0,\"t\":0,\"l\":0,\"b\":0})\n", + "fig.show()" + ] + }, + { + "cell_type": "markdown", + "id": "e929cd07", + "metadata": {}, + "source": [ + "#### Base Tiles from the USGS, radar overlay from Environment Canada\n", + "\n", + "Here is the same example, with in addition, a WMS layer from Environment Canada which displays near-real-time radar imagery in partly-transparent raster tiles, rendered above the `go.Scattermap` trace, as is the default:\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "ea72e7c8", + "metadata": { + "lines_to_next_cell": 2 + }, + "outputs": [], + "source": [ + "import pandas as pd\n", + "us_cities = pd.read_csv(\"https://raw.githubusercontent.com/plotly/datasets/master/us-cities-top-1k.csv\")\n", + "\n", + "import plotly.express as px\n", + "\n", + "fig = px.scatter_map(us_cities, lat=\"lat\", lon=\"lon\", hover_name=\"City\", hover_data=[\"State\", \"Population\"],\n", + " color_discrete_sequence=[\"fuchsia\"], zoom=3, height=300)\n", + "fig.update_layout(\n", + " map_style=\"white-bg\",\n", + " map_layers=[\n", + " {\n", + " \"below\": 'traces',\n", + " \"sourcetype\": \"raster\",\n", + " \"sourceattribution\": \"United States Geological Survey\",\n", + " \"source\": [\n", + " \"https://basemap.nationalmap.gov/arcgis/rest/services/USGSImageryOnly/MapServer/tile/{z}/{y}/{x}\"\n", + " ]\n", + " },\n", + " {\n", + " \"sourcetype\": \"raster\",\n", + " \"sourceattribution\": \"Government of Canada\",\n", + " \"source\": [\"https://geo.weather.gc.ca/geomet/?\"\n", + " \"SERVICE=WMS&VERSION=1.3.0&REQUEST=GetMap&BBOX={bbox-epsg-3857}&CRS=EPSG:3857\"\n", + " \"&WIDTH=1000&HEIGHT=1000&LAYERS=RADAR_1KM_RDBR&TILED=true&FORMAT=image/png\"],\n", + " }\n", + " ])\n", + "fig.update_layout(margin={\"r\":0,\"t\":0,\"l\":0,\"b\":0})\n", + "fig.show()" + ] + }, + { + "cell_type": "markdown", + "id": "4c159782", + "metadata": {}, + "source": [ + "#### Dark tiles example\n", + "\n", + "Here is a map rendered with the `\"dark\"` style." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "46189e77", + "metadata": {}, + "outputs": [], + "source": [ + "import pandas as pd\n", + "us_cities = pd.read_csv(\"https://raw.githubusercontent.com/plotly/datasets/master/us-cities-top-1k.csv\")\n", + "\n", + "import plotly.express as px\n", + "\n", + "fig = px.scatter_map(us_cities, lat=\"lat\", lon=\"lon\", hover_name=\"City\", hover_data=[\"State\", \"Population\"],\n", + " color_discrete_sequence=[\"fuchsia\"], zoom=3, height=300)\n", + "fig.update_layout(map_style=\"dark\")\n", + "fig.update_layout(margin={\"r\":0,\"t\":0,\"l\":0,\"b\":0})\n", + "fig.show()" + ] + }, + { + "cell_type": "markdown", + "id": "fc070fdd", + "metadata": {}, + "source": [ + "#### Stamen Watercolor using a Custom Style URL\n", + "\n", + "Here's an example of using a custom style URL that points to the [Stadia Maps](https://docs.stadiamaps.com/map-styles/stamen-watercolor) service to use the `stamen_watercolor` base map.\n", + "\n", + "```python\n", + "import pandas as pd\n", + "quakes = pd.read_csv('https://raw.githubusercontent.com/plotly/datasets/master/earthquakes-23k.csv')\n", + "\n", + "import plotly.graph_objects as go\n", + "fig = go.Figure(go.Densitymap(lat=quakes.Latitude, lon=quakes.Longitude, z=quakes.Magnitude,\n", + " radius=10))\n", + "fig.update_layout(map_style=\"https://tiles.stadiamaps.com/styles/stamen_watercolor.json?api_key=YOUR-API-KEY\", map_center_lon=180)\n", + "fig.update_layout(margin={\"r\":0,\"t\":0,\"l\":0,\"b\":0})\n", + "fig.show()\n", + "```" + ] + }, + { + "cell_type": "markdown", + "id": "4cbe9e8e", + "metadata": {}, + "source": [ + "### Mapbox\n", + "\n", + "> Mapbox traces are deprecated and may be removed in a future version of Plotly.py.\n", + "\n", + "#### How Layers Work in Mapbox Tile Maps\n", + "\n", + "Mapbox tile maps are composed of various layers, of three different types:\n", + "\n", + "1. `layout.mapbox.style` defines is the lowest layers, also known as your \"base map\"\n", + "2. The various traces in `data` are by default rendered above the base map (although this can be controlled via the `below` attribute).\n", + "3. `layout.mapbox.layers` is an array that defines more layers that are by default rendered above the traces in `data` (although this can also be controlled via the `below` attribute).\n", + "\n", + "#### Mapbox Access Tokens and When You Need Them\n", + "\n", + "The word \"mapbox\" in the trace names and `layout.mapbox` refers to the Mapbox GL JS open-source library, which is integrated into Plotly.py.\n", + "\n", + "If your basemap in `layout.mapbox.style` uses data from the Mapbox _service_, then you will need to register for a free account at https://mapbox.com/ and obtain a Mapbox Access token. This token should be provided in `layout.mapbox.access_token` (or, if using Plotly Express, via the `px.set_mapbox_access_token()` configuration function).\n", + "\n", + "If you basemap in `layout.mapbox.style` uses maps from the [Stadia Maps service](https://www.stadiamaps.com) (see below for details), you'll need to register for a Stadia Maps account and token.\n", + "\n", + "\n", + "#### Base Maps in `layout.mapbox.style`\n", + "\n", + "The accepted values for `layout.mapbox.style` are one of:\n", + "\n", + "- `\"white-bg\"` yields an empty white canvas which results in no external HTTP requests\n", + "- `\"open-street-map\"`, `\"carto-positron\"`, and `\"carto-darkmatter\"` yield maps composed of _raster_ tiles from various public tile servers which do not require signups or access tokens.\n", + "- `\"basic\"`, `\"streets\"`, `\"outdoors\"`, `\"light\"`, `\"dark\"`, `\"satellite\"`, or `\"satellite-streets\"` yield maps composed of _vector_ tiles from the Mapbox service, and _do_ require a Mapbox Access Token or an on-premise Mapbox installation.\n", + "- `\"stamen-terrain\"`, `\"stamen-toner\"` or `\"stamen-watercolor\"` yield maps composed of _raster_ tiles from the [Stadia Maps service](https://www.stadiamaps.com), and require a Stadia Maps account and token.\n", + "- A Mapbox service style URL, which requires a Mapbox Access Token or an on-premise Mapbox installation.\n", + "- A Mapbox Style object as defined at https://docs.mapbox.com/mapbox-gl-js/style-spec/\n", + "\n", + "#### OpenStreetMap tiles: no token needed\n", + "\n", + "Here is a simple map rendered with OpenStreetMaps tiles, without needing a Mapbox Access Token:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "01bfe15b", + "metadata": {}, + "outputs": [], + "source": [ + "import pandas as pd\n", + "us_cities = pd.read_csv(\"https://raw.githubusercontent.com/plotly/datasets/master/us-cities-top-1k.csv\")\n", + "\n", + "import plotly.express as px\n", + "\n", + "fig = px.scatter_mapbox(us_cities, lat=\"lat\", lon=\"lon\", hover_name=\"City\", hover_data=[\"State\", \"Population\"],\n", + " color_discrete_sequence=[\"fuchsia\"], zoom=3, height=300)\n", + "fig.update_layout(mapbox_style=\"open-street-map\")\n", + "fig.update_layout(margin={\"r\":0,\"t\":0,\"l\":0,\"b\":0})\n", + "fig.show()" + ] + }, + { + "cell_type": "markdown", + "id": "0d4e39d2", + "metadata": {}, + "source": [ + "#### Using `layout.mapbox.layers` to Specify a Base Map\n", + "\n", + "If you have access to your own private tile servers, or wish to use a tile server not included in the list above, the recommended approach is to set `layout.mapbox.style` to `\"white-bg\"` and to use `layout.mapbox.layers` with `below` to specify a custom base map.\n", + "\n", + "> If you omit the `below` attribute when using this approach, your data will likely be hidden by fully-opaque raster tiles!\n", + "\n", + "#### Base Tiles from the USGS: no token needed\n", + "\n", + "Here is an example of a map which uses a public USGS imagery map, specified in `layout.mapbox.layers`, and which is rendered _below_ the `data` layer.\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "376e2aa1", + "metadata": {}, + "outputs": [], + "source": [ + "import pandas as pd\n", + "us_cities = pd.read_csv(\"https://raw.githubusercontent.com/plotly/datasets/master/us-cities-top-1k.csv\")\n", + "\n", + "import plotly.express as px\n", + "\n", + "fig = px.scatter_mapbox(us_cities, lat=\"lat\", lon=\"lon\", hover_name=\"City\", hover_data=[\"State\", \"Population\"],\n", + " color_discrete_sequence=[\"fuchsia\"], zoom=3, height=300)\n", + "fig.update_layout(\n", + " mapbox_style=\"white-bg\",\n", + " mapbox_layers=[\n", + " {\n", + " \"below\": 'traces',\n", + " \"sourcetype\": \"raster\",\n", + " \"sourceattribution\": \"United States Geological Survey\",\n", + " \"source\": [\n", + " \"https://basemap.nationalmap.gov/arcgis/rest/services/USGSImageryOnly/MapServer/tile/{z}/{y}/{x}\"\n", + " ]\n", + " }\n", + " ])\n", + "fig.update_layout(margin={\"r\":0,\"t\":0,\"l\":0,\"b\":0})\n", + "fig.show()" + ] + }, + { + "cell_type": "markdown", + "id": "098d4061", + "metadata": {}, + "source": [ + "#### Dark tiles from Mapbox service: free token needed\n", + "\n", + "Here is a map rendered with the `\"dark\"` style from the Mapbox service, which requires an Access Token:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "84263036", + "metadata": {}, + "outputs": [], + "source": [ + "token = open(\".mapbox_token\").read() # you will need your own token\n", + "\n", + "import pandas as pd\n", + "us_cities = pd.read_csv(\"https://raw.githubusercontent.com/plotly/datasets/master/us-cities-top-1k.csv\")\n", + "\n", + "import plotly.express as px\n", + "\n", + "fig = px.scatter_mapbox(us_cities, lat=\"lat\", lon=\"lon\", hover_name=\"City\", hover_data=[\"State\", \"Population\"],\n", + " color_discrete_sequence=[\"fuchsia\"], zoom=3, height=300)\n", + "fig.update_layout(mapbox_style=\"dark\", mapbox_accesstoken=token)\n", + "fig.update_layout(margin={\"r\":0,\"t\":0,\"l\":0,\"b\":0})\n", + "fig.show()" + ] + }, + { + "cell_type": "markdown", + "id": "1f64fdc1", + "metadata": {}, + "source": [ + "## Setting Map Bounds\n", + "\n", + "*New in 5.11*\n", + "\n", + "Set bounds for a map to specify an area outside which a user interacting with the map can't pan or zoom. Here we set a maximum longitude of `-180`, a minimum longitude of `-50`, a maximum latitude of `90`, and a minimum latitude of `20`." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "1799d8d8", + "metadata": {}, + "outputs": [], + "source": [ + "import plotly.express as px\n", + "import pandas as pd\n", + "\n", + "us_cities = pd.read_csv(\n", + " \"https://raw.githubusercontent.com/plotly/datasets/master/us-cities-top-1k.csv\"\n", + ")\n", + "\n", + "fig = px.scatter_map(\n", + " us_cities,\n", + " lat=\"lat\",\n", + " lon=\"lon\",\n", + " hover_name=\"City\",\n", + " hover_data=[\"State\", \"Population\"],\n", + " color_discrete_sequence=[\"fuchsia\"],\n", + " zoom=3,\n", + " height=300,\n", + ")\n", + "fig.update_layout(map_style=\"open-street-map\")\n", + "fig.update_layout(margin={\"r\": 0, \"t\": 0, \"l\": 0, \"b\": 0})\n", + "fig.update_layout(map_bounds={\"west\": -180, \"east\": -50, \"south\": 20, \"north\": 90})\n", + "fig.show()" + ] + }, + { + "cell_type": "markdown", + "id": "2cc93813", + "metadata": {}, + "source": [ + "#### Reference\n", + "\n", + "See https://plotly.com/python/reference/layout/map/ for more information and options on Maplibre-based tile maps and https://plotly.com/python/reference/layout/mapbox/ for Mapbox-based tile maps.\n" + ] + }, + { + "cell_type": "markdown", + "id": "21d3de6d", + "metadata": {}, + "source": [ + "### What About Dash?\n", + "\n", + "[Dash](https://dash.plot.ly/) is an open-source framework for building analytical applications, with no Javascript required, and it is tightly integrated with the Plotly graphing library.\n", + "\n", + "Learn about how to install Dash at https://dash.plot.ly/installation.\n", + "\n", + "Everywhere in this page that you see `fig.show()`, you can display the same figure in a Dash application by passing it to the `figure` argument of the [`Graph` component](https://dash.plot.ly/dash-core-components/graph) from the built-in `dash_core_components` package like this:\n", + "\n", + "```python\n", + "import plotly.graph_objects as go # or plotly.express as px\n", + "fig = go.Figure() # or any Plotly Express function e.g. px.bar(...)\n", + "# fig.add_trace( ... )\n", + "# fig.update_layout( ... )\n", + "\n", + "from dash import Dash, dcc, html\n", + "\n", + "app = Dash()\n", + "app.layout = html.Div([\n", + " dcc.Graph(figure=fig)\n", + "])\n", + "\n", + "app.run_server(debug=True, use_reloader=False) # Turn off reloader if inside Jupyter\n", + "```" + ] + } + ], + "metadata": { + "jupytext": { + "notebook_metadata_filter": "all" + }, + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "language": "python", + "name": "python3" + }, + "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" + }, + "plotly": { + "description": "How to make tile-based maps in Python with various base layers.", + "display_as": "maps", + "language": "python", + "layout": "base", + "name": "Tile Map Layers", + "order": 9, + "page_type": "u-guide", + "permalink": "python/tile-map-layers/", + "redirect_from": "python/mapbox-layers/", + "thumbnail": "thumbnail/mapbox-layers.png" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/tile-scatter-maps.ipynb b/tile-scatter-maps.ipynb new file mode 100644 index 000000000..9cf7c967c --- /dev/null +++ b/tile-scatter-maps.ipynb @@ -0,0 +1,529 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "2bed3493", + "metadata": {}, + "source": [ + "### Basic example with Plotly Express\n", + "\n", + "Here we show the [Plotly Express](/python/plotly-express/) function `px.scatter_map` for a scatter plot on a tile map.\n", + "\n", + "[Plotly Express](/python/plotly-express/) is the easy-to-use, high-level interface to Plotly, which [operates on a variety of types of data](/python/px-arguments/) and produces [easy-to-style figures](/python/styling-plotly-express/)." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "221cbf40", + "metadata": {}, + "outputs": [], + "source": [ + "import plotly.express as px\n", + "df = px.data.carshare()\n", + "fig = px.scatter_map(df, lat=\"centroid_lat\", lon=\"centroid_lon\", color=\"peak_hour\", size=\"car_hours\",\n", + " color_continuous_scale=px.colors.cyclical.IceFire, size_max=15, zoom=10)\n", + "fig.show()" + ] + }, + { + "cell_type": "markdown", + "id": "c353e963", + "metadata": {}, + "source": [ + "### Basic Example with GeoPandas\n", + "\n", + "`px.scatter_map` can work well with [GeoPandas](https://geopandas.org/) dataframes whose `geometry` is of type `Point`." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "54c8e56d", + "metadata": {}, + "outputs": [], + "source": [ + "import plotly.express as px\n", + "import geopandas as gpd\n", + "\n", + "geo_df = gpd.read_file(gpd.datasets.get_path('naturalearth_cities'))\n", + "\n", + "fig = px.scatter_map(geo_df,\n", + " lat=geo_df.geometry.y,\n", + " lon=geo_df.geometry.x,\n", + " hover_name=\"name\",\n", + " zoom=1)\n", + "fig.show()" + ] + }, + { + "cell_type": "markdown", + "id": "e48ebc12", + "metadata": {}, + "source": [ + "#### Basic Example" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "ffafbf0b", + "metadata": {}, + "outputs": [], + "source": [ + "import plotly.graph_objects as go\n", + "\n", + "fig = go.Figure(go.Scattermap(\n", + " lat=['45.5017'],\n", + " lon=['-73.5673'],\n", + " mode='markers',\n", + " marker=go.scattermap.Marker(\n", + " size=14\n", + " ),\n", + " text=['Montreal'],\n", + " ))\n", + "\n", + "fig.update_layout(\n", + " hovermode='closest',\n", + " map=dict(\n", + " bearing=0,\n", + " center=go.layout.map.Center(\n", + " lat=45,\n", + " lon=-73\n", + " ),\n", + " pitch=0,\n", + " zoom=5\n", + " )\n", + ")\n", + "\n", + "fig.show()" + ] + }, + { + "cell_type": "markdown", + "id": "c15d118b", + "metadata": {}, + "source": [ + "#### Multiple Markers" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "c5ad19d7", + "metadata": {}, + "outputs": [], + "source": [ + "import plotly.graph_objects as go\n", + "\n", + "fig = go.Figure(go.Scattermap(\n", + " lat=['38.91427','38.91538','38.91458',\n", + " '38.92239','38.93222','38.90842',\n", + " '38.91931','38.93260','38.91368',\n", + " '38.88516','38.921894','38.93206',\n", + " '38.91275'],\n", + " lon=['-77.02827','-77.02013','-77.03155',\n", + " '-77.04227','-77.02854','-77.02419',\n", + " '-77.02518','-77.03304','-77.04509',\n", + " '-76.99656','-77.042438','-77.02821',\n", + " '-77.01239'],\n", + " mode='markers',\n", + " marker=go.scattermap.Marker(\n", + " size=9\n", + " ),\n", + " text=[\"The coffee bar\",\"Bistro Bohem\",\"Black Cat\",\n", + " \"Snap\",\"Columbia Heights Coffee\",\"Azi's Cafe\",\n", + " \"Blind Dog Cafe\",\"Le Caprice\",\"Filter\",\n", + " \"Peregrine\",\"Tryst\",\"The Coupe\",\n", + " \"Big Bear Cafe\"],\n", + " ))\n", + "\n", + "fig.update_layout(\n", + " autosize=True,\n", + " hovermode='closest',\n", + " map=dict(\n", + " bearing=0,\n", + " center=dict(\n", + " lat=38.92,\n", + " lon=-77.07\n", + " ),\n", + " pitch=0,\n", + " zoom=10\n", + " ),\n", + ")\n", + "\n", + "fig.show()" + ] + }, + { + "cell_type": "markdown", + "id": "12a29783", + "metadata": {}, + "source": [ + "#### Nuclear Waste Sites on Campuses" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "c199b381", + "metadata": {}, + "outputs": [], + "source": [ + "import plotly.graph_objects as go\n", + "import pandas as pd\n", + "\n", + "df = pd.read_csv('https://raw.githubusercontent.com/plotly/datasets/master/Nuclear%20Waste%20Sites%20on%20American%20Campuses.csv')\n", + "site_lat = df.lat\n", + "site_lon = df.lon\n", + "locations_name = df.text\n", + "\n", + "fig = go.Figure()\n", + "\n", + "fig.add_trace(go.Scattermap(\n", + " lat=site_lat,\n", + " lon=site_lon,\n", + " mode='markers',\n", + " marker=go.scattermap.Marker(\n", + " size=17,\n", + " color='rgb(255, 0, 0)',\n", + " opacity=0.7\n", + " ),\n", + " text=locations_name,\n", + " hoverinfo='text'\n", + " ))\n", + "\n", + "fig.add_trace(go.Scattermap(\n", + " lat=site_lat,\n", + " lon=site_lon,\n", + " mode='markers',\n", + " marker=go.scattermap.Marker(\n", + " size=8,\n", + " color='rgb(242, 177, 172)',\n", + " opacity=0.7\n", + " ),\n", + " hoverinfo='none'\n", + " ))\n", + "\n", + "fig.update_layout(\n", + " title=dict(text='Nuclear Waste Sites on Campus'),\n", + " autosize=True,\n", + " hovermode='closest',\n", + " showlegend=False,\n", + " map=dict(\n", + " bearing=0,\n", + " center=dict(\n", + " lat=38,\n", + " lon=-94\n", + " ),\n", + " pitch=0,\n", + " zoom=3,\n", + " style='light'\n", + " ),\n", + ")\n", + "\n", + "fig.show()" + ] + }, + { + "cell_type": "markdown", + "id": "b0767918", + "metadata": {}, + "source": [ + "### Set Marker Symbols\n", + "\n", + "You can define the symbol on your map by setting [`symbol`](https://plotly.com/python/reference/scattermap/#scattermap-marker-symbol) attribute." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "bbe98fed", + "metadata": {}, + "outputs": [], + "source": [ + "import plotly.graph_objects as go\n", + "\n", + "fig = go.Figure(go.Scattermap(\n", + " mode = \"markers+text+lines\",\n", + " lon = [-75, -80, -50], lat = [45, 20, -20],\n", + " marker = {'size': 20, 'symbol': [\"bus\", \"harbor\", \"airport\"]},\n", + " text = [\"Bus\", \"Harbor\", \"airport\"],textposition = \"bottom right\"))\n", + "\n", + "fig.update_layout(\n", + " map = {\n", + " 'style': \"outdoors\", 'zoom': 0.7},\n", + " showlegend = False)\n", + "\n", + "fig.show()" + ] + }, + { + "cell_type": "markdown", + "id": "da1dfded", + "metadata": {}, + "source": [ + "#### Add Clusters\n", + "\n", + "*New in 5.11*\n", + "\n", + "Display clusters of data points by setting `cluster`. Here, we enable clusters with `enabled=True`. You can also enable clusters by setting other `cluster` properties. Other available properties include `color` (for setting the color of the clusters), `size` (for setting the size of a cluster step), and `step` (for configuring how many points it takes to create a cluster or advance to the next cluster step)." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "3ca56475", + "metadata": {}, + "outputs": [], + "source": [ + "import plotly.express as px\n", + "import pandas as pd\n", + "\n", + "df = pd.read_csv(\n", + " \"https://raw.githubusercontent.com/plotly/datasets/master/2011_february_us_airport_traffic.csv\"\n", + ")\n", + "fig = px.scatter_map(df, lat=\"lat\", lon=\"long\", size=\"cnt\", zoom=3)\n", + "fig.update_traces(cluster=dict(enabled=True))\n", + "fig.show()\n" + ] + }, + { + "cell_type": "markdown", + "id": "3bfcd2c8", + "metadata": {}, + "source": [ + "#### Font Customization\n", + "\n", + "You can customize the font on `go.Scattermap` traces with `textfont`. For example, you can set the font `family`." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "7caae1ed", + "metadata": {}, + "outputs": [], + "source": [ + "import plotly.graph_objects as go\n", + "\n", + "fig = go.Figure(go.Scattermap(\n", + " mode = \"markers+text+lines\",\n", + " lon = [-75, -80, -50], lat = [45, 20, -20],\n", + " marker = {'size': 20, 'symbol': [\"bus\", \"harbor\", \"airport\"]},\n", + " text = [\"Bus\", \"Harbor\", \"airport\"], textposition = \"bottom right\",\n", + " textfont = dict(size=18, color=\"black\", family=\"Open Sans Bold\")\n", + " ))\n", + "\n", + "fig.update_layout(\n", + " map = {\n", + " 'style': \"outdoors\", 'zoom': 0.7},\n", + " showlegend = False,)\n", + "\n", + "fig.show()" + ] + }, + { + "cell_type": "markdown", + "id": "5d8529d8", + "metadata": {}, + "source": [ + "`go.Scattermap` supports the following values for `textfont.family`:\n", + "\n", + "'Metropolis Black Italic', 'Metropolis Black', 'Metropolis Bold Italic', 'Metropolis Bold', 'Metropolis Extra Bold Italic', 'Metropolis Extra Bold', 'Metropolis Extra Light Italic', 'Metropolis Extra Light', 'Metropolis Light Italic', 'Metropolis Light', 'Metropolis Medium Italic', 'Metropolis Medium', 'Metropolis Regular Italic', 'Metropolis Regular', 'Metropolis Semi Bold Italic', 'Metropolis Semi Bold', 'Metropolis Thin Italic', 'Metropolis Thin', 'Open Sans Bold Italic', 'Open Sans Bold', 'Open Sans Extrabold Italic', 'Open Sans Extrabold', 'Open Sans Italic', 'Open Sans Light Italic', 'Open Sans Light', 'Open Sans Regular', 'Open Sans Semibold Italic', 'Open Sans Semibold', 'Klokantech Noto Sans Bold', 'Klokantech Noto Sans CJK Bold', 'Klokantech Noto Sans CJK Regular', 'Klokantech Noto Sans Italic', and 'Klokantech Noto Sans Regular'." + ] + }, + { + "cell_type": "markdown", + "id": "5682734d", + "metadata": {}, + "source": [ + "##### Font Weight\n", + "\n", + "*New in 5.23*\n", + "\n", + "You can specify a numeric font weight on `go.Scattermap` with `textfont.weight`." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "0dfc8b66", + "metadata": {}, + "outputs": [], + "source": [ + "import plotly.graph_objects as go\n", + "\n", + "fig = go.Figure(go.Scattermap(\n", + " mode = \"markers+text+lines\",\n", + " lon = [-75, -80, -50], lat = [45, 20, -20],\n", + " marker = dict(size=20, symbol=[\"bus\", \"harbor\", \"airport\"]),\n", + " text = [\"Bus\", \"Harbor\", \"airport\"], textposition = \"bottom right\",\n", + " textfont = dict(size=18, color=\"black\", weight=900)\n", + " ))\n", + "\n", + "fig.update_layout(\n", + " map = dict(\n", + " style=\"outdoors\", zoom=0.7),\n", + " showlegend = False,)\n", + "\n", + "fig.show()" + ] + }, + { + "cell_type": "markdown", + "id": "90c9e4bd", + "metadata": {}, + "source": [ + "## Mapbox Maps\n", + "\n", + "> Mapbox traces are deprecated and may be removed in a future version of Plotly.py.\n", + "\n", + "The earlier examples using `px.scatter_map` and `go.Scattermap` use [Maplibre](https://maplibre.org/maplibre-gl-js/docs/) for rendering. These traces were introduced in Plotly.py 5.24 and are now the recommended way to create scatter plots on tile-based maps. There are also traces that use [Mapbox](https://docs.mapbox.com): `px.scatter_mapbox` and `go.Scattermapbox`\n", + "\n", + "To plot on Mapbox maps with Plotly you _may_ need a Mapbox account and a public [Mapbox Access Token](https://www.mapbox.com/studio). See our [Mapbox Map Layers](/python/mapbox-layers/) documentation for more information.\n", + "\n", + "Here's the first example rewritten to use `px.scatter_mapbox`." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "2c4ff855", + "metadata": {}, + "outputs": [], + "source": [ + "import plotly.express as px\n", + "px.set_mapbox_access_token(open(\".mapbox_token\").read())\n", + "df = px.data.carshare()\n", + "fig = px.scatter_mapbox(df, lat=\"centroid_lat\", lon=\"centroid_lon\", color=\"peak_hour\", size=\"car_hours\",\n", + " color_continuous_scale=px.colors.cyclical.IceFire, size_max=15, zoom=10)\n", + "fig.show()\n" + ] + }, + { + "cell_type": "markdown", + "id": "91760153", + "metadata": {}, + "source": [ + "And here's an example using Graph Objects:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "0c32266e", + "metadata": {}, + "outputs": [], + "source": [ + "import plotly.graph_objects as go\n", + "\n", + "mapbox_access_token = open(\".mapbox_token\").read()\n", + "\n", + "fig = go.Figure(go.Scattermapbox(\n", + " lat=['45.5017'],\n", + " lon=['-73.5673'],\n", + " mode='markers',\n", + " marker=go.scattermapbox.Marker(\n", + " size=14\n", + " ),\n", + " text=['Montreal'],\n", + " ))\n", + "\n", + "fig.update_layout(\n", + " hovermode='closest',\n", + " mapbox=dict(\n", + " accesstoken=mapbox_access_token,\n", + " bearing=0,\n", + " center=go.layout.mapbox.Center(\n", + " lat=45,\n", + " lon=-73\n", + " ),\n", + " pitch=0,\n", + " zoom=5\n", + " )\n", + ")\n", + "\n", + "fig.show()" + ] + }, + { + "cell_type": "markdown", + "id": "3cc6cafe", + "metadata": {}, + "source": [ + "#### Reference\n", + "\n", + "See [function reference for `px.scatter_map`](https://plotly.com/python-api-reference/generated/plotly.express.scatter_map) or https://plotly.com/python/reference/scattermap/ for more information about the attributes available.\n", + "\n", + "For Mapbox-based tile maps, see [function reference for `px.scatter_mapbox`](https://plotly.com/python-api-reference/generated/plotly.express.scatter_mapbox) or https://plotly.com/python/reference/scattermapbox/.\n" + ] + }, + { + "cell_type": "markdown", + "id": "b8930013", + "metadata": {}, + "source": [ + "### What About Dash?\n", + "\n", + "[Dash](https://dash.plot.ly/) is an open-source framework for building analytical applications, with no Javascript required, and it is tightly integrated with the Plotly graphing library.\n", + "\n", + "Learn about how to install Dash at https://dash.plot.ly/installation.\n", + "\n", + "Everywhere in this page that you see `fig.show()`, you can display the same figure in a Dash application by passing it to the `figure` argument of the [`Graph` component](https://dash.plot.ly/dash-core-components/graph) from the built-in `dash_core_components` package like this:\n", + "\n", + "```python\n", + "import plotly.graph_objects as go # or plotly.express as px\n", + "fig = go.Figure() # or any Plotly Express function e.g. px.bar(...)\n", + "# fig.add_trace( ... )\n", + "# fig.update_layout( ... )\n", + "\n", + "from dash import Dash, dcc, html\n", + "\n", + "app = Dash()\n", + "app.layout = html.Div([\n", + " dcc.Graph(figure=fig)\n", + "])\n", + "\n", + "app.run_server(debug=True, use_reloader=False) # Turn off reloader if inside Jupyter\n", + "```" + ] + } + ], + "metadata": { + "jupytext": { + "notebook_metadata_filter": "all" + }, + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "language": "python", + "name": "python3" + }, + "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" + }, + "plotly": { + "description": "How to make scatter plots on tile maps in Python.", + "display_as": "maps", + "language": "python", + "layout": "base", + "name": "Scatter Plots on Tile Maps", + "order": 10, + "page_type": "u-guide", + "permalink": "python/tile-scatter-maps/", + "redirect_from": "python/scattermapbox/", + "thumbnail": "thumbnail/scatter-mapbox.jpg" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/time-series.ipynb b/time-series.ipynb new file mode 100644 index 000000000..84c29e7f5 --- /dev/null +++ b/time-series.ipynb @@ -0,0 +1,769 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "6f78b1ea", + "metadata": {}, + "source": [ + "### Time Series using Axes of type `date`\n", + "\n", + "Time series can be represented using either `plotly.express` functions (`px.line`, `px.scatter`, `px.bar` etc) or `plotly.graph_objects` charts objects (`go.Scatter`, `go.Bar` etc). For more examples of such charts, see the documentation of [line and scatter plots](https://plotly.com/python/line-and-scatter/) or [bar charts](/python/bar-charts/).\n", + "\n", + "For financial applications, Plotly can also be used to create [Candlestick charts](/python/candlestick-charts/) and [OHLC charts](/python/ohlc-charts/), which default to date axes.\n", + "\n", + "Plotly auto-sets the axis type to a date format when the corresponding data are either ISO-formatted date strings or if they're a [date pandas column](https://pandas.pydata.org/pandas-docs/stable/user_guide/timeseries.html) or [datetime NumPy array](https://docs.scipy.org/doc/numpy/reference/arrays.datetime.html)." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "c0c5a4f8", + "metadata": {}, + "outputs": [], + "source": [ + "# Using plotly.express\n", + "import plotly.express as px\n", + "\n", + "df = px.data.stocks()\n", + "fig = px.line(df, x='date', y=\"GOOG\")\n", + "fig.show()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "a74f5d23", + "metadata": {}, + "outputs": [], + "source": [ + "# Using graph_objects\n", + "import plotly.graph_objects as go\n", + "\n", + "import pandas as pd\n", + "df = pd.read_csv('https://raw.githubusercontent.com/plotly/datasets/master/finance-charts-apple.csv')\n", + "\n", + "fig = go.Figure([go.Scatter(x=df['Date'], y=df['AAPL.High'])])\n", + "fig.show()" + ] + }, + { + "cell_type": "markdown", + "id": "49237af6", + "metadata": {}, + "source": [ + "### Time Series in Dash\n", + "\n", + "[Dash](https://plotly.com/dash/) is the best way to build analytical apps in Python using Plotly figures. To run the app below, run `pip install dash`, click \"Download\" to get the code and run `python app.py`.\n", + "\n", + "Get started with [the official Dash docs](https://dash.plotly.com/installation) and **learn how to effortlessly [style](https://plotly.com/dash/design-kit/) & [deploy](https://plotly.com/dash/app-manager/) apps like this with Dash Enterprise.**\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "a527d74e", + "metadata": { + "hide_code": true + }, + "outputs": [], + "source": [ + "from IPython.display import IFrame\n", + "snippet_url = 'https://python-docs-dash-snippets.herokuapp.com/python-docs-dash-snippets/'\n", + "IFrame(snippet_url + 'time-series', width='100%', height=1200)" + ] + }, + { + "cell_type": "markdown", + "id": "822d3e08", + "metadata": {}, + "source": [ + "

Sign up for Dash Club → Free cheat sheets plus updates from Chris Parmer and Adam Schroeder delivered to your inbox every two months. Includes tips and tricks, community apps, and deep dives into the Dash architecture.\n", + "Join now.

" + ] + }, + { + "cell_type": "markdown", + "id": "ec201794", + "metadata": {}, + "source": [ + "### Different Chart Types on Date Axes\n", + "\n", + "Any kind of cartesian chart can be placed on `date` axes, for example this bar chart of relative stock ticker values." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "6682779e", + "metadata": {}, + "outputs": [], + "source": [ + "import plotly.express as px\n", + "\n", + "df = px.data.stocks(indexed=True)-1\n", + "fig = px.bar(df, x=df.index, y=\"GOOG\")\n", + "fig.show()" + ] + }, + { + "cell_type": "markdown", + "id": "15ca9af8", + "metadata": {}, + "source": [ + "Or this [facetted](/python/facet-plots/) area plot:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "ab54fb75", + "metadata": {}, + "outputs": [], + "source": [ + "import plotly.express as px\n", + "\n", + "df = px.data.stocks(indexed=True)-1\n", + "fig = px.area(df, facet_col=\"company\", facet_col_wrap=2)\n", + "fig.show()" + ] + }, + { + "cell_type": "markdown", + "id": "72c97809", + "metadata": {}, + "source": [ + "### Configuring Tick Labels\n", + "\n", + "By default, the tick labels (and optional ticks) are associated with a specific grid-line, and represent an *instant* in time, for example, \"00:00 on February 1, 2018\". Tick labels can be formatted using the `tickformat` attribute (which accepts the [d3 time-format formatting strings](https://github.com/d3/d3-time-format)) to display only the month and year, but they still represent an instant by default, so in the figure below, the text of the label \"Feb 2018\" spans part of the month of January and part of the month of February. The `dtick` attribute controls the spacing between gridlines, and the `\"M1\"` setting means \"1 month\". This attribute also accepts a number of milliseconds, which can be scaled up to days by multiplying by `24*60*60*1000`.\n", + "\n", + "Date axis tick labels have the special property that any portion after the first instance of `'\\n'` in `tickformat` will appear on a second line only once per unique value, as with the year numbers in the example below. To have the year number appear on every tick label, `'
'` should be used instead of `'\\n'`.\n", + "\n", + "Note that by default, the formatting of values of X and Y values in the hover label matches that of the tick labels of the corresponding axes, so when customizing the tick labels to something broad like \"month\", it's usually necessary to [customize the hover label](/python/hover-text-and-formatting/) to something narrower like the actual date, as below." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "06ec7324", + "metadata": {}, + "outputs": [], + "source": [ + "import plotly.express as px\n", + "df = px.data.stocks()\n", + "fig = px.line(df, x=\"date\", y=df.columns,\n", + " hover_data={\"date\": \"|%B %d, %Y\"},\n", + " title='custom tick labels')\n", + "fig.update_xaxes(\n", + " dtick=\"M1\",\n", + " tickformat=\"%b\\n%Y\")\n", + "fig.show()" + ] + }, + { + "cell_type": "markdown", + "id": "eee46a1f", + "metadata": {}, + "source": [ + "### Moving Tick Labels to the Middle of the Period\n", + "\n", + "_new in 4.10_\n", + "\n", + "By setting the `ticklabelmode` attribute to `\"period\"` (the default is `\"instant\"`) we can move the tick labels to the middle of the period they represent. The gridlines remain at the beginning of each month (thanks to `dtick=\"M1\"`) but the labels now span the month they refer to." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "baef101b", + "metadata": {}, + "outputs": [], + "source": [ + "import plotly.express as px\n", + "df = px.data.stocks()\n", + "fig = px.line(df, x=\"date\", y=df.columns,\n", + " hover_data={\"date\": \"|%B %d, %Y\"},\n", + " title='custom tick labels with ticklabelmode=\"period\"')\n", + "fig.update_xaxes(\n", + " dtick=\"M1\",\n", + " tickformat=\"%b\\n%Y\",\n", + " ticklabelmode=\"period\")\n", + "fig.show()" + ] + }, + { + "cell_type": "markdown", + "id": "2b739dd8", + "metadata": {}, + "source": [ + "### Adding Minor Ticks\n", + "\n", + "_new in 5.8_\n", + "\n", + "You can add minor ticks to an axis with `minor`. This takes a `dict` of properties to apply to minor ticks. See the [figure reference](https://plotly.com/python/reference/layout/xaxis/#layout-xaxis-minor) for full details on the accepted keys in this dict.\n", + "\n", + "In this example, we've added minor ticks to the inside of the x-axis and turned on minor grid lines." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "32321d70", + "metadata": {}, + "outputs": [], + "source": [ + "import pandas as pd\n", + "import plotly.express as px\n", + "\n", + "df = px.data.stocks()\n", + "fig = px.line(df, x='date', y=\"GOOG\")\n", + "\n", + "fig.update_xaxes(minor=dict(ticks=\"inside\", showgrid=True))\n", + "\n", + "fig.show()" + ] + }, + { + "cell_type": "markdown", + "id": "c6a9b9e4", + "metadata": {}, + "source": [ + "#### Monthly Period Labels With Weekly Minor Ticks\n", + "\n", + "_new in 5.8_\n", + "\n", + "You can set `dtick` on `minor` to control the spacing for minor ticks and grid lines. In the following example, by setting `dtick=7*24*60*60*1000` (the number of milliseconds in a week) and setting `tick0=\"2016-07-03\"` (the first Sunday in our data), a minor tick and grid line is displayed for the start of each week. When zoomed out, we can see where each month and week begins and ends." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "babf2296", + "metadata": {}, + "outputs": [], + "source": [ + "import pandas as pd\n", + "import plotly.express as px\n", + "\n", + "df = pd.read_csv('https://raw.githubusercontent.com/plotly/datasets/master/finance-charts-apple.csv')\n", + "df = df.loc[(df[\"Date\"] >= \"2016-07-01\") & (df[\"Date\"] <= \"2016-12-01\")]\n", + "\n", + "fig = px.line(df, x='Date', y='AAPL.High')\n", + "fig.update_xaxes(ticks= \"outside\",\n", + " ticklabelmode= \"period\", \n", + " tickcolor= \"black\", \n", + " ticklen=10, \n", + " minor=dict(\n", + " ticklen=4, \n", + " dtick=7*24*60*60*1000, \n", + " tick0=\"2016-07-03\", \n", + " griddash='dot', \n", + " gridcolor='white')\n", + " )\n", + "\n", + "fig.show()" + ] + }, + { + "cell_type": "markdown", + "id": "a60745b3", + "metadata": {}, + "source": [ + "### Summarizing Time-series Data with Histograms\n", + "\n", + "Plotly [histograms](/python/histograms/) are powerful data-aggregation tools which even work on date axes. In the figure below, we pass in daily data and display it as monthly averages by setting `histfunc=\"avg` and `xbins_size=\"M1\"`." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "6fd23e4f", + "metadata": {}, + "outputs": [], + "source": [ + "import plotly.express as px\n", + "import plotly.graph_objects as go\n", + "import pandas as pd\n", + "\n", + "df = pd.read_csv('https://raw.githubusercontent.com/plotly/datasets/master/finance-charts-apple.csv')\n", + "\n", + "fig = px.histogram(df, x=\"Date\", y=\"AAPL.Close\", histfunc=\"avg\", title=\"Histogram on Date Axes\")\n", + "fig.update_traces(xbins_size=\"M1\")\n", + "fig.update_xaxes(showgrid=True, ticklabelmode=\"period\", dtick=\"M1\", tickformat=\"%b\\n%Y\")\n", + "fig.update_layout(bargap=0.1)\n", + "fig.add_trace(go.Scatter(mode=\"markers\", x=df[\"Date\"], y=df[\"AAPL.Close\"], name=\"daily\"))\n", + "fig.show()" + ] + }, + { + "cell_type": "markdown", + "id": "3b109ee4", + "metadata": {}, + "source": [ + "### Displaying Period Data\n", + "\n", + "_new in 4.11_\n", + "\n", + "If your data coded \"January 1\" or \"January 31\" in fact refers to data collected throughout the month of January, for example, you can configure your traces to display their marks at the start end, or middle of the month with the `xperiod` and `xperiodalignment` attributes. In the example below, the raw data is all coded with an X value of the 10th of the month, but is binned into monthly periods with `xperiod=\"M1\"` and then displayed at the start, middle and end of the period." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "b8db94e1", + "metadata": {}, + "outputs": [], + "source": [ + "import plotly.graph_objects as go\n", + "import pandas as pd\n", + "\n", + "df = pd.DataFrame(dict(\n", + " date=[\"2020-01-10\", \"2020-02-10\", \"2020-03-10\", \"2020-04-10\", \"2020-05-10\", \"2020-06-10\"],\n", + " value=[1,2,3,1,2,3]\n", + "))\n", + "\n", + "fig = go.Figure()\n", + "fig.add_trace(go.Scatter(\n", + " name=\"Raw Data\",\n", + " mode=\"markers+lines\", x=df[\"date\"], y=df[\"value\"],\n", + " marker_symbol=\"star\"\n", + "))\n", + "fig.add_trace(go.Scatter(\n", + " name=\"Start-aligned\",\n", + " mode=\"markers+lines\", x=df[\"date\"], y=df[\"value\"],\n", + " xperiod=\"M1\",\n", + " xperiodalignment=\"start\"\n", + "))\n", + "fig.add_trace(go.Scatter(\n", + " name=\"Middle-aligned\",\n", + " mode=\"markers+lines\", x=df[\"date\"], y=df[\"value\"],\n", + " xperiod=\"M1\",\n", + " xperiodalignment=\"middle\"\n", + "))\n", + "fig.add_trace(go.Scatter(\n", + " name=\"End-aligned\",\n", + " mode=\"markers+lines\", x=df[\"date\"], y=df[\"value\"],\n", + " xperiod=\"M1\",\n", + " xperiodalignment=\"end\"\n", + "))\n", + "fig.add_trace(go.Bar(\n", + " name=\"Middle-aligned\",\n", + " x=df[\"date\"], y=df[\"value\"],\n", + " xperiod=\"M1\",\n", + " xperiodalignment=\"middle\"\n", + "))\n", + "fig.update_xaxes(showgrid=True, ticklabelmode=\"period\")\n", + "fig.show()" + ] + }, + { + "cell_type": "markdown", + "id": "acd5867a", + "metadata": {}, + "source": [ + "### Hover Templates with Mixtures of Period data\n", + "\n", + "*New in v5.0*\n", + "\n", + "When displaying periodic data with mixed-sized periods (i.e. quarterly and monthly) in conjunction with [`x` or `x unified` hovermodes and using `hovertemplate`](https://plotly.com/python/hover-text-and-formatting/), the `xhoverformat` attribute can be used to control how each period's X value is displayed, and the special `%{xother}` hover-template directive can be used to control how the X value is displayed for points that do not share the exact X coordinate with the point that is being hovered on. `%{xother}` will return an empty string when the X value is the one being hovered on, otherwise it will return `(%{x})`. The special `%{_xother}`, `%{xother_}` and `%{_xother_}` variations will display with spaces before, after or around the parentheses, respectively." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "6e5838db", + "metadata": {}, + "outputs": [], + "source": [ + "import plotly.graph_objects as go\n", + "\n", + "fig = go.Figure()\n", + "\n", + "fig.add_trace(go.Bar(\n", + " x=[\"2020-01-01\", \"2020-04-01\", \"2020-07-01\"],\n", + " y=[1000, 1500, 1700],\n", + " xperiod=\"M3\",\n", + " xperiodalignment=\"middle\",\n", + " xhoverformat=\"Q%q\",\n", + " hovertemplate=\"%{y}%{_xother}\"\n", + "))\n", + "\n", + "fig.add_trace(go.Scatter(\n", + " x=[\"2020-01-01\", \"2020-02-01\", \"2020-03-01\",\n", + " \"2020-04-01\", \"2020-05-01\", \"2020-06-01\",\n", + " \"2020-07-01\", \"2020-08-01\", \"2020-09-01\"],\n", + " y=[1100,1050,1200,1300,1400,1700,1500,1400,1600],\n", + " xperiod=\"M1\",\n", + " xperiodalignment=\"middle\",\n", + " hovertemplate=\"%{y}%{_xother}\"\n", + "))\n", + "\n", + "fig.update_layout(hovermode=\"x unified\")\n", + "fig.show()" + ] + }, + { + "cell_type": "markdown", + "id": "62a90484", + "metadata": { + "tags": [] + }, + "source": [ + "### Time Series Plot with Custom Date Range\n", + "\n", + "The data range can be set manually using either `datetime.datetime` objects, or date strings." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "f71e0fe3", + "metadata": {}, + "outputs": [], + "source": [ + "# Using plotly.express\n", + "import plotly.express as px\n", + "\n", + "import pandas as pd\n", + "df = pd.read_csv('https://raw.githubusercontent.com/plotly/datasets/master/finance-charts-apple.csv')\n", + "\n", + "fig = px.line(df, x='Date', y='AAPL.High', range_x=['2016-07-01','2016-12-31'])\n", + "fig.show()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "0e441fdf", + "metadata": {}, + "outputs": [], + "source": [ + "# Using graph_objects\n", + "\n", + "import plotly.graph_objects as go\n", + "import datetime\n", + "\n", + "x = [datetime.datetime(year=2013, month=10, day=4),\n", + " datetime.datetime(year=2013, month=11, day=5),\n", + " datetime.datetime(year=2013, month=12, day=6)]\n", + "\n", + "fig = go.Figure(data=[go.Scatter(x=x, y=[1, 3, 6])])\n", + "# Use datetime objects to set xaxis range\n", + "fig.update_layout(xaxis_range=[datetime.datetime(2013, 10, 17),\n", + " datetime.datetime(2013, 11, 20)])\n", + "fig.show()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "ca28e4e7", + "metadata": {}, + "outputs": [], + "source": [ + "import plotly.graph_objects as go\n", + "import pandas as pd\n", + "df = pd.read_csv('https://raw.githubusercontent.com/plotly/datasets/master/finance-charts-apple.csv')\n", + "\n", + "fig = px.line(df, x='Date', y='AAPL.High')\n", + "\n", + "# Use date string to set xaxis range\n", + "fig.update_layout(xaxis_range=['2016-07-01','2016-12-31'],\n", + " title_text=\"Manually Set Date Range\")\n", + "fig.show()" + ] + }, + { + "cell_type": "markdown", + "id": "09ae928e", + "metadata": {}, + "source": [ + "### Time Series With Range Slider\n", + "\n", + "A range slider is a small subplot-like area below a plot which allows users to pan and zoom the X-axis while maintaining an overview of the chart. Check out the reference for more options: https://plotly.com/python/reference/layout/xaxis/#layout-xaxis-rangeslider" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "33ec7885", + "metadata": {}, + "outputs": [], + "source": [ + "import plotly.express as px\n", + "import pandas as pd\n", + "\n", + "df = pd.read_csv('https://raw.githubusercontent.com/plotly/datasets/master/finance-charts-apple.csv')\n", + "\n", + "fig = px.line(df, x='Date', y='AAPL.High', title='Time Series with Rangeslider')\n", + "\n", + "fig.update_xaxes(rangeslider_visible=True)\n", + "fig.show()" + ] + }, + { + "cell_type": "markdown", + "id": "1a5fae64", + "metadata": {}, + "source": [ + "### Time Series with Range Selector Buttons\n", + "\n", + "Range selector buttons are special controls that work well with time series and range sliders, and allow users to easily set the range of the x-axis. Check out the reference for more options: https://plotly.com/python/reference/layout/xaxis/#layout-xaxis-rangeselector" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "05eb22f9", + "metadata": {}, + "outputs": [], + "source": [ + "import plotly.express as px\n", + "import pandas as pd\n", + "\n", + "df = pd.read_csv('https://raw.githubusercontent.com/plotly/datasets/master/finance-charts-apple.csv')\n", + "\n", + "fig = px.line(df, x='Date', y='AAPL.High', title='Time Series with Range Slider and Selectors')\n", + "\n", + "fig.update_xaxes(\n", + " rangeslider_visible=True,\n", + " rangeselector=dict(\n", + " buttons=list([\n", + " dict(count=1, label=\"1m\", step=\"month\", stepmode=\"backward\"),\n", + " dict(count=6, label=\"6m\", step=\"month\", stepmode=\"backward\"),\n", + " dict(count=1, label=\"YTD\", step=\"year\", stepmode=\"todate\"),\n", + " dict(count=1, label=\"1y\", step=\"year\", stepmode=\"backward\"),\n", + " dict(step=\"all\")\n", + " ])\n", + " )\n", + ")\n", + "fig.show()" + ] + }, + { + "cell_type": "markdown", + "id": "74424d15", + "metadata": {}, + "source": [ + "### Customizing Tick Label Formatting by Zoom Level\n", + "\n", + "The `tickformatstops` attribute can be used to customize the formatting of tick labels depending on the zoom level. Try zooming in to the chart below and see how the tick label formatting changes. Check out the reference for more options: https://plotly.com/python/reference/layout/xaxis/#layout-xaxis-tickformatstops" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "7dc596f7", + "metadata": {}, + "outputs": [], + "source": [ + "import plotly.graph_objects as go\n", + "import pandas as pd\n", + "\n", + "df = pd.read_csv('https://raw.githubusercontent.com/plotly/datasets/master/finance-charts-apple.csv')\n", + "\n", + "fig = go.Figure(go.Scatter(\n", + " x = df['Date'],\n", + " y = df['mavg']\n", + "))\n", + "\n", + "fig.update_xaxes(\n", + " rangeslider_visible=True,\n", + " tickformatstops = [\n", + " dict(dtickrange=[None, 1000], value=\"%H:%M:%S.%L ms\"),\n", + " dict(dtickrange=[1000, 60000], value=\"%H:%M:%S s\"),\n", + " dict(dtickrange=[60000, 3600000], value=\"%H:%M m\"),\n", + " dict(dtickrange=[3600000, 86400000], value=\"%H:%M h\"),\n", + " dict(dtickrange=[86400000, 604800000], value=\"%e. %b d\"),\n", + " dict(dtickrange=[604800000, \"M1\"], value=\"%e. %b w\"),\n", + " dict(dtickrange=[\"M1\", \"M12\"], value=\"%b '%y M\"),\n", + " dict(dtickrange=[\"M12\", None], value=\"%Y Y\")\n", + " ]\n", + ")\n", + "\n", + "fig.show()" + ] + }, + { + "cell_type": "markdown", + "id": "8683354a", + "metadata": {}, + "source": [ + "### Hiding Weekends and Holidays\n", + "\n", + "The `rangebreaks` attribute available on x- and y-axes of type `date` can be used to hide certain time-periods. In the example below, we show two plots: one in default mode to show gaps in the data, and one where we hide weekends and holidays to show an uninterrupted trading history. Note the smaller gaps between the grid lines for December 21 and January 4, where holidays were removed. Check out the reference for more options: https://plotly.com/python/reference/layout/xaxis/#layout-xaxis-rangebreaks\n", + "\n", + "> Note: a known limitation of this feature is that it does not support `scattergl` traces. When using this feature on plots with more than a few hundred data points with `px.scatter` or `px.line` or `px.area`, you may need to pass in `render_mode=\"svg\"` to ensure that the underlying trace type is `scatter` and not `scattergl`." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "069efd53", + "metadata": {}, + "outputs": [], + "source": [ + "import plotly.express as px\n", + "import pandas as pd\n", + "\n", + "df = pd.read_csv('https://raw.githubusercontent.com/plotly/datasets/master/finance-charts-apple.csv')\n", + "\n", + "fig = px.scatter(df, x='Date', y='AAPL.High', range_x=['2015-12-01', '2016-01-15'],\n", + " title=\"Default Display with Gaps\")\n", + "fig.show()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "608023c8", + "metadata": {}, + "outputs": [], + "source": [ + "import plotly.express as px\n", + "import pandas as pd\n", + "\n", + "df = pd.read_csv('https://raw.githubusercontent.com/plotly/datasets/master/finance-charts-apple.csv')\n", + "\n", + "fig = px.scatter(df, x='Date', y='AAPL.High', range_x=['2015-12-01', '2016-01-15'],\n", + " title=\"Hide Weekend and Holiday Gaps with rangebreaks\")\n", + "fig.update_xaxes(\n", + " rangebreaks=[\n", + " dict(bounds=[\"sat\", \"mon\"]), #hide weekends\n", + " dict(values=[\"2015-12-25\", \"2016-01-01\"]) # hide Christmas and New Year's\n", + " ]\n", + ")\n", + "fig.show()" + ] + }, + { + "cell_type": "markdown", + "id": "21ff08de", + "metadata": {}, + "source": [ + "### Hiding Non-Business Hours\n", + "\n", + "The `rangebreaks` feature described above works for hiding hourly periods as well." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "93ba109e", + "metadata": {}, + "outputs": [], + "source": [ + "import plotly.express as px\n", + "import pandas as pd\n", + "import numpy as np\n", + "np.random.seed(1)\n", + "\n", + "work_week_40h = pd.date_range(start='2020-03-01', end='2020-03-07', freq=\"BH\")\n", + "\n", + "df = pd.DataFrame(dict(\n", + " date = work_week_40h,\n", + " value = np.cumsum(np.random.rand(40)-0.5)\n", + "))\n", + "\n", + "fig = px.scatter(df, x=\"date\", y=\"value\",\n", + " title=\"Default Display with Gaps\")\n", + "fig.show()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "32487581", + "metadata": { + "lines_to_next_cell": 2 + }, + "outputs": [], + "source": [ + "import plotly.express as px\n", + "import pandas as pd\n", + "import numpy as np\n", + "np.random.seed(1)\n", + "\n", + "work_week_40h = pd.date_range(start='2020-03-01', end='2020-03-07', freq=\"BH\")\n", + "\n", + "df = pd.DataFrame(dict(\n", + " date = work_week_40h,\n", + " value = np.cumsum(np.random.rand(40)-0.5)\n", + "))\n", + "\n", + "fig = px.scatter(df, x=\"date\", y=\"value\",\n", + " title=\"Hide Non-Business Hour Gaps with rangebreaks\")\n", + "fig.update_xaxes(\n", + " rangebreaks=[\n", + " dict(bounds=[17, 9], pattern=\"hour\"), #hide hours outside of 9am-5pm\n", + " ]\n", + ")\n", + "fig.show()" + ] + }, + { + "cell_type": "markdown", + "id": "83606615", + "metadata": {}, + "source": [ + "### What About Dash?\n", + "\n", + "[Dash](https://dash.plot.ly/) is an open-source framework for building analytical applications, with no Javascript required, and it is tightly integrated with the Plotly graphing library.\n", + "\n", + "Learn about how to install Dash at https://dash.plot.ly/installation.\n", + "\n", + "Everywhere in this page that you see `fig.show()`, you can display the same figure in a Dash application by passing it to the `figure` argument of the [`Graph` component](https://dash.plot.ly/dash-core-components/graph) from the built-in `dash_core_components` package like this:\n", + "\n", + "```python\n", + "import plotly.graph_objects as go # or plotly.express as px\n", + "fig = go.Figure() # or any Plotly Express function e.g. px.bar(...)\n", + "# fig.add_trace( ... )\n", + "# fig.update_layout( ... )\n", + "\n", + "from dash import Dash, dcc, html\n", + "\n", + "app = Dash()\n", + "app.layout = html.Div([\n", + " dcc.Graph(figure=fig)\n", + "])\n", + "\n", + "app.run_server(debug=True, use_reloader=False) # Turn off reloader if inside Jupyter\n", + "```" + ] + } + ], + "metadata": { + "jupytext": { + "notebook_metadata_filter": "all" + }, + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "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.8.8" + }, + "plotly": { + "description": "How to plot date and time in python.", + "display_as": "financial", + "language": "python", + "layout": "base", + "name": "Time Series and Date Axes", + "order": 1, + "page_type": "example_index", + "permalink": "python/time-series/", + "thumbnail": "thumbnail/time-series.jpg" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/tree-plots.ipynb b/tree-plots.ipynb new file mode 100644 index 000000000..ccd289e42 --- /dev/null +++ b/tree-plots.ipynb @@ -0,0 +1,234 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "61b7a162", + "metadata": {}, + "source": [ + "#### Set Up Tree with [igraph](http://igraph.org/python/)\n", + "\n", + "Install igraph with `pip install igraph`." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "9384bc0a", + "metadata": {}, + "outputs": [], + "source": [ + "!pip install igraph" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "93b15ac8", + "metadata": {}, + "outputs": [], + "source": [ + "import igraph\n", + "from igraph import Graph, EdgeSeq\n", + "nr_vertices = 25\n", + "v_label = list(map(str, range(nr_vertices)))\n", + "G = Graph.Tree(nr_vertices, 2) # 2 stands for children number\n", + "lay = G.layout('rt')\n", + "\n", + "position = {k: lay[k] for k in range(nr_vertices)}\n", + "Y = [lay[k][1] for k in range(nr_vertices)]\n", + "M = max(Y)\n", + "\n", + "es = EdgeSeq(G) # sequence of edges\n", + "E = [e.tuple for e in G.es] # list of edges\n", + "\n", + "L = len(position)\n", + "Xn = [position[k][0] for k in range(L)]\n", + "Yn = [2*M-position[k][1] for k in range(L)]\n", + "Xe = []\n", + "Ye = []\n", + "for edge in E:\n", + " Xe+=[position[edge[0]][0],position[edge[1]][0], None]\n", + " Ye+=[2*M-position[edge[0]][1],2*M-position[edge[1]][1], None]\n", + "\n", + "labels = v_label" + ] + }, + { + "cell_type": "markdown", + "id": "42b8bcce", + "metadata": {}, + "source": [ + "#### Create Plotly Traces" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "5efea99b", + "metadata": {}, + "outputs": [], + "source": [ + "import plotly.graph_objects as go\n", + "fig = go.Figure()\n", + "fig.add_trace(go.Scatter(x=Xe,\n", + " y=Ye,\n", + " mode='lines',\n", + " line=dict(color='rgb(210,210,210)', width=1),\n", + " hoverinfo='none'\n", + " ))\n", + "fig.add_trace(go.Scatter(x=Xn,\n", + " y=Yn,\n", + " mode='markers',\n", + " name='bla',\n", + " marker=dict(symbol='circle-dot',\n", + " size=18,\n", + " color='#6175c1', #'#DB4551',\n", + " line=dict(color='rgb(50,50,50)', width=1)\n", + " ),\n", + " text=labels,\n", + " hoverinfo='text',\n", + " opacity=0.8\n", + " ))" + ] + }, + { + "cell_type": "markdown", + "id": "5b974925", + "metadata": {}, + "source": [ + "#### Create Text Inside the Circle via Annotations" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "bcc11a9f", + "metadata": {}, + "outputs": [], + "source": [ + "def make_annotations(pos, text, font_size=10, font_color='rgb(250,250,250)'):\n", + " L=len(pos)\n", + " if len(text)!=L:\n", + " raise ValueError('The lists pos and text must have the same len')\n", + " annotations = []\n", + " for k in range(L):\n", + " annotations.append(\n", + " dict(\n", + " text=labels[k], # or replace labels with a different list for the text within the circle\n", + " x=pos[k][0], y=2*M-position[k][1],\n", + " xref='x1', yref='y1',\n", + " font=dict(color=font_color, size=font_size),\n", + " showarrow=False)\n", + " )\n", + " return annotations" + ] + }, + { + "cell_type": "markdown", + "id": "4e398fcc", + "metadata": {}, + "source": [ + "#### Add Axis Specifications and Create the Layout" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "b6ff0ff1", + "metadata": {}, + "outputs": [], + "source": [ + "axis = dict(showline=False, # hide axis line, grid, ticklabels and title\n", + " zeroline=False,\n", + " showgrid=False,\n", + " showticklabels=False,\n", + " )\n", + "\n", + "fig.update_layout(title= 'Tree with Reingold-Tilford Layout',\n", + " annotations=make_annotations(position, v_label),\n", + " font_size=12,\n", + " showlegend=False,\n", + " xaxis=axis,\n", + " yaxis=axis,\n", + " margin=dict(l=40, r=40, b=85, t=100),\n", + " hovermode='closest',\n", + " plot_bgcolor='rgb(248,248,248)'\n", + " )\n", + "fig.show()" + ] + }, + { + "cell_type": "markdown", + "id": "224f34fc", + "metadata": {}, + "source": [ + "#### Reference\n", + "See https://plotly.com/python/reference/ for more information and chart attribute options and http://igraph.org/python/ for more information about the igraph package!" + ] + }, + { + "cell_type": "markdown", + "id": "cd56f26e", + "metadata": {}, + "source": [ + "### What About Dash?\n", + "\n", + "[Dash](https://dash.plot.ly/) is an open-source framework for building analytical applications, with no Javascript required, and it is tightly integrated with the Plotly graphing library.\n", + "\n", + "Learn about how to install Dash at https://dash.plot.ly/installation.\n", + "\n", + "Everywhere in this page that you see `fig.show()`, you can display the same figure in a Dash application by passing it to the `figure` argument of the [`Graph` component](https://dash.plot.ly/dash-core-components/graph) from the built-in `dash_core_components` package like this:\n", + "\n", + "```python\n", + "import plotly.graph_objects as go # or plotly.express as px\n", + "fig = go.Figure() # or any Plotly Express function e.g. px.bar(...)\n", + "# fig.add_trace( ... )\n", + "# fig.update_layout( ... )\n", + "\n", + "from dash import Dash, dcc, html\n", + "\n", + "app = Dash()\n", + "app.layout = html.Div([\n", + " dcc.Graph(figure=fig)\n", + "])\n", + "\n", + "app.run_server(debug=True, use_reloader=False) # Turn off reloader if inside Jupyter\n", + "```" + ] + } + ], + "metadata": { + "jupytext": { + "notebook_metadata_filter": "all" + }, + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "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.7.3" + }, + "plotly": { + "description": "How to make interactive tree-plot in Python with Plotly. An examples of a tree-plot in Plotly.", + "display_as": "statistical", + "language": "python", + "layout": "base", + "name": "Tree-plots", + "order": 9, + "permalink": "python/tree-plots/", + "thumbnail": "thumbnail/treeplot.jpg" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/treemaps.ipynb b/treemaps.ipynb new file mode 100644 index 000000000..8cf51503f --- /dev/null +++ b/treemaps.ipynb @@ -0,0 +1,695 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "727e1972", + "metadata": {}, + "source": [ + "[Treemap charts](https://en.wikipedia.org/wiki/Treemapping) visualize hierarchical data using nested rectangles. The input data format is the same as for [Sunburst Charts](https://plotly.com/python/sunburst-charts/) and [Icicle Charts](https://plotly.com/python/icicle-charts/): the hierarchy is defined by [labels](https://plotly.com/python/reference/treemap/#treemap-labels) (`names` for `px.treemap`) and [parents](https://plotly.com/python/reference/treemap/#treemap-parents) attributes. Click on one sector to zoom in/out, which also displays a pathbar in the upper-left corner of your treemap. To zoom out you can use the path bar as well.\n", + "\n", + "### Basic Treemap with plotly.express\n", + "\n", + "[Plotly Express](/python/plotly-express/) is the easy-to-use, high-level interface to Plotly, which [operates on a variety of types of data](/python/px-arguments/) and produces [easy-to-style figures](/python/styling-plotly-express/).\n", + "\n", + "With `px.treemap`, each row of the DataFrame is represented as a sector of the treemap." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "b4574e7e", + "metadata": {}, + "outputs": [], + "source": [ + "import plotly.express as px\n", + "fig = px.treemap(\n", + " names = [\"Eve\",\"Cain\", \"Seth\", \"Enos\", \"Noam\", \"Abel\", \"Awan\", \"Enoch\", \"Azura\"],\n", + " parents = [\"\", \"Eve\", \"Eve\", \"Seth\", \"Seth\", \"Eve\", \"Eve\", \"Awan\", \"Eve\"]\n", + ")\n", + "fig.update_traces(root_color=\"lightgrey\")\n", + "fig.update_layout(margin = dict(t=50, l=25, r=25, b=25))\n", + "fig.show()" + ] + }, + { + "cell_type": "markdown", + "id": "5d7e38de", + "metadata": {}, + "source": [ + "### Treemap of a rectangular DataFrame with plotly.express\n", + "\n", + "Hierarchical data are often stored as a rectangular dataframe, with different columns corresponding to different levels of the hierarchy. `px.treemap` can take a `path` parameter corresponding to a list of columns. Note that `id` and `parent` should not be provided if `path` is given." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "5c526a39", + "metadata": {}, + "outputs": [], + "source": [ + "import plotly.express as px\n", + "df = px.data.tips()\n", + "fig = px.treemap(df, path=[px.Constant(\"all\"), 'day', 'time', 'sex'], values='total_bill')\n", + "fig.update_traces(root_color=\"lightgrey\")\n", + "fig.update_layout(margin = dict(t=50, l=25, r=25, b=25))\n", + "fig.show()" + ] + }, + { + "cell_type": "markdown", + "id": "18420244", + "metadata": {}, + "source": [ + "### Treemap of a rectangular DataFrame with continuous color argument in px.treemap\n", + "\n", + "If a `color` argument is passed, the color of a node is computed as the average of the color values of its children, weighted by their values.\n", + "\n", + "**Note**: for best results, ensure that the first `path` element is a single root node. In the examples below we are creating a dummy column containing identical values for each row to achieve this." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "5e1552e6", + "metadata": {}, + "outputs": [], + "source": [ + "import plotly.express as px\n", + "import numpy as np\n", + "df = px.data.gapminder().query(\"year == 2007\")\n", + "fig = px.treemap(df, path=[px.Constant(\"world\"), 'continent', 'country'], values='pop',\n", + " color='lifeExp', hover_data=['iso_alpha'],\n", + " color_continuous_scale='RdBu',\n", + " color_continuous_midpoint=np.average(df['lifeExp'], weights=df['pop']))\n", + "fig.update_layout(margin = dict(t=50, l=25, r=25, b=25))\n", + "fig.show()" + ] + }, + { + "cell_type": "markdown", + "id": "9563abad", + "metadata": {}, + "source": [ + "### Treemap of a rectangular DataFrame with discrete color argument in px.treemap\n", + "\n", + "When the argument of `color` corresponds to non-numerical data, discrete colors are used. If a sector has the same value of the `color` column for all its children, then the corresponding color is used, otherwise the first color of the discrete color sequence is used." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "f6324f82", + "metadata": {}, + "outputs": [], + "source": [ + "import plotly.express as px\n", + "df = px.data.tips()\n", + "fig = px.treemap(df, path=[px.Constant(\"all\"), 'sex', 'day', 'time'],\n", + " values='total_bill', color='day')\n", + "fig.update_layout(margin = dict(t=50, l=25, r=25, b=25))\n", + "fig.show()" + ] + }, + { + "cell_type": "markdown", + "id": "939d866d", + "metadata": {}, + "source": [ + "In the example below the color of Saturday and Sunday sectors is the same as Dinner because there are only Dinner entries for Saturday and Sunday. However, for Female -> Friday there are both lunches and dinners, hence the \"mixed\" color (blue here) is used." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "b030e10f", + "metadata": {}, + "outputs": [], + "source": [ + "import plotly.express as px\n", + "df = px.data.tips()\n", + "fig = px.treemap(df, path=[px.Constant(\"all\"), 'sex', 'day', 'time'],\n", + " values='total_bill', color='time')\n", + "fig.update_layout(margin = dict(t=50, l=25, r=25, b=25))\n", + "fig.show()" + ] + }, + { + "cell_type": "markdown", + "id": "fd1952d7", + "metadata": {}, + "source": [ + "### Using an explicit mapping for discrete colors\n", + "\n", + "For more information about discrete colors, see the [dedicated page](/python/discrete-color)." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "af88c70d", + "metadata": {}, + "outputs": [], + "source": [ + "import plotly.express as px\n", + "df = px.data.tips()\n", + "fig = px.treemap(df, path=[px.Constant(\"all\"), 'sex', 'day', 'time'],\n", + " values='total_bill', color='time',\n", + " color_discrete_map={'(?)':'lightgrey', 'Lunch':'gold', 'Dinner':'darkblue'})\n", + "fig.update_layout(margin = dict(t=50, l=25, r=25, b=25))\n", + "fig.show()" + ] + }, + { + "cell_type": "markdown", + "id": "fde00f8b", + "metadata": {}, + "source": [ + "### Rectangular data with missing values\n", + "\n", + "If the dataset is not fully rectangular, missing values should be supplied as `None`." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "7be07bff", + "metadata": {}, + "outputs": [], + "source": [ + "import plotly.express as px\n", + "import pandas as pd\n", + "vendors = [\"A\", \"B\", \"C\", \"D\", None, \"E\", \"F\", \"G\", \"H\", None]\n", + "sectors = [\"Tech\", \"Tech\", \"Finance\", \"Finance\", \"Other\",\n", + " \"Tech\", \"Tech\", \"Finance\", \"Finance\", \"Other\"]\n", + "regions = [\"North\", \"North\", \"North\", \"North\", \"North\",\n", + " \"South\", \"South\", \"South\", \"South\", \"South\"]\n", + "sales = [1, 3, 2, 4, 1, 2, 2, 1, 4, 1]\n", + "df = pd.DataFrame(\n", + " dict(vendors=vendors, sectors=sectors, regions=regions, sales=sales)\n", + ")\n", + "df[\"all\"] = \"all\" # in order to have a single root node\n", + "print(df)\n", + "fig = px.treemap(df, path=['all', 'regions', 'sectors', 'vendors'], values='sales')\n", + "fig.update_traces(root_color=\"lightgrey\")\n", + "fig.update_layout(margin = dict(t=50, l=25, r=25, b=25))\n", + "fig.show()" + ] + }, + { + "cell_type": "markdown", + "id": "180f0727", + "metadata": {}, + "source": [ + "### Treemap with Rounded Corners" + ] + }, + { + "cell_type": "markdown", + "id": "b349e3e1", + "metadata": {}, + "source": [ + "*New in 5.12*\n", + "\n", + "Update treemap sectors to have rounded corners by configuring the `cornerradius` in px." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "65d40bac", + "metadata": {}, + "outputs": [], + "source": [ + "import plotly.express as px\n", + "fig = px.treemap(\n", + " names = [\"Eve\",\"Cain\", \"Seth\", \"Enos\", \"Noam\", \"Abel\", \"Awan\", \"Enoch\", \"Azura\"],\n", + " parents = [\"\", \"Eve\", \"Eve\", \"Seth\", \"Seth\", \"Eve\", \"Eve\", \"Awan\", \"Eve\"]\n", + ")\n", + "fig.update_traces(marker=dict(cornerradius=5))\n", + "fig.show()" + ] + }, + { + "cell_type": "markdown", + "id": "7aa7f7be", + "metadata": {}, + "source": [ + "### Basic Treemap with go.Treemap\n", + "\n", + "If Plotly Express does not provide a good starting point, it is also possible to use [the more generic `go.Treemap` class from `plotly.graph_objects`](/python/graph-objects/)." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "e25029c9", + "metadata": {}, + "outputs": [], + "source": [ + "import plotly.graph_objects as go\n", + "\n", + "fig = go.Figure(go.Treemap(\n", + " labels = [\"Eve\",\"Cain\", \"Seth\", \"Enos\", \"Noam\", \"Abel\", \"Awan\", \"Enoch\", \"Azura\"],\n", + " parents = [\"\", \"Eve\", \"Eve\", \"Seth\", \"Seth\", \"Eve\", \"Eve\", \"Awan\", \"Eve\"],\n", + " root_color=\"lightgrey\"\n", + "))\n", + "\n", + "fig.update_layout(margin = dict(t=50, l=25, r=25, b=25))\n", + "fig.show()" + ] + }, + { + "cell_type": "markdown", + "id": "a8ee0955", + "metadata": {}, + "source": [ + "### Set Different Attributes in Treemap\n", + "\n", + "This example uses the following attributes:\n", + "\n", + "1. [values](https://plotly.com/python/reference/treemap/#treemap-values): sets the values associated with each of the sectors.\n", + "2. [textinfo](https://plotly.com/python/reference/treemap/#treemap-textinfo): determines which trace information appear on the graph that can be 'text', 'value', 'current path', 'percent root', 'percent entry', and 'percent parent', or any combination of them.\n", + "3. [pathbar](https://plotly.com/python/reference/treemap/#treemap-pathbar): a main extra feature of treemap to display the current path of the visible portion of the hierarchical map. It may also be useful for zooming out of the graph.\n", + "4. [branchvalues](https://plotly.com/python/reference/treemap/#treemap-branchvalues): determines how the items in `values` are summed. When set to \"total\", items in `values` are taken to be value of all its descendants. In the example below Eve = 65, which is equal to 14 + 12 + 10 + 2 + 6 + 6 + 1 + 4.\n", + " When set to \"remainder\", items in `values` corresponding to the root and the branches sectors are taken to be the extra part not part of the sum of the values at their leaves." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "9ba19266", + "metadata": {}, + "outputs": [], + "source": [ + "import plotly.graph_objects as go\n", + "from plotly.subplots import make_subplots\n", + "\n", + "labels = [\"Eve\", \"Cain\", \"Seth\", \"Enos\", \"Noam\", \"Abel\", \"Awan\", \"Enoch\", \"Azura\"]\n", + "parents = [\"\", \"Eve\", \"Eve\", \"Seth\", \"Seth\", \"Eve\", \"Eve\", \"Awan\", \"Eve\"]\n", + "\n", + "fig = make_subplots(\n", + " cols = 2, rows = 1,\n", + " column_widths = [0.4, 0.4],\n", + " subplot_titles = ('branchvalues: remainder
 
', 'branchvalues: total
 
'),\n", + " specs = [[{'type': 'treemap', 'rowspan': 1}, {'type': 'treemap'}]]\n", + ")\n", + "\n", + "fig.add_trace(go.Treemap(\n", + " labels = labels,\n", + " parents = parents,\n", + " values = [10, 14, 12, 10, 2, 6, 6, 1, 4],\n", + " textinfo = \"label+value+percent parent+percent entry+percent root\",\n", + " root_color=\"lightgrey\"\n", + "),row = 1, col = 1)\n", + "\n", + "fig.add_trace(go.Treemap(\n", + " branchvalues = \"total\",\n", + " labels = labels,\n", + " parents = parents,\n", + " values = [65, 14, 12, 10, 2, 6, 6, 1, 4],\n", + " textinfo = \"label+value+percent parent+percent entry\",\n", + " root_color=\"lightgrey\"\n", + "),row = 1, col = 2)\n", + "\n", + "fig.update_layout(margin = dict(t=50, l=25, r=25, b=25))\n", + "fig.show()" + ] + }, + { + "cell_type": "markdown", + "id": "fc8c8bb9", + "metadata": {}, + "source": [ + "### Set Color of Treemap Sectors\n", + "\n", + "There are three different ways to change the color of the sectors in Treemap:\n", + "\n", + "1. [marker.colors](https://plotly.com/python/reference/treemap/#treemap-marker-colors), 2) [colorway](https://plotly.com/python/reference/treemap/#treemap-colorway), 3) [colorscale](https://plotly.com/python/reference/treemap/#treemap-colorscale). The following examples show how to use each of them." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "446dc47f", + "metadata": {}, + "outputs": [], + "source": [ + "import plotly.graph_objects as go\n", + "\n", + "values = [0, 11, 12, 13, 14, 15, 20, 30]\n", + "labels = [\"container\", \"A1\", \"A2\", \"A3\", \"A4\", \"A5\", \"B1\", \"B2\"]\n", + "parents = [\"\", \"container\", \"A1\", \"A2\", \"A3\", \"A4\", \"container\", \"B1\"]\n", + "\n", + "fig = go.Figure(go.Treemap(\n", + " labels = labels,\n", + " values = values,\n", + " parents = parents,\n", + " marker_colors = [\"pink\", \"royalblue\", \"lightgray\", \"purple\",\n", + " \"cyan\", \"lightgray\", \"lightblue\", \"lightgreen\"]\n", + "))\n", + "\n", + "fig.update_layout(margin = dict(t=50, l=25, r=25, b=25))\n", + "fig.show()" + ] + }, + { + "cell_type": "markdown", + "id": "52e78d72", + "metadata": {}, + "source": [ + "This example uses `treemapcolorway` attribute, which should be set in layout." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "8b945c89", + "metadata": {}, + "outputs": [], + "source": [ + "import plotly.graph_objects as go\n", + "\n", + "values = [0, 11, 12, 13, 14, 15, 20, 30]\n", + "labels = [\"container\", \"A1\", \"A2\", \"A3\", \"A4\", \"A5\", \"B1\", \"B2\"]\n", + "parents = [\"\", \"container\", \"A1\", \"A2\", \"A3\", \"A4\", \"container\", \"B1\"]\n", + "\n", + "fig = go.Figure(go.Treemap(\n", + " labels = labels,\n", + " values = values,\n", + " parents = parents,\n", + " root_color=\"lightblue\"\n", + "))\n", + "\n", + "fig.update_layout(\n", + " treemapcolorway = [\"pink\", \"lightgray\"],\n", + " margin = dict(t=50, l=25, r=25, b=25)\n", + ")\n", + "fig.show()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "c73c4959", + "metadata": {}, + "outputs": [], + "source": [ + "import plotly.graph_objects as go\n", + "\n", + "values = [0, 11, 12, 13, 14, 15, 20, 30]\n", + "labels = [\"container\", \"A1\", \"A2\", \"A3\", \"A4\", \"A5\", \"B1\", \"B2\"]\n", + "parents = [\"\", \"container\", \"A1\", \"A2\", \"A3\", \"A4\", \"container\", \"B1\"]\n", + "\n", + "fig = go.Figure(go.Treemap(\n", + " labels = labels,\n", + " values = values,\n", + " parents = parents,\n", + " marker_colorscale = 'Blues'\n", + "))\n", + "\n", + "fig.update_layout(margin = dict(t=50, l=25, r=25, b=25))\n", + "\n", + "fig.show()" + ] + }, + { + "cell_type": "markdown", + "id": "e7d83713", + "metadata": {}, + "source": [ + "### Treemap chart with a continuous colorscale\n", + "\n", + "The example below visualizes a breakdown of sales (corresponding to sector width) and call success rate (corresponding to sector color) by region, county and salesperson level. For example, when exploring the data you can see that although the East region is behaving poorly, the Tyler county is still above average -- however, its performance is reduced by the poor success rate of salesperson GT.\n", + "\n", + "In the right subplot which has a `maxdepth` of two levels, click on a sector to see its breakdown to lower levels." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "9a59124c", + "metadata": {}, + "outputs": [], + "source": [ + "import plotly.graph_objects as go\n", + "from plotly.subplots import make_subplots\n", + "import pandas as pd\n", + "\n", + "df = pd.read_csv('https://raw.githubusercontent.com/plotly/datasets/master/sales_success.csv')\n", + "print(df.head())\n", + "\n", + "levels = ['salesperson', 'county', 'region'] # levels used for the hierarchical chart\n", + "color_columns = ['sales', 'calls']\n", + "value_column = 'calls'\n", + "\n", + "def build_hierarchical_dataframe(df, levels, value_column, color_columns=None):\n", + " \"\"\"\n", + " Build a hierarchy of levels for Sunburst or Treemap charts.\n", + "\n", + " Levels are given starting from the bottom to the top of the hierarchy,\n", + " ie the last level corresponds to the root.\n", + " \"\"\"\n", + " df_list = []\n", + " for i, level in enumerate(levels):\n", + " df_tree = pd.DataFrame(columns=['id', 'parent', 'value', 'color'])\n", + " dfg = df.groupby(levels[i:]).sum()\n", + " dfg = dfg.reset_index()\n", + " df_tree['id'] = dfg[level].copy()\n", + " if i < len(levels) - 1:\n", + " df_tree['parent'] = dfg[levels[i+1]].copy()\n", + " else:\n", + " df_tree['parent'] = 'total'\n", + " df_tree['value'] = dfg[value_column]\n", + " df_tree['color'] = dfg[color_columns[0]] / dfg[color_columns[1]]\n", + " df_list.append(df_tree)\n", + " total = pd.Series(dict(id='total', parent='',\n", + " value=df[value_column].sum(),\n", + " color=df[color_columns[0]].sum() / df[color_columns[1]].sum()), name=0)\n", + " df_list.append(total)\n", + " df_all_trees = pd.concat(df_list, ignore_index=True)\n", + " return df_all_trees\n", + "\n", + "\n", + "df_all_trees = build_hierarchical_dataframe(df, levels, value_column, color_columns)\n", + "average_score = df['sales'].sum() / df['calls'].sum()\n", + "\n", + "fig = make_subplots(1, 2, specs=[[{\"type\": \"domain\"}, {\"type\": \"domain\"}]],)\n", + "\n", + "fig.add_trace(go.Treemap(\n", + " labels=df_all_trees['id'],\n", + " parents=df_all_trees['parent'],\n", + " values=df_all_trees['value'],\n", + " branchvalues='total',\n", + " marker=dict(\n", + " colors=df_all_trees['color'],\n", + " colorscale='RdBu',\n", + " cmid=average_score),\n", + " hovertemplate='%{label}
Sales: %{value}
Success rate: %{color:.2f}',\n", + " name=''\n", + " ), 1, 1)\n", + "\n", + "fig.add_trace(go.Treemap(\n", + " labels=df_all_trees['id'],\n", + " parents=df_all_trees['parent'],\n", + " values=df_all_trees['value'],\n", + " branchvalues='total',\n", + " marker=dict(\n", + " colors=df_all_trees['color'],\n", + " colorscale='RdBu',\n", + " cmid=average_score),\n", + " hovertemplate='%{label}
Sales: %{value}
Success rate: %{color:.2f}',\n", + " maxdepth=2\n", + " ), 1, 2)\n", + "\n", + "fig.update_layout(margin = dict(t=50, l=25, r=25, b=25))\n", + "fig.show()" + ] + }, + { + "cell_type": "markdown", + "id": "c4625251", + "metadata": {}, + "source": [ + "### Nested Layers in Treemap\n", + "\n", + "The following example uses hierarchical data that includes layers and grouping. Treemap and [Sunburst](https://plotly.com/python/sunburst-charts/) charts reveal insights into the data, and the format of your hierarchical data. [maxdepth](https://plotly.com/python/reference/treemap/#treemap-maxdepth) attribute sets the number of rendered sectors from the given level." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "755d4d52", + "metadata": {}, + "outputs": [], + "source": [ + "import plotly.graph_objects as go\n", + "\n", + "import pandas as pd\n", + "\n", + "df = pd.read_csv('https://raw.githubusercontent.com/plotly/datasets/96c0bd/sunburst-coffee-flavors-complete.csv')\n", + "\n", + "fig = go.Figure()\n", + "\n", + "fig.add_trace(go.Treemap(\n", + " ids = df.ids,\n", + " labels = df.labels,\n", + " parents = df.parents,\n", + " maxdepth=3,\n", + " root_color=\"lightgrey\"\n", + "))\n", + "\n", + "fig.update_layout(margin = dict(t=50, l=25, r=25, b=25))\n", + "\n", + "fig.show()" + ] + }, + { + "cell_type": "markdown", + "id": "450b799f", + "metadata": {}, + "source": [ + "### Controlling text fontsize with uniformtext\n", + "\n", + "If you want all the text labels to have the same size, you can use the `uniformtext` layout parameter. The `minsize` attribute sets the font size, and the `mode` attribute sets what happens for labels which cannot fit with the desired fontsize: either `hide` them or `show` them with overflow.\n", + "\n", + "*Note: animated transitions are currently not implemented when `uniformtext` is used.*" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "fac75903", + "metadata": {}, + "outputs": [], + "source": [ + "import plotly.graph_objects as go\n", + "import pandas as pd\n", + "\n", + "df = pd.read_csv('https://raw.githubusercontent.com/plotly/datasets/96c0bd/sunburst-coffee-flavors-complete.csv')\n", + "\n", + "fig = go.Figure(go.Treemap(\n", + " ids = df.ids,\n", + " labels = df.labels,\n", + " parents = df.parents,\n", + " pathbar_textfont_size=15,\n", + " root_color=\"lightgrey\"\n", + "))\n", + "fig.update_layout(\n", + " uniformtext=dict(minsize=10, mode='hide'),\n", + " margin = dict(t=50, l=25, r=25, b=25)\n", + ")\n", + "fig.show()" + ] + }, + { + "cell_type": "markdown", + "id": "a57d4fa8", + "metadata": {}, + "source": [ + "### Pattern Fills\n", + "\n", + "*New in 5.15*\n", + "\n", + "Treemap charts support [patterns](/python/pattern-hatching-texture/) (also known as hatching or texture) in addition to color. In this example, we apply a pattern to the root node." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "b305d781", + "metadata": {}, + "outputs": [], + "source": [ + "import plotly.graph_objects as go\n", + "\n", + "fig = go.Figure(\n", + " go.Treemap(\n", + " labels = [\"Eve\",\"Cain\", \"Seth\", \"Enos\", \"Noam\", \"Abel\", \"Awan\", \"Enoch\", \"Azura\"],\n", + " parents=[\"\", \"Eve\", \"Eve\", \"Seth\", \"Seth\", \"Eve\", \"Eve\", \"Awan\", \"Eve\"],\n", + " root_color=\"lightgrey\",\n", + " textfont_size=20,\n", + " marker=dict(pattern=dict(shape=[\"|\"], solidity=0.80)),\n", + " )\n", + ")\n", + "\n", + "fig.show()" + ] + }, + { + "cell_type": "markdown", + "id": "dff99385", + "metadata": {}, + "source": [ + "#### Reference\n", + "\n", + "See [function reference for `px.treemap()`](https://plotly.com/python-api-reference/generated/plotly.express.treemap) or https://plotly.com/python/reference/treemap/ for more information and chart attribute options!\n" + ] + }, + { + "cell_type": "markdown", + "id": "043fb013", + "metadata": {}, + "source": [ + "### What About Dash?\n", + "\n", + "[Dash](https://dash.plot.ly/) is an open-source framework for building analytical applications, with no Javascript required, and it is tightly integrated with the Plotly graphing library.\n", + "\n", + "Learn about how to install Dash at https://dash.plot.ly/installation.\n", + "\n", + "Everywhere in this page that you see `fig.show()`, you can display the same figure in a Dash application by passing it to the `figure` argument of the [`Graph` component](https://dash.plot.ly/dash-core-components/graph) from the built-in `dash_core_components` package like this:\n", + "\n", + "```python\n", + "import plotly.graph_objects as go # or plotly.express as px\n", + "fig = go.Figure() # or any Plotly Express function e.g. px.bar(...)\n", + "# fig.add_trace( ... )\n", + "# fig.update_layout( ... )\n", + "\n", + "from dash import Dash, dcc, html\n", + "\n", + "app = Dash()\n", + "app.layout = html.Div([\n", + " dcc.Graph(figure=fig)\n", + "])\n", + "\n", + "app.run_server(debug=True, use_reloader=False) # Turn off reloader if inside Jupyter\n", + "```" + ] + } + ], + "metadata": { + "jupytext": { + "notebook_metadata_filter": "all" + }, + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "language": "python", + "name": "python3" + }, + "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.11" + }, + "plotly": { + "description": "How to make Treemap Charts with Plotly", + "display_as": "basic", + "language": "python", + "layout": "base", + "name": "Treemap Charts", + "order": 13, + "page_type": "u-guide", + "permalink": "python/treemaps/", + "thumbnail": "thumbnail/treemap.png" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/trisurf.ipynb b/trisurf.ipynb new file mode 100644 index 000000000..726de1d7b --- /dev/null +++ b/trisurf.ipynb @@ -0,0 +1,206 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "59308af0", + "metadata": {}, + "source": [ + "Trisurf plots can be made using a [figure factory](/python/figure-factories/) as detailed in this page.\n", + "\n", + "#### Torus" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "d8156b31", + "metadata": {}, + "outputs": [], + "source": [ + "import plotly.figure_factory as ff\n", + "\n", + "import numpy as np\n", + "from scipy.spatial import Delaunay\n", + "\n", + "u = np.linspace(0, 2*np.pi, 20)\n", + "v = np.linspace(0, 2*np.pi, 20)\n", + "u,v = np.meshgrid(u,v)\n", + "u = u.flatten()\n", + "v = v.flatten()\n", + "\n", + "x = (3 + (np.cos(v)))*np.cos(u)\n", + "y = (3 + (np.cos(v)))*np.sin(u)\n", + "z = np.sin(v)\n", + "\n", + "points2D = np.vstack([u,v]).T\n", + "tri = Delaunay(points2D)\n", + "simplices = tri.simplices\n", + "\n", + "fig = ff.create_trisurf(x=x, y=y, z=z,\n", + " simplices=simplices,\n", + " title=dict(text=\"Torus\"), aspectratio=dict(x=1, y=1, z=0.3))\n", + "fig.show()" + ] + }, + { + "cell_type": "markdown", + "id": "06d473cd", + "metadata": {}, + "source": [ + "#### Mobius Band\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "beece4a9", + "metadata": { + "lines_to_next_cell": 2 + }, + "outputs": [], + "source": [ + "import plotly.figure_factory as ff\n", + "\n", + "import numpy as np\n", + "from scipy.spatial import Delaunay\n", + "\n", + "u = np.linspace(0, 2*np.pi, 24)\n", + "v = np.linspace(-1, 1, 8)\n", + "u,v = np.meshgrid(u,v)\n", + "u = u.flatten()\n", + "v = v.flatten()\n", + "\n", + "tp = 1 + 0.5*v*np.cos(u/2.)\n", + "x = tp*np.cos(u)\n", + "y = tp*np.sin(u)\n", + "z = 0.5*v*np.sin(u/2.)\n", + "\n", + "points2D = np.vstack([u,v]).T\n", + "tri = Delaunay(points2D)\n", + "simplices = tri.simplices\n", + "\n", + "fig = ff.create_trisurf(x=x, y=y, z=z,\n", + " colormap=\"Portland\",\n", + " simplices=simplices,\n", + " title=dict(text=\"Mobius Band\"))\n", + "fig.show()" + ] + }, + { + "cell_type": "markdown", + "id": "d7a8fbc3", + "metadata": {}, + "source": [ + "#### Boy's Surface\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "27d26f2b", + "metadata": {}, + "outputs": [], + "source": [ + "import plotly.figure_factory as ff\n", + "\n", + "import numpy as np\n", + "from scipy.spatial import Delaunay\n", + "\n", + "u=np.linspace(-np.pi/2, np.pi/2, 60)\n", + "v=np.linspace(0, np.pi, 60)\n", + "u,v=np.meshgrid(u,v)\n", + "u=u.flatten()\n", + "v=v.flatten()\n", + "\n", + "x = (np.sqrt(2)*(np.cos(v)*np.cos(v))*np.cos(2*u) + np.cos(u)*np.sin(2*v))/(2 - np.sqrt(2)*np.sin(3*u)*np.sin(2*v))\n", + "y = (np.sqrt(2)*(np.cos(v)*np.cos(v))*np.sin(2*u) - np.sin(u)*np.sin(2*v))/(2 - np.sqrt(2)*np.sin(3*u)*np.sin(2*v))\n", + "z = (3*(np.cos(v)*np.cos(v)))/(2 - np.sqrt(2)*np.sin(3*u)*np.sin(2*v))\n", + "\n", + "points2D = np.vstack([u, v]).T\n", + "tri = Delaunay(points2D)\n", + "simplices = tri.simplices\n", + "\n", + "fig = ff.create_trisurf(x=x, y=y, z=z,\n", + " colormap=['rgb(50, 0, 75)', 'rgb(200, 0, 200)', '#c8dcc8'],\n", + " show_colorbar=True,\n", + " simplices=simplices,\n", + " title=dict(text=\"Boy's Surface\"))\n", + "fig.show()" + ] + }, + { + "cell_type": "markdown", + "id": "be1ce0e1", + "metadata": {}, + "source": [ + "#### Reference\n", + "\n", + "For more info on `ff.create_trisurf()`, see the [full function reference](https://plotly.com/python-api-reference/generated/plotly.figure_factory.create_trisurf.html)\n" + ] + }, + { + "cell_type": "markdown", + "id": "6481f2d8", + "metadata": {}, + "source": [ + "### What About Dash?\n", + "\n", + "[Dash](https://dash.plot.ly/) is an open-source framework for building analytical applications, with no Javascript required, and it is tightly integrated with the Plotly graphing library.\n", + "\n", + "Learn about how to install Dash at https://dash.plot.ly/installation.\n", + "\n", + "Everywhere in this page that you see `fig.show()`, you can display the same figure in a Dash application by passing it to the `figure` argument of the [`Graph` component](https://dash.plot.ly/dash-core-components/graph) from the built-in `dash_core_components` package like this:\n", + "\n", + "```python\n", + "import plotly.graph_objects as go # or plotly.express as px\n", + "fig = go.Figure() # or any Plotly Express function e.g. px.bar(...)\n", + "# fig.add_trace( ... )\n", + "# fig.update_layout( ... )\n", + "\n", + "from dash import Dash, dcc, html\n", + "\n", + "app = Dash()\n", + "app.layout = html.Div([\n", + " dcc.Graph(figure=fig)\n", + "])\n", + "\n", + "app.run_server(debug=True, use_reloader=False) # Turn off reloader if inside Jupyter\n", + "```" + ] + } + ], + "metadata": { + "jupytext": { + "notebook_metadata_filter": "all" + }, + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "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.7.7" + }, + "plotly": { + "description": "How to make tri-surf plots in Python with Plotly. Trisurfs are formed by replacing the boundaries of a compact surface by touching triangles.", + "display_as": "3d_charts", + "language": "python", + "layout": "base", + "name": "Trisurf Plots", + "order": 8, + "permalink": "python/trisurf/", + "thumbnail": "thumbnail/trisurf.jpg" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/troubleshooting.ipynb b/troubleshooting.ipynb new file mode 100644 index 000000000..32d29e4ff --- /dev/null +++ b/troubleshooting.ipynb @@ -0,0 +1,142 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "343db126", + "metadata": {}, + "source": [ + "### Version Problems\n", + "\n", + "In order to follow the examples in this documentation site, you should have the latest version of `plotly` installed (5.x), as detailed in the [Getting Started](/python/getting-started) guide. This documentation (under https://plotly.com/python) is compatible with `plotly` version 4.x but *not* with version 3.x, for which the documentation is available under https://plotly.com/python/v3. In general you must also have the correct version of the underlying Plotly.js rendering engine installed, and the way to do that depends on the environment in which you are rendering figures: Dash, Jupyter Lab or Classic Notebook, VSCode etc. Read on for details about troubleshooting `plotly` in these environments.\n", + "\n", + "### Import Problems\n", + "\n", + "It's very important that you not have a file named `plotly.py` in the same directory as the Python script you're running, and this includes not naming the script itself `plotly.py`, otherwise importing `plotly` can fail with mysterious error messages.\n", + "\n", + "Beyond this, most `import` problems or `AttributeError`s can be traced back to having multiple versions of `plotly` installed, for example once with `conda` and once with `pip`. It's often worthwhile to uninstall with both methods before following the [Getting Started](/python/getting-started) instructions from scratch with one or the other. You can run the following commands in a terminal to fully remove `plotly` before installing again:\n", + "\n", + "```bash\n", + "$ conda uninstall plotly\n", + "$ pip uninstall plotly\n", + "```\n", + "\n", + "> Problems can also arise if you have a file named `plotly.py` in the same directory as the code you are executing.\n", + "\n", + "### Dash Problems\n", + "\n", + "If you are encountering problems using `plotly` with [Dash](https://dash.plotly.com/) please first ensure that you have upgraded `dash` to the latest version, which will automatically upgrade `dash-core-components` to the latest version, ensuring that Dash is using an up-to-date version of the Plotly.js rendering engine for `plotly`. If this does not resolve your issue, please visit our [Dash Community Forum](https://community.plotly.com/) and we will be glad to help you out.\n", + "\n", + "This is an example of a `plotly` graph correctly rendering inside `dash`:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "6865eec6", + "metadata": { + "hide_code": true + }, + "outputs": [], + "source": [ + "from IPython.display import IFrame\n", + "snippet_url = 'https://python-docs-dash-snippets.herokuapp.com/python-docs-dash-snippets/'\n", + "IFrame(snippet_url + 'renderers', width='100%', height=1200)" + ] + }, + { + "cell_type": "markdown", + "id": "633287e0", + "metadata": {}, + "source": [ + "

Sign up for Dash Club → Free cheat sheets plus updates from Chris Parmer and Adam Schroeder delivered to your inbox every two months. Includes tips and tricks, community apps, and deep dives into the Dash architecture.\n", + "Join now.

" + ] + }, + { + "cell_type": "markdown", + "id": "cdcda529", + "metadata": {}, + "source": [ + "### VSCode Notebook, Nteract and Streamlit Problems\n", + "\n", + "Plotly figures render in VSCode using a Plotly.js version bundled with the [vscode-python extension](https://code.visualstudio.com/docs/languages/python), and unfortunately it's often a little out of date compared to the latest version of the `plotly` module, so the very latest features may not work until the following release of the vscode-python extension. In any case, regularly upgrading your vscode-python extension to the latest version will ensure you have access to the greatest number of recent features.\n", + "\n", + "The situation is similar for environments like Nteract and Streamlit: in these environments you will need a version of these projects that bundles a version Plotly.js that supports the features in the version of `plotly` that you are running.\n", + "\n", + "### Orca Problems\n", + "\n", + "> Note: as of `plotly` version 4.9, we recommend using [`kaleido`](https://github.com/plotly/Kaleido)\n", + "> instead of Orca for [static image export](/python/static-image-export/)\n", + "\n", + "If you get an error message stating that the `orca` executable that was found is not valid, this may be because another executable with the same name was found on your system. Please specify the complete path to the Plotly-Orca binary that you downloaded (for instance in the Miniconda folder) with the following command:\n", + "\n", + "`plotly.io.orca.config.executable = '/home/your_name/miniconda3/bin/orca'`\n" + ] + }, + { + "cell_type": "markdown", + "id": "8f4937d7", + "metadata": {}, + "source": [ + "### What About Dash?\n", + "\n", + "[Dash](https://dash.plot.ly/) is an open-source framework for building analytical applications, with no Javascript required, and it is tightly integrated with the Plotly graphing library.\n", + "\n", + "Learn about how to install Dash at https://dash.plot.ly/installation.\n", + "\n", + "Everywhere in this page that you see `fig.show()`, you can display the same figure in a Dash application by passing it to the `figure` argument of the [`Graph` component](https://dash.plot.ly/dash-core-components/graph) from the built-in `dash_core_components` package like this:\n", + "\n", + "```python\n", + "import plotly.graph_objects as go # or plotly.express as px\n", + "fig = go.Figure() # or any Plotly Express function e.g. px.bar(...)\n", + "# fig.add_trace( ... )\n", + "# fig.update_layout( ... )\n", + "\n", + "from dash import Dash, dcc, html\n", + "\n", + "app = Dash()\n", + "app.layout = html.Div([\n", + " dcc.Graph(figure=fig)\n", + "])\n", + "\n", + "app.run_server(debug=True, use_reloader=False) # Turn off reloader if inside Jupyter\n", + "```" + ] + } + ], + "metadata": { + "jupytext": { + "notebook_metadata_filter": "all" + }, + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "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.8.8" + }, + "plotly": { + "description": "How to troubleshoot import and rendering problems in Plotly with Python.", + "display_as": "file_settings", + "language": "python", + "layout": "base", + "name": "Troubleshooting", + "order": 27, + "page_type": "u-guide", + "permalink": "python/troubleshooting/", + "thumbnail": "thumbnail/modebar-icons.png" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/v4-migration.ipynb b/v4-migration.ipynb new file mode 100644 index 000000000..8447387d3 --- /dev/null +++ b/v4-migration.ipynb @@ -0,0 +1,441 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "5ffd6e95", + "metadata": {}, + "source": [ + "### Upgrading to Version 4\n", + "\n", + "Upgrading to version 4 of `plotly` is a matter of following the instructions in the [Getting Started](/python/getting-started/) guide and reinstalling the packages, subject to the notices below.\n", + "\n", + "### Getting Help\n", + "\n", + "If you encounter issues in upgrading from version 3 to version 4, please reach out in our [Community Forum](https://community.plotly.com/c/api/python) or if you've found an issue or regression in version 4, please report a [Github Issue](https://github.com/plotly/plotly.py/issues/new)" + ] + }, + { + "cell_type": "markdown", + "id": "c96c9639", + "metadata": {}, + "source": [ + "### Online features (`plotly.plotly`) moved to `chart-studio` package\n", + "\n", + "Prior versions of plotly.py contained functionality for creating figures in both \"online\" and \"offline\" modes. In \"online\" mode figures were uploaded to the Chart Studio cloud (or on-premise) service, whereas in \"offline\" mode figures were rendered locally. **Version 4 of `plotly` is \"offline\"-only: all \"online\" functionality has been removed from the main `plotly` distribution package and moved to the new `chart-studio` distribution package.**\n", + "\n", + "To migrate version 3 \"online\" functionality, first install the `chart-studio` package using pip...\n", + "\n", + "```\n", + "$ pip install chart-studio\n", + "```\n", + "\n", + "or conda.\n", + "\n", + "```\n", + "$ conda install -c plotly chart-studio\n", + "```\n", + "\n", + "Then, update your Python import statements to import \"online\" functionality from the top-level `chart_studio` package, rather than the top-level `plotly` package. For example. replace\n", + "\n", + "```python\n", + "from plotly.plotly import plot, iplot\n", + "```\n", + "\n", + "with\n", + "\n", + "```python\n", + "from chart_studio.plotly import plot, iplot\n", + "```\n", + "\n", + "Similarly,\n", + " - Replace **`plotly.api`** with **`chart_studio.api`**\n", + " - Replace **`plotly.dashboard_objs`** with **`chart_studio.dashboard_objs`**\n", + " - Replace **`plotly.grid_objs`** with **`chart_studio.grid_objs`**\n", + " - Replace **`plotly.presentation_objs`** with **`chart_studio.presentation_objs`**\n", + " - Replace **`plotly.widgets`** with **`chart_studio.widgets`**" + ] + }, + { + "cell_type": "markdown", + "id": "2289219f", + "metadata": {}, + "source": [ + "### Offline features (`plotly.offline`) replaced by Renderers framework & HTML export\n", + "\n", + "Version 4 introduces a new renderers framework that is a generalization of version 3's `plotly.offline.init_notebook_mode` and `plotly.offline.iplot` functions for displaying figures. *This is a non-breaking change*: the `plotly.offline.iplot` function is still available and has been reimplemented on top of the renderers framework, so no changes are required when porting to version 4. Going forward, we recommend using the renderers framework directly. See [Displaying plotly figures](/python/renderers) for more information.\n", + "\n", + "\n", + "In version 3, the `plotly.offline.plot` function was used to export figures to HTML files. In version 4, this function has been reimplemented on top of the new `to_html` and `write_html` functions from the `plotly.io` module. These functions have a slightly more consistent API (see docstrings for details), and going forward we recommend using them directly when performing HTML export. When working with a graph object figure, these functions are also available as the `.to_html` and `.write_html` figure methods." + ] + }, + { + "cell_type": "markdown", + "id": "117622aa", + "metadata": {}, + "source": [ + "### New default theme\n", + "An updated `\"plotly\"` theme has been enabled by default in version 4." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "28530792", + "metadata": {}, + "outputs": [], + "source": [ + "import plotly.graph_objects as go\n", + "from plotly.subplots import make_subplots\n", + "import pandas as pd\n", + "\n", + "# Make figure with subplots\n", + "fig = make_subplots(rows=1, cols=2, specs=[[{\"type\": \"bar\"},\n", + " {\"type\": \"surface\"}]])\n", + "\n", + "# Add bar traces to subplot (1, 1)\n", + "fig.add_trace(go.Bar(y=[2, 1, 3]), row=1, col=1)\n", + "fig.add_trace(go.Bar(y=[3, 2, 1]), row=1, col=1)\n", + "fig.add_trace(go.Bar(y=[2.5, 2.5, 3.5]), row=1, col=1)\n", + "\n", + "# Add surface trace to subplot (1, 2)\n", + "# Read data from a csv\n", + "z_data = pd.read_csv(\"https://raw.githubusercontent.com/plotly/datasets/master/api_docs/mt_bruno_elevation.csv\")\n", + "fig.add_surface(z=z_data)\n", + "\n", + "# Hide legend\n", + "fig.update_layout(\n", + " showlegend=False,\n", + " title_text=\"Default Theme\",\n", + " height=500,\n", + " width=800,\n", + ")\n", + "\n", + "fig.show()" + ] + }, + { + "cell_type": "markdown", + "id": "4a95222f", + "metadata": {}, + "source": [ + "You can revert to the version 3 figure appearance by disabling the default theme as follows:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "35044050", + "metadata": {}, + "outputs": [], + "source": [ + "import plotly.io as pio\n", + "pio.templates.default = \"none\"\n", + "\n", + "import plotly.graph_objects as go\n", + "from plotly.subplots import make_subplots\n", + "import pandas as pd\n", + "\n", + "# Make figure with subplots\n", + "fig = make_subplots(rows=1, cols=2, specs=[[{\"type\": \"bar\"},\n", + " {\"type\": \"surface\"}]])\n", + "\n", + "# Add bar traces to subplot (1, 1)\n", + "fig.add_trace(go.Bar(y=[2, 1, 3]), row=1, col=1)\n", + "fig.add_trace(go.Bar(y=[3, 2, 1]), row=1, col=1)\n", + "fig.add_trace(go.Bar(y=[2.5, 2.5, 3.5]), row=1, col=1)\n", + "\n", + "# Add surface trace to subplot (1, 2)\n", + "# Read data from a csv\n", + "z_data = pd.read_csv(\"https://raw.githubusercontent.com/plotly/datasets/master/api_docs/mt_bruno_elevation.csv\")\n", + "fig.add_surface(z=z_data)\n", + "\n", + "# Hide legend\n", + "fig.update_layout(\n", + " showlegend=False,\n", + " title_text=\"Default Theme Disabled\",\n", + " height=500,\n", + " width=800,\n", + ")\n", + "\n", + "fig.show()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "fb9cdd59", + "metadata": {}, + "outputs": [], + "source": [ + "# Restore default theme\n", + "pio.templates.default = \"plotly\"" + ] + }, + { + "cell_type": "markdown", + "id": "5dd301e0", + "metadata": {}, + "source": [ + "See [Theming and templates](/python/templates) for more information on theming in plotly.py version 4.\n", + "\n", + "### Add trace return value\n", + "In version 3, the `add_trace` graph object figure method returned a reference to the newly created trace. This was also the case for the `add_{trace_type}` methods (e.g. `add_scatter`, `add_bar`, etc.). In version 4, these methods return a reference to the calling figure. This change was made to support method chaining of figure operations. For example" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "887b12ec", + "metadata": {}, + "outputs": [], + "source": [ + "from plotly.subplots import make_subplots\n", + "(make_subplots(rows=1, cols=2)\n", + " .add_scatter(y=[2, 1, 3], row=1, col=1)\n", + " .add_bar(y=[3, 2, 1], row=1, col=2)\n", + " .update_layout(\n", + " title_text=\"Figure title\",\n", + " showlegend=False,\n", + " width=800,\n", + " height=500,\n", + " )\n", + " .show())" + ] + }, + { + "cell_type": "markdown", + "id": "971a8663", + "metadata": {}, + "source": [ + "Code that relied on the `add_*` methods to return a reference to the newly created trace will need to be updated to access the trace from the returned figure. This can be done by appending `.data[-1]` to the add trace expression.\n", + "\n", + "Here is an example of a version 3 code snippet that adds a scatter trace to a figure, assigns the result to a variable named `scatter`, and then modifies the marker size of the scatter trace.\n", + "\n", + "```python\n", + "import plotly.graph_objs as go\n", + "fig = go.Figure()\n", + "scatter = fig.add_trace(go.Scatter(y=[2, 3, 1]))\n", + "scatter.marker.size = 20\n", + "```\n", + "In version 4, this would be replaced with the following:\n", + "\n", + "```python\n", + "import plotly.graph_objects as go\n", + "fig = go.Figure()\n", + "scatter = fig.add_trace(go.Scatter(y=[2, 3, 1])).data[-1]\n", + "scatter.marker.size = 20\n", + "```" + ] + }, + { + "cell_type": "markdown", + "id": "25c37857", + "metadata": {}, + "source": [ + "### `make_subplots` updates\n", + "The `make_subplots` function has been overhauled to support all trace types and to support the integration of Plotly Express. Here are a few changes to be aware of when porting code that uses `make_subplots` to version 4.\n", + "\n", + "#### New preferred import location\n", + "The preferred import location of the `make_subplots` function is now `plotly.subplots.make_subplots`. For compatibility, this function is still available as `plotly.tools.make_subplots`.\n", + "\n", + "#### Grid no longer printed by default\n", + "When the `print_grid` argument to `make_subplots` is set to `True`, a text representation of the subplot grid is printed by the `make_subplots` function. In version 3, the default value of `print_grid` was `True`. In version 4, the default value of `print_grid` is `False`.\n", + "\n", + "#### New `row_heights` argument to replace `row_width`\n", + "The legacy argument for specifying the relative height of subplot rows was called `row_width`. A new `row_heights` argument has been introduced for this purpose.\n", + "\n", + "> Note: Although it is not mentioned in the docstring for `plotly.subplots.make_subplots`, the legacy `row_width` argument, with the legacy behavior, is still available in version 4.\n", + "\n", + "In addition to having a more consistent name, values specified to the new `row_heights` argument properly honor the `start_cell` argument. With the legacy `row_width` argument, the list of heights was always interpreted from the bottom row to the top row, even if `start_cell==\"top-left\"`. With the new `row_heights` argument, the list of heights is interpreted from top to bottom if `start_cell==\"top-left\"` and from bottom to top if `start_cell==\"bottom-left\"`.\n", + "\n", + "When porting code from `row_width` to `row_heights`, the order of the heights list must be reversed if `start_cell==\"top-left\"` or `start_cell` was unspecified.\n", + "\n", + "Here is a version 3 compatible example that uses the `row_width` argument to create a figure with subplots where the top row is twice as tall as the bottom row." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "d2169d19", + "metadata": {}, + "outputs": [], + "source": [ + "from plotly.subplots import make_subplots\n", + "\n", + "fig = make_subplots(\n", + " rows=2, cols=1,\n", + " row_width=[0.33, 0.67],\n", + " start_cell=\"top-left\")\n", + "\n", + "fig.add_scatter(y=[2, 1, 3], row=1, col=1)\n", + "fig.add_bar(y=[2, 3, 1], row=2, col=1)\n", + "fig.show()" + ] + }, + { + "cell_type": "markdown", + "id": "8a41d146", + "metadata": {}, + "source": [ + "And here is the equivalent, version 4 example. Note how the order to the height list is reversed compared to the example above." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "1fc696a6", + "metadata": {}, + "outputs": [], + "source": [ + "from plotly.subplots import make_subplots\n", + "\n", + "fig = make_subplots(\n", + " rows=2, cols=1,\n", + " row_heights=[0.67, 0.33],\n", + " start_cell=\"top-left\")\n", + "\n", + "fig.add_scatter(y=[2, 1, 3], row=1, col=1)\n", + "fig.add_bar(y=[2, 3, 1], row=2, col=1)\n", + "fig.show()" + ] + }, + { + "cell_type": "markdown", + "id": "327bafc6", + "metadata": {}, + "source": [ + "#### Implementation of shared axes with `make_subplots`\n", + "The implementation of shared axis support in the `make_subplots` function has been simplified. Prior to version 4, shared y-axes were implemented by associating a single `yaxis` object with multiple `xaxis` objects, and vica versa.\n", + "\n", + "In version 4, every 2D Cartesian subplot has a dedicated x-axis and and a dedicated y-axis. Axes are now \"shared\" by being linked together using the `matches` axis property.\n", + "\n", + "For legacy code that makes use of the `make_subplots` and add trace APIs, this change does not require any action on the user's part. However, legacy code that uses `make_subplots` to create a figure with shared axes, and then manipulates the axes directly, may require updates. The output of the `.print_grid` method on a figure created using `make_subplots` can be used to identify which axis objects are associated with each subplot." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "e2d25b0f", + "metadata": {}, + "outputs": [], + "source": [ + "from plotly.subplots import make_subplots\n", + "fig = make_subplots(rows=1, cols=2, shared_yaxes=True)\n", + "fig.print_grid()\n", + "print(fig)" + ] + }, + { + "cell_type": "markdown", + "id": "e79c958a", + "metadata": {}, + "source": [ + "### Trace UIDs\n", + "In version 3, all trace graph objects were copied and assigned a new `uid` property when being added to a `Figure`. In version 4, these `uid` properties are only generated automatically when a trace is added to a `FigureWidget`. When a trace is added to a standard `Figure` graph object the input `uid`, if provided, is accepted as is.\n", + "\n", + "### Timezones\n", + "Prior to version 4, when plotly.py was passed a `datetime` that included a timezone, the `datetime` was automatically converted to UTC. In version 4, this conversion is no longer performed, and `datetime` objects are accepted and displayed in local time." + ] + }, + { + "cell_type": "markdown", + "id": "17e0de45", + "metadata": {}, + "source": [ + "### Headless image export on Linux with Xvfb.\n", + "In version 4, the static image export logic attempts to automatically detect whether to call the orca image export utility using Xvfb. Xvfb is needed for orca to work in a Linux environment if an X11 display server is not available. By default, Xvfb is used if plotly.py is running on Linux if no X11 display server is detected and `Xvfb` is available on the system `PATH`.\n", + "\n", + "This new behavior can be disabled by setting the `use_xvfb` orca configuration option to `False` as follows:\n", + "\n", + "```python\n", + "import plotly.io as pio\n", + "pio.orca.config.use_xvfb = False\n", + "```" + ] + }, + { + "cell_type": "markdown", + "id": "301bfd12", + "metadata": {}, + "source": [ + "### Removals\n", + "\n", + "#### fileopt argument removal\n", + "The `fileopt` argument to `chart_studio.plotly.plot` has been removed, so in-place modifications to previously published figures are no longer supported.\n", + "\n", + "#### Legacy online `GraphWidget`\n", + "The legacy online-only `GraphWidget` class has been removed. Please use the `plotly.graph_objects.FigureWidget` class instead. See the [Figure Widget Overview](/python/figurewidget/) for more information.\n", + "\n", + "### Recommended style updates\n", + "\n", + "#### Import from `graph_objects` instead of `graph_objs`\n", + "The legacy `plotly.graph_objs` package has been aliased as `plotly.graph_objects` because the latter is much easier to communicate verbally. The `plotly.graph_objs` package is still available for backward compatibility." + ] + }, + { + "cell_type": "markdown", + "id": "3ff52e65", + "metadata": {}, + "source": [ + "### What About Dash?\n", + "\n", + "[Dash](https://dash.plot.ly/) is an open-source framework for building analytical applications, with no Javascript required, and it is tightly integrated with the Plotly graphing library.\n", + "\n", + "Learn about how to install Dash at https://dash.plot.ly/installation.\n", + "\n", + "Everywhere in this page that you see `fig.show()`, you can display the same figure in a Dash application by passing it to the `figure` argument of the [`Graph` component](https://dash.plot.ly/dash-core-components/graph) from the built-in `dash_core_components` package like this:\n", + "\n", + "```python\n", + "import plotly.graph_objects as go # or plotly.express as px\n", + "fig = go.Figure() # or any Plotly Express function e.g. px.bar(...)\n", + "# fig.add_trace( ... )\n", + "# fig.update_layout( ... )\n", + "\n", + "from dash import Dash, dcc, html\n", + "\n", + "app = Dash()\n", + "app.layout = html.Div([\n", + " dcc.Graph(figure=fig)\n", + "])\n", + "\n", + "app.run_server(debug=True, use_reloader=False) # Turn off reloader if inside Jupyter\n", + "```" + ] + } + ], + "metadata": { + "jupytext": { + "notebook_metadata_filter": "all" + }, + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "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.6.7" + }, + "plotly": { + "description": "Migration guide for upgrading from version 3 to version 4", + "display_as": "file_settings", + "language": "python", + "layout": "base", + "name": "Version 4 Migration Guide", + "order": 39, + "page_type": "example_index", + "permalink": "python/v4-migration/", + "thumbnail": "thumbnail/v4-migration.png" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/v6-changes.ipynb b/v6-changes.ipynb new file mode 100644 index 000000000..1a661919b --- /dev/null +++ b/v6-changes.ipynb @@ -0,0 +1,193 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "7f0df024", + "metadata": {}, + "source": [ + "This page outlines the changes in Plotly.py version 6 and cases where you may need to update your charts or tools that you use for working with Plotly.py." + ] + }, + { + "cell_type": "markdown", + "id": "dbada94d", + "metadata": {}, + "source": [ + "## Jupyter Notebook Support\n", + "\n", + "Versions of Jupyter Notebook earlier than version 7 are no longer supported. To upgrade to the latest Jupyter Notebook:\n", + "\n", + "```\n", + "pip install notebook --upgrade\n", + "```\n", + "\n", + "## Change to anywidget for go.FigureWidget\n", + "\n", + "[go.FigureWidget](https://plotly.com/python/figurewidget/) now uses [anywidget](https://anywidget.dev/). Install `anywidget` with:\n", + "\n", + "```python\n", + "pip install anywidget\n", + "```\n", + "\n", + "## Processing NumPy and NumPy-Convertible Arrays\n", + "\n", + "Plotly.py now takes advantage of recent changes in how Plotly.js handles typed arrays for improved performance. See the [performance page](https://plotly.com/python/performance/) for more details.\n", + "\n", + "> If you are using Plotly.py 6 or later with Dash Design Kit, you may need to upgrade your Dash Design Kit version. See the [Dash Design Kit Compatibility section on the performance page](/python/performance/#dash-design-kit-compatibility) for more details.\n", + "\n", + "\n", + "## Dataframe Support\n", + "\n", + "Plotly Express now uses [Narwhals](https://narwhals-dev.github.io/narwhals/) to natively support pandas, Polars, and PyArrow. With this change, the [performance](https://plotly.com/python/performance/) of using Polars or PyArrow with Plotly Express is significantly improved.\n", + "\n", + "## Mapbox Deprecation\n", + "\n", + "Mapbox-based traces are deprecated and will be removed in a future version of Plotly.py. Use [Maplibre-based](https://plotly.com/python/mapbox-to-maplibre/) traces instead.\n", + "\n", + "## Removed Attributes\n", + "\n", + "The following attributes have been removed in Plotly.py 6.\n", + "\n", + "### `titlefont`,`titleposition`, `titleside`, and `titleoffset`\n", + "\n", + "The layout attributes `titlefont`,`titleposition`, `titleside`, and `titleoffset` have been removed. Replace them with `title.font`, `title.position`, `title.side`, and `title.offset`.\n", + "\n", + "The following example shows how to use `layout.title.font`:\n", + "\n", + "```python\n", + "import plotly.graph_objects as go\n", + "\n", + "fig = go.Figure(\n", + " data=[\n", + " go.Bar(\n", + " x=[\"A\", \"B\", \"C\", \"D\"],\n", + " y=[10, 15, 13, 17]\n", + " )\n", + " ],\n", + " layout=dict(\n", + " title=dict(\n", + " text=\"Chart Title\",\n", + " font=dict(\n", + " size=40\n", + " )\n", + " )\n", + " ),\n", + " # Previously the title font could be set like this:\n", + " # titlefont=dict(size=40)\n", + ")\n", + "\n", + "fig.show()\n", + "```\n", + "\n", + "## Removed Traces\n", + "\n", + "The following traces have been removed.\n", + "\n", + "### `heatmapgl`\n", + "\n", + "The `heatmapgl` trace has been removed. Use [`heatmap`](/python/heatmaps/) instead.\n", + "\n", + "\n", + "### `pointcloud`\n", + "\n", + "The `pointcloud` trace has been removed. Use [`scattergl`](/python/reference/scattergl/).\n" + ] + }, + { + "cell_type": "markdown", + "id": "9f481cb0", + "metadata": { + "lines_to_next_cell": 2 + }, + "source": [ + "## Other Removed Features\n", + "\n", + "### Transforms\n", + "\n", + "Transforms, which were deprecated in Plotly.py v5, have been removed. You can achieve similar functionality by preprocessing the data with a dataframe library.\n", + "\n", + "For example, a transform to filter the data:\n", + "\n", + "```python\n", + " dict(\n", + " type = 'filter',\n", + " target = df['year'],\n", + " orientation = '=',\n", + " value = 2007\n", + " ),\n", + "```\n", + "\n", + "Could be rewritten using Pandas:\n", + "\n", + "```python\n", + "df_2007 = df[df['year'] == 2007]\n", + "```" + ] + }, + { + "cell_type": "markdown", + "id": "e6c0ac64", + "metadata": {}, + "source": [ + "### What About Dash?\n", + "\n", + "[Dash](https://dash.plot.ly/) is an open-source framework for building analytical applications, with no Javascript required, and it is tightly integrated with the Plotly graphing library.\n", + "\n", + "Learn about how to install Dash at https://dash.plot.ly/installation.\n", + "\n", + "Everywhere in this page that you see `fig.show()`, you can display the same figure in a Dash application by passing it to the `figure` argument of the [`Graph` component](https://dash.plot.ly/dash-core-components/graph) from the built-in `dash_core_components` package like this:\n", + "\n", + "```python\n", + "import plotly.graph_objects as go # or plotly.express as px\n", + "fig = go.Figure() # or any Plotly Express function e.g. px.bar(...)\n", + "# fig.add_trace( ... )\n", + "# fig.update_layout( ... )\n", + "\n", + "from dash import Dash, dcc, html\n", + "\n", + "app = Dash()\n", + "app.layout = html.Div([\n", + " dcc.Graph(figure=fig)\n", + "])\n", + "\n", + "app.run_server(debug=True, use_reloader=False) # Turn off reloader if inside Jupyter\n", + "```" + ] + } + ], + "metadata": { + "jupytext": { + "notebook_metadata_filter": "all" + }, + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "language": "python", + "name": "python3" + }, + "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.11.10" + }, + "plotly": { + "description": "Guide to changes in version 6 of Plotly.py and how to migrate from version 5", + "display_as": "file_settings", + "language": "python", + "layout": "base", + "name": "Changes in Version 6", + "order": 8, + "page_type": "example_index", + "permalink": "python/v6-migration/", + "thumbnail": "thumbnail/v4-migration.png" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/violin.ipynb b/violin.ipynb new file mode 100644 index 000000000..ce79f0180 --- /dev/null +++ b/violin.ipynb @@ -0,0 +1,479 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "21e43131", + "metadata": {}, + "source": [ + "## Violin Plot with Plotly Express\n", + "\n", + "A [violin plot](https://en.wikipedia.org/wiki/Violin_plot) is a statistical representation of numerical data. It is similar to a [box plot](https://plotly.com/python/box-plots/), with the addition of a rotated [kernel density](https://en.wikipedia.org/wiki/Kernel_density_estimation) plot on each side.\n", + "\n", + "Alternatives to violin plots for visualizing distributions include [histograms](https://plotly.com/python/histograms/), [box plots](https://plotly.com/python/box-plots/), [ECDF plots](https://plotly.com/python/ecdf-plots/) and [strip charts](https://plotly.com/python/strip-charts/).\n", + "\n", + "\n", + "### Basic Violin Plot with Plotly Express\n", + "\n", + "[Plotly Express](/python/plotly-express/) is the easy-to-use, high-level interface to Plotly, which [operates on a variety of types of data](/python/px-arguments/) and produces [easy-to-style figures](/python/styling-plotly-express/)." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "2543e049", + "metadata": {}, + "outputs": [], + "source": [ + "import plotly.express as px\n", + "\n", + "df = px.data.tips()\n", + "fig = px.violin(df, y=\"total_bill\")\n", + "fig.show()" + ] + }, + { + "cell_type": "markdown", + "id": "13afe47d", + "metadata": {}, + "source": [ + "### Violin plot with box and data points" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "9552ceb3", + "metadata": {}, + "outputs": [], + "source": [ + "import plotly.express as px\n", + "\n", + "df = px.data.tips()\n", + "fig = px.violin(df, y=\"total_bill\", box=True, # draw box plot inside the violin\n", + " points='all', # can be 'outliers', or False\n", + " )\n", + "fig.show()" + ] + }, + { + "cell_type": "markdown", + "id": "d299b88a", + "metadata": {}, + "source": [ + "### Multiple Violin Plots" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "078c6b70", + "metadata": {}, + "outputs": [], + "source": [ + "import plotly.express as px\n", + "\n", + "df = px.data.tips()\n", + "fig = px.violin(df, y=\"tip\", x=\"smoker\", color=\"sex\", box=True, points=\"all\",\n", + " hover_data=df.columns)\n", + "fig.show()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "19dedfdb", + "metadata": {}, + "outputs": [], + "source": [ + "import plotly.express as px\n", + "\n", + "df = px.data.tips()\n", + "fig = px.violin(df, y=\"tip\", color=\"sex\",\n", + " violinmode='overlay', # draw violins on top of each other\n", + " # default violinmode is 'group' as in example above\n", + " hover_data=df.columns)\n", + "fig.show()" + ] + }, + { + "cell_type": "markdown", + "id": "fd01811e", + "metadata": {}, + "source": [ + "## Violin Plot with go.Violin\n", + "\n", + "If Plotly Express does not provide a good starting point, you can use [the more generic `go.Violin` class from `plotly.graph_objects`](/python/graph-objects/). All the options of `go.Violin` are documented in the reference https://plotly.com/python/reference/violin/\n", + "\n", + "#### Basic Violin Plot" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "e04aba77", + "metadata": {}, + "outputs": [], + "source": [ + "import plotly.graph_objects as go\n", + "\n", + "import pandas as pd\n", + "\n", + "df = pd.read_csv(\"https://raw.githubusercontent.com/plotly/datasets/master/violin_data.csv\")\n", + "\n", + "fig = go.Figure(data=go.Violin(y=df['total_bill'], box_visible=True, line_color='black',\n", + " meanline_visible=True, fillcolor='lightseagreen', opacity=0.6,\n", + " x0='Total Bill'))\n", + "\n", + "fig.update_layout(yaxis_zeroline=False)\n", + "fig.show()" + ] + }, + { + "cell_type": "markdown", + "id": "4fdd3e1c", + "metadata": {}, + "source": [ + "#### Multiple Traces" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "dd9d44d9", + "metadata": {}, + "outputs": [], + "source": [ + "import plotly.graph_objects as go\n", + "\n", + "import pandas as pd\n", + "\n", + "df = pd.read_csv(\"https://raw.githubusercontent.com/plotly/datasets/master/violin_data.csv\")\n", + "\n", + "fig = go.Figure()\n", + "\n", + "days = ['Thur', 'Fri', 'Sat', 'Sun']\n", + "\n", + "for day in days:\n", + " fig.add_trace(go.Violin(x=df['day'][df['day'] == day],\n", + " y=df['total_bill'][df['day'] == day],\n", + " name=day,\n", + " box_visible=True,\n", + " meanline_visible=True))\n", + "\n", + "fig.show()" + ] + }, + { + "cell_type": "markdown", + "id": "51c0fdbd", + "metadata": {}, + "source": [ + "#### Grouped Violin Plot" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "59428bb7", + "metadata": {}, + "outputs": [], + "source": [ + "import plotly.graph_objects as go\n", + "\n", + "import pandas as pd\n", + "\n", + "df = pd.read_csv(\"https://raw.githubusercontent.com/plotly/datasets/master/violin_data.csv\")\n", + "\n", + "fig = go.Figure()\n", + "\n", + "fig.add_trace(go.Violin(x=df['day'][ df['sex'] == 'Male' ],\n", + " y=df['total_bill'][ df['sex'] == 'Male' ],\n", + " legendgroup='M', scalegroup='M', name='M',\n", + " line_color='blue')\n", + " )\n", + "fig.add_trace(go.Violin(x=df['day'][ df['sex'] == 'Female' ],\n", + " y=df['total_bill'][ df['sex'] == 'Female' ],\n", + " legendgroup='F', scalegroup='F', name='F',\n", + " line_color='orange')\n", + " )\n", + "\n", + "fig.update_traces(box_visible=True, meanline_visible=True)\n", + "fig.update_layout(violinmode='group')\n", + "fig.show()" + ] + }, + { + "cell_type": "markdown", + "id": "a9c7ec22", + "metadata": {}, + "source": [ + "#### Split Violin Plot" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "c22c930d", + "metadata": {}, + "outputs": [], + "source": [ + "import plotly.graph_objects as go\n", + "\n", + "import pandas as pd\n", + "\n", + "df = pd.read_csv(\"https://raw.githubusercontent.com/plotly/datasets/master/violin_data.csv\")\n", + "\n", + "fig = go.Figure()\n", + "\n", + "fig.add_trace(go.Violin(x=df['day'][ df['smoker'] == 'Yes' ],\n", + " y=df['total_bill'][ df['smoker'] == 'Yes' ],\n", + " legendgroup='Yes', scalegroup='Yes', name='Yes',\n", + " side='negative',\n", + " line_color='blue')\n", + " )\n", + "fig.add_trace(go.Violin(x=df['day'][ df['smoker'] == 'No' ],\n", + " y=df['total_bill'][ df['smoker'] == 'No' ],\n", + " legendgroup='No', scalegroup='No', name='No',\n", + " side='positive',\n", + " line_color='orange')\n", + " )\n", + "fig.update_traces(meanline_visible=True)\n", + "fig.update_layout(violingap=0, violinmode='overlay')\n", + "fig.show()" + ] + }, + { + "cell_type": "markdown", + "id": "09232f6e", + "metadata": {}, + "source": [ + "#### Advanced Violin Plot" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "4d7d9240", + "metadata": {}, + "outputs": [], + "source": [ + "import plotly.graph_objects as go\n", + "\n", + "import pandas as pd\n", + "\n", + "df = pd.read_csv(\"https://raw.githubusercontent.com/plotly/datasets/master/violin_data.csv\")\n", + "\n", + "pointpos_male = [-0.9,-1.1,-0.6,-0.3]\n", + "pointpos_female = [0.45,0.55,1,0.4]\n", + "show_legend = [True,False,False,False]\n", + "\n", + "fig = go.Figure()\n", + "\n", + "for i in range(0,len(pd.unique(df['day']))):\n", + " fig.add_trace(go.Violin(x=df['day'][(df['sex'] == 'Male') &\n", + " (df['day'] == pd.unique(df['day'])[i])],\n", + " y=df['total_bill'][(df['sex'] == 'Male')&\n", + " (df['day'] == pd.unique(df['day'])[i])],\n", + " legendgroup='M', scalegroup='M', name='M',\n", + " side='negative',\n", + " pointpos=pointpos_male[i], # where to position points\n", + " line_color='lightseagreen',\n", + " showlegend=show_legend[i])\n", + " )\n", + " fig.add_trace(go.Violin(x=df['day'][(df['sex'] == 'Female') &\n", + " (df['day'] == pd.unique(df['day'])[i])],\n", + " y=df['total_bill'][(df['sex'] == 'Female')&\n", + " (df['day'] == pd.unique(df['day'])[i])],\n", + " legendgroup='F', scalegroup='F', name='F',\n", + " side='positive',\n", + " pointpos=pointpos_female[i],\n", + " line_color='mediumpurple',\n", + " showlegend=show_legend[i])\n", + " )\n", + "\n", + "# update characteristics shared by all traces\n", + "fig.update_traces(meanline_visible=True,\n", + " points='all', # show all points\n", + " jitter=0.05, # add some jitter on points for better visibility\n", + " scalemode='count') #scale violin plot area with total count\n", + "fig.update_layout(\n", + " title_text=\"Total bill distribution
scaled by number of bills per gender\",\n", + " violingap=0, violingroupgap=0, violinmode='overlay')\n", + "fig.show()" + ] + }, + { + "cell_type": "markdown", + "id": "47226b82", + "metadata": {}, + "source": [ + "#### Ridgeline plot\n", + "\n", + "A ridgeline plot ([previously known as Joy Plot](https://serialmentor.com/blog/2017/9/15/goodbye-joyplots)) shows the distribution of a numerical value for several groups. They can be used for visualizing changes in distributions over time or space." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "1e804577", + "metadata": {}, + "outputs": [], + "source": [ + "import plotly.graph_objects as go\n", + "from plotly.colors import n_colors\n", + "import numpy as np\n", + "np.random.seed(1)\n", + "\n", + "# 12 sets of normal distributed random data, with increasing mean and standard deviation\n", + "data = (np.linspace(1, 2, 12)[:, np.newaxis] * np.random.randn(12, 200) +\n", + " (np.arange(12) + 2 * np.random.random(12))[:, np.newaxis])\n", + "\n", + "colors = n_colors('rgb(5, 200, 200)', 'rgb(200, 10, 10)', 12, colortype='rgb')\n", + "\n", + "fig = go.Figure()\n", + "for data_line, color in zip(data, colors):\n", + " fig.add_trace(go.Violin(x=data_line, line_color=color))\n", + "\n", + "fig.update_traces(orientation='h', side='positive', width=3, points=False)\n", + "fig.update_layout(xaxis_showgrid=False, xaxis_zeroline=False)\n", + "fig.show()" + ] + }, + { + "cell_type": "markdown", + "id": "ff481ae5", + "metadata": {}, + "source": [ + "### Violin Plot With Only Points\n", + "\n", + "A [strip chart](/python/strip-charts/) is like a violin plot with points showing, and no violin:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "4d8730f1", + "metadata": {}, + "outputs": [], + "source": [ + "import plotly.express as px\n", + "df = px.data.tips()\n", + "fig = px.strip(df, x='day', y='tip')\n", + "fig.show()" + ] + }, + { + "cell_type": "markdown", + "id": "0d4d4934", + "metadata": {}, + "source": [ + "### Choosing The Algorithm For Computing Quartiles\n", + "\n", + "*New in 5.10*\n", + "\n", + "By default, quartiles for violin plots are computed using the `linear` method (for more about linear interpolation, see #10 listed on [http://jse.amstat.org/v14n3/langford.html](http://jse.amstat.org/v14n3/langford.html) and [https://en.wikipedia.org/wiki/Quartile](https://en.wikipedia.org/wiki/Quartile) for more details).\n", + "\n", + "However, you can also choose to use an `exclusive` or an `inclusive` algorithm to compute quartiles.\n", + "\n", + "The _exclusive_ algorithm uses the median to divide the ordered dataset into two halves. If the sample is odd, it does not include the median in either half. Q1 is then the median of the lower half and Q3 is the median of the upper half.\n", + "\n", + "The _inclusive_ algorithm also uses the median to divide the ordered dataset into two halves, but if the sample is odd, it includes the median in both halves. Q1 is then the median of the lower half and Q3 the median of the upper half." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "c024700c", + "metadata": {}, + "outputs": [], + "source": [ + "import plotly.express as px\n", + "\n", + "df = px.data.tips()\n", + "fig = px.violin(df, y=\"total_bill\")\n", + "fig.update_traces(quartilemethod=\"exclusive\") # or \"inclusive\", or \"linear\" by default\n", + "\n", + "fig.show()" + ] + }, + { + "cell_type": "markdown", + "id": "8bc29ba1", + "metadata": {}, + "source": [ + "#### Reference\n", + "\n", + "See [function reference for `px.violin()`](https://plotly.com/python-api-reference/generated/plotly.express.violin) or https://plotly.com/python/reference/violin/ for more information and chart attribute options!\n" + ] + }, + { + "cell_type": "markdown", + "id": "d340680e", + "metadata": {}, + "source": [ + "### What About Dash?\n", + "\n", + "[Dash](https://dash.plot.ly/) is an open-source framework for building analytical applications, with no Javascript required, and it is tightly integrated with the Plotly graphing library.\n", + "\n", + "Learn about how to install Dash at https://dash.plot.ly/installation.\n", + "\n", + "Everywhere in this page that you see `fig.show()`, you can display the same figure in a Dash application by passing it to the `figure` argument of the [`Graph` component](https://dash.plot.ly/dash-core-components/graph) from the built-in `dash_core_components` package like this:\n", + "\n", + "```python\n", + "import plotly.graph_objects as go # or plotly.express as px\n", + "fig = go.Figure() # or any Plotly Express function e.g. px.bar(...)\n", + "# fig.add_trace( ... )\n", + "# fig.update_layout( ... )\n", + "\n", + "from dash import Dash, dcc, html\n", + "\n", + "app = Dash()\n", + "app.layout = html.Div([\n", + " dcc.Graph(figure=fig)\n", + "])\n", + "\n", + "app.run_server(debug=True, use_reloader=False) # Turn off reloader if inside Jupyter\n", + "```" + ] + } + ], + "metadata": { + "jupytext": { + "notebook_metadata_filter": "all" + }, + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "language": "python", + "name": "python3" + }, + "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.9.0" + }, + "plotly": { + "description": "How to make violin plots in Python with Plotly.", + "display_as": "statistical", + "language": "python", + "layout": "base", + "name": "Violin Plots", + "order": 10, + "page_type": "u-guide", + "permalink": "python/violin/", + "redirect_from": [ + "/python/violin-plot/", + "/python/violin-plots/" + ], + "thumbnail": "thumbnail/violin.jpg" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/waterfall-charts.ipynb b/waterfall-charts.ipynb new file mode 100644 index 000000000..48213eaab --- /dev/null +++ b/waterfall-charts.ipynb @@ -0,0 +1,221 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "3606a87c", + "metadata": {}, + "source": [ + "### Simple Waterfall Chart" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "e15f220f", + "metadata": {}, + "outputs": [], + "source": [ + "import plotly.graph_objects as go\n", + "\n", + "fig = go.Figure(go.Waterfall(\n", + " name = \"20\", orientation = \"v\",\n", + " measure = [\"relative\", \"relative\", \"total\", \"relative\", \"relative\", \"total\"],\n", + " x = [\"Sales\", \"Consulting\", \"Net revenue\", \"Purchases\", \"Other expenses\", \"Profit before tax\"],\n", + " textposition = \"outside\",\n", + " text = [\"+60\", \"+80\", \"\", \"-40\", \"-20\", \"Total\"],\n", + " y = [60, 80, 0, -40, -20, 0],\n", + " connector = {\"line\":{\"color\":\"rgb(63, 63, 63)\"}},\n", + "))\n", + "\n", + "fig.update_layout(\n", + " title = \"Profit and loss statement 2018\",\n", + " showlegend = True\n", + ")\n", + "\n", + "fig.show()" + ] + }, + { + "cell_type": "markdown", + "id": "339d418a", + "metadata": {}, + "source": [ + "### Multi Category Waterfall Chart\n", + "This example uses the [waterfallgroupgap attribute](https://plotly.com/python/reference/layout/#layout-waterfallgroupgap), which sets a gap between bars." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "4100fb5d", + "metadata": {}, + "outputs": [], + "source": [ + "import plotly.graph_objects as go\n", + "\n", + "fig = go.Figure()\n", + "\n", + "fig.add_trace(go.Waterfall(\n", + " x = [[\"2016\", \"2017\", \"2017\", \"2017\", \"2017\", \"2018\", \"2018\", \"2018\", \"2018\"],\n", + " [\"initial\", \"q1\", \"q2\", \"q3\", \"total\", \"q1\", \"q2\", \"q3\", \"total\"]],\n", + " measure = [\"absolute\", \"relative\", \"relative\", \"relative\", \"total\", \"relative\", \"relative\", \"relative\", \"total\"],\n", + " y = [1, 2, 3, -1, None, 1, 2, -4, None],\n", + " base = 1000\n", + "))\n", + "\n", + "fig.add_trace(go.Waterfall(\n", + " x = [[\"2016\", \"2017\", \"2017\", \"2017\", \"2017\", \"2018\", \"2018\", \"2018\", \"2018\"],\n", + " [\"initial\", \"q1\", \"q2\", \"q3\", \"total\", \"q1\", \"q2\", \"q3\", \"total\"]],\n", + " measure = [\"absolute\", \"relative\", \"relative\", \"relative\", \"total\", \"relative\", \"relative\", \"relative\", \"total\"],\n", + " y = [1.1, 2.2, 3.3, -1.1, None, 1.1, 2.2, -4.4, None],\n", + " base = 1000\n", + "))\n", + "\n", + "fig.update_layout(\n", + " waterfallgroupgap = 0.5,\n", + ")\n", + "\n", + "fig.show()" + ] + }, + { + "cell_type": "markdown", + "id": "7b5fefe2", + "metadata": {}, + "source": [ + "### Setting Marker Size and Color\n", + "This example uses [decreasing](https://plotly.com/python/reference/waterfall/#waterfall-decreasing), [increasing](https://plotly.com/python/reference/waterfall/#waterfall-increasing), and [totals](https://plotly.com/python/reference/waterfall/#waterfall-totals) attributes to customize the bars." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "b7052aa4", + "metadata": {}, + "outputs": [], + "source": [ + "import plotly.graph_objects as go\n", + "\n", + "fig = go.Figure(go.Waterfall(\n", + " x = [[\"2016\", \"2017\", \"2017\", \"2017\", \"2017\", \"2018\", \"2018\", \"2018\", \"2018\"],\n", + " [\"initial\", \"q1\", \"q2\", \"q3\", \"total\", \"q1\", \"q2\", \"q3\", \"total\"]],\n", + " measure = [\"absolute\", \"relative\", \"relative\", \"relative\", \"total\", \"relative\", \"relative\", \"relative\", \"total\"],\n", + " y = [10, 20, 30, -10, None, 10, 20, -40, None], base = 300,\n", + " decreasing = {\"marker\":{\"color\":\"Maroon\", \"line\":{\"color\":\"red\", \"width\":2}}},\n", + " increasing = {\"marker\":{\"color\":\"Teal\"}},\n", + " totals = {\"marker\":{\"color\":\"deep sky blue\", \"line\":{\"color\":\"blue\", \"width\":3}}}\n", + "))\n", + "\n", + "fig.update_layout(title = \"Profit and loss statement\", waterfallgap = 0.3)\n", + "\n", + "fig.show()" + ] + }, + { + "cell_type": "markdown", + "id": "34a18ce0", + "metadata": {}, + "source": [ + "### Horizontal Waterfall Chart" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "3853fa45", + "metadata": {}, + "outputs": [], + "source": [ + "import plotly.graph_objects as go\n", + "\n", + "fig = go.Figure(go.Waterfall(\n", + " name = \"2018\", orientation = \"h\", measure = [\"relative\", \"relative\", \"relative\", \"relative\", \"total\", \"relative\",\n", + " \"relative\", \"relative\", \"relative\", \"total\", \"relative\", \"relative\", \"total\", \"relative\", \"total\"],\n", + " y = [\"Sales\", \"Consulting\", \"Maintenance\", \"Other revenue\", \"Net revenue\", \"Purchases\", \"Material expenses\",\n", + " \"Personnel expenses\", \"Other expenses\", \"Operating profit\", \"Investment income\", \"Financial income\",\n", + " \"Profit before tax\", \"Income tax (15%)\", \"Profit after tax\"],\n", + " x = [375, 128, 78, 27, None, -327, -12, -78, -12, None, 32, 89, None, -45, None],\n", + " connector = {\"mode\":\"between\", \"line\":{\"width\":4, \"color\":\"rgb(0, 0, 0)\", \"dash\":\"solid\"}}\n", + "))\n", + "\n", + "fig.update_layout(title = \"Profit and loss statement 2018\")\n", + "\n", + "fig.show()" + ] + }, + { + "cell_type": "markdown", + "id": "45f74b13", + "metadata": {}, + "source": [ + "#### Reference\n", + "See https://plotly.com/python/reference/waterfall/ for more information and chart attribute options!\n" + ] + }, + { + "cell_type": "markdown", + "id": "3486d9d3", + "metadata": {}, + "source": [ + "### What About Dash?\n", + "\n", + "[Dash](https://dash.plot.ly/) is an open-source framework for building analytical applications, with no Javascript required, and it is tightly integrated with the Plotly graphing library.\n", + "\n", + "Learn about how to install Dash at https://dash.plot.ly/installation.\n", + "\n", + "Everywhere in this page that you see `fig.show()`, you can display the same figure in a Dash application by passing it to the `figure` argument of the [`Graph` component](https://dash.plot.ly/dash-core-components/graph) from the built-in `dash_core_components` package like this:\n", + "\n", + "```python\n", + "import plotly.graph_objects as go # or plotly.express as px\n", + "fig = go.Figure() # or any Plotly Express function e.g. px.bar(...)\n", + "# fig.add_trace( ... )\n", + "# fig.update_layout( ... )\n", + "\n", + "from dash import Dash, dcc, html\n", + "\n", + "app = Dash()\n", + "app.layout = html.Div([\n", + " dcc.Graph(figure=fig)\n", + "])\n", + "\n", + "app.run_server(debug=True, use_reloader=False) # Turn off reloader if inside Jupyter\n", + "```" + ] + } + ], + "metadata": { + "jupytext": { + "notebook_metadata_filter": "all" + }, + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "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.6.7" + }, + "plotly": { + "description": "How to make waterfall plots in Python with Plotly.", + "display_as": "financial", + "language": "python", + "layout": "base", + "name": "Waterfall Charts", + "order": 3, + "page_type": "example_index", + "permalink": "python/waterfall-charts/", + "thumbnail": "thumbnail/waterfall-charts.jpg" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/wide-form.ipynb b/wide-form.ipynb new file mode 100644 index 000000000..849db8cc3 --- /dev/null +++ b/wide-form.ipynb @@ -0,0 +1,605 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "10521972", + "metadata": {}, + "source": [ + "### Plotly Express works with Column-oriented, Matrix or Geographic Data\n", + "\n", + "Plotly Express provides functions to visualize a variety of types of data. Most functions such as `px.bar` or `px.scatter` expect to operate on column-oriented data of the type you might store in a `DataFrame` (in either \"long\" or \"wide\" format, see below). These functions use Pandas internally to process the data, but also accept other types of DataFrames as arguments. See the [Plotly Express arguments page](/python/px-arguments/) for more details.\n", + "\n", + "[`px.imshow` operates on matrix-like data](/python/imshow/) you might store in a `numpy` or `xarray` array and functions like [`px.choropleth` and `px.choropleth_mapbox` can operate on geographic data](/python/maps/) of the kind you might store in a GeoPandas `GeoDataFrame`. This page details how to provide a specific form of column-oriented data to 2D-Cartesian Plotly Express functions, but you can also check out our [detailed column-input-format documentation](/python/px-arguments/).\n", + "\n", + "### Plotly Express works with Long-, Wide-, and Mixed-Form Data\n", + "\n", + "*Until version 4.8, Plotly Express only operated on long-form (previously called \"tidy\") data, but now accepts wide-form and mixed-form data as well.*\n", + "\n", + "There are three common conventions for storing column-oriented data, usually in a data frame with column names:\n", + "\n", + "* **long-form data** has one row per observation, and one column per variable. This is suitable for storing and displaying multivariate data i.e. with dimension greater than 2. This format is sometimes called \"tidy\".\n", + "* **wide-form data** has one row per value of one of the first variable, and one column per value of the second variable. This is suitable for storing and displaying 2-dimensional data.\n", + "* **mixed-form data** is a hybrid of long-form and wide-form data, with one row per value of one variable, and some columns representing values of another, and some columns representing more variables.\n", + "\n", + "Every Plotly Express function can operate on long-form data (other than `px.imshow` which operates only on wide-form input), and in addition, the following 2D-Cartesian functions can operate on wide-form and mixed-form data: `px.scatter`, `px.line`, `px.area`, `px.bar`, `px.histogram`, `px.violin`, `px.box`, `px.strip`, `px.funnel`, `px.density_heatmap` and `px.density_contour`.\n", + "\n", + "By way of example here is the same data, represented in long-form first, and then in wide-form:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "91258b14", + "metadata": {}, + "outputs": [], + "source": [ + "import plotly.express as px\n", + "long_df = px.data.medals_long()\n", + "long_df" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "865352a5", + "metadata": {}, + "outputs": [], + "source": [ + "import plotly.express as px\n", + "wide_df = px.data.medals_wide()\n", + "wide_df" + ] + }, + { + "cell_type": "markdown", + "id": "f3749f25", + "metadata": {}, + "source": [ + "Plotly Express can produce **the same plot from either form**. For the long-form input, `x` and `y` are set to the respective column names." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "d976406a", + "metadata": {}, + "outputs": [], + "source": [ + "import plotly.express as px\n", + "long_df = px.data.medals_long()\n", + "\n", + "fig = px.bar(long_df, x=\"nation\", y=\"count\", color=\"medal\", title=\"Long-Form Input\")\n", + "fig.show()" + ] + }, + { + "cell_type": "markdown", + "id": "e9f6388c", + "metadata": {}, + "source": [ + "For the wide-form input, we **pass in a list of column-names `y`**, which is enough to trigger the wide-form processing mode. Wide-form mode is also the default if neither `x` nor `y` are specified, see section at bottom regarding Wide-Form Defaults." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "76b41167", + "metadata": {}, + "outputs": [], + "source": [ + "import plotly.express as px\n", + "wide_df = px.data.medals_wide()\n", + "\n", + "fig = px.bar(wide_df, x=\"nation\", y=[\"gold\", \"silver\", \"bronze\"], title=\"Wide-Form Input\")\n", + "fig.show()" + ] + }, + { + "cell_type": "markdown", + "id": "7e341755", + "metadata": {}, + "source": [ + "### Labeling axes, legends and hover text\n", + "\n", + "You might notice that y-axis and legend labels are slightly different for the second plot: they are \"value\" and \"variable\", respectively, and this is also reflected in the hoverlabel text. This is because Plotly Express performed an [internal Pandas `melt()` operation](https://pandas.pydata.org/docs/reference/api/pandas.melt.html) to convert the wide-form data into long-form for plotting, and used the Pandas convention for assign column names to the intermediate long-form data. Note that the labels \"medal\" and \"count\" do not appear in the wide-form data frame, so in this case, you must supply these yourself, (or see below regarding using a data frame with named row- and column-indexes). You can [rename these labels with the `labels` argument](/python/styling-plotly-express/):" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "b25312c8", + "metadata": {}, + "outputs": [], + "source": [ + "import plotly.express as px\n", + "wide_df = px.data.medals_wide()\n", + "\n", + "fig = px.bar(wide_df, x=\"nation\", y=[\"gold\", \"silver\", \"bronze\"], title=\"Wide-Form Input, relabelled\",\n", + " labels={\"value\": \"count\", \"variable\": \"medal\"})\n", + "fig.show()" + ] + }, + { + "cell_type": "markdown", + "id": "9f45ef73", + "metadata": {}, + "source": [ + "Plotly Express figures created using wide-form data can be [styled just like any other Plotly Express figure](/python/styling-plotly-express/):" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "4bdc6211", + "metadata": {}, + "outputs": [], + "source": [ + "import plotly.express as px\n", + "wide_df = px.data.medals_wide()\n", + "\n", + "fig = px.bar(wide_df, x=\"nation\", y=[\"gold\", \"silver\", \"bronze\"],\n", + " title=\"Wide-Form Input, styled\",\n", + " labels={\"value\": \"Medal Count\", \"variable\": \"Medal\", \"nation\": \"Olympic Nation\"},\n", + " color_discrete_map={\"gold\": \"gold\", \"silver\": \"silver\", \"bronze\": \"#c96\"},\n", + " template=\"simple_white\"\n", + " )\n", + "fig.update_layout(font_family=\"Rockwell\", showlegend=False)\n", + "fig.show()" + ] + }, + { + "cell_type": "markdown", + "id": "d6004fd7", + "metadata": {}, + "source": [ + "### Data Frames with Named Indexes\n", + "\n", + "Pandas `DataFrames` support not only column names and \"row names\" via the value of `index`, but the indexes themselves can be named. Here is how to assign one column of the wide sample data frame above as the index, and to name the column index. The result \"indexed\" sample data frame can also be obtained by calling `px.data.medals_wide(indexed=True)`" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "660c9ef9", + "metadata": {}, + "outputs": [], + "source": [ + "import plotly.express as px\n", + "wide_df = px.data.medals_wide()\n", + "wide_df = wide_df.set_index(\"nation\")\n", + "wide_df.columns.name = \"medals\"\n", + "wide_df" + ] + }, + { + "cell_type": "markdown", + "id": "b29ed3d0", + "metadata": {}, + "source": [ + "When working with a data frame like the one above, you can pass the index references directly as arguments, to benefit from automatic labelling for everything except the y axis label, which will default to \"values\", but this can be overridden with the `labels` argument as above:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "901ff3c7", + "metadata": {}, + "outputs": [], + "source": [ + "import plotly.express as px\n", + "wide_df = px.data.medals_wide(indexed=True)\n", + "\n", + "fig = px.bar(wide_df, x=wide_df.index, y=wide_df.columns)\n", + "fig.show()" + ] + }, + { + "cell_type": "markdown", + "id": "b2d65df7", + "metadata": {}, + "source": [ + "If you transpose `x` and `y`, thereby assigning the columns to `x`, the orientation will be switched to horizontal:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "a819f7cc", + "metadata": {}, + "outputs": [], + "source": [ + "import plotly.express as px\n", + "wide_df = px.data.medals_wide(indexed=True)\n", + "\n", + "fig = px.bar(wide_df, x=wide_df.columns, y=wide_df.index)\n", + "fig.show()" + ] + }, + { + "cell_type": "markdown", + "id": "f5a01e83", + "metadata": {}, + "source": [ + "### Assigning Inferred Columns to Non-Default Arguments" + ] + }, + { + "cell_type": "markdown", + "id": "bf83c6a4", + "metadata": {}, + "source": [ + "In the examples above, the columns of the wide data frame are assigned by default as an \"inferred\" column named `variable` to the `color` argument (see section below for documentation of the default behaviours), but this is not a hard constraint. The `variable` column can be assigned to any Plotly Express argument, for example to accomplish faceting, and `color` can be reassigned to any other value. More generally, when plotting with a data frame without named indexes, you can reassign the inferred column named `variable` and `value` to any argument:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "5e9a75bc", + "metadata": {}, + "outputs": [], + "source": [ + "import plotly.express as px\n", + "wide_df = px.data.medals_wide(indexed=False)\n", + "\n", + "fig = px.bar(wide_df, x=\"nation\", y=[\"gold\", \"silver\", \"bronze\"], facet_col=\"variable\", color=\"nation\")\n", + "fig.show()" + ] + }, + { + "cell_type": "markdown", + "id": "e6d3e38d", + "metadata": {}, + "source": [ + "You can also prevent `color` from getting assigned if you're mapping `variable` to some other argument:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "145aaf13", + "metadata": {}, + "outputs": [], + "source": [ + "import plotly.express as px\n", + "wide_df = px.data.medals_wide(indexed=False)\n", + "\n", + "fig = px.bar(wide_df, x=\"nation\", y=[\"gold\", \"silver\", \"bronze\"], facet_col=\"variable\", color=px.NO_COLOR)\n", + "fig.show()" + ] + }, + { + "cell_type": "markdown", + "id": "e1de4d58", + "metadata": {}, + "source": [ + "If using a data frame's named indexes, either explicitly or relying on the defaults, the row-index references (i.e. `df.index`) or column-index names (i.e. the value of `df.columns.name`) must be used:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "439baae9", + "metadata": {}, + "outputs": [], + "source": [ + "import plotly.express as px\n", + "wide_df = px.data.medals_wide(indexed=True)\n", + "\n", + "fig = px.bar(wide_df, facet_col=\"medal\", color=wide_df.index)\n", + "fig.show()" + ] + }, + { + "cell_type": "markdown", + "id": "f12af616", + "metadata": {}, + "source": [ + "### Mixed-Form Data\n", + "\n", + "In some cases, a data frame is neither clearly long-form nor wide-form, and we can call this \"mixed-form\". For example, in the data frame below, if it contained only the `experiment` columns, the data could be described as wide-form, and if it contained only `gender` and `group` it could be described as long-form, but it contains both, so it is best described as mixed-form data:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "8731a6e8", + "metadata": {}, + "outputs": [], + "source": [ + "import plotly.express as px\n", + "mixed_df = px.data.experiment(indexed=True)\n", + "mixed_df.head()" + ] + }, + { + "cell_type": "markdown", + "id": "5ea5ded5", + "metadata": {}, + "source": [ + "We can visualize just the wide-form portion of the data frame easily with a [violin chart](/python/violin/). As a special note, we'll assign the index, which is the participant ID, to the `hover_data`, so that hovering over outlier points will identify their row." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "8e72c650", + "metadata": { + "lines_to_next_cell": 0 + }, + "outputs": [], + "source": [ + "import plotly.express as px\n", + "mixed_df = px.data.experiment(indexed=True)\n", + "\n", + "fig = px.violin(mixed_df, y=[\"experiment_1\", \"experiment_2\", \"experiment_3\"], hover_data=[mixed_df.index])\n", + "fig.show()" + ] + }, + { + "cell_type": "markdown", + "id": "5142bf51", + "metadata": {}, + "source": [ + "\n" + ] + }, + { + "cell_type": "markdown", + "id": "1452f11e", + "metadata": {}, + "source": [ + "We are not limited to visualizing only the wide-form portion of the data, however. We can also leverage the long-form portion of the data frame, for example to color by participant `gender` and facet by participant `group`, all without having to manipulate the data frame:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "97744d1b", + "metadata": {}, + "outputs": [], + "source": [ + "import plotly.express as px\n", + "mixed_df = px.data.experiment(indexed=True)\n", + "\n", + "fig = px.violin(mixed_df, y=[\"experiment_1\", \"experiment_2\", \"experiment_3\"],\n", + " color=\"gender\", facet_col=\"group\", hover_data=[mixed_df.index])\n", + "fig.show()" + ] + }, + { + "cell_type": "markdown", + "id": "eb9ec718", + "metadata": {}, + "source": [ + "In the plots above, the column names provided to `y` are internally mapped to long-form column called `variable`, as is apparent in the x-axis labels. We can reassign `variable` to another argument as well, in this case we'll assign it to `facet_col` and reassign `group` to the `x` axis. We'll switch to a [box plot](/python/box-plots/) for variety." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "446ed531", + "metadata": {}, + "outputs": [], + "source": [ + "import plotly.express as px\n", + "mixed_df = px.data.experiment(indexed=True)\n", + "\n", + "fig = px.box(mixed_df, x=\"group\", y=[\"experiment_1\", \"experiment_2\", \"experiment_3\"],\n", + " color=\"gender\", facet_col=\"variable\", hover_data=[mixed_df.index])\n", + "fig.show()" + ] + }, + { + "cell_type": "markdown", + "id": "df423f87", + "metadata": {}, + "source": [ + "One interesting thing about a mixed-form data frame like this is that it remains easy to plot, say, one experiment against another, which would require some preliminary data wrangling if this was represented as a pure long-form dataset:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "f98d0e50", + "metadata": {}, + "outputs": [], + "source": [ + "import plotly.express as px\n", + "mixed_df = px.data.experiment(indexed=True)\n", + "\n", + "fig = px.scatter(mixed_df, x=\"experiment_1\", y=\"experiment_2\",\n", + " color=\"group\", facet_col=\"gender\", hover_data=[mixed_df.index])\n", + "fig.show()" + ] + }, + { + "cell_type": "markdown", + "id": "25b4f2d2", + "metadata": {}, + "source": [ + "In fact, we can even visualize the results of every experiment against every other, using a [scatterplot matrix](/python/splom/):" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "612360bb", + "metadata": {}, + "outputs": [], + "source": [ + "import plotly.express as px\n", + "mixed_df = px.data.experiment(indexed=True)\n", + "\n", + "fig = px.scatter_matrix(mixed_df, dimensions=[\"experiment_1\", \"experiment_2\", \"experiment_3\"], color=\"gender\")\n", + "fig.show()" + ] + }, + { + "cell_type": "markdown", + "id": "fc88ae4e", + "metadata": {}, + "source": [ + "### Wide-Form Defaults\n", + "\n", + "For bar, scatter, line and area charts, the pattern of assigning `x=df.index`, `y=df.columns`, `color=\"variable\"` is so common that if you provide neither `x` nor `y` this is the default behaviour. An exception is made for bar charts when the values are not continuous variables, in which case the default is similar to histograms, with `x=df.columns`, `color=\"variable\"` and `y=`.\n", + "\n", + "For violin and box plots, the default is to assign `x=variable`, `y=df.columns` and for histograms the default is `x=df.columns`, `color=\"variable\"`\n", + "\n", + "These defaults are also filled in if you specify only `y` (`x` for histograms) as a list-of-columns. See below for orientation control." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "aeaecea6", + "metadata": {}, + "outputs": [], + "source": [ + "import plotly.express as px\n", + "wide_df = px.data.medals_wide(indexed=True)\n", + "\n", + "fig = px.bar(wide_df)\n", + "fig.show()\n", + "\n", + "fig = px.area(wide_df)\n", + "fig.show()\n", + "\n", + "fig = px.line(wide_df)\n", + "fig.show()\n", + "\n", + "fig = px.scatter(wide_df)\n", + "fig.show()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "87a98ae4", + "metadata": {}, + "outputs": [], + "source": [ + "import plotly.express as px\n", + "\n", + "mixed_df = px.data.experiment(indexed=True)\n", + "wide_df = mixed_df[[\"experiment_1\", \"experiment_2\", \"experiment_3\"]]\n", + "\n", + "fig = px.histogram(wide_df)\n", + "fig.show()\n", + "\n", + "fig = px.violin(wide_df)\n", + "fig.show()\n", + "\n", + "fig = px.box(wide_df)\n", + "fig.show()" + ] + }, + { + "cell_type": "markdown", + "id": "7e9f7e6b", + "metadata": {}, + "source": [ + "### Orientation Control When Using Defaults\n", + "\n", + "If you specify neither `x` nor `y`, you can swap the default behaviour of `x` and `y` by setting `orientation=\"h\"`.\n", + "\n", + "If you specify only `x` as a list-of-columns (`y` in the case of histograms), then the defaults are filled in as if `orientation=\"h\"`" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "7bb956c4", + "metadata": { + "lines_to_next_cell": 2 + }, + "outputs": [], + "source": [ + "import plotly.express as px\n", + "wide_df = px.data.medals_wide(indexed=True)\n", + "\n", + "fig = px.bar(wide_df, orientation=\"h\")\n", + "fig.show()\n", + "\n", + "fig = px.area(wide_df, x=wide_df.columns)\n", + "fig.show()\n", + "\n", + "mixed_df = px.data.experiment(indexed=True)\n", + "wide_df = mixed_df[[\"experiment_1\", \"experiment_2\", \"experiment_3\"]]\n", + "\n", + "fig = px.histogram(wide_df, orientation=\"h\")\n", + "fig.show()\n", + "\n", + "fig = px.violin(wide_df, orientation=\"h\")\n", + "fig.show()\n", + "\n", + "fig = px.box(wide_df, orientation=\"h\")\n", + "fig.show()" + ] + }, + { + "cell_type": "markdown", + "id": "646019c9", + "metadata": {}, + "source": [ + "### What About Dash?\n", + "\n", + "[Dash](https://dash.plot.ly/) is an open-source framework for building analytical applications, with no Javascript required, and it is tightly integrated with the Plotly graphing library.\n", + "\n", + "Learn about how to install Dash at https://dash.plot.ly/installation.\n", + "\n", + "Everywhere in this page that you see `fig.show()`, you can display the same figure in a Dash application by passing it to the `figure` argument of the [`Graph` component](https://dash.plot.ly/dash-core-components/graph) from the built-in `dash_core_components` package like this:\n", + "\n", + "```python\n", + "import plotly.graph_objects as go # or plotly.express as px\n", + "fig = go.Figure() # or any Plotly Express function e.g. px.bar(...)\n", + "# fig.add_trace( ... )\n", + "# fig.update_layout( ... )\n", + "\n", + "from dash import Dash, dcc, html\n", + "\n", + "app = Dash()\n", + "app.layout = html.Div([\n", + " dcc.Graph(figure=fig)\n", + "])\n", + "\n", + "app.run_server(debug=True, use_reloader=False) # Turn off reloader if inside Jupyter\n", + "```" + ] + } + ], + "metadata": { + "jupytext": { + "notebook_metadata_filter": "all" + }, + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "language": "python", + "name": "python3" + }, + "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.4" + }, + "plotly": { + "description": "Plotly Express' 2D-Cartesian functions accept data in long-, wide-, and mixed-form.", + "display_as": "file_settings", + "language": "python", + "layout": "base", + "name": "Plotly Express Wide-Form Support", + "order": 34, + "page_type": "u-guide", + "permalink": "python/wide-form/", + "thumbnail": "thumbnail/plotly-express.png" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/wind-rose-charts.ipynb b/wind-rose-charts.ipynb new file mode 100644 index 000000000..229c558cb --- /dev/null +++ b/wind-rose-charts.ipynb @@ -0,0 +1,158 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "842bdaee", + "metadata": {}, + "source": [ + "### Wind Rose Chart with Plotly Express\n", + "\n", + "A [wind rose chart](https://en.wikipedia.org/wiki/Wind_rose) (also known as a polar bar chart) is a graphical tool used to visualize how wind speed and direction are typically distributed at a given location. You can use the `px.bar_polar` function from Plotly Express as below, otherwise use `go.Barpolar` as explained in the next section.\n", + "\n", + "[Plotly Express](/python/plotly-express/) is the easy-to-use, high-level interface to Plotly, which [operates on a variety of types of data](/python/px-arguments/) and produces [easy-to-style figures](/python/styling-plotly-express/)." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "b09bc6e9", + "metadata": {}, + "outputs": [], + "source": [ + "import plotly.express as px\n", + "df = px.data.wind()\n", + "fig = px.bar_polar(df, r=\"frequency\", theta=\"direction\",\n", + " color=\"strength\", template=\"plotly_dark\",\n", + " color_discrete_sequence= px.colors.sequential.Plasma_r)\n", + "fig.show()" + ] + }, + { + "cell_type": "markdown", + "id": "67810a08", + "metadata": {}, + "source": [ + "#### Basic Wind Rose Chart" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "ccd93dea", + "metadata": {}, + "outputs": [], + "source": [ + "import plotly.graph_objects as go\n", + "\n", + "fig = go.Figure()\n", + "\n", + "fig.add_trace(go.Barpolar(\n", + " r=[77.5, 72.5, 70.0, 45.0, 22.5, 42.5, 40.0, 62.5],\n", + " name='11-14 m/s',\n", + " marker_color='rgb(106,81,163)'\n", + "))\n", + "fig.add_trace(go.Barpolar(\n", + " r=[57.5, 50.0, 45.0, 35.0, 20.0, 22.5, 37.5, 55.0],\n", + " name='8-11 m/s',\n", + " marker_color='rgb(158,154,200)'\n", + "))\n", + "fig.add_trace(go.Barpolar(\n", + " r=[40.0, 30.0, 30.0, 35.0, 7.5, 7.5, 32.5, 40.0],\n", + " name='5-8 m/s',\n", + " marker_color='rgb(203,201,226)'\n", + "))\n", + "fig.add_trace(go.Barpolar(\n", + " r=[20.0, 7.5, 15.0, 22.5, 2.5, 2.5, 12.5, 22.5],\n", + " name='< 5 m/s',\n", + " marker_color='rgb(242,240,247)'\n", + "))\n", + "\n", + "fig.update_traces(text=['North', 'N-E', 'East', 'S-E', 'South', 'S-W', 'West', 'N-W'])\n", + "fig.update_layout(\n", + " title=dict(text='Wind Speed Distribution in Laurel, NE'),\n", + " font_size=16,\n", + " legend_font_size=16,\n", + " polar_radialaxis_ticksuffix='%',\n", + " polar_angularaxis_rotation=90,\n", + "\n", + ")\n", + "fig.show()" + ] + }, + { + "cell_type": "markdown", + "id": "dfc13eb2", + "metadata": {}, + "source": [ + "#### Reference\n", + "\n", + "See [function reference for `px.(bar_polar)`](https://plotly.com/python-api-reference/generated/plotly.express.bar_polar) or https://plotly.com/python/reference/barpolar/ for more information and chart attribute options!\n" + ] + }, + { + "cell_type": "markdown", + "id": "843a648c", + "metadata": {}, + "source": [ + "### What About Dash?\n", + "\n", + "[Dash](https://dash.plot.ly/) is an open-source framework for building analytical applications, with no Javascript required, and it is tightly integrated with the Plotly graphing library.\n", + "\n", + "Learn about how to install Dash at https://dash.plot.ly/installation.\n", + "\n", + "Everywhere in this page that you see `fig.show()`, you can display the same figure in a Dash application by passing it to the `figure` argument of the [`Graph` component](https://dash.plot.ly/dash-core-components/graph) from the built-in `dash_core_components` package like this:\n", + "\n", + "```python\n", + "import plotly.graph_objects as go # or plotly.express as px\n", + "fig = go.Figure() # or any Plotly Express function e.g. px.bar(...)\n", + "# fig.add_trace( ... )\n", + "# fig.update_layout( ... )\n", + "\n", + "from dash import Dash, dcc, html\n", + "\n", + "app = Dash()\n", + "app.layout = html.Div([\n", + " dcc.Graph(figure=fig)\n", + "])\n", + "\n", + "app.run_server(debug=True, use_reloader=False) # Turn off reloader if inside Jupyter\n", + "```" + ] + } + ], + "metadata": { + "jupytext": { + "notebook_metadata_filter": "all" + }, + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "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.6.7" + }, + "plotly": { + "description": "How to graph wind rose charts in python. Wind Rose charts display wind speed and direction of a given location.", + "display_as": "scientific", + "language": "python", + "layout": "base", + "name": "Wind Rose and Polar Bar Charts", + "order": 19, + "page_type": "u-guide", + "permalink": "python/wind-rose-charts/", + "thumbnail": "thumbnail/wind-rose.jpg" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +}