├── .github └── FUNDING.yml ├── .gitignore ├── CODE_OF_CONDUCT.md ├── LICENSE ├── README.md ├── environment.yml ├── environments └── plotlylab.yaml ├── notebooks ├── 0. Theme.ipynb ├── 1. Simple Notebook.ipynb ├── 2. Offline iplot and export.ipynb ├── 3. Figure Widget.ipynb ├── 4. Chart Editor.ipynb ├── 5. Dash App Viewer.ipynb ├── 6. Plotly Help Resources.ipynb ├── 7. R support.ipynb ├── 8. plotly_express Walkthrough.ipynb ├── 9. plotly_express Dash App.ipynb ├── datasets │ └── iris.csv ├── images │ ├── JupyterLabLight.png │ └── PlotlyLabLight.png └── out │ └── gapminder-styled.plotly.json ├── plotlylab ├── __init__.py ├── labapp.py ├── labextensionapp.py └── labhubapp.py ├── recipe ├── build.sh └── meta.yaml ├── setup.py └── start /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | custom: https://plot.ly/products/consulting-and-oem/ 2 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .idea/ 2 | .ipynb_checkpoints/ 3 | -------------------------------------------------------------------------------- /CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | # Contributor Covenant Code of Conduct 2 | 3 | ## Our Pledge 4 | 5 | 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. 6 | 7 | ## Our Standards 8 | 9 | Examples of behavior that contributes to creating a positive environment include: 10 | 11 | * Using welcoming and inclusive language 12 | * Being respectful of differing viewpoints and experiences 13 | * Gracefully accepting constructive criticism 14 | * Focusing on what is best for the community 15 | * Showing empathy towards other community members 16 | 17 | Examples of unacceptable behavior by participants include: 18 | 19 | * The use of sexualized language or imagery and unwelcome sexual attention or advances 20 | * Trolling, insulting/derogatory comments, and personal or political attacks 21 | * Public or private harassment 22 | * Publishing others' private information, such as a physical or electronic address, without explicit permission 23 | * Other conduct which could reasonably be considered inappropriate in a professional setting 24 | 25 | ## Our Responsibilities 26 | 27 | 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. 28 | 29 | 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. 30 | 31 | ## Scope 32 | 33 | 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. 34 | 35 | ## Enforcement 36 | 37 | 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. 38 | 39 | 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. 40 | 41 | ## Attribution 42 | 43 | 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 . 44 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2016-2024 Plotly Technologies Inc. 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in 13 | all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | THE SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## PlotlyLab 2 | 3 |
4 | 5 | Maintained by Plotly 6 | 7 |
8 | 9 | WIP project to bundle a collection of Plotly-related JupyterLab extensions 10 | into a single conda package. For background on this approach, 11 | see https://github.com/jonmmease/jupyterlab_delux 12 | 13 | Try now with Binder: 14 | 15 | [![Binder](https://mybinder.org/badge_logo.svg)](https://mybinder.org/v2/gh/plotly/plotlylab/master?urlpath=lab%2Ftree%2Fnotebooks) 16 | 17 | 18 | 19 | **Note:** A conda environment with PlotlyLab installed with take up around 20 | 3.5 GB of disk space 21 | 22 | ### Demo (screencasts) 23 | 24 | Usage and ideas behind PlotlyLab 25 | 26 | 1. [PlotlyLab prototype overview](https://drive.google.com/file/d/1AkCGyemyZSNpvW9plDGJPQ2K3MCidIOW/view?usp=sharing) 27 | 28 | 2. [PlotlyLab as a conda package](https://drive.google.com/file/d/1YrEOTn37Ofti3ubHfgyJlfTJE9d1Vjy3/view?usp=sharing) 29 | 30 | 3. [Ideas for future directions](https://drive.google.com/file/d/1PlOMGmbcqLDHnJdOe_DAen3U1k59KR9n/view?usp=sharing) 31 | 32 | 4. [R language support](https://drive.google.com/file/d/1tZRQP2zUh5-eD1W8IKuBQj1DJnllDh6e/view?usp=sharing) 33 | 34 | ### Installation instructions 35 | 36 | 1. Download and install miniconda: https://conda.io/miniconda.html 37 | 38 | 2. Create a new conda environment and install PlotlyLab 39 | 40 | ``` 41 | $ conda create -n plotlylab -c plotly -c r python plotlylab 42 | $ conda activate plotlylab 43 | ``` 44 | 45 | 3. Launch plotlylab 46 | 47 | ``` 48 | $ plotly-lab 49 | ``` 50 | 51 | 4. Try out the notebooks in the 52 | [`notebooks/`](https://github.com/plotly/plotlylab/tree/master/notebooks) 53 | directory for a guided tour 54 | 55 | ### Other commands 56 | 57 | List preinstalled conda packages 58 | ``` 59 | $ conda list 60 | ``` 61 | 62 | List preinstalled JuptyerLab extensions 63 | 64 | ``` 65 | $ plotly-labextension list 66 | ``` 67 | 68 | Install an additional extension into plotlylab (requires nodejs) 69 | ``` 70 | $ plotly-labextension install some-extension 71 | ``` 72 | 73 | Launch plain JupyterLab (no preinstalled extensions) 74 | ``` 75 | $ jupyter-lab 76 | ``` 77 | 78 | Update version of PlotlyLab after new releases 79 | 80 | ``` 81 | $ conda update -c plotly plotlylab 82 | ``` 83 | 84 | Uninstall `plotlylab` environment 85 | 86 | ``` 87 | $ conda remove -n plotlylab --all 88 | ``` 89 | 90 | ### Build instructions 91 | Build the `plotlylab` conda package with 92 | ```bash 93 | $ conda build -c plotly -c r recipe/ 94 | ``` 95 | 96 | Then test it out by creating a new conda environment 97 | 98 | ``` 99 | $ conda create -n try_plotlylab -c plotly -c r --use-local python plotlylab 100 | $ conda activate try_plotlylab 101 | ``` 102 | -------------------------------------------------------------------------------- /environment.yml: -------------------------------------------------------------------------------- 1 | name: plotlylab-environment 2 | channels: 3 | - main 4 | - plotly 5 | - r 6 | dependencies: 7 | - plotlylab -------------------------------------------------------------------------------- /environments/plotlylab.yaml: -------------------------------------------------------------------------------- 1 | name: plotlylab_0.1.0a2 2 | channels: 3 | - main 4 | - plotly 5 | - r 6 | dependencies: 7 | - plotlylab =0.1.0a2 8 | -------------------------------------------------------------------------------- /notebooks/0. Theme.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "markdown", 5 | "metadata": {}, 6 | "source": [ 7 | "### Custom Theme\n", 8 | "PlotlyLab includes a custom light-theme that is based on the [Plotly Branch & Style Guide](https://brand.plot.ly/)\n", 9 | "\n", 10 | "switch between the \"PlotlyLab Light\" and \"JupyterLab Light\" themes under \"Settings -> JupyterLab Theme\"\n", 11 | "\n", 12 | "This theme is implemented in the `plotlylab-light-theme` extension (https://github.com/plotly/plotlylab-light-theme)\n", 13 | "\n", 14 | "#### JupyterLab Light\n", 15 | "![](images/JupyterLabLight.png)\n", 16 | "\n", 17 | "#### PlotlyLab Light\n", 18 | "![](images/PlotlyLabLight.png)" 19 | ] 20 | }, 21 | { 22 | "cell_type": "code", 23 | "execution_count": null, 24 | "metadata": {}, 25 | "outputs": [], 26 | "source": [] 27 | } 28 | ], 29 | "metadata": { 30 | "kernelspec": { 31 | "display_name": "Python 3", 32 | "language": "python", 33 | "name": "python3" 34 | }, 35 | "language_info": { 36 | "codemirror_mode": { 37 | "name": "ipython", 38 | "version": 3 39 | }, 40 | "file_extension": ".py", 41 | "mimetype": "text/x-python", 42 | "name": "python", 43 | "nbconvert_exporter": "python", 44 | "pygments_lexer": "ipython3", 45 | "version": "3.7.2" 46 | } 47 | }, 48 | "nbformat": 4, 49 | "nbformat_minor": 2 50 | } 51 | -------------------------------------------------------------------------------- /notebooks/1. Simple Notebook.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "markdown", 5 | "metadata": {}, 6 | "source": [ 7 | "## Overview\n", 8 | "This is a simple notebook that demonstrates some of the features of the JupyterNotebook in JupyterLab. This is a Markdown call, which can contain the usual markdown formatting like **bold**, *italic*, and ~~strikethrough~~ text,\n", 9 | "\n", 10 | " - Bulleted\n", 11 | " - lists\n", 12 | " \n", 13 | "And\n", 14 | " 1. Numbered\n", 15 | " 2. Lists\n", 16 | " \n", 17 | "Markdown cells also support $\\LaTeX$ formatting" 18 | ] 19 | }, 20 | { 21 | "cell_type": "code", 22 | "execution_count": 1, 23 | "metadata": {}, 24 | "outputs": [], 25 | "source": [ 26 | "# Import pandas\n", 27 | "import pandas as pd" 28 | ] 29 | }, 30 | { 31 | "cell_type": "code", 32 | "execution_count": 2, 33 | "metadata": {}, 34 | "outputs": [ 35 | { 36 | "data": { 37 | "text/html": [ 38 | "
\n", 39 | "\n", 52 | "\n", 53 | " \n", 54 | " \n", 55 | " \n", 56 | " \n", 57 | " \n", 58 | " \n", 59 | " \n", 60 | " \n", 61 | " \n", 62 | " \n", 63 | " \n", 64 | " \n", 65 | " \n", 66 | " \n", 67 | " \n", 68 | " \n", 69 | " \n", 70 | " \n", 71 | " \n", 72 | " \n", 73 | " \n", 74 | " \n", 75 | " \n", 76 | " \n", 77 | " \n", 78 | " \n", 79 | " \n", 80 | " \n", 81 | " \n", 82 | "
ab
02X
11Y
22Z
34X
\n", 83 | "
" 84 | ], 85 | "text/plain": [ 86 | " a b\n", 87 | "0 2 X\n", 88 | "1 1 Y\n", 89 | "2 2 Z\n", 90 | "3 4 X" 91 | ] 92 | }, 93 | "execution_count": 2, 94 | "metadata": {}, 95 | "output_type": "execute_result" 96 | } 97 | ], 98 | "source": [ 99 | "# Create a DataFrame\n", 100 | "df = pd.DataFrame({'a': [2, 1, 2, 4], 'b': ['X', 'Y', 'Z', 'X']})\n", 101 | "df" 102 | ] 103 | }, 104 | { 105 | "cell_type": "markdown", 106 | "metadata": {}, 107 | "source": [ 108 | "## ipywidgets\n", 109 | "PlotlyLab comes with ipywidgets preinstalled and with the jupyter widgets JupyterLab extension preinstalled. So widget examples from the [ipywidgets user guide](https://ipywidgets.readthedocs.io/en/stable/user_guide.html) just work." 110 | ] 111 | }, 112 | { 113 | "cell_type": "code", 114 | "execution_count": 1, 115 | "metadata": {}, 116 | "outputs": [], 117 | "source": [ 118 | "from ipywidgets import interact" 119 | ] 120 | }, 121 | { 122 | "cell_type": "code", 123 | "execution_count": 2, 124 | "metadata": {}, 125 | "outputs": [ 126 | { 127 | "data": { 128 | "application/vnd.jupyter.widget-view+json": { 129 | "model_id": "a8ae04396ddb43308ba88c1ee3cc6716", 130 | "version_major": 2, 131 | "version_minor": 0 132 | }, 133 | "text/plain": [ 134 | "interactive(children=(Checkbox(value=True, description='x'), FloatSlider(value=1.0, description='y', max=3.0, …" 135 | ] 136 | }, 137 | "metadata": {}, 138 | "output_type": "display_data" 139 | } 140 | ], 141 | "source": [ 142 | "@interact(x=True, y=1.0)\n", 143 | "def g(x, y):\n", 144 | " return (x, y)" 145 | ] 146 | }, 147 | { 148 | "cell_type": "code", 149 | "execution_count": null, 150 | "metadata": {}, 151 | "outputs": [], 152 | "source": [] 153 | } 154 | ], 155 | "metadata": { 156 | "kernelspec": { 157 | "display_name": "Python 3", 158 | "language": "python", 159 | "name": "python3" 160 | }, 161 | "language_info": { 162 | "codemirror_mode": { 163 | "name": "ipython", 164 | "version": 3 165 | }, 166 | "file_extension": ".py", 167 | "mimetype": "text/x-python", 168 | "name": "python", 169 | "nbconvert_exporter": "python", 170 | "pygments_lexer": "ipython3", 171 | "version": "3.7.2" 172 | } 173 | }, 174 | "nbformat": 4, 175 | "nbformat_minor": 2 176 | } 177 | -------------------------------------------------------------------------------- /notebooks/2. Offline iplot and export.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "markdown", 5 | "metadata": {}, 6 | "source": [ 7 | "### Imports" 8 | ] 9 | }, 10 | { 11 | "cell_type": "code", 12 | "execution_count": 6, 13 | "metadata": {}, 14 | "outputs": [], 15 | "source": [ 16 | "from plotly.offline import iplot, plot\n", 17 | "import plotly.graph_objs as go\n", 18 | "import plotly.io as pio" 19 | ] 20 | }, 21 | { 22 | "cell_type": "markdown", 23 | "metadata": {}, 24 | "source": [ 25 | "### Create a figure" 26 | ] 27 | }, 28 | { 29 | "cell_type": "code", 30 | "execution_count": 7, 31 | "metadata": {}, 32 | "outputs": [ 33 | { 34 | "data": { 35 | "text/plain": [ 36 | "Figure({\n", 37 | " 'data': [{'name': 'SF Zoo',\n", 38 | " 'type': 'bar',\n", 39 | " 'uid': 'f86db27d-81ec-4444-ba1a-9f0209b70890',\n", 40 | " 'x': [giraffes, orangutans, monkeys],\n", 41 | " 'y': [20, 14, 23]},\n", 42 | " {'name': 'LA Zoo',\n", 43 | " 'type': 'bar',\n", 44 | " 'uid': '13af91ad-8af6-4dfd-a558-a1524b5d1e7a',\n", 45 | " 'x': [giraffes, orangutans, monkeys],\n", 46 | " 'y': [12, 18, 29]}],\n", 47 | " 'layout': {'barmode': 'group'}\n", 48 | "})" 49 | ] 50 | }, 51 | "execution_count": 7, 52 | "metadata": {}, 53 | "output_type": "execute_result" 54 | } 55 | ], 56 | "source": [ 57 | "trace1 = go.Bar(\n", 58 | " x=['giraffes', 'orangutans', 'monkeys'],\n", 59 | " y=[20, 14, 23],\n", 60 | " name='SF Zoo'\n", 61 | ")\n", 62 | "trace2 = go.Bar(\n", 63 | " x=['giraffes', 'orangutans', 'monkeys'],\n", 64 | " y=[12, 18, 29],\n", 65 | " name='LA Zoo'\n", 66 | ")\n", 67 | "\n", 68 | "data = [trace1, trace2]\n", 69 | "layout = go.Layout(\n", 70 | " barmode='group'\n", 71 | ")\n", 72 | "\n", 73 | "fig = go.Figure(data=data, layout=layout)\n", 74 | "fig" 75 | ] 76 | }, 77 | { 78 | "cell_type": "markdown", 79 | "metadata": {}, 80 | "source": [ 81 | "### Display figure with offlien iplot\n", 82 | "No `init_offline_mode` needed. This relies on the `@jupyterlab/plotly-extension` extension (https://github.com/jupyterlab/jupyter-renderers/tree/master/packages/plotly-extension)" 83 | ] 84 | }, 85 | { 86 | "cell_type": "code", 87 | "execution_count": 8, 88 | "metadata": {}, 89 | "outputs": [ 90 | { 91 | "data": { 92 | "application/vnd.plotly.v1+json": { 93 | "config": { 94 | "linkText": "Export to plot.ly", 95 | "plotlyServerURL": "https://plot.ly", 96 | "showLink": false 97 | }, 98 | "data": [ 99 | { 100 | "name": "SF Zoo", 101 | "type": "bar", 102 | "uid": "f86db27d-81ec-4444-ba1a-9f0209b70890", 103 | "x": [ 104 | "giraffes", 105 | "orangutans", 106 | "monkeys" 107 | ], 108 | "y": [ 109 | 20, 110 | 14, 111 | 23 112 | ] 113 | }, 114 | { 115 | "name": "LA Zoo", 116 | "type": "bar", 117 | "uid": "13af91ad-8af6-4dfd-a558-a1524b5d1e7a", 118 | "x": [ 119 | "giraffes", 120 | "orangutans", 121 | "monkeys" 122 | ], 123 | "y": [ 124 | 12, 125 | 18, 126 | 29 127 | ] 128 | } 129 | ], 130 | "layout": { 131 | "autosize": true, 132 | "barmode": "group", 133 | "xaxis": { 134 | "autorange": true, 135 | "range": [ 136 | -0.5, 137 | 2.5 138 | ], 139 | "type": "category" 140 | }, 141 | "yaxis": { 142 | "autorange": true, 143 | "range": [ 144 | 0, 145 | 30.526315789473685 146 | ], 147 | "type": "linear" 148 | } 149 | } 150 | }, 151 | "image/png": "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jIYJQBFx8ODBOOecc2Lnzp2feO7QoUMxPDwcr7zySkxPT8ett94aq1atSrjKTxKAak8AAoAaBKC6E4CKTwCiYQJQRKxfvz76+vqiv79/1g4cOBARES+//HJceOGFMTQ0FCtWrOh8PJsAVHsCEADUIADVnQBUfAIQDROAepgAVHsCEADUIADVnQBUfAIQDROAepgAVHsCEADUIADVnQBUfAIQDROAepgAVHsCEADUIADVnQBUfAIQDROAepgAVHsCEADUIADVnQBUfAIQDROAepgAVHsCEADUIADVnQBUfAIQDROAepgAVHsCEADUIADVnQBUfAIQDROAepgAVHsCEADUIADVnQBUfAIQDROAepgAVHsCEADUIADVnQBUfAIQDROAepgAVHsCEADUIADVnQBUfAIQDROAepgAVHsCEADUIADVnQBUfAIQDROAepgAVHsCEADUIADVnQBUfAIQDROAepgAVHsCEADUIADVnQBUfAIQDROAepgAVHsCEADUIADVnQBUfAIQDROAepgAVHsCEFDOvq0RbzxlFbf95ezffakEoLoTgIpPAKJhAlAPE4BqTwACynniuvwX45azf/qr7N99qQSguhOAik8AomECUA8TgGpPAALKEYDqTgBKf91hOROAik8AomECUA8TgGpPAALKEYDqTgBKf91hOROAik8AomECUA8TgGpPAALKEYDqTgBKf91hOROAik8AomECUA8TgGpPAALKEYDqTgBKf91hOROAik8AomECUA8TgGpPAALKEYDqTgBKf91hOROAik8AomECUA8TgGpPAALKEYDqTgBKf91hOROAik8AomECUA8TgGpPAALKEYDqTgBKf91hOROAik8AomECUA8TgGpPAALKEYDqTgBKf91hOROAik8AomECUA8TgGpPAALKEYDqTgBKf91hOROAik8AomECUA8TgGpPAALKEYDqTgBKf91hOROAik8AomECUA8TgGpPAALKEYDqTgBKf91hOROAik8AomECUA8TgGpPAALKEYDqTgBKf91hOROAik8AomECUA8TgGpPAALKEYDqTgBKf91hOROAik8AomECUA8TgGpPAALKEYDqTgBKf91hOROAik8AomECUA8TgGpPAALKEYDqTgBKf91hOROAik8AomECUA8TgGpPAALKEYDqTgBKf91hOROAik8AomECUA8T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Io4dOxYzMzPxve99L1577bW5+YcAtMKuXbvirLPO6nzH4Y9//ONYsmRJRJzePWzLli1xxhlnxMsvvxwRH9yfLr300og48T3zRAFo//790d/f/x/6td0ngVP10b3kt7/9bUREXHfddbF48eKYmpqKI0eOxMDAQOzdu/ek983Puh99PADdcsst8Y//+I8RESd8rffDH/4wrrnmms41rl69OtatW+feBghAMFceffTRzhfmiIiNGzd+ZgD60pe+FDMzM53P/fi/nXn88cfjiiuu6Dz+tAD005/+dNbnvPfee/HXf/3XcfTo0XjttddiwYIF8fzzz8eRI0f+Mn+zQKs99thjceWVV3YeHzlyJM4444yYnJw8rXvYli1bYnBwsPPcxMRE57t8TnTPPNUA9Hl/bfdJ4FRt2bIlhoaGOo/vueeeuPXWWzuPv/rVr8brr79+0vvmZ92PPgpAjz76aCxfvjymp6cjIk74Wm/Pnj3xpS99KSYnJ2NmZibmz58fO3bscG8DBCCYK/fff3/ceOONncebN2/+zAC0YMGCzucdO3Ys/uVf/iW+/vWvxze+8Y04//zzO2+riPj0APTAAw/EwMBAjI6OdvbRv0mP+OBbkJcuXRpnnXVWrF69Og4fPvwX//sH2mPdunWxevXqWR8bGBiI7du3n9Y97PjP/fjjE90zTzUAfd5fO8J9Ejg1W7Zs6cSaiIh77703vve973UeL1q0KP7whz+c1n3z448nJiZiYGAgzjrrrLj++us7n3Oy13rLli2L9evXx6uvvhpf//rXOz/OvQ1qE4Bgjjz88MNx7bXXdh4//fTTpxSAnnrqqRgbG4vJycmIiPj5z39+0gD0i1/8Yta/Of8sBw4ciOXLl8cPfvCD//jfIFDG+vXr46qrruo8/ujfZB86dOi07mEn+j89J7pn/v73v4/zzz+/89xLL730qQHo8/7aH+c+CZzIqQag07lvHh+AhoaGYteuXXH++efHxo0bIyJO+lrvZz/7WXzrW9+KNWvWxIMPPviJ593boCYBCObI7373uxgeHo79+/fH1NRUfPvb3z6lAPTwww/HihUrIiLi3XffjSuuuKLznvGITw9A77zzToyMjHTeZ/773/++84cQ/uhHP4p77703jh07FseOHYsbbrjhU18YAHyW3bt3x+DgYGzfvj0iIh566KG47LLLIuL07mEn+j89J7pn7t69OwYGBjr/5vrmm2/uBKDJycmYN29eHD58+HP/2u6TwKk61QB0OvfN4wPQR/e+1157Lc4+++zYt2/fCV/rRUQcPHgw/uZv/ibOPvvszh9+794GCEAwh773ve/FOeecE5deemk88sgj8dWvfjUiThyA9u3bF0uWLIlFixbF8uXLY9OmTTEyMhJr166NiE8PQBERzz//fIyNjcVXv/rVuOSSSzp/yN++fftixYoV8ZWvfCXOPffcWLlyZbz33ntz9s8AaIeNGzfG2NhY5960Y8eOiDi9e9jJvgvns+6ZERH//M//HGNjY/Htb387fvjDH876jqArrrgiBgcH44UXXvhcv7b7JHCqTjUARZz6ffOzAlDEB/e+73znOxHx2a/1PnLllVfGN7/5zc5j9zZAAII59PE/FPU3v/nNrPdkAzCbeybA53fzzTfHI488kn0ZQBcRgGCO7Nu3LwYGBuKNN96IY8eOxY033hhr1qzJviyAruSeCfD5bd26NUZHRzt/BhpAhAAEc+onP/lJnHvuuTE6OhpXXXVV7N+/P/uSALqWeybA6bvzzjvjK1/5Sjz33HPZlwJ0GQEIAAAAoOUEIAAAAICWE4AAAAAAWk4AAgAAAGg5AQgAAACg5QQgAAAAgJYTgAAAAABaTgACAAAAaDkBCAAAAKDlBCAAAACAlhOAAAAAAFpOAAIAAABoOQEIAAAAoOUEIAAAAICWE4AAAAAAWk4AAgAAAGg5AQgAAACg5QQgAAAAgJYTgAAAAABaTgACAAAAaDkBCAAAAKDlBCAAAACAlhOAAAAAAFpOAAIAAABoOQEIAAAAoOUEIAAAAICWE4AAAAAAWk4AAgAAAGg5AQgAAACg5QQgAAAAgJYTgAAAAABaTgACAAAAaDkBCAAAAKDlBCAAAACAlhOAAAAAAFpOAAIAAABoOQEIAAAAoOUEIAAAAICWE4AAAAAAWk4AAgAAAGg5AQgAAACg5QQgAAAAgJYTgAAAAABaTgACAAAAaDkBCAAAAKDlBCAAAACAlhOAAAAAAFpOAAIAAABoOQEIAAAAoOUEIAAAAICWE4AAAAAAWk4AAgAAAGg5AQgAAACg5QQgAAAAgJYTgAAAAABaTgACAAAAaDkBCAAAAKDlBCAAAACAlhOAAAAAAFpOAAIAAABoOQEIAAAAoOUEIAAAAICWE4AAAAAAWk4AAgAAAGi5/x/iZZMt5Dw0iwAAAABJRU5ErkJggg==" 152 | }, 153 | "metadata": {}, 154 | "output_type": "display_data" 155 | } 156 | ], 157 | "source": [ 158 | "iplot(fig)" 159 | ] 160 | }, 161 | { 162 | "cell_type": "markdown", 163 | "metadata": {}, 164 | "source": [ 165 | "### Save figure to standalone HTML file" 166 | ] 167 | }, 168 | { 169 | "cell_type": "code", 170 | "execution_count": 9, 171 | "metadata": {}, 172 | "outputs": [], 173 | "source": [ 174 | "plot(fig, filename='out/bars.html', auto_open=False);" 175 | ] 176 | }, 177 | { 178 | "cell_type": "markdown", 179 | "metadata": {}, 180 | "source": [ 181 | "Then open directly in a JupyterLab pane by right clicking on `out/bars.html` and \"Open With -> View HTML\". This relies on the `@mflevine/jupyterlab_html` extension (https://github.com/mflevine/jupyterlab_html)" 182 | ] 183 | }, 184 | { 185 | "cell_type": "markdown", 186 | "metadata": {}, 187 | "source": [ 188 | "### Save to json file" 189 | ] 190 | }, 191 | { 192 | "cell_type": "code", 193 | "execution_count": 10, 194 | "metadata": {}, 195 | "outputs": [], 196 | "source": [ 197 | "pio.write_json(fig, 'out/bars.plotly.json')" 198 | ] 199 | }, 200 | { 201 | "cell_type": "markdown", 202 | "metadata": {}, 203 | "source": [ 204 | "Right click \"Open With -> Plotly\" (Not PlotlyEditor yet) to view the figure in a JupyterLab tab. This also relies on the `@jupyterlab/plotly-extension` extension." 205 | ] 206 | }, 207 | { 208 | "cell_type": "markdown", 209 | "metadata": {}, 210 | "source": [ 211 | "### Save to static image\n", 212 | "This relies on the `plotly-orca` conda package (https://anaconda.org/plotly/plotly-orca), which is preinstalled with PlotlyLab" 213 | ] 214 | }, 215 | { 216 | "cell_type": "code", 217 | "execution_count": 11, 218 | "metadata": {}, 219 | "outputs": [], 220 | "source": [ 221 | "pio.write_image(fig, 'out/bars.png')" 222 | ] 223 | }, 224 | { 225 | "cell_type": "code", 226 | "execution_count": 12, 227 | "metadata": {}, 228 | "outputs": [], 229 | "source": [ 230 | "pio.write_image(fig, 'out/bars.pdf')" 231 | ] 232 | }, 233 | { 234 | "cell_type": "markdown", 235 | "metadata": {}, 236 | "source": [ 237 | "Then view PNG and PDF by double clicking in thd JupyterLab file menu, this will open the files inside JupyterLab" 238 | ] 239 | }, 240 | { 241 | "cell_type": "code", 242 | "execution_count": null, 243 | "metadata": {}, 244 | "outputs": [], 245 | "source": [] 246 | } 247 | ], 248 | "metadata": { 249 | "kernelspec": { 250 | "display_name": "Python 3", 251 | "language": "python", 252 | "name": "python3" 253 | }, 254 | "language_info": { 255 | "codemirror_mode": { 256 | "name": "ipython", 257 | "version": 3 258 | }, 259 | "file_extension": ".py", 260 | "mimetype": "text/x-python", 261 | "name": "python", 262 | "nbconvert_exporter": "python", 263 | "pygments_lexer": "ipython3", 264 | "version": "3.7.2" 265 | } 266 | }, 267 | "nbformat": 4, 268 | "nbformat_minor": 2 269 | } 270 | -------------------------------------------------------------------------------- /notebooks/3. Figure Widget.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "markdown", 5 | "metadata": {}, 6 | "source": [ 7 | "# Overview\n", 8 | "\n", 9 | "This notebook introduces the plotly.py `FigureWidget` that was introduced in version 3" 10 | ] 11 | }, 12 | { 13 | "cell_type": "markdown", 14 | "metadata": {}, 15 | "source": [ 16 | "### Imports" 17 | ] 18 | }, 19 | { 20 | "cell_type": "code", 21 | "execution_count": 1, 22 | "metadata": {}, 23 | "outputs": [], 24 | "source": [ 25 | "# pandas\n", 26 | "import pandas as pd\n", 27 | "\n", 28 | "# numpy\n", 29 | "import numpy as np" 30 | ] 31 | }, 32 | { 33 | "cell_type": "markdown", 34 | "metadata": {}, 35 | "source": [ 36 | "### Load iris dataset" 37 | ] 38 | }, 39 | { 40 | "cell_type": "code", 41 | "execution_count": 2, 42 | "metadata": {}, 43 | "outputs": [], 44 | "source": [ 45 | "iris_df = pd.read_csv('datasets/iris.csv')" 46 | ] 47 | }, 48 | { 49 | "cell_type": "markdown", 50 | "metadata": {}, 51 | "source": [ 52 | "## Create and display an empty FigureWidget\n", 53 | "A FigureWidget behaves almost identically to a Figure but it is also an ipywidget that can be displayed directly in the notebook without calling `iplot`" 54 | ] 55 | }, 56 | { 57 | "cell_type": "code", 58 | "execution_count": 3, 59 | "metadata": {}, 60 | "outputs": [], 61 | "source": [ 62 | "import plotly.graph_objs as go" 63 | ] 64 | }, 65 | { 66 | "cell_type": "code", 67 | "execution_count": 4, 68 | "metadata": {}, 69 | "outputs": [ 70 | { 71 | "data": { 72 | "application/vnd.jupyter.widget-view+json": { 73 | "model_id": "", 74 | "version_major": 2, 75 | "version_minor": 0 76 | }, 77 | "text/plain": [ 78 | "FigureWidget({\n", 79 | " 'data': [], 'layout': {}\n", 80 | "})" 81 | ] 82 | }, 83 | "metadata": {}, 84 | "output_type": "display_data" 85 | } 86 | ], 87 | "source": [ 88 | "f1 = go.FigureWidget()\n", 89 | "f1" 90 | ] 91 | }, 92 | { 93 | "cell_type": "markdown", 94 | "metadata": {}, 95 | "source": [ 96 | "# Tab completion \n", 97 | "Entering ``f1.add_`` displays add methods for all of the supported trace types" 98 | ] 99 | }, 100 | { 101 | "cell_type": "code", 102 | "execution_count": 5, 103 | "metadata": {}, 104 | "outputs": [], 105 | "source": [ 106 | "# f1.add_scatter" 107 | ] 108 | }, 109 | { 110 | "cell_type": "markdown", 111 | "metadata": {}, 112 | "source": [ 113 | "Entering ``f1.add_scatter()`` displays the names of all of the top-level properties for the scatter trace type\n", 114 | "\n", 115 | "Entering ``f1.add_scatter()`` displays the signature pop-up. Expanding this pop-up reveals the method doc string which contains the descriptions of all of the top level properties" 116 | ] 117 | }, 118 | { 119 | "cell_type": "code", 120 | "execution_count": 6, 121 | "metadata": {}, 122 | "outputs": [], 123 | "source": [ 124 | "# f1.add_scatter(" 125 | ] 126 | }, 127 | { 128 | "cell_type": "markdown", 129 | "metadata": {}, 130 | "source": [ 131 | "# Add scatter trace" 132 | ] 133 | }, 134 | { 135 | "cell_type": "code", 136 | "execution_count": 7, 137 | "metadata": { 138 | "scrolled": false 139 | }, 140 | "outputs": [], 141 | "source": [ 142 | "scatt1 = f1.add_scatter(x=iris_df.sepal_length, y=iris_df.petal_width)" 143 | ] 144 | }, 145 | { 146 | "cell_type": "code", 147 | "execution_count": 8, 148 | "metadata": {}, 149 | "outputs": [], 150 | "source": [ 151 | "# That's not what we wanted, change the mode to 'markers'\n", 152 | "scatt1.mode = 'markers'" 153 | ] 154 | }, 155 | { 156 | "cell_type": "code", 157 | "execution_count": 9, 158 | "metadata": {}, 159 | "outputs": [], 160 | "source": [ 161 | "# Set size to 8\n", 162 | "scatt1.marker.size = 8" 163 | ] 164 | }, 165 | { 166 | "cell_type": "code", 167 | "execution_count": 10, 168 | "metadata": {}, 169 | "outputs": [], 170 | "source": [ 171 | "# Add axis labels\n", 172 | "f1.layout.xaxis.title = 'sepal_length'\n", 173 | "f1.layout.yaxis.title = 'petal_width'" 174 | ] 175 | }, 176 | { 177 | "cell_type": "code", 178 | "execution_count": 11, 179 | "metadata": {}, 180 | "outputs": [], 181 | "source": [ 182 | "# Hover info\n", 183 | "scatt1.text = iris_df.species\n", 184 | "scatt1.hoverinfo = 'text+x+y'\n", 185 | "f1.layout.hovermode = 'closest'" 186 | ] 187 | }, 188 | { 189 | "cell_type": "markdown", 190 | "metadata": {}, 191 | "source": [ 192 | "## Animate marker size change" 193 | ] 194 | }, 195 | { 196 | "cell_type": "code", 197 | "execution_count": 12, 198 | "metadata": {}, 199 | "outputs": [], 200 | "source": [ 201 | "# Set marker size based on petal_length\n", 202 | "with f1.batch_animate(duration=1000):\n", 203 | " scatt1.marker.size = np.sqrt(iris_df.petal_length.values * 50)\n", 204 | " " 205 | ] 206 | }, 207 | { 208 | "cell_type": "code", 209 | "execution_count": 14, 210 | "metadata": {}, 211 | "outputs": [], 212 | "source": [ 213 | "# Restore constant marker size\n", 214 | "with f1.batch_animate(duration=1000):\n", 215 | " scatt1.marker.size = 8" 216 | ] 217 | }, 218 | { 219 | "cell_type": "markdown", 220 | "metadata": {}, 221 | "source": [ 222 | "## Configure colorscale for brushing" 223 | ] 224 | }, 225 | { 226 | "cell_type": "code", 227 | "execution_count": 15, 228 | "metadata": {}, 229 | "outputs": [], 230 | "source": [ 231 | "scatt1.marker.colorbar = None\n", 232 | "scatt1.marker.colorscale = [[0, 'lightgray'], [0.5, 'lightgray'], [0.5, 'red'], [1, 'red']]\n", 233 | "scatt1.marker.cmin = -0.5\n", 234 | "scatt1.marker.cmax = 1.5\n", 235 | "scatt1.marker.colorbar.ticks = 'outside'\n", 236 | "scatt1.marker.colorbar.tickvals = [0, 1]\n", 237 | "scatt1.marker.colorbar.ticktext = ['unselected', 'selected']" 238 | ] 239 | }, 240 | { 241 | "cell_type": "code", 242 | "execution_count": 16, 243 | "metadata": {}, 244 | "outputs": [], 245 | "source": [ 246 | "# Reset colors to zeros (unselected)\n", 247 | "scatt1.marker.color = np.zeros(len(iris_df))\n", 248 | "selected = np.zeros(len(iris_df))" 249 | ] 250 | }, 251 | { 252 | "cell_type": "markdown", 253 | "metadata": {}, 254 | "source": [ 255 | "### Configure brushing callback" 256 | ] 257 | }, 258 | { 259 | "cell_type": "code", 260 | "execution_count": 17, 261 | "metadata": {}, 262 | "outputs": [], 263 | "source": [ 264 | "# Assigning these variables here is not required. But doing so tricks Jupyter into \n", 265 | "# providing property tab completion on the parameters to the brush function below\n", 266 | "from plotly.callbacks import Points\n", 267 | "trace, points = scatt1, Points()" 268 | ] 269 | }, 270 | { 271 | "cell_type": "code", 272 | "execution_count": 18, 273 | "metadata": {}, 274 | "outputs": [], 275 | "source": [ 276 | "def brush(trace, points, selector):\n", 277 | " inds = np.array(points.point_inds)\n", 278 | " if inds.size:\n", 279 | " selected[inds] = 1\n", 280 | " trace.marker.color = selected" 281 | ] 282 | }, 283 | { 284 | "cell_type": "code", 285 | "execution_count": 19, 286 | "metadata": {}, 287 | "outputs": [], 288 | "source": [ 289 | "scatt1.on_selection(brush)" 290 | ] 291 | }, 292 | { 293 | "cell_type": "markdown", 294 | "metadata": {}, 295 | "source": [ 296 | "Now box or lasso select points on the figure and see them turn red" 297 | ] 298 | }, 299 | { 300 | "cell_type": "code", 301 | "execution_count": 20, 302 | "metadata": {}, 303 | "outputs": [], 304 | "source": [ 305 | "# Reset brush\n", 306 | "selected = np.zeros(len(iris_df))\n", 307 | "scatt1.marker.color = selected" 308 | ] 309 | }, 310 | { 311 | "cell_type": "markdown", 312 | "metadata": {}, 313 | "source": [ 314 | "## Create second plot with different features" 315 | ] 316 | }, 317 | { 318 | "cell_type": "code", 319 | "execution_count": 21, 320 | "metadata": { 321 | "scrolled": false 322 | }, 323 | "outputs": [ 324 | { 325 | "data": { 326 | "application/vnd.jupyter.widget-view+json": { 327 | "model_id": "d2d66ab3932348ad85533247d3d4a62e", 328 | "version_major": 2, 329 | "version_minor": 0 330 | }, 331 | "text/plain": [ 332 | "FigureWidget({\n", 333 | " 'data': [{'mode': 'markers',\n", 334 | " 'type': 'scatter',\n", 335 | " 'uid': '83da315…" 336 | ] 337 | }, 338 | "metadata": {}, 339 | "output_type": "display_data" 340 | } 341 | ], 342 | "source": [ 343 | "f2 = go.FigureWidget(data=[\n", 344 | " go.Scatter(x=iris_df.petal_length, y=iris_df.sepal_width, mode='markers')])\n", 345 | "f2" 346 | ] 347 | }, 348 | { 349 | "cell_type": "code", 350 | "execution_count": 22, 351 | "metadata": {}, 352 | "outputs": [], 353 | "source": [ 354 | "# Set axis titles\n", 355 | "f2.layout.xaxis.title = 'petal_length'\n", 356 | "f2.layout.yaxis.title = 'sepal_width'" 357 | ] 358 | }, 359 | { 360 | "cell_type": "code", 361 | "execution_count": 23, 362 | "metadata": {}, 363 | "outputs": [], 364 | "source": [ 365 | "# Grab trace reference\n", 366 | "scatt2 = f2.data[0]" 367 | ] 368 | }, 369 | { 370 | "cell_type": "code", 371 | "execution_count": 24, 372 | "metadata": { 373 | "scrolled": false 374 | }, 375 | "outputs": [], 376 | "source": [ 377 | "# Set marker styles / colorbars to match between figures\n", 378 | "scatt2.marker = scatt1.marker" 379 | ] 380 | }, 381 | { 382 | "cell_type": "code", 383 | "execution_count": 25, 384 | "metadata": {}, 385 | "outputs": [], 386 | "source": [ 387 | "# Configure brush on both plots to update both plots\n", 388 | "def brush(trace, points, state):\n", 389 | " inds = np.array(points.point_inds)\n", 390 | " if inds.size:\n", 391 | " selected = scatt1.marker.color.copy()\n", 392 | " selected[inds] = 1\n", 393 | " scatt1.marker.color = selected\n", 394 | " scatt2.marker.color = selected \n", 395 | " \n", 396 | "scatt1.on_selection(brush)\n", 397 | "scatt2.on_selection(brush)" 398 | ] 399 | }, 400 | { 401 | "cell_type": "code", 402 | "execution_count": 26, 403 | "metadata": {}, 404 | "outputs": [], 405 | "source": [ 406 | "# Reset brush\n", 407 | "def reset_brush(btn):\n", 408 | " selected = np.zeros(len(iris_df))\n", 409 | " scatt1.marker.color = selected\n", 410 | " scatt2.marker.color = selected" 411 | ] 412 | }, 413 | { 414 | "cell_type": "code", 415 | "execution_count": 27, 416 | "metadata": {}, 417 | "outputs": [], 418 | "source": [ 419 | "# ipywidgets\n", 420 | "from ipywidgets import HBox, VBox, Button" 421 | ] 422 | }, 423 | { 424 | "cell_type": "code", 425 | "execution_count": 28, 426 | "metadata": {}, 427 | "outputs": [ 428 | { 429 | "data": { 430 | "application/vnd.jupyter.widget-view+json": { 431 | "model_id": "efb5fb9cc2f74c3eac698f3fbddb1650", 432 | "version_major": 2, 433 | "version_minor": 0 434 | }, 435 | "text/plain": [ 436 | "Button(description='clear', style=ButtonStyle())" 437 | ] 438 | }, 439 | "metadata": {}, 440 | "output_type": "display_data" 441 | } 442 | ], 443 | "source": [ 444 | "# Create reset button\n", 445 | "button = Button(description=\"clear\")\n", 446 | "button.on_click(reset_brush)\n", 447 | "button" 448 | ] 449 | }, 450 | { 451 | "cell_type": "code", 452 | "execution_count": 29, 453 | "metadata": {}, 454 | "outputs": [], 455 | "source": [ 456 | "# Hide colorbar for figure 1\n", 457 | "scatt1.marker.showscale = False" 458 | ] 459 | }, 460 | { 461 | "cell_type": "code", 462 | "execution_count": 30, 463 | "metadata": {}, 464 | "outputs": [], 465 | "source": [ 466 | "# Set dragmode to lasso for both plots\n", 467 | "f1.layout.dragmode = 'lasso'\n", 468 | "f2.layout.dragmode = 'lasso'" 469 | ] 470 | }, 471 | { 472 | "cell_type": "code", 473 | "execution_count": 31, 474 | "metadata": {}, 475 | "outputs": [], 476 | "source": [ 477 | "# Display two figures and the reset button\n", 478 | "f1.layout.width = 500\n", 479 | "f2.layout.width = 500" 480 | ] 481 | }, 482 | { 483 | "cell_type": "code", 484 | "execution_count": 32, 485 | "metadata": {}, 486 | "outputs": [ 487 | { 488 | "data": { 489 | "application/vnd.jupyter.widget-view+json": { 490 | "model_id": "18bb3ccf3a424035997cc84e745ee8fe", 491 | "version_major": 2, 492 | "version_minor": 0 493 | }, 494 | "text/plain": [ 495 | "VBox(children=(HBox(children=(FigureWidget({\n", 496 | " 'data': [{'hoverinfo': 'text+x+y',\n", 497 | " 'marker': {'…" 498 | ] 499 | }, 500 | "metadata": {}, 501 | "output_type": "display_data" 502 | } 503 | ], 504 | "source": [ 505 | "VBox([HBox([f1, f2]), button])" 506 | ] 507 | }, 508 | { 509 | "cell_type": "code", 510 | "execution_count": null, 511 | "metadata": {}, 512 | "outputs": [], 513 | "source": [] 514 | } 515 | ], 516 | "metadata": { 517 | "kernelspec": { 518 | "display_name": "Python 3", 519 | "language": "python", 520 | "name": "python3" 521 | }, 522 | "language_info": { 523 | "codemirror_mode": { 524 | "name": "ipython", 525 | "version": 3 526 | }, 527 | "file_extension": ".py", 528 | "mimetype": "text/x-python", 529 | "name": "python", 530 | "nbconvert_exporter": "python", 531 | "pygments_lexer": "ipython3", 532 | "version": "3.6.7" 533 | }, 534 | "widgets": { 535 | "application/vnd.jupyter.widget-state+json": { 536 | "state": { 537 | "1b20bf0384764e14b6f8f12afc4f2c9e": { 538 | "model_module": "@jupyter-widgets/base", 539 | "model_module_version": "1.1.0", 540 | "model_name": "LayoutModel", 541 | "state": {} 542 | }, 543 | "1fa42c2d77c74bc4ba7fda9261ae791a": { 544 | "buffers": [ 545 | { 546 | "data": "ZmZmZmZmFECamZmZmZkTQM3MzMzMzBJAZmZmZmZmEkAAAAAAAAAUQJqZmZmZmRVAZmZmZmZmEkAAAAAAAAAUQJqZmZmZmRFAmpmZmZmZE0CamZmZmZkVQDMzMzMzMxNAMzMzMzMzE0AzMzMzMzMRQDMzMzMzMxdAzczMzMzMFkCamZmZmZkVQGZmZmZmZhRAzczMzMzMFkBmZmZmZmYUQJqZmZmZmRVAZmZmZmZmFEBmZmZmZmYSQGZmZmZmZhRAMzMzMzMzE0AAAAAAAAAUQAAAAAAAABRAzczMzMzMFEDNzMzMzMwUQM3MzMzMzBJAMzMzMzMzE0CamZmZmZkVQM3MzMzMzBRAAAAAAAAAFkCamZmZmZkTQAAAAAAAABRAAAAAAAAAFkCamZmZmZkTQJqZmZmZmRFAZmZmZmZmFEAAAAAAAAAUQAAAAAAAABJAmpmZmZmZEUAAAAAAAAAUQGZmZmZmZhRAMzMzMzMzE0BmZmZmZmYUQGZmZmZmZhJAMzMzMzMzFUAAAAAAAAAUQAAAAAAAABxAmpmZmZmZGUCamZmZmZkbQAAAAAAAABZAAAAAAAAAGkDNzMzMzMwWQDMzMzMzMxlAmpmZmZmZE0BmZmZmZmYaQM3MzMzMzBRAAAAAAAAAFECamZmZmZkXQAAAAAAAABhAZmZmZmZmGEBmZmZmZmYWQM3MzMzMzBpAZmZmZmZmFkAzMzMzMzMXQM3MzMzMzBhAZmZmZmZmFkCamZmZmZkXQGZmZmZmZhhAMzMzMzMzGUBmZmZmZmYYQJqZmZmZmRlAZmZmZmZmGkAzMzMzMzMbQM3MzMzMzBpAAAAAAAAAGEDNzMzMzMwWQAAAAAAAABZAAAAAAAAAFkAzMzMzMzMXQAAAAAAAABhAmpmZmZmZFUAAAAAAAAAYQM3MzMzMzBpAMzMzMzMzGUBmZmZmZmYWQAAAAAAAABZAAAAAAAAAFkBmZmZmZmYYQDMzMzMzMxdAAAAAAAAAFEBmZmZmZmYWQM3MzMzMzBZAzczMzMzMFkDNzMzMzMwYQGZmZmZmZhRAzczMzMzMFkAzMzMzMzMZQDMzMzMzMxdAZmZmZmZmHEAzMzMzMzMZQAAAAAAAABpAZmZmZmZmHkCamZmZmZkTQDMzMzMzMx1AzczMzMzMGkDNzMzMzMwcQAAAAAAAABpAmpmZmZmZGUAzMzMzMzMbQM3MzMzMzBZAMzMzMzMzF0CamZmZmZkZQAAAAAAAABpAzczMzMzMHkDNzMzMzMweQAAAAAAAABhAmpmZmZmZG0BmZmZmZmYWQM3MzMzMzB5AMzMzMzMzGUDNzMzMzMwaQM3MzMzMzBxAzczMzMzMGEBmZmZmZmYYQJqZmZmZmRlAzczMzMzMHECamZmZmZkdQJqZmZmZmR9AmpmZmZmZGUAzMzMzMzMZQGZmZmZmZhhAzczMzMzMHkAzMzMzMzMZQJqZmZmZmRlAAAAAAAAAGECamZmZmZkbQM3MzMzMzBpAmpmZmZmZG0AzMzMzMzMXQDMzMzMzMxtAzczMzMzMGkDNzMzMzMwaQDMzMzMzMxlAAAAAAAAAGkDNzMzMzMwYQJqZmZmZmRdA", 547 | "encoding": "base64", 548 | "path": [ 549 | "_data", 550 | 0, 551 | "x" 552 | ] 553 | }, 554 | { 555 | "data": "mpmZmZmZyT+amZmZmZnJP5qZmZmZmck/mpmZmZmZyT+amZmZmZnJP5qZmZmZmdk/MzMzMzMz0z+amZmZmZnJP5qZmZmZmck/mpmZmZmZuT+amZmZmZnJP5qZmZmZmck/mpmZmZmZuT+amZmZmZm5P5qZmZmZmck/mpmZmZmZ2T+amZmZmZnZPzMzMzMzM9M/MzMzMzMz0z8zMzMzMzPTP5qZmZmZmck/mpmZmZmZ2T+amZmZmZnJPwAAAAAAAOA/mpmZmZmZyT+amZmZmZnJP5qZmZmZmdk/mpmZmZmZyT+amZmZmZnJP5qZmZmZmck/mpmZmZmZyT+amZmZmZnZP5qZmZmZmbk/mpmZmZmZyT+amZmZmZm5P5qZmZmZmck/mpmZmZmZyT+amZmZmZm5P5qZmZmZmck/mpmZmZmZyT8zMzMzMzPTPzMzMzMzM9M/mpmZmZmZyT8zMzMzMzPjP5qZmZmZmdk/MzMzMzMz0z+amZmZmZnJP5qZmZmZmck/mpmZmZmZyT+amZmZmZnJP2ZmZmZmZvY/AAAAAAAA+D8AAAAAAAD4P83MzMzMzPQ/AAAAAAAA+D/NzMzMzMz0P5qZmZmZmfk/AAAAAAAA8D/NzMzMzMz0P2ZmZmZmZvY/AAAAAAAA8D8AAAAAAAD4PwAAAAAAAPA/ZmZmZmZm9j/NzMzMzMz0P2ZmZmZmZvY/AAAAAAAA+D8AAAAAAADwPwAAAAAAAPg/mpmZmZmZ8T/NzMzMzMz8P83MzMzMzPQ/AAAAAAAA+D8zMzMzMzPzP83MzMzMzPQ/ZmZmZmZm9j9mZmZmZmb2PzMzMzMzM/s/AAAAAAAA+D8AAAAAAADwP5qZmZmZmfE/AAAAAAAA8D8zMzMzMzPzP5qZmZmZmfk/AAAAAAAA+D+amZmZmZn5PwAAAAAAAPg/zczMzMzM9D/NzMzMzMz0P83MzMzMzPQ/MzMzMzMz8z9mZmZmZmb2PzMzMzMzM/M/AAAAAAAA8D/NzMzMzMz0PzMzMzMzM/M/zczMzMzM9D/NzMzMzMz0P5qZmZmZmfE/zczMzMzM9D8AAAAAAAAEQGZmZmZmZv4/zczMzMzMAEDNzMzMzMz8P5qZmZmZmQFAzczMzMzMAEAzMzMzMzP7P83MzMzMzPw/zczMzMzM/D8AAAAAAAAEQAAAAAAAAABAZmZmZmZm/j/NzMzMzMwAQAAAAAAAAABAMzMzMzMzA0BmZmZmZmYCQM3MzMzMzPw/mpmZmZmZAUBmZmZmZmYCQAAAAAAAAPg/ZmZmZmZmAkAAAAAAAAAAQAAAAAAAAABAzczMzMzM/D/NzMzMzMwAQM3MzMzMzPw/zczMzMzM/D/NzMzMzMz8P83MzMzMzABAmpmZmZmZ+T9mZmZmZmb+PwAAAAAAAABAmpmZmZmZAUAAAAAAAAD4P2ZmZmZmZvY/ZmZmZmZmAkAzMzMzMzMDQM3MzMzMzPw/zczMzMzM/D/NzMzMzMwAQDMzMzMzMwNAZmZmZmZmAkBmZmZmZmb+P2ZmZmZmZgJAAAAAAAAABEBmZmZmZmYCQGZmZmZmZv4/AAAAAAAAAEBmZmZmZmYCQM3MzMzMzPw/", 556 | "encoding": "base64", 557 | "path": [ 558 | "_data", 559 | 0, 560 | "y" 561 | ] 562 | }, 563 | { 564 | "data": "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA", 565 | "encoding": "base64", 566 | "path": [ 567 | "_data", 568 | 0, 569 | "marker", 570 | "color" 571 | ] 572 | } 573 | ], 574 | "model_module": "plotlywidget", 575 | "model_module_version": "^0.2.1", 576 | "model_name": "FigureModel", 577 | "state": { 578 | "_data": [ 579 | { 580 | "hoverinfo": "text+x+y", 581 | "marker": { 582 | "cmax": 1.5, 583 | "cmin": -0.5, 584 | "colorbar": { 585 | "ticks": "outside", 586 | "ticktext": [ 587 | "unselected", 588 | "selected" 589 | ], 590 | "tickvals": [ 591 | 0, 592 | 1 593 | ] 594 | }, 595 | "colorscale": [ 596 | [ 597 | 0, 598 | "lightgray" 599 | ], 600 | [ 601 | 0.5, 602 | "lightgray" 603 | ], 604 | [ 605 | 0.5, 606 | "red" 607 | ], 608 | [ 609 | 1, 610 | "red" 611 | ] 612 | ], 613 | "showscale": false, 614 | "size": 8 615 | }, 616 | "mode": "markers", 617 | "text": [ 618 | "setosa", 619 | "setosa", 620 | "setosa", 621 | "setosa", 622 | "setosa", 623 | "setosa", 624 | "setosa", 625 | "setosa", 626 | "setosa", 627 | "setosa", 628 | "setosa", 629 | "setosa", 630 | "setosa", 631 | "setosa", 632 | "setosa", 633 | "setosa", 634 | "setosa", 635 | "setosa", 636 | "setosa", 637 | "setosa", 638 | "setosa", 639 | "setosa", 640 | "setosa", 641 | "setosa", 642 | "setosa", 643 | "setosa", 644 | "setosa", 645 | "setosa", 646 | "setosa", 647 | "setosa", 648 | "setosa", 649 | "setosa", 650 | "setosa", 651 | "setosa", 652 | "setosa", 653 | "setosa", 654 | "setosa", 655 | "setosa", 656 | "setosa", 657 | "setosa", 658 | "setosa", 659 | "setosa", 660 | "setosa", 661 | "setosa", 662 | "setosa", 663 | "setosa", 664 | "setosa", 665 | "setosa", 666 | "setosa", 667 | "setosa", 668 | "versicolor", 669 | "versicolor", 670 | "versicolor", 671 | "versicolor", 672 | "versicolor", 673 | "versicolor", 674 | "versicolor", 675 | "versicolor", 676 | "versicolor", 677 | "versicolor", 678 | "versicolor", 679 | "versicolor", 680 | "versicolor", 681 | "versicolor", 682 | "versicolor", 683 | "versicolor", 684 | "versicolor", 685 | "versicolor", 686 | "versicolor", 687 | "versicolor", 688 | "versicolor", 689 | "versicolor", 690 | "versicolor", 691 | "versicolor", 692 | "versicolor", 693 | "versicolor", 694 | "versicolor", 695 | "versicolor", 696 | "versicolor", 697 | "versicolor", 698 | "versicolor", 699 | "versicolor", 700 | "versicolor", 701 | "versicolor", 702 | "versicolor", 703 | "versicolor", 704 | "versicolor", 705 | "versicolor", 706 | "versicolor", 707 | "versicolor", 708 | "versicolor", 709 | "versicolor", 710 | "versicolor", 711 | "versicolor", 712 | "versicolor", 713 | "versicolor", 714 | "versicolor", 715 | "versicolor", 716 | "versicolor", 717 | "versicolor", 718 | "virginica", 719 | "virginica", 720 | "virginica", 721 | "virginica", 722 | "virginica", 723 | "virginica", 724 | "virginica", 725 | "virginica", 726 | "virginica", 727 | "virginica", 728 | "virginica", 729 | "virginica", 730 | "virginica", 731 | "virginica", 732 | "virginica", 733 | "virginica", 734 | "virginica", 735 | "virginica", 736 | "virginica", 737 | "virginica", 738 | "virginica", 739 | "virginica", 740 | "virginica", 741 | "virginica", 742 | "virginica", 743 | "virginica", 744 | "virginica", 745 | "virginica", 746 | "virginica", 747 | "virginica", 748 | "virginica", 749 | "virginica", 750 | "virginica", 751 | "virginica", 752 | "virginica", 753 | "virginica", 754 | "virginica", 755 | "virginica", 756 | "virginica", 757 | "virginica", 758 | "virginica", 759 | "virginica", 760 | "virginica", 761 | "virginica", 762 | "virginica", 763 | "virginica", 764 | "virginica", 765 | "virginica", 766 | "virginica", 767 | "virginica" 768 | ], 769 | "type": "scatter", 770 | "uid": "93e6f4d8-a0d7-11e8-93f3-645aede86e5b" 771 | } 772 | ], 773 | "_js2py_pointsCallback": {}, 774 | "_js2py_relayout": {}, 775 | "_js2py_restyle": {}, 776 | "_js2py_update": {}, 777 | "_last_layout_edit_id": 33, 778 | "_last_trace_edit_id": 28, 779 | "_layout": { 780 | "dragmode": "lasso", 781 | "hovermode": "closest", 782 | "width": 500, 783 | "xaxis": { 784 | "title": "sepal_length" 785 | }, 786 | "yaxis": { 787 | "title": "petal_width" 788 | } 789 | }, 790 | "_py2js_deleteTraces": {}, 791 | "_py2js_moveTraces": {}, 792 | "_py2js_removeLayoutProps": {}, 793 | "_py2js_removeTraceProps": {}, 794 | "_py2js_update": {}, 795 | "_view_count": 2 796 | } 797 | }, 798 | "2ede5939ee91466193cd64f8cd4b6421": { 799 | "model_module": "@jupyter-widgets/base", 800 | "model_module_version": "1.1.0", 801 | "model_name": "LayoutModel", 802 | "state": {} 803 | }, 804 | "534b8fdcc7954a41966f869ee542da3b": { 805 | "model_module": "@jupyter-widgets/controls", 806 | "model_module_version": "1.3.0", 807 | "model_name": "VBoxModel", 808 | "state": { 809 | "children": [ 810 | "IPY_MODEL_b1ae5982e77d45b697633cd7d543dbec", 811 | "IPY_MODEL_de6bef56bf3b41e68055c1af346e98a9" 812 | ], 813 | "layout": "IPY_MODEL_99ebb20eda34489c9ae081604714d15d" 814 | } 815 | }, 816 | "99ebb20eda34489c9ae081604714d15d": { 817 | "model_module": "@jupyter-widgets/base", 818 | "model_module_version": "1.1.0", 819 | "model_name": "LayoutModel", 820 | "state": {} 821 | }, 822 | "b058b15b17934604ab9af843abdd94fb": { 823 | "model_module": "@jupyter-widgets/controls", 824 | "model_module_version": "1.3.0", 825 | "model_name": "ButtonStyleModel", 826 | "state": {} 827 | }, 828 | "b1ae5982e77d45b697633cd7d543dbec": { 829 | "model_module": "@jupyter-widgets/controls", 830 | "model_module_version": "1.3.0", 831 | "model_name": "HBoxModel", 832 | "state": { 833 | "children": [ 834 | "IPY_MODEL_1fa42c2d77c74bc4ba7fda9261ae791a", 835 | "IPY_MODEL_c30a1a8a78424bafad2883760e446734" 836 | ], 837 | "layout": "IPY_MODEL_1b20bf0384764e14b6f8f12afc4f2c9e" 838 | } 839 | }, 840 | "c30a1a8a78424bafad2883760e446734": { 841 | "buffers": [ 842 | { 843 | "data": "ZmZmZmZm9j9mZmZmZmb2P83MzMzMzPQ/AAAAAAAA+D9mZmZmZmb2PzMzMzMzM/s/ZmZmZmZm9j8AAAAAAAD4P2ZmZmZmZvY/AAAAAAAA+D8AAAAAAAD4P5qZmZmZmfk/ZmZmZmZm9j+amZmZmZnxPzMzMzMzM/M/AAAAAAAA+D/NzMzMzMz0P2ZmZmZmZvY/MzMzMzMz+z8AAAAAAAD4PzMzMzMzM/s/AAAAAAAA+D8AAAAAAADwPzMzMzMzM/s/ZmZmZmZm/j+amZmZmZn5P5qZmZmZmfk/AAAAAAAA+D9mZmZmZmb2P5qZmZmZmfk/mpmZmZmZ+T8AAAAAAAD4PwAAAAAAAPg/ZmZmZmZm9j8AAAAAAAD4PzMzMzMzM/M/zczMzMzM9D8AAAAAAAD4P83MzMzMzPQ/AAAAAAAA+D/NzMzMzMz0P83MzMzMzPQ/zczMzMzM9D+amZmZmZn5P2ZmZmZmZv4/ZmZmZmZm9j+amZmZmZn5P2ZmZmZmZvY/AAAAAAAA+D9mZmZmZmb2P83MzMzMzBJAAAAAAAAAEkCamZmZmZkTQAAAAAAAABBAZmZmZmZmEkAAAAAAAAASQM3MzMzMzBJAZmZmZmZmCkBmZmZmZmYSQDMzMzMzMw9AAAAAAAAADEDNzMzMzMwQQAAAAAAAABBAzczMzMzMEkDNzMzMzMwMQJqZmZmZmRFAAAAAAAAAEkBmZmZmZmYQQAAAAAAAABJAMzMzMzMzD0AzMzMzMzMTQAAAAAAAABBAmpmZmZmZE0DNzMzMzMwSQDMzMzMzMxFAmpmZmZmZEUAzMzMzMzMTQAAAAAAAABRAAAAAAAAAEkAAAAAAAAAMQGZmZmZmZg5AmpmZmZmZDUAzMzMzMzMPQGZmZmZmZhRAAAAAAAAAEkAAAAAAAAASQM3MzMzMzBJAmpmZmZmZEUBmZmZmZmYQQAAAAAAAABBAmpmZmZmZEUBmZmZmZmYSQAAAAAAAABBAZmZmZmZmCkDNzMzMzMwQQM3MzMzMzBBAzczMzMzMEEAzMzMzMzMRQAAAAAAAAAhAZmZmZmZmEEAAAAAAAAAYQGZmZmZmZhRAmpmZmZmZF0BmZmZmZmYWQDMzMzMzMxdAZmZmZmZmGkAAAAAAAAASQDMzMzMzMxlAMzMzMzMzF0BmZmZmZmYYQGZmZmZmZhRAMzMzMzMzFUAAAAAAAAAWQAAAAAAAABRAZmZmZmZmFEAzMzMzMzMVQAAAAAAAABZAzczMzMzMGkCamZmZmZkbQAAAAAAAABRAzczMzMzMFkCamZmZmZkTQM3MzMzMzBpAmpmZmZmZE0DNzMzMzMwWQAAAAAAAABhAMzMzMzMzE0CamZmZmZkTQGZmZmZmZhZAMzMzMzMzF0BmZmZmZmYYQJqZmZmZmRlAZmZmZmZmFkBmZmZmZmYUQGZmZmZmZhZAZmZmZmZmGEBmZmZmZmYWQAAAAAAAABZAMzMzMzMzE0CamZmZmZkVQGZmZmZmZhZAZmZmZmZmFEBmZmZmZmYUQJqZmZmZmRdAzczMzMzMFkDNzMzMzMwUQAAAAAAAABRAzczMzMzMFECamZmZmZkVQGZmZmZmZhRA", 844 | "encoding": "base64", 845 | "path": [ 846 | "_data", 847 | 0, 848 | "x" 849 | ] 850 | }, 851 | { 852 | "data": "AAAAAAAADEAAAAAAAAAIQJqZmZmZmQlAzczMzMzMCEDNzMzMzMwMQDMzMzMzMw9AMzMzMzMzC0AzMzMzMzMLQDMzMzMzMwdAzczMzMzMCECamZmZmZkNQDMzMzMzMwtAAAAAAAAACEAAAAAAAAAIQAAAAAAAABBAmpmZmZmZEUAzMzMzMzMPQAAAAAAAAAxAZmZmZmZmDkBmZmZmZmYOQDMzMzMzMwtAmpmZmZmZDUDNzMzMzMwMQGZmZmZmZgpAMzMzMzMzC0AAAAAAAAAIQDMzMzMzMwtAAAAAAAAADEAzMzMzMzMLQJqZmZmZmQlAzczMzMzMCEAzMzMzMzMLQGZmZmZmZhBAzczMzMzMEEDNzMzMzMwIQJqZmZmZmQlAAAAAAAAADEDNzMzMzMwIQAAAAAAAAAhAMzMzMzMzC0AAAAAAAAAMQGZmZmZmZgJAmpmZmZmZCUAAAAAAAAAMQGZmZmZmZg5AAAAAAAAACEBmZmZmZmYOQJqZmZmZmQlAmpmZmZmZDUBmZmZmZmYKQJqZmZmZmQlAmpmZmZmZCUDNzMzMzMwIQGZmZmZmZgJAZmZmZmZmBkBmZmZmZmYGQGZmZmZmZgpAMzMzMzMzA0AzMzMzMzMHQJqZmZmZmQVAAAAAAAAAAEAAAAAAAAAIQJqZmZmZmQFAMzMzMzMzB0AzMzMzMzMHQM3MzMzMzAhAAAAAAAAACECamZmZmZkFQJqZmZmZmQFAAAAAAAAABECamZmZmZkJQGZmZmZmZgZAAAAAAAAABEBmZmZmZmYGQDMzMzMzMwdAAAAAAAAACEBmZmZmZmYGQAAAAAAAAAhAMzMzMzMzB0DNzMzMzMwEQDMzMzMzMwNAMzMzMzMzA0CamZmZmZkFQJqZmZmZmQVAAAAAAAAACEAzMzMzMzMLQM3MzMzMzAhAZmZmZmZmAkAAAAAAAAAIQAAAAAAAAARAzczMzMzMBEAAAAAAAAAIQM3MzMzMzARAZmZmZmZmAkCamZmZmZkFQAAAAAAAAAhAMzMzMzMzB0AzMzMzMzMHQAAAAAAAAARAZmZmZmZmBkBmZmZmZmYKQJqZmZmZmQVAAAAAAAAACEAzMzMzMzMHQAAAAAAAAAhAAAAAAAAACEAAAAAAAAAEQDMzMzMzMwdAAAAAAAAABEDNzMzMzMwMQJqZmZmZmQlAmpmZmZmZBUAAAAAAAAAIQAAAAAAAAARAZmZmZmZmBkCamZmZmZkJQAAAAAAAAAhAZmZmZmZmDkDNzMzMzMwEQJqZmZmZmQFAmpmZmZmZCUBmZmZmZmYGQGZmZmZmZgZAmpmZmZmZBUBmZmZmZmYKQJqZmZmZmQlAZmZmZmZmBkAAAAAAAAAIQGZmZmZmZgZAAAAAAAAACEBmZmZmZmYGQGZmZmZmZg5AZmZmZmZmBkBmZmZmZmYGQM3MzMzMzARAAAAAAAAACEAzMzMzMzMLQM3MzMzMzAhAAAAAAAAACEDNzMzMzMwIQM3MzMzMzAhAzczMzMzMCECamZmZmZkFQJqZmZmZmQlAZmZmZmZmCkAAAAAAAAAIQAAAAAAAAARAAAAAAAAACEAzMzMzMzMLQAAAAAAAAAhA", 853 | "encoding": "base64", 854 | "path": [ 855 | "_data", 856 | 0, 857 | "y" 858 | ] 859 | }, 860 | { 861 | "data": "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA", 862 | "encoding": "base64", 863 | "path": [ 864 | "_data", 865 | 0, 866 | "marker", 867 | "color" 868 | ] 869 | } 870 | ], 871 | "model_module": "plotlywidget", 872 | "model_module_version": "^0.2.1", 873 | "model_name": "FigureModel", 874 | "state": { 875 | "_data": [ 876 | { 877 | "marker": { 878 | "cauto": false, 879 | "cmax": 1.5, 880 | "cmin": -0.5, 881 | "colorbar": { 882 | "ticks": "outside", 883 | "ticktext": [ 884 | "unselected", 885 | "selected" 886 | ], 887 | "tickvals": [ 888 | 0, 889 | 1 890 | ] 891 | }, 892 | "colorscale": [ 893 | [ 894 | 0, 895 | "lightgray" 896 | ], 897 | [ 898 | 0.5, 899 | "lightgray" 900 | ], 901 | [ 902 | 0.5, 903 | "red" 904 | ], 905 | [ 906 | 1, 907 | "red" 908 | ] 909 | ], 910 | "showscale": true, 911 | "size": 8 912 | }, 913 | "mode": "markers", 914 | "type": "scatter", 915 | "uid": "940ad93e-a0d7-11e8-bc5b-645aede86e5b" 916 | } 917 | ], 918 | "_js2py_pointsCallback": {}, 919 | "_js2py_relayout": {}, 920 | "_js2py_restyle": {}, 921 | "_js2py_update": {}, 922 | "_last_layout_edit_id": 5, 923 | "_last_trace_edit_id": 1, 924 | "_layout": { 925 | "dragmode": "lasso", 926 | "width": 500, 927 | "xaxis": { 928 | "title": "petal_length" 929 | }, 930 | "yaxis": { 931 | "title": "sepal_width" 932 | } 933 | }, 934 | "_py2js_addTraces": {}, 935 | "_py2js_animate": {}, 936 | "_py2js_deleteTraces": {}, 937 | "_py2js_moveTraces": {}, 938 | "_py2js_removeLayoutProps": {}, 939 | "_py2js_removeTraceProps": {}, 940 | "_py2js_update": {}, 941 | "_view_count": 2 942 | } 943 | }, 944 | "de6bef56bf3b41e68055c1af346e98a9": { 945 | "model_module": "@jupyter-widgets/controls", 946 | "model_module_version": "1.3.0", 947 | "model_name": "ButtonModel", 948 | "state": { 949 | "description": "clear", 950 | "layout": "IPY_MODEL_2ede5939ee91466193cd64f8cd4b6421", 951 | "style": "IPY_MODEL_b058b15b17934604ab9af843abdd94fb" 952 | } 953 | } 954 | }, 955 | "version_major": 2, 956 | "version_minor": 0 957 | } 958 | } 959 | }, 960 | "nbformat": 4, 961 | "nbformat_minor": 2 962 | } 963 | -------------------------------------------------------------------------------- /notebooks/4. Chart Editor.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "markdown", 5 | "metadata": {}, 6 | "source": [ 7 | "## JupyterLab Chart Editor\n", 8 | "This example relies on the `jupyterlab-chart-editor` extension (https://github.com/plotly/jupyterlab-chart-editor)" 9 | ] 10 | }, 11 | { 12 | "cell_type": "markdown", 13 | "metadata": {}, 14 | "source": [ 15 | "### Imports" 16 | ] 17 | }, 18 | { 19 | "cell_type": "code", 20 | "execution_count": 1, 21 | "metadata": {}, 22 | "outputs": [], 23 | "source": [ 24 | "import plotly.graph_objs as go\n", 25 | "import plotly.io as pio\n", 26 | "\n", 27 | "import pandas as pd" 28 | ] 29 | }, 30 | { 31 | "cell_type": "markdown", 32 | "metadata": {}, 33 | "source": [ 34 | "### Load Dataset\n", 35 | "Load gapminder dataset for 1982" 36 | ] 37 | }, 38 | { 39 | "cell_type": "code", 40 | "execution_count": 2, 41 | "metadata": {}, 42 | "outputs": [ 43 | { 44 | "data": { 45 | "text/html": [ 46 | "
\n", 47 | "\n", 60 | "\n", 61 | " \n", 62 | " \n", 63 | " \n", 64 | " \n", 65 | " \n", 66 | " \n", 67 | " \n", 68 | " \n", 69 | " \n", 70 | " \n", 71 | " \n", 72 | " \n", 73 | " \n", 74 | " \n", 75 | " \n", 76 | " \n", 77 | " \n", 78 | " \n", 79 | " \n", 80 | " \n", 81 | " \n", 82 | " \n", 83 | " \n", 84 | " \n", 85 | " \n", 86 | " \n", 87 | " \n", 88 | " \n", 89 | " \n", 90 | " \n", 91 | " \n", 92 | " \n", 93 | " \n", 94 | " \n", 95 | " \n", 96 | " \n", 97 | " \n", 98 | " \n", 99 | " \n", 100 | " \n", 101 | " \n", 102 | " \n", 103 | " \n", 104 | " \n", 105 | " \n", 106 | " \n", 107 | " \n", 108 | " \n", 109 | " \n", 110 | " \n", 111 | " \n", 112 | " \n", 113 | " \n", 114 | " \n", 115 | " \n", 116 | " \n", 117 | " \n", 118 | " \n", 119 | "
countryyearpopcontinentlifeExpgdpPercap
30Algeria198220033753.0Africa61.3685745.160213
42Angola19827016384.0Africa39.9422756.953672
126Benin19823641603.0Africa50.9041277.897616
162Botswana1982970347.0Africa61.4844551.142150
198Burkina Faso19826634596.0Africa48.122807.198586
\n", 120 | "
" 121 | ], 122 | "text/plain": [ 123 | " country year pop continent lifeExp gdpPercap\n", 124 | "30 Algeria 1982 20033753.0 Africa 61.368 5745.160213\n", 125 | "42 Angola 1982 7016384.0 Africa 39.942 2756.953672\n", 126 | "126 Benin 1982 3641603.0 Africa 50.904 1277.897616\n", 127 | "162 Botswana 1982 970347.0 Africa 61.484 4551.142150\n", 128 | "198 Burkina Faso 1982 6634596.0 Africa 48.122 807.198586" 129 | ] 130 | }, 131 | "execution_count": 2, 132 | "metadata": {}, 133 | "output_type": "execute_result" 134 | } 135 | ], 136 | "source": [ 137 | "data = pd.read_csv(\"https://raw.githubusercontent.com/plotly/datasets/master/gapminderDataFiveYear.csv\")\n", 138 | "df_1982 = data[data['year']==1982]\n", 139 | "df_1982 = df_1982.sort_values(['continent', 'country'])\n", 140 | "df_1982.head()" 141 | ] 142 | }, 143 | { 144 | "cell_type": "markdown", 145 | "metadata": {}, 146 | "source": [ 147 | "### Create empty FigureWidget" 148 | ] 149 | }, 150 | { 151 | "cell_type": "code", 152 | "execution_count": 3, 153 | "metadata": {}, 154 | "outputs": [ 155 | { 156 | "data": { 157 | "application/vnd.jupyter.widget-view+json": { 158 | "model_id": "146a0eaac2d04a22af1dac5992779143", 159 | "version_major": 2, 160 | "version_minor": 0 161 | }, 162 | "text/plain": [ 163 | "FigureWidget({\n", 164 | " 'data': [], 'layout': {}\n", 165 | "})" 166 | ] 167 | }, 168 | "metadata": {}, 169 | "output_type": "display_data" 170 | } 171 | ], 172 | "source": [ 173 | "fig = go.FigureWidget()\n", 174 | "fig" 175 | ] 176 | }, 177 | { 178 | "cell_type": "markdown", 179 | "metadata": {}, 180 | "source": [ 181 | "### Add scatter trace per continent" 182 | ] 183 | }, 184 | { 185 | "cell_type": "code", 186 | "execution_count": 4, 187 | "metadata": {}, 188 | "outputs": [], 189 | "source": [ 190 | "for continent, continent_df in df_1982.groupby('continent'):\n", 191 | " fig.add_scatter(x=continent_df.lifeExp,\n", 192 | " y=continent_df.gdpPercap,\n", 193 | " marker={'size': continent_df['pop'].tolist(), 'sizemode': 'area', 'sizeref': 200000},\n", 194 | " mode='markers',\n", 195 | " text=continent_df.country,\n", 196 | " name=continent)" 197 | ] 198 | }, 199 | { 200 | "cell_type": "markdown", 201 | "metadata": {}, 202 | "source": [ 203 | "### Write figure to json file" 204 | ] 205 | }, 206 | { 207 | "cell_type": "code", 208 | "execution_count": 5, 209 | "metadata": {}, 210 | "outputs": [], 211 | "source": [ 212 | "pio.write_json(fig, 'out/gapminder.plotly.json')" 213 | ] 214 | }, 215 | { 216 | "cell_type": "markdown", 217 | "metadata": {}, 218 | "source": [ 219 | "### Edit with JupyterLab chart editor\n", 220 | "Right click on `out/gapminder.plotly.json` and \"Open With -> PlotlyEditor\"\n", 221 | "\n", 222 | "Make edits to the chart to add a title, axis titles, trace coloring, annotations, etc. Then use \"File -> Save As\" to save modified figure as `out/gapminder-styled.plotly.json" 223 | ] 224 | }, 225 | { 226 | "cell_type": "markdown", 227 | "metadata": {}, 228 | "source": [ 229 | "### Read modified figure back into Python" 230 | ] 231 | }, 232 | { 233 | "cell_type": "code", 234 | "execution_count": 6, 235 | "metadata": {}, 236 | "outputs": [ 237 | { 238 | "data": { 239 | "application/vnd.jupyter.widget-view+json": { 240 | "model_id": "ad9a132a34f1469f96015448db81b90c", 241 | "version_major": 2, 242 | "version_minor": 0 243 | }, 244 | "text/plain": [ 245 | "FigureWidget({\n", 246 | " 'data': [{'hoverinfo': 'text',\n", 247 | " 'marker': {'line': {'width': 2},\n", 248 | " …" 249 | ] 250 | }, 251 | "metadata": {}, 252 | "output_type": "display_data" 253 | } 254 | ], 255 | "source": [ 256 | "fig_styled = pio.read_json('out/gapminder-styled.plotly.json',\n", 257 | " output_type='FigureWidget')\n", 258 | "fig_styled" 259 | ] 260 | }, 261 | { 262 | "cell_type": "markdown", 263 | "metadata": {}, 264 | "source": [ 265 | "### Save edited figure to HTML" 266 | ] 267 | }, 268 | { 269 | "cell_type": "code", 270 | "execution_count": 19, 271 | "metadata": {}, 272 | "outputs": [], 273 | "source": [ 274 | "# Save as standalone html\n", 275 | "from plotly.offline import plot\n", 276 | "plot(fig_styled, filename='out/gapminder-styled.html', auto_open=False);" 277 | ] 278 | }, 279 | { 280 | "cell_type": "markdown", 281 | "metadata": {}, 282 | "source": [ 283 | "### Save edited figure as pdf" 284 | ] 285 | }, 286 | { 287 | "cell_type": "code", 288 | "execution_count": 20, 289 | "metadata": {}, 290 | "outputs": [], 291 | "source": [ 292 | "# Save as image\n", 293 | "pio.write_image(fig_styled, 'out/gapminder-styled.pdf')" 294 | ] 295 | } 296 | ], 297 | "metadata": { 298 | "kernelspec": { 299 | "display_name": "Python 3", 300 | "language": "python", 301 | "name": "python3" 302 | }, 303 | "language_info": { 304 | "codemirror_mode": { 305 | "name": "ipython", 306 | "version": 3 307 | }, 308 | "file_extension": ".py", 309 | "mimetype": "text/x-python", 310 | "name": "python", 311 | "nbconvert_exporter": "python", 312 | "pygments_lexer": "ipython3", 313 | "version": "3.6.7" 314 | } 315 | }, 316 | "nbformat": 4, 317 | "nbformat_minor": 2 318 | } 319 | -------------------------------------------------------------------------------- /notebooks/5. Dash App Viewer.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "markdown", 5 | "metadata": {}, 6 | "source": [ 7 | "## Example of jupyterlab-dash extension" 8 | ] 9 | }, 10 | { 11 | "cell_type": "markdown", 12 | "metadata": {}, 13 | "source": [ 14 | "### Imports" 15 | ] 16 | }, 17 | { 18 | "cell_type": "code", 19 | "execution_count": 2, 20 | "metadata": {}, 21 | "outputs": [], 22 | "source": [ 23 | "import dash\n", 24 | "import dash_core_components as dcc\n", 25 | "import dash_html_components as html\n", 26 | "import pandas as pd\n", 27 | "import plotly.graph_objs as go" 28 | ] 29 | }, 30 | { 31 | "cell_type": "markdown", 32 | "metadata": {}, 33 | "source": [ 34 | "### Load and preprocess data" 35 | ] 36 | }, 37 | { 38 | "cell_type": "code", 39 | "execution_count": 3, 40 | "metadata": {}, 41 | "outputs": [], 42 | "source": [ 43 | "df = pd.read_csv(\n", 44 | " 'https://gist.githubusercontent.com/chriddyp/'\n", 45 | " 'cb5392c35661370d95f300086accea51/raw/'\n", 46 | " '8e0768211f6b747c0db42a9ce9a0937dafcbd8b2/'\n", 47 | " 'indicators.csv')\n", 48 | "\n", 49 | "available_indicators = df['Indicator Name'].unique()" 50 | ] 51 | }, 52 | { 53 | "cell_type": "markdown", 54 | "metadata": {}, 55 | "source": [ 56 | "### Build AppViewer \n", 57 | "This is a small Python class that starts the dash development server and notifies the JupyterLab extension of updates.\n", 58 | "\n", 59 | "Both the Python module and JavaScript extension are located in the `jupyterlab_dash` repository (https://github.com/plotly/jupyterlab_dash)\n", 60 | "\n", 61 | "**Note:** make sure to choose an open port, the port not available error message isn't presented yet, the view call will just hang for 10 seconds and then fail." 62 | ] 63 | }, 64 | { 65 | "cell_type": "code", 66 | "execution_count": 4, 67 | "metadata": {}, 68 | "outputs": [], 69 | "source": [ 70 | "from jupyterlab_dash import AppViewer\n", 71 | "viewer = AppViewer()" 72 | ] 73 | }, 74 | { 75 | "cell_type": "markdown", 76 | "metadata": {}, 77 | "source": [ 78 | "### Build App\n", 79 | "Use a single cell to define the app and to call `viewer.show(app)`. This way, the app design can be changed and the cell rerun repeatedly to customize the app, without needing to reload the dataset." 80 | ] 81 | }, 82 | { 83 | "cell_type": "code", 84 | "execution_count": 5, 85 | "metadata": {}, 86 | "outputs": [], 87 | "source": [ 88 | "external_stylesheets = ['https://codepen.io/chriddyp/pen/bWLwgP.css']\n", 89 | "app = dash.Dash(__name__, external_stylesheets=external_stylesheets)\n", 90 | "\n", 91 | "app.layout = html.Div([\n", 92 | " html.Div([\n", 93 | "\n", 94 | " html.Div([\n", 95 | " dcc.Dropdown(\n", 96 | " id='xaxis-column',\n", 97 | " options=[{'label': i, 'value': i} for i in available_indicators],\n", 98 | " value='Fertility rate, total (births per woman)'\n", 99 | " ),\n", 100 | " dcc.RadioItems(\n", 101 | " id='xaxis-type',\n", 102 | " options=[{'label': i, 'value': i} for i in ['Linear', 'Log']],\n", 103 | " value='Linear',\n", 104 | " labelStyle={'display': 'inline-block'}\n", 105 | " )\n", 106 | " ],\n", 107 | " style={'width': '48%', 'display': 'inline-block'}),\n", 108 | "\n", 109 | " html.Div([\n", 110 | " dcc.Dropdown(\n", 111 | " id='yaxis-column',\n", 112 | " options=[{'label': i, 'value': i} for i in available_indicators],\n", 113 | " value='Life expectancy at birth, total (years)'\n", 114 | " ),\n", 115 | " dcc.RadioItems(\n", 116 | " id='yaxis-type',\n", 117 | " options=[{'label': i, 'value': i} for i in ['Linear', 'Log']],\n", 118 | " value='Linear',\n", 119 | " labelStyle={'display': 'inline-block'}\n", 120 | " )\n", 121 | " ],style={'width': '48%', 'float': 'right', 'display': 'inline-block'})\n", 122 | " ]),\n", 123 | "\n", 124 | " dcc.Graph(id='indicator-graphic'),\n", 125 | "\n", 126 | " dcc.Slider(\n", 127 | " id='year--slider',\n", 128 | " min=df['Year'].min(),\n", 129 | " max=df['Year'].max(),\n", 130 | " value=df['Year'].max(),\n", 131 | " marks={str(year): str(year) for year in df['Year'].unique()}\n", 132 | " )\n", 133 | "])\n", 134 | "\n", 135 | "# Callbacks\n", 136 | "@app.callback(\n", 137 | " dash.dependencies.Output('indicator-graphic', 'figure'),\n", 138 | " [dash.dependencies.Input('xaxis-column', 'value'),\n", 139 | " dash.dependencies.Input('yaxis-column', 'value'),\n", 140 | " dash.dependencies.Input('xaxis-type', 'value'),\n", 141 | " dash.dependencies.Input('yaxis-type', 'value'),\n", 142 | " dash.dependencies.Input('year--slider', 'value')])\n", 143 | "def update_graph(xaxis_column_name, yaxis_column_name,\n", 144 | " xaxis_type, yaxis_type,\n", 145 | " year_value):\n", 146 | " dff = df[df['Year'] == year_value]\n", 147 | " return {\n", 148 | " 'data': [go.Scatter(\n", 149 | " x=dff[dff['Indicator Name'] == xaxis_column_name]['Value'],\n", 150 | " y=dff[dff['Indicator Name'] == yaxis_column_name]['Value'],\n", 151 | " text=dff[dff['Indicator Name'] == yaxis_column_name]['Country Name'],\n", 152 | " mode='markers',\n", 153 | " marker={\n", 154 | " 'size': 15,\n", 155 | " 'opacity': 0.5,\n", 156 | " 'color': 'blue',\n", 157 | " 'line': {'width': 2, 'color': 'white'}\n", 158 | " }\n", 159 | " )],\n", 160 | " 'layout': go.Layout(\n", 161 | " title='World Data',\n", 162 | " xaxis={\n", 163 | " 'title': xaxis_column_name,\n", 164 | " 'type': 'linear' if xaxis_type == 'Linear' else 'log'\n", 165 | " },\n", 166 | " yaxis={\n", 167 | " 'title': yaxis_column_name,\n", 168 | " 'type': 'linear' if yaxis_type == 'Linear' else 'log'\n", 169 | " },\n", 170 | " margin={'l': 40, 'b': 40, 't': 100, 'r': 0},\n", 171 | " hovermode='closest',\n", 172 | " )\n", 173 | " }\n", 174 | "\n", 175 | "viewer.show(app)" 176 | ] 177 | }, 178 | { 179 | "cell_type": "code", 180 | "execution_count": 11, 181 | "metadata": {}, 182 | "outputs": [], 183 | "source": [ 184 | "viewer.show(app)" 185 | ] 186 | }, 187 | { 188 | "cell_type": "markdown", 189 | "metadata": {}, 190 | "source": [ 191 | "### Limitations\n", 192 | "This `multiprocessing` approach does not work on Windows" 193 | ] 194 | }, 195 | { 196 | "cell_type": "code", 197 | "execution_count": null, 198 | "metadata": {}, 199 | "outputs": [], 200 | "source": [] 201 | } 202 | ], 203 | "metadata": { 204 | "kernelspec": { 205 | "display_name": "Python 3", 206 | "language": "python", 207 | "name": "python3" 208 | }, 209 | "language_info": { 210 | "codemirror_mode": { 211 | "name": "ipython", 212 | "version": 3 213 | }, 214 | "file_extension": ".py", 215 | "mimetype": "text/x-python", 216 | "name": "python", 217 | "nbconvert_exporter": "python", 218 | "pygments_lexer": "ipython3", 219 | "version": "3.6.8" 220 | } 221 | }, 222 | "nbformat": 4, 223 | "nbformat_minor": 2 224 | } 225 | -------------------------------------------------------------------------------- /notebooks/6. Plotly Help Resources.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "markdown", 5 | "metadata": {}, 6 | "source": [ 7 | "### Help resources\n", 8 | "The `jupyterlab_plotlyhelp` extension (https://github.com/plotly/jupyterlab_plotlyhelp) adds several plotly resources to the JupyterLab Help menu.\n", 9 | "\n", 10 | "With this, the plotly.py docs, plotly property reference, and Dash user guide can all be opened in JupyterLab tabs" 11 | ] 12 | }, 13 | { 14 | "cell_type": "code", 15 | "execution_count": null, 16 | "metadata": {}, 17 | "outputs": [], 18 | "source": [] 19 | } 20 | ], 21 | "metadata": { 22 | "kernelspec": { 23 | "display_name": "Python 3", 24 | "language": "python", 25 | "name": "python3" 26 | }, 27 | "language_info": { 28 | "codemirror_mode": { 29 | "name": "ipython", 30 | "version": 3 31 | }, 32 | "file_extension": ".py", 33 | "mimetype": "text/x-python", 34 | "name": "python", 35 | "nbconvert_exporter": "python", 36 | "pygments_lexer": "ipython3", 37 | "version": "3.6.7" 38 | } 39 | }, 40 | "nbformat": 4, 41 | "nbformat_minor": 2 42 | } 43 | -------------------------------------------------------------------------------- /notebooks/9. plotly_express Dash App.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "markdown", 5 | "metadata": {}, 6 | "source": [ 7 | "## Imports" 8 | ] 9 | }, 10 | { 11 | "cell_type": "code", 12 | "execution_count": 1, 13 | "metadata": {}, 14 | "outputs": [], 15 | "source": [ 16 | "import plotly_express as px\n", 17 | "import dash\n", 18 | "import dash_html_components as html\n", 19 | "import dash_core_components as dcc\n", 20 | "from dash.dependencies import Input, Output" 21 | ] 22 | }, 23 | { 24 | "cell_type": "markdown", 25 | "metadata": {}, 26 | "source": [ 27 | "## Prepare Data" 28 | ] 29 | }, 30 | { 31 | "cell_type": "code", 32 | "execution_count": 2, 33 | "metadata": {}, 34 | "outputs": [], 35 | "source": [ 36 | "tips = px.data.tips()\n", 37 | "col_options = [dict(label=x, value=x) for x in tips.columns]\n", 38 | "dimensions = [\"x\", \"y\", \"color\", \"facet_col\", \"facet_row\"]" 39 | ] 40 | }, 41 | { 42 | "cell_type": "markdown", 43 | "metadata": {}, 44 | "source": [ 45 | "## Build jupyterlab-dash app viewer" 46 | ] 47 | }, 48 | { 49 | "cell_type": "code", 50 | "execution_count": 3, 51 | "metadata": {}, 52 | "outputs": [], 53 | "source": [ 54 | "from jupyterlab_dash import AppViewer\n", 55 | "viewer = AppViewer()" 56 | ] 57 | }, 58 | { 59 | "cell_type": "markdown", 60 | "metadata": {}, 61 | "source": [ 62 | "## Build and display Dash app" 63 | ] 64 | }, 65 | { 66 | "cell_type": "code", 67 | "execution_count": 4, 68 | "metadata": {}, 69 | "outputs": [], 70 | "source": [ 71 | "app = dash.Dash(\n", 72 | " __name__, external_stylesheets=[\"https://codepen.io/chriddyp/pen/bWLwgP.css\"]\n", 73 | ")\n", 74 | "\n", 75 | "app.layout = html.Div(\n", 76 | " [\n", 77 | " html.H1(\"Demo: Plotly Express in Dash with Tips Dataset\"),\n", 78 | " html.Div(\n", 79 | " [\n", 80 | " html.P([d + \":\", dcc.Dropdown(id=d, options=col_options)])\n", 81 | " for d in dimensions\n", 82 | " ],\n", 83 | " style={\"width\": \"25%\", \"float\": \"left\"},\n", 84 | " ),\n", 85 | " dcc.Graph(id=\"graph\", style={\"width\": \"75%\", \"display\": \"inline-block\"}),\n", 86 | " ]\n", 87 | ")\n", 88 | "\n", 89 | "\n", 90 | "@app.callback(Output(\"graph\", \"figure\"), [Input(d, \"value\") for d in dimensions])\n", 91 | "def make_figure(x, y, color, facet_col, facet_row):\n", 92 | " return px.scatter(\n", 93 | " tips,\n", 94 | " x=x,\n", 95 | " y=y,\n", 96 | " color=color,\n", 97 | " facet_col=facet_col,\n", 98 | " facet_row=facet_row,\n", 99 | " height=500)\n", 100 | "\n", 101 | "viewer.show(app)" 102 | ] 103 | }, 104 | { 105 | "cell_type": "code", 106 | "execution_count": null, 107 | "metadata": {}, 108 | "outputs": [], 109 | "source": [] 110 | } 111 | ], 112 | "metadata": { 113 | "kernelspec": { 114 | "display_name": "Python 3", 115 | "language": "python", 116 | "name": "python3" 117 | }, 118 | "language_info": { 119 | "codemirror_mode": { 120 | "name": "ipython", 121 | "version": 3 122 | }, 123 | "file_extension": ".py", 124 | "mimetype": "text/x-python", 125 | "name": "python", 126 | "nbconvert_exporter": "python", 127 | "pygments_lexer": "ipython3", 128 | "version": "3.6.8" 129 | } 130 | }, 131 | "nbformat": 4, 132 | "nbformat_minor": 2 133 | } 134 | -------------------------------------------------------------------------------- /notebooks/datasets/iris.csv: -------------------------------------------------------------------------------- 1 | sepal_length,sepal_width,petal_length,petal_width,species 2 | 5.1,3.5,1.4,0.2,setosa 3 | 4.9,3.0,1.4,0.2,setosa 4 | 4.7,3.2,1.3,0.2,setosa 5 | 4.6,3.1,1.5,0.2,setosa 6 | 5.0,3.6,1.4,0.2,setosa 7 | 5.4,3.9,1.7,0.4,setosa 8 | 4.6,3.4,1.4,0.3,setosa 9 | 5.0,3.4,1.5,0.2,setosa 10 | 4.4,2.9,1.4,0.2,setosa 11 | 4.9,3.1,1.5,0.1,setosa 12 | 5.4,3.7,1.5,0.2,setosa 13 | 4.8,3.4,1.6,0.2,setosa 14 | 4.8,3.0,1.4,0.1,setosa 15 | 4.3,3.0,1.1,0.1,setosa 16 | 5.8,4.0,1.2,0.2,setosa 17 | 5.7,4.4,1.5,0.4,setosa 18 | 5.4,3.9,1.3,0.4,setosa 19 | 5.1,3.5,1.4,0.3,setosa 20 | 5.7,3.8,1.7,0.3,setosa 21 | 5.1,3.8,1.5,0.3,setosa 22 | 5.4,3.4,1.7,0.2,setosa 23 | 5.1,3.7,1.5,0.4,setosa 24 | 4.6,3.6,1.0,0.2,setosa 25 | 5.1,3.3,1.7,0.5,setosa 26 | 4.8,3.4,1.9,0.2,setosa 27 | 5.0,3.0,1.6,0.2,setosa 28 | 5.0,3.4,1.6,0.4,setosa 29 | 5.2,3.5,1.5,0.2,setosa 30 | 5.2,3.4,1.4,0.2,setosa 31 | 4.7,3.2,1.6,0.2,setosa 32 | 4.8,3.1,1.6,0.2,setosa 33 | 5.4,3.4,1.5,0.4,setosa 34 | 5.2,4.1,1.5,0.1,setosa 35 | 5.5,4.2,1.4,0.2,setosa 36 | 4.9,3.1,1.5,0.1,setosa 37 | 5.0,3.2,1.2,0.2,setosa 38 | 5.5,3.5,1.3,0.2,setosa 39 | 4.9,3.1,1.5,0.1,setosa 40 | 4.4,3.0,1.3,0.2,setosa 41 | 5.1,3.4,1.5,0.2,setosa 42 | 5.0,3.5,1.3,0.3,setosa 43 | 4.5,2.3,1.3,0.3,setosa 44 | 4.4,3.2,1.3,0.2,setosa 45 | 5.0,3.5,1.6,0.6,setosa 46 | 5.1,3.8,1.9,0.4,setosa 47 | 4.8,3.0,1.4,0.3,setosa 48 | 5.1,3.8,1.6,0.2,setosa 49 | 4.6,3.2,1.4,0.2,setosa 50 | 5.3,3.7,1.5,0.2,setosa 51 | 5.0,3.3,1.4,0.2,setosa 52 | 7.0,3.2,4.7,1.4,versicolor 53 | 6.4,3.2,4.5,1.5,versicolor 54 | 6.9,3.1,4.9,1.5,versicolor 55 | 5.5,2.3,4.0,1.3,versicolor 56 | 6.5,2.8,4.6,1.5,versicolor 57 | 5.7,2.8,4.5,1.3,versicolor 58 | 6.3,3.3,4.7,1.6,versicolor 59 | 4.9,2.4,3.3,1.0,versicolor 60 | 6.6,2.9,4.6,1.3,versicolor 61 | 5.2,2.7,3.9,1.4,versicolor 62 | 5.0,2.0,3.5,1.0,versicolor 63 | 5.9,3.0,4.2,1.5,versicolor 64 | 6.0,2.2,4.0,1.0,versicolor 65 | 6.1,2.9,4.7,1.4,versicolor 66 | 5.6,2.9,3.6,1.3,versicolor 67 | 6.7,3.1,4.4,1.4,versicolor 68 | 5.6,3.0,4.5,1.5,versicolor 69 | 5.8,2.7,4.1,1.0,versicolor 70 | 6.2,2.2,4.5,1.5,versicolor 71 | 5.6,2.5,3.9,1.1,versicolor 72 | 5.9,3.2,4.8,1.8,versicolor 73 | 6.1,2.8,4.0,1.3,versicolor 74 | 6.3,2.5,4.9,1.5,versicolor 75 | 6.1,2.8,4.7,1.2,versicolor 76 | 6.4,2.9,4.3,1.3,versicolor 77 | 6.6,3.0,4.4,1.4,versicolor 78 | 6.8,2.8,4.8,1.4,versicolor 79 | 6.7,3.0,5.0,1.7,versicolor 80 | 6.0,2.9,4.5,1.5,versicolor 81 | 5.7,2.6,3.5,1.0,versicolor 82 | 5.5,2.4,3.8,1.1,versicolor 83 | 5.5,2.4,3.7,1.0,versicolor 84 | 5.8,2.7,3.9,1.2,versicolor 85 | 6.0,2.7,5.1,1.6,versicolor 86 | 5.4,3.0,4.5,1.5,versicolor 87 | 6.0,3.4,4.5,1.6,versicolor 88 | 6.7,3.1,4.7,1.5,versicolor 89 | 6.3,2.3,4.4,1.3,versicolor 90 | 5.6,3.0,4.1,1.3,versicolor 91 | 5.5,2.5,4.0,1.3,versicolor 92 | 5.5,2.6,4.4,1.2,versicolor 93 | 6.1,3.0,4.6,1.4,versicolor 94 | 5.8,2.6,4.0,1.2,versicolor 95 | 5.0,2.3,3.3,1.0,versicolor 96 | 5.6,2.7,4.2,1.3,versicolor 97 | 5.7,3.0,4.2,1.2,versicolor 98 | 5.7,2.9,4.2,1.3,versicolor 99 | 6.2,2.9,4.3,1.3,versicolor 100 | 5.1,2.5,3.0,1.1,versicolor 101 | 5.7,2.8,4.1,1.3,versicolor 102 | 6.3,3.3,6.0,2.5,virginica 103 | 5.8,2.7,5.1,1.9,virginica 104 | 7.1,3.0,5.9,2.1,virginica 105 | 6.3,2.9,5.6,1.8,virginica 106 | 6.5,3.0,5.8,2.2,virginica 107 | 7.6,3.0,6.6,2.1,virginica 108 | 4.9,2.5,4.5,1.7,virginica 109 | 7.3,2.9,6.3,1.8,virginica 110 | 6.7,2.5,5.8,1.8,virginica 111 | 7.2,3.6,6.1,2.5,virginica 112 | 6.5,3.2,5.1,2.0,virginica 113 | 6.4,2.7,5.3,1.9,virginica 114 | 6.8,3.0,5.5,2.1,virginica 115 | 5.7,2.5,5.0,2.0,virginica 116 | 5.8,2.8,5.1,2.4,virginica 117 | 6.4,3.2,5.3,2.3,virginica 118 | 6.5,3.0,5.5,1.8,virginica 119 | 7.7,3.8,6.7,2.2,virginica 120 | 7.7,2.6,6.9,2.3,virginica 121 | 6.0,2.2,5.0,1.5,virginica 122 | 6.9,3.2,5.7,2.3,virginica 123 | 5.6,2.8,4.9,2.0,virginica 124 | 7.7,2.8,6.7,2.0,virginica 125 | 6.3,2.7,4.9,1.8,virginica 126 | 6.7,3.3,5.7,2.1,virginica 127 | 7.2,3.2,6.0,1.8,virginica 128 | 6.2,2.8,4.8,1.8,virginica 129 | 6.1,3.0,4.9,1.8,virginica 130 | 6.4,2.8,5.6,2.1,virginica 131 | 7.2,3.0,5.8,1.6,virginica 132 | 7.4,2.8,6.1,1.9,virginica 133 | 7.9,3.8,6.4,2.0,virginica 134 | 6.4,2.8,5.6,2.2,virginica 135 | 6.3,2.8,5.1,1.5,virginica 136 | 6.1,2.6,5.6,1.4,virginica 137 | 7.7,3.0,6.1,2.3,virginica 138 | 6.3,3.4,5.6,2.4,virginica 139 | 6.4,3.1,5.5,1.8,virginica 140 | 6.0,3.0,4.8,1.8,virginica 141 | 6.9,3.1,5.4,2.1,virginica 142 | 6.7,3.1,5.6,2.4,virginica 143 | 6.9,3.1,5.1,2.3,virginica 144 | 5.8,2.7,5.1,1.9,virginica 145 | 6.8,3.2,5.9,2.3,virginica 146 | 6.7,3.3,5.7,2.5,virginica 147 | 6.7,3.0,5.2,2.3,virginica 148 | 6.3,2.5,5.0,1.9,virginica 149 | 6.5,3.0,5.2,2.0,virginica 150 | 6.2,3.4,5.4,2.3,virginica 151 | 5.9,3.0,5.1,1.8,virginica 152 | -------------------------------------------------------------------------------- /notebooks/images/JupyterLabLight.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/plotly/plotlylab/c9adbce663fb83eff05766634f4499fe62705f67/notebooks/images/JupyterLabLight.png -------------------------------------------------------------------------------- /notebooks/images/PlotlyLabLight.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/plotly/plotlylab/c9adbce663fb83eff05766634f4499fe62705f67/notebooks/images/PlotlyLabLight.png -------------------------------------------------------------------------------- /notebooks/out/gapminder-styled.plotly.json: -------------------------------------------------------------------------------- 1 | {"data":[{"marker":{"size":[20033753,7016384,3641603,970347,6634596,4580410,9250831,2476971,4875118,348643,30646495,1774735,9025951,305991,45681811,285483,2637297,38111756,753874,715523,11400338,4710497,825987,17661452,1411807,1956875,3344074,9171477,6502825,6998256,1622136,992040,20198730,12587223,1099010,6437188,73039376,517810,5507565,98593,6147783,3464522,5828892,31140029,20367053,649901,19844382,2644765,6734098,12939400,6100407,7636524],"sizemode":"area","sizeref":200000,"opacity":0.75,"line":{"width":2}},"mode":"markers","name":"Africa","text":["Algeria","Angola","Benin","Botswana","Burkina Faso","Burundi","Cameroon","Central African Republic","Chad","Comoros","Congo, Dem. Rep.","Congo, Rep.","Cote d'Ivoire","Djibouti","Egypt","Equatorial Guinea","Eritrea","Ethiopia","Gabon","Gambia","Ghana","Guinea","Guinea-Bissau","Kenya","Lesotho","Liberia","Libya","Madagascar","Malawi","Mali","Mauritania","Mauritius","Morocco","Mozambique","Namibia","Niger","Nigeria","Reunion","Rwanda","Sao Tome and Principe","Senegal","Sierra Leone","Somalia","South Africa","Sudan","Swaziland","Tanzania","Togo","Tunisia","Uganda","Zambia","Zimbabwe"],"type":"scatter","x":[61.368,39.942,50.903999999999996,61.483999999999995,48.122,47.471000000000004,52.961000000000006,48.295,49.516999999999996,52.933,47.784,56.695,53.983000000000004,48.812,56.006,43.662,43.89,44.916000000000004,56.56399999999999,45.58,53.744,42.891000000000005,39.327,58.766000000000005,55.078,44.852,62.155,48.968999999999994,45.641999999999996,43.916000000000004,53.599,66.711,59.65,42.795,58.968,42.598,45.826,69.885,46.218,60.351000000000006,52.379,38.445,42.955,58.161,50.338,55.56100000000001,50.608000000000004,55.471000000000004,64.048,49.849,51.821000000000005,60.363],"y":[5745.160213,2756.953672,1277.897616,4551.14215,807.1985855,559.6032309999999,2367.983282,956.7529906999999,797.9081006,1267.100083,673.7478181,4879.507522,2602.710169,2879.4680670000002,3503.729636,927.8253427000001,524.8758493,577.8607471,15113.36194,835.8096107999999,876.032569,857.2503577,838.1239671,1348.225791,797.2631074,572.1995694,17364.275380000003,1302.878658,632.8039209,618.0140640999999,1481.150189,3688.037739,2702.620356,462.2114149,4191.100511,909.7221354000001,1576.97375,5267.219353,881.5706467,1890.2181170000001,1518.479984,1465.010784,1176.807031,8568.266228,1895.544073,3895.384018,874.2426069,1344.577953,3560.2331740000004,682.2662267999999,1408.678565,788.8550411],"hoverinfo":"text"},{"marker":{"size":[29341374,5642224,128962939,25201900,11487112,27764644,2424367,9789224,5968349,8365850,4474873,6395630,5198399,3669448,2298309,71640904,2979423,2036305,3366439,18125129,3279001,1116479,232187835,2953997,15620766],"sizemode":"area","sizeref":200000,"opacity":0.75,"line":{"width":2}},"mode":"markers","name":"Americas","text":["Argentina","Bolivia","Brazil","Canada","Chile","Colombia","Costa Rica","Cuba","Dominican Republic","Ecuador","El Salvador","Guatemala","Haiti","Honduras","Jamaica","Mexico","Nicaragua","Panama","Paraguay","Peru","Puerto Rico","Trinidad and Tobago","United States","Uruguay","Venezuela"],"type":"scatter","x":[69.942,53.858999999999995,63.336000000000006,75.76,70.565,66.653,73.45,73.717,63.727,64.342,56.604,58.137,51.461000000000006,60.909,71.21,67.405,59.298,70.472,66.874,61.406000000000006,73.75,68.832,74.65,70.805,68.557],"y":[8997.897412,3156.510452,7030.835878,22898.79214,5095.665738000001,4397.575659,5262.734751,7316.9181069999995,2861.092386,7213.7912670000005,4098.344175,4820.49479,2011.1595489999997,3121.7607940000003,6068.05135,9611.147541,3470.3381560000003,7009.601598,4258.5036039999995,6434.501797,10330.98915,9119.528607,25009.55914,6920.223051000001,11152.410109999999],"hoverinfo":"text"},{"marker":{"size":[12881816,377967,93074406,7272485,1000281000,5264500,708000000,153343000,43072751,14173318,3858421,118454974,2347031,17647518,39326000,1497494,3086876,14441916,1756032,34680442,15796314,1301048,91462088,53456774,11254672,2651869,15410151,9410494,18501390,48827160,56142181,1425876,9657618],"sizemode":"area","sizeref":200000,"opacity":0.75,"line":{"width":2}},"mode":"markers","name":"Asia","text":["Afghanistan","Bahrain","Bangladesh","Cambodia","China","Hong Kong, China","India","Indonesia","Iran","Iraq","Israel","Japan","Jordan","Korea, Dem. Rep.","Korea, Rep.","Kuwait","Lebanon","Malaysia","Mongolia","Myanmar","Nepal","Oman","Pakistan","Philippines","Saudi Arabia","Singapore","Sri Lanka","Syria","Taiwan","Thailand","Vietnam","West Bank and Gaza","Yemen, Rep."],"type":"scatter","x":[39.854,69.05199999999999,50.00899999999999,50.957,65.525,75.45,56.596000000000004,56.159,59.62,62.038000000000004,74.45,77.11,63.739,69.1,67.123,71.309,66.983,68,57.489,58.056000000000004,49.593999999999994,62.728,56.158,62.082,63.012,71.76,68.757,64.59,72.16,64.597,58.816,64.406,49.113],"y":[978.0114388000001,19211.14731,676.9818656,624.4754784,962.4213804999999,14560.530509999999,855.7235377000001,1516.872988,7608.334602,14517.90711,15367.0292,19384.10571,4161.415959,4106.525293,5622.942464,31354.03573,7640.519520999999,4920.355951,2000.603139,424,718.3730947,12954.791009999999,1443.429832,2603.273765,33693.17525,15169.161119999999,1648.0797890000001,3761.8377149999997,7426.354773999999,2393.219781,707.2357863,4336.032082,1977.5570100000002],"hoverinfo":"text"},{"marker":{"size":[2780097,7574613,9856303,4172693,8892098,4413368,10303704,5117810,4826933,54433565,78335266,9786480,10705535,233997,3480000,56535636,562548,14310401,4114787,36227381,9859650,22356726,9032824,5048043,1861252,37983310,8325260,6468126,47328791,56339704],"sizemode":"area","sizeref":200000,"opacity":0.75,"line":{"width":2}},"mode":"markers","name":"Europe","text":["Albania","Austria","Belgium","Bosnia and Herzegovina","Bulgaria","Croatia","Czech Republic","Denmark","Finland","France","Germany","Greece","Hungary","Iceland","Ireland","Italy","Montenegro","Netherlands","Norway","Poland","Portugal","Romania","Serbia","Slovak Republic","Slovenia","Spain","Sweden","Switzerland","Turkey","United Kingdom"],"type":"scatter","x":[70.42,73.18,73.93,70.69,71.08,70.46,70.96,74.63,74.55,74.89,73.8,75.24,69.39,76.99,73.1,74.98,74.101,76.05,75.97,71.32,72.77,69.66,70.16199999999999,70.8,71.063,76.3,76.42,76.21,61.036,74.04],"y":[3630.880722,21597.083619999998,20979.84589,4126.613157,8224.191647,13221.82184,15377.22855,21688.04048,18533.15761,20293.89746,22031.532740000002,15268.420890000001,12545.99066,23269.6075,12618.321409999999,16537.4835,11222.58762,21399.46046,26298.635309999998,8451.531004,11753.84291,9605.314053,15181.0927,11348.54585,17866.72175,13926.169969999999,20667.38125,28397.715119999997,4241.356344,18232.42452],"hoverinfo":"text"},{"marker":{"size":[15184200,3210650],"sizemode":"area","sizeref":200000,"opacity":0.75,"line":{"width":2}},"mode":"markers","name":"Oceania","text":["Australia","New Zealand"],"type":"scatter","x":[74.74,73.84],"y":[19477.009280000002,17632.4104],"hoverinfo":"text"}],"layout":{"xaxis":{"type":"linear","range":[35.88697406187943,81.10763744044014],"autorange":true,"title":"Life Expectancy","gridcolor":"rgb(255, 255, 255)"},"yaxis":{"type":"log","range":[2.4253362195013377,4.873216198010077],"autorange":true,"title":"GDP per capita","gridcolor":"rgb(255, 255, 255)"},"autosize":false,"title":"Life Expectancy and GDP per capita (1982)","colorway":["#1b9e77","#d95f02","#7570b3","#e7298a","#66a61e","#e6ab02","#a6761d","#666666"],"plot_bgcolor":"rgb(237, 237, 237)","paper_bgcolor":"rgb(237, 237, 237)","hovermode":"closest","annotations":[{"text":"Saudi Arabia","x":62.83741781091268,"y":4.563406388230065,"arrowhead":4},{"text":"China","x":69.93154454715993,"y":2.8020037511943707,"ax":37,"ay":18},{"text":"Continent","ax":-37,"ay":-59,"showarrow":false,"x":1.177534599674822,"y":1.0273118029739776,"xref":"paper","yref":"paper","font":{"size":15}}],"legend":{"x":1.02,"y":0.9553903345724907},"width":700,"height":500},"frames":[]} -------------------------------------------------------------------------------- /plotlylab/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/plotly/plotlylab/c9adbce663fb83eff05766634f4499fe62705f67/plotlylab/__init__.py -------------------------------------------------------------------------------- /plotlylab/labapp.py: -------------------------------------------------------------------------------- 1 | import os 2 | import sys 3 | from jupyterlab import labapp 4 | 5 | 6 | def main(): 7 | os.environ['JUPYTERLAB_DIR'] = os.path.join( 8 | sys.prefix, 'share', 'jupyter', 'plotlylab') 9 | labapp.LabApp.app_dir = os.environ['JUPYTERLAB_DIR'] 10 | labapp.main() 11 | 12 | 13 | if __name__ == '__main__': 14 | sys.exit(main()) 15 | -------------------------------------------------------------------------------- /plotlylab/labextensionapp.py: -------------------------------------------------------------------------------- 1 | import os 2 | import sys 3 | from jupyterlab import labextensions 4 | 5 | 6 | def main(): 7 | os.environ['JUPYTERLAB_DIR'] = os.path.join( 8 | sys.prefix, 'share', 'jupyter', 'plotlylab') 9 | labextensions.BaseExtensionApp.app_dir = os.environ['JUPYTERLAB_DIR'] 10 | labextensions.main() 11 | 12 | 13 | if __name__ == '__main__': 14 | sys.exit(main()) 15 | -------------------------------------------------------------------------------- /plotlylab/labhubapp.py: -------------------------------------------------------------------------------- 1 | import os 2 | import sys 3 | from jupyterlab.labhubapp import SingleUserLabApp 4 | 5 | 6 | class SingleUserPlotlyLabApp(SingleUserLabApp): 7 | def __init__(self, **kwargs): 8 | super().__init__(**kwargs) 9 | os.environ['JUPYTERLAB_DIR'] = os.path.join( 10 | sys.prefix, 'share', 'jupyter', 'plotlylab') 11 | 12 | self.app_dir = os.environ['JUPYTERLAB_DIR'] 13 | 14 | 15 | def main(argv=None): 16 | return SingleUserPlotlyLabApp.launch_instance(argv) 17 | 18 | 19 | if __name__ == "__main__": 20 | main() 21 | -------------------------------------------------------------------------------- /recipe/build.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | # Install plotlylab wrapper 4 | python -m pip install --no-deps --ignore-installed . 5 | 6 | # Avoid "JavaScript heap out of memory" errors during extension installation 7 | export NODE_OPTIONS=--max-old-space-size=4096 8 | 9 | # Set JUPYTERLAB_DIR before installing to isolate our extensions 10 | export JUPYTERLAB_DIR=$PREFIX/share/jupyter/plotlylab 11 | 12 | # Extensions to install 13 | jupyter labextension install jupyterlab-dash@0.1.0-alpha.2 --no-build 14 | jupyter labextension install @jupyter-widgets/jupyterlab-manager@0.38 --no-build 15 | jupyter labextension install plotlywidget@0.8.0 --no-build 16 | jupyter labextension install @jupyterlab/plotly-extension@0.18 --no-build 17 | jupyter labextension install jupyterlab-chart-editor@1.1 --no-build 18 | jupyter labextension install git+ssh://git@github.com/plotly/jupyterlab_plotlyhelp.git --no-build 19 | jupyter labextension install plotlylab-light-theme --no-build 20 | jupyter labextension install @mflevine/jupyterlab_html --no-build 21 | 22 | jupyter lab build --name='PlotlyLab' 23 | jupyter lab clean 24 | 25 | unset NODE_OPTIONS 26 | -------------------------------------------------------------------------------- /recipe/meta.yaml: -------------------------------------------------------------------------------- 1 | {% set data = load_setup_py_data() %} 2 | {% set version = data.get('version') %} 3 | 4 | package: 5 | name: plotlylab 6 | version: {{ version }} 7 | 8 | source: 9 | path: ../ 10 | 11 | build: 12 | number: 0 13 | noarch: python 14 | entry_points: 15 | - plotly-lab = plotlylab.labapp:main 16 | - plotly-labextension = plotlylab.labextensionapp:main 17 | 18 | requirements: 19 | build: 20 | - python >=3 21 | - pip 22 | - nodejs 23 | - jupyterlab =0.35 24 | run: 25 | # ## Python packages corresponding to extensions installed in build.sh ## 26 | # main channel 27 | - ipywidgets >=7.2 28 | - jupyterlab =0.35 29 | - psutil 30 | - pandas 31 | - numpy 32 | 33 | # plotly 34 | - plotly =3.7.1 35 | - jupyterlab-dash =0.1.0a2 36 | - plotly_express 37 | - plotly-orca 38 | - colorlover 39 | 40 | # dash 41 | - dash 42 | - dash-core-components 43 | - dash-html-components 44 | - dash-table 45 | - dash-daq 46 | 47 | # R support (r channel) 48 | - r-irkernel 49 | - r-plotly 50 | - r-processx 51 | - openjdk 52 | 53 | about: 54 | home: https://github.com/plotly/plotlylab 55 | license: MIT 56 | license_file: LICENSE 57 | summary: A proof-of-concept JupyterLab distribution integrating Plotly technologies 58 | -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | from setuptools import setup, Command 2 | 3 | version = '0.1.0a4' 4 | 5 | 6 | class WriteEnvironmentFileCommand(Command): 7 | description = 'Generate class hierarchy from Plotly JSON schema' 8 | user_options = [] 9 | 10 | def initialize_options(self): 11 | pass 12 | 13 | def finalize_options(self): 14 | pass 15 | 16 | def run(self): 17 | with open('environments/plotlylab.yaml', 'w') as f: 18 | f.write("""\ 19 | name: plotlylab_{version} 20 | channels: 21 | - main 22 | - plotly 23 | - r 24 | dependencies: 25 | - plotlylab ={version} 26 | """.format(version=version)) 27 | 28 | 29 | setup_args = dict( 30 | name='plotlylab', 31 | description='The Plotly JupyterLab distribution', 32 | version=version, 33 | packages=['plotlylab'], 34 | entry_points={'console_scripts': [ 35 | 'plotly-lab = plotlylab.labapp:main', 36 | 'plotly-labextension = plotlylab.labextensionapp:main', 37 | 'plotly-labhub = plotlylab.labhubapp:main' 38 | ]}, 39 | author='The Plotly Development Team', 40 | url='https://github.com/plotly/plotlylab', 41 | license='MIT', 42 | platforms="Linux, Mac OS X, Windows", 43 | cmdclass=dict(writeenvironment=WriteEnvironmentFileCommand) 44 | ) 45 | 46 | setup(**setup_args) 47 | -------------------------------------------------------------------------------- /start: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | import sys 3 | import shutil 4 | import subprocess 5 | 6 | cmd = ['plotly-lab'] + sys.argv[2:] 7 | 8 | print(cmd, flush=True) 9 | 10 | subprocess.run(cmd) --------------------------------------------------------------------------------