xarray-plotly 0.0.1__tar.gz → 0.0.3__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (44) hide show
  1. xarray_plotly-0.0.3/.github/dependabot.yml +25 -0
  2. xarray_plotly-0.0.3/.github/workflows/dependabot-auto-merge.yml +24 -0
  3. xarray_plotly-0.0.3/PKG-INFO +89 -0
  4. xarray_plotly-0.0.3/README.md +47 -0
  5. xarray_plotly-0.0.3/docs/api.md +28 -0
  6. xarray_plotly-0.0.3/docs/examples/advanced.ipynb +320 -0
  7. xarray_plotly-0.0.3/docs/examples/plot-types.ipynb +382 -0
  8. {xarray_plotly-0.0.1 → xarray_plotly-0.0.3}/docs/getting-started.ipynb +30 -34
  9. xarray_plotly-0.0.3/docs/index.md +11 -0
  10. {xarray_plotly-0.0.1 → xarray_plotly-0.0.3}/mkdocs.yml +1 -1
  11. {xarray_plotly-0.0.1 → xarray_plotly-0.0.3}/pyproject.toml +12 -11
  12. xarray_plotly-0.0.3/xarray_plotly/__init__.py +81 -0
  13. xarray_plotly-0.0.3/xarray_plotly/accessor.py +275 -0
  14. {xarray_plotly-0.0.1 → xarray_plotly-0.0.3}/xarray_plotly/common.py +45 -63
  15. {xarray_plotly-0.0.1 → xarray_plotly-0.0.3}/xarray_plotly/config.py +48 -73
  16. xarray_plotly-0.0.3/xarray_plotly.egg-info/PKG-INFO +89 -0
  17. {xarray_plotly-0.0.1 → xarray_plotly-0.0.3}/xarray_plotly.egg-info/SOURCES.txt +2 -0
  18. xarray_plotly-0.0.3/xarray_plotly.egg-info/requires.txt +19 -0
  19. xarray_plotly-0.0.1/PKG-INFO +0 -175
  20. xarray_plotly-0.0.1/README.md +0 -134
  21. xarray_plotly-0.0.1/docs/api.md +0 -90
  22. xarray_plotly-0.0.1/docs/examples/advanced.ipynb +0 -425
  23. xarray_plotly-0.0.1/docs/examples/plot-types.ipynb +0 -381
  24. xarray_plotly-0.0.1/docs/index.md +0 -90
  25. xarray_plotly-0.0.1/xarray_plotly/__init__.py +0 -73
  26. xarray_plotly-0.0.1/xarray_plotly/accessor.py +0 -336
  27. xarray_plotly-0.0.1/xarray_plotly.egg-info/PKG-INFO +0 -175
  28. xarray_plotly-0.0.1/xarray_plotly.egg-info/requires.txt +0 -18
  29. {xarray_plotly-0.0.1 → xarray_plotly-0.0.3}/.github/workflows/ci.yml +0 -0
  30. {xarray_plotly-0.0.1 → xarray_plotly-0.0.3}/.github/workflows/docs.yml +0 -0
  31. {xarray_plotly-0.0.1 → xarray_plotly-0.0.3}/.github/workflows/release.yml +0 -0
  32. {xarray_plotly-0.0.1 → xarray_plotly-0.0.3}/.gitignore +0 -0
  33. {xarray_plotly-0.0.1 → xarray_plotly-0.0.3}/.pre-commit-config.yaml +0 -0
  34. {xarray_plotly-0.0.1 → xarray_plotly-0.0.3}/CONTRIBUTING.md +0 -0
  35. {xarray_plotly-0.0.1 → xarray_plotly-0.0.3}/LICENSE +0 -0
  36. {xarray_plotly-0.0.1 → xarray_plotly-0.0.3}/setup.cfg +0 -0
  37. {xarray_plotly-0.0.1 → xarray_plotly-0.0.3}/tests/__init__.py +0 -0
  38. {xarray_plotly-0.0.1 → xarray_plotly-0.0.3}/tests/test_accessor.py +0 -0
  39. {xarray_plotly-0.0.1 → xarray_plotly-0.0.3}/tests/test_common.py +0 -0
  40. {xarray_plotly-0.0.1 → xarray_plotly-0.0.3}/tests/test_config.py +0 -0
  41. {xarray_plotly-0.0.1 → xarray_plotly-0.0.3}/xarray_plotly/plotting.py +0 -0
  42. {xarray_plotly-0.0.1 → xarray_plotly-0.0.3}/xarray_plotly/py.typed +0 -0
  43. {xarray_plotly-0.0.1 → xarray_plotly-0.0.3}/xarray_plotly.egg-info/dependency_links.txt +0 -0
  44. {xarray_plotly-0.0.1 → xarray_plotly-0.0.3}/xarray_plotly.egg-info/top_level.txt +0 -0
@@ -0,0 +1,25 @@
1
+ version: 2
2
+ updates:
3
+ - package-ecosystem: "pip"
4
+ directory: "/"
5
+ schedule:
6
+ interval: "weekly"
7
+ groups:
8
+ dev-dependencies:
9
+ patterns:
10
+ - "pytest*"
11
+ - "mypy"
12
+ - "ruff"
13
+ - "pre-commit"
14
+ - "nbstripout"
15
+ docs-dependencies:
16
+ patterns:
17
+ - "mkdocs*"
18
+ - "pooch"
19
+ - "netcdf4"
20
+ - "jupyter"
21
+
22
+ - package-ecosystem: "github-actions"
23
+ directory: "/"
24
+ schedule:
25
+ interval: "weekly"
@@ -0,0 +1,24 @@
1
+ name: Dependabot auto-merge
2
+ on: pull_request
3
+
4
+ permissions:
5
+ contents: write
6
+ pull-requests: write
7
+
8
+ jobs:
9
+ dependabot:
10
+ runs-on: ubuntu-latest
11
+ if: github.actor == 'dependabot[bot]'
12
+ steps:
13
+ - name: Dependabot metadata
14
+ id: metadata
15
+ uses: dependabot/fetch-metadata@v2
16
+ with:
17
+ github-token: "${{ secrets.GITHUB_TOKEN }}"
18
+
19
+ - name: Auto-merge minor and patch updates
20
+ if: steps.metadata.outputs.update-type == 'version-update:semver-patch' || steps.metadata.outputs.update-type == 'version-update:semver-minor'
21
+ run: gh pr merge --auto --squash "$PR_URL"
22
+ env:
23
+ PR_URL: ${{ github.event.pull_request.html_url }}
24
+ GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
@@ -0,0 +1,89 @@
1
+ Metadata-Version: 2.4
2
+ Name: xarray_plotly
3
+ Version: 0.0.3
4
+ Summary: Interactive Plotly Express plotting accessor for xarray
5
+ Author: Felix
6
+ License: MIT
7
+ Project-URL: Homepage, https://github.com/FBumann/xarray_plotly
8
+ Project-URL: Documentation, https://fbumann.github.io/xarray_plotly
9
+ Project-URL: Repository, https://github.com/FBumann/xarray_plotly
10
+ Keywords: xarray,plotly,visualization,interactive,plotting
11
+ Classifier: Development Status :: 3 - Alpha
12
+ Classifier: Intended Audience :: Science/Research
13
+ Classifier: License :: OSI Approved :: MIT License
14
+ Classifier: Operating System :: OS Independent
15
+ Classifier: Programming Language :: Python :: 3
16
+ Classifier: Programming Language :: Python :: 3.10
17
+ Classifier: Programming Language :: Python :: 3.11
18
+ Classifier: Programming Language :: Python :: 3.12
19
+ Classifier: Programming Language :: Python :: 3.13
20
+ Classifier: Topic :: Scientific/Engineering :: Visualization
21
+ Requires-Python: >=3.10
22
+ Description-Content-Type: text/markdown
23
+ License-File: LICENSE
24
+ Requires-Dist: xarray>=2023.1.0
25
+ Requires-Dist: plotly>=5.0.0
26
+ Requires-Dist: pandas>=1.5.0
27
+ Provides-Extra: dev
28
+ Requires-Dist: pytest==8.3.5; extra == "dev"
29
+ Requires-Dist: pytest-cov==6.0.0; extra == "dev"
30
+ Requires-Dist: mypy==1.14.1; extra == "dev"
31
+ Requires-Dist: ruff==0.9.2; extra == "dev"
32
+ Requires-Dist: pre-commit==4.0.1; extra == "dev"
33
+ Requires-Dist: nbstripout==0.8.1; extra == "dev"
34
+ Provides-Extra: docs
35
+ Requires-Dist: mkdocs==1.6.1; extra == "docs"
36
+ Requires-Dist: mkdocs-material==9.5.49; extra == "docs"
37
+ Requires-Dist: mkdocstrings[python]==0.27.0; extra == "docs"
38
+ Requires-Dist: mkdocs-jupyter==0.25.1; extra == "docs"
39
+ Requires-Dist: mkdocs-plotly-plugin==0.1.3; extra == "docs"
40
+ Requires-Dist: jupyter==1.1.1; extra == "docs"
41
+ Dynamic: license-file
42
+
43
+ # xarray_plotly
44
+
45
+ **Interactive Plotly Express plotting for xarray**
46
+
47
+ [![PyPI version](https://badge.fury.io/py/xarray_plotly.svg)](https://badge.fury.io/py/xarray_plotly)
48
+ [![Python](https://img.shields.io/pypi/pyversions/xarray_plotly.svg)](https://pypi.org/project/xarray_plotly/)
49
+ [![CI](https://github.com/FBumann/xarray_plotly/actions/workflows/ci.yml/badge.svg)](https://github.com/FBumann/xarray_plotly/actions)
50
+ [![Docs](https://img.shields.io/badge/docs-fbumann.github.io-blue)](https://fbumann.github.io/xarray_plotly/)
51
+ [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
52
+
53
+ ## Installation
54
+
55
+ ```bash
56
+ pip install xarray_plotly
57
+ ```
58
+
59
+ ## Quick Start
60
+
61
+ ```python
62
+ import xarray as xr
63
+ import numpy as np
64
+ import xarray_plotly # registers the accessor
65
+
66
+ da = xr.DataArray(
67
+ np.random.randn(100, 3).cumsum(axis=0),
68
+ dims=["time", "city"],
69
+ coords={"time": np.arange(100), "city": ["NYC", "LA", "Chicago"]},
70
+ )
71
+
72
+ # Accessor style
73
+ fig = da.plotly.line()
74
+ fig.show()
75
+
76
+ # Or with xpx() for IDE code completion
77
+ from xarray_plotly import xpx
78
+ fig = xpx(da).line()
79
+ ```
80
+
81
+ **Why `xpx()`?** The accessor (`da.plotly`) works but IDEs can't provide code completion for it. This is because xarray accessors are registered dynamically at runtime, making them invisible to static type checkers. The `xpx()` function provides the same functionality with full IDE support. This limitation could only be solved by xarray itself, if at all — it may be a fundamental Python limitation.
82
+
83
+ ## Documentation
84
+
85
+ Full documentation: [https://fbumann.github.io/xarray_plotly](https://fbumann.github.io/xarray_plotly)
86
+
87
+ ## License
88
+
89
+ MIT
@@ -0,0 +1,47 @@
1
+ # xarray_plotly
2
+
3
+ **Interactive Plotly Express plotting for xarray**
4
+
5
+ [![PyPI version](https://badge.fury.io/py/xarray_plotly.svg)](https://badge.fury.io/py/xarray_plotly)
6
+ [![Python](https://img.shields.io/pypi/pyversions/xarray_plotly.svg)](https://pypi.org/project/xarray_plotly/)
7
+ [![CI](https://github.com/FBumann/xarray_plotly/actions/workflows/ci.yml/badge.svg)](https://github.com/FBumann/xarray_plotly/actions)
8
+ [![Docs](https://img.shields.io/badge/docs-fbumann.github.io-blue)](https://fbumann.github.io/xarray_plotly/)
9
+ [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
10
+
11
+ ## Installation
12
+
13
+ ```bash
14
+ pip install xarray_plotly
15
+ ```
16
+
17
+ ## Quick Start
18
+
19
+ ```python
20
+ import xarray as xr
21
+ import numpy as np
22
+ import xarray_plotly # registers the accessor
23
+
24
+ da = xr.DataArray(
25
+ np.random.randn(100, 3).cumsum(axis=0),
26
+ dims=["time", "city"],
27
+ coords={"time": np.arange(100), "city": ["NYC", "LA", "Chicago"]},
28
+ )
29
+
30
+ # Accessor style
31
+ fig = da.plotly.line()
32
+ fig.show()
33
+
34
+ # Or with xpx() for IDE code completion
35
+ from xarray_plotly import xpx
36
+ fig = xpx(da).line()
37
+ ```
38
+
39
+ **Why `xpx()`?** The accessor (`da.plotly`) works but IDEs can't provide code completion for it. This is because xarray accessors are registered dynamically at runtime, making them invisible to static type checkers. The `xpx()` function provides the same functionality with full IDE support. This limitation could only be solved by xarray itself, if at all — it may be a fundamental Python limitation.
40
+
41
+ ## Documentation
42
+
43
+ Full documentation: [https://fbumann.github.io/xarray_plotly](https://fbumann.github.io/xarray_plotly)
44
+
45
+ ## License
46
+
47
+ MIT
@@ -0,0 +1,28 @@
1
+ # API Reference
2
+
3
+ ::: xarray_plotly.xpx
4
+
5
+ ::: xarray_plotly.accessor.DataArrayPlotlyAccessor
6
+ options:
7
+ members:
8
+ - line
9
+ - bar
10
+ - area
11
+ - scatter
12
+ - box
13
+ - imshow
14
+
15
+ ::: xarray_plotly.config
16
+ options:
17
+ members:
18
+ - notebook
19
+ - get_options
20
+ - set_options
21
+ - Options
22
+
23
+ ::: xarray_plotly.common
24
+ options:
25
+ members:
26
+ - auto
27
+ - SLOT_ORDERS
28
+ - assign_slots
@@ -0,0 +1,320 @@
1
+ {
2
+ "cells": [
3
+ {
4
+ "cell_type": "markdown",
5
+ "metadata": {},
6
+ "source": [
7
+ "# Advanced Usage\n",
8
+ "\n",
9
+ "This notebook covers advanced Plotly customization. All kwargs are passed directly to [Plotly Express](https://plotly.com/python/plotly-express/), and figures can be modified using the full [Plotly Graph Objects API](https://plotly.com/python/graph-objects/)."
10
+ ]
11
+ },
12
+ {
13
+ "cell_type": "code",
14
+ "execution_count": null,
15
+ "metadata": {},
16
+ "outputs": [],
17
+ "source": [
18
+ "import plotly.express as px\n",
19
+ "import plotly.graph_objects as go\n",
20
+ "import xarray as xr\n",
21
+ "\n",
22
+ "from xarray_plotly import config, xpx\n",
23
+ "\n",
24
+ "config.notebook()"
25
+ ]
26
+ },
27
+ {
28
+ "cell_type": "code",
29
+ "execution_count": null,
30
+ "metadata": {},
31
+ "outputs": [],
32
+ "source": [
33
+ "# Load sample data\n",
34
+ "df_stocks = px.data.stocks().set_index(\"date\")\n",
35
+ "df_stocks.index = df_stocks.index.astype(\"datetime64[ns]\")\n",
36
+ "\n",
37
+ "stocks = xr.DataArray(\n",
38
+ " df_stocks.values,\n",
39
+ " dims=[\"date\", \"company\"],\n",
40
+ " coords={\"date\": df_stocks.index, \"company\": df_stocks.columns.tolist()},\n",
41
+ " name=\"price\",\n",
42
+ ")\n",
43
+ "\n",
44
+ "df_gap = px.data.gapminder()\n",
45
+ "countries = [\"United States\", \"China\", \"Germany\", \"Brazil\", \"Nigeria\"]\n",
46
+ "df_life = df_gap[df_gap[\"country\"].isin(countries)].pivot(\n",
47
+ " index=\"year\", columns=\"country\", values=\"lifeExp\"\n",
48
+ ")\n",
49
+ "life_exp = xr.DataArray(\n",
50
+ " df_life.values,\n",
51
+ " dims=[\"year\", \"country\"],\n",
52
+ " coords={\"year\": df_life.index, \"country\": df_life.columns.tolist()},\n",
53
+ " name=\"life_exp\",\n",
54
+ ")"
55
+ ]
56
+ },
57
+ {
58
+ "cell_type": "markdown",
59
+ "metadata": {},
60
+ "source": [
61
+ "## Passing Kwargs to Plotly Express\n",
62
+ "\n",
63
+ "All keyword arguments are passed directly to [Plotly Express functions](https://plotly.com/python-api-reference/plotly.express.html):"
64
+ ]
65
+ },
66
+ {
67
+ "cell_type": "code",
68
+ "execution_count": null,
69
+ "metadata": {},
70
+ "outputs": [],
71
+ "source": [
72
+ "# All px.line kwargs work: template, labels, colors, etc.\n",
73
+ "fig = xpx(stocks).line(\n",
74
+ " title=\"Stock Performance\",\n",
75
+ " template=\"plotly_white\",\n",
76
+ " labels={\"price\": \"Normalized Price\", \"date\": \"Date\", \"company\": \"Ticker\"},\n",
77
+ " color_discrete_sequence=px.colors.qualitative.Set2,\n",
78
+ ")\n",
79
+ "fig"
80
+ ]
81
+ },
82
+ {
83
+ "cell_type": "code",
84
+ "execution_count": null,
85
+ "metadata": {},
86
+ "outputs": [],
87
+ "source": [
88
+ "# px.imshow kwargs: colorscale, midpoint, aspect\n",
89
+ "# See: https://plotly.com/python/imshow/\n",
90
+ "life_change = life_exp - life_exp.isel(year=0)\n",
91
+ "\n",
92
+ "fig = xpx(life_change).imshow(\n",
93
+ " color_continuous_scale=\"RdBu\",\n",
94
+ " color_continuous_midpoint=0,\n",
95
+ " aspect=\"auto\",\n",
96
+ " title=\"Life Expectancy Change Since 1952\",\n",
97
+ ")\n",
98
+ "fig"
99
+ ]
100
+ },
101
+ {
102
+ "cell_type": "code",
103
+ "execution_count": null,
104
+ "metadata": {},
105
+ "outputs": [],
106
+ "source": [
107
+ "# px.bar kwargs: barmode, text_auto\n",
108
+ "# See: https://plotly.com/python/bar-charts/\n",
109
+ "fig = xpx(life_exp.sel(year=[1952, 1982, 2007])).bar(\n",
110
+ " barmode=\"group\",\n",
111
+ " text_auto=\".1f\",\n",
112
+ " title=\"Life Expectancy\",\n",
113
+ ")\n",
114
+ "fig"
115
+ ]
116
+ },
117
+ {
118
+ "cell_type": "markdown",
119
+ "metadata": {},
120
+ "source": [
121
+ "## Modifying Figures After Creation\n",
122
+ "\n",
123
+ "All methods return a Plotly `Figure`. Use the [Figure API](https://plotly.com/python/creating-and-updating-figures/) to customize:"
124
+ ]
125
+ },
126
+ {
127
+ "cell_type": "markdown",
128
+ "metadata": {},
129
+ "source": [
130
+ "### update_layout\n",
131
+ "\n",
132
+ "Modify [layout properties](https://plotly.com/python/reference/layout/): legend, margins, fonts, etc."
133
+ ]
134
+ },
135
+ {
136
+ "cell_type": "code",
137
+ "execution_count": null,
138
+ "metadata": {},
139
+ "outputs": [],
140
+ "source": [
141
+ "fig = xpx(stocks).line()\n",
142
+ "\n",
143
+ "fig.update_layout(\n",
144
+ " title={\"text\": \"Stock Prices\", \"x\": 0.5, \"font\": {\"size\": 24}},\n",
145
+ " legend={\n",
146
+ " \"orientation\": \"h\",\n",
147
+ " \"yanchor\": \"bottom\",\n",
148
+ " \"y\": 1.02,\n",
149
+ " \"xanchor\": \"center\",\n",
150
+ " \"x\": 0.5,\n",
151
+ " },\n",
152
+ " margin={\"t\": 80},\n",
153
+ ")\n",
154
+ "fig"
155
+ ]
156
+ },
157
+ {
158
+ "cell_type": "markdown",
159
+ "metadata": {},
160
+ "source": [
161
+ "### update_traces\n",
162
+ "\n",
163
+ "Modify [trace properties](https://plotly.com/python/reference/): line width, markers, opacity, etc."
164
+ ]
165
+ },
166
+ {
167
+ "cell_type": "code",
168
+ "execution_count": null,
169
+ "metadata": {},
170
+ "outputs": [],
171
+ "source": [
172
+ "fig = xpx(stocks).line()\n",
173
+ "fig.update_traces(line={\"width\": 3})\n",
174
+ "fig.update_layout(title=\"Thicker Lines\")\n",
175
+ "fig"
176
+ ]
177
+ },
178
+ {
179
+ "cell_type": "code",
180
+ "execution_count": null,
181
+ "metadata": {},
182
+ "outputs": [],
183
+ "source": [
184
+ "fig = xpx(stocks).scatter()\n",
185
+ "fig.update_traces(marker={\"size\": 12, \"opacity\": 0.7, \"line\": {\"width\": 1, \"color\": \"white\"}})\n",
186
+ "fig.update_layout(title=\"Custom Markers\")\n",
187
+ "fig"
188
+ ]
189
+ },
190
+ {
191
+ "cell_type": "markdown",
192
+ "metadata": {},
193
+ "source": [
194
+ "### update_xaxes / update_yaxes\n",
195
+ "\n",
196
+ "Modify [axis properties](https://plotly.com/python/axes/): range slider, tick format, etc."
197
+ ]
198
+ },
199
+ {
200
+ "cell_type": "code",
201
+ "execution_count": null,
202
+ "metadata": {},
203
+ "outputs": [],
204
+ "source": [
205
+ "fig = xpx(stocks).line(title=\"With Range Slider\")\n",
206
+ "fig.update_xaxes(rangeslider_visible=True)\n",
207
+ "fig.update_yaxes(tickformat=\".0%\")\n",
208
+ "fig"
209
+ ]
210
+ },
211
+ {
212
+ "cell_type": "markdown",
213
+ "metadata": {},
214
+ "source": [
215
+ "### Adding Shapes and Annotations\n",
216
+ "\n",
217
+ "Add reference lines, [shapes](https://plotly.com/python/shapes/), and [annotations](https://plotly.com/python/text-and-annotations/):"
218
+ ]
219
+ },
220
+ {
221
+ "cell_type": "code",
222
+ "execution_count": null,
223
+ "metadata": {},
224
+ "outputs": [],
225
+ "source": [
226
+ "fig = xpx(stocks).line(title=\"With Reference Lines and Annotations\")\n",
227
+ "\n",
228
+ "fig.add_hline(y=1.0, line_dash=\"dash\", line_color=\"gray\", annotation_text=\"Baseline\")\n",
229
+ "fig.add_vline(x=\"2018-10-01\", line_dash=\"dot\", line_color=\"red\")\n",
230
+ "\n",
231
+ "fig.add_annotation(\n",
232
+ " x=\"2018-10-01\",\n",
233
+ " y=1.4,\n",
234
+ " text=\"Market correction\",\n",
235
+ " showarrow=True,\n",
236
+ " arrowhead=2,\n",
237
+ " ax=40,\n",
238
+ " ay=-40,\n",
239
+ ")\n",
240
+ "fig"
241
+ ]
242
+ },
243
+ {
244
+ "cell_type": "markdown",
245
+ "metadata": {},
246
+ "source": [
247
+ "### Adding Traces with Graph Objects\n",
248
+ "\n",
249
+ "Add custom traces using [Plotly Graph Objects](https://plotly.com/python/graph-objects/):"
250
+ ]
251
+ },
252
+ {
253
+ "cell_type": "code",
254
+ "execution_count": null,
255
+ "metadata": {},
256
+ "outputs": [],
257
+ "source": [
258
+ "fig = xpx(stocks.sel(company=\"GOOG\")).line(title=\"GOOG with Moving Average\")\n",
259
+ "\n",
260
+ "# Add moving average as a new trace\n",
261
+ "goog = stocks.sel(company=\"GOOG\")\n",
262
+ "ma_20 = goog.rolling(date=20, center=True).mean()\n",
263
+ "\n",
264
+ "fig.add_trace(\n",
265
+ " go.Scatter(\n",
266
+ " x=ma_20.coords[\"date\"].values,\n",
267
+ " y=ma_20.values,\n",
268
+ " mode=\"lines\",\n",
269
+ " name=\"20-day MA\",\n",
270
+ " line={\"dash\": \"dash\", \"color\": \"red\", \"width\": 2},\n",
271
+ " )\n",
272
+ ")\n",
273
+ "fig"
274
+ ]
275
+ },
276
+ {
277
+ "cell_type": "markdown",
278
+ "metadata": {},
279
+ "source": [
280
+ "## Exporting Figures\n",
281
+ "\n",
282
+ "See [static image export](https://plotly.com/python/static-image-export/) and [HTML export](https://plotly.com/python/interactive-html-export/).\n",
283
+ "\n",
284
+ "```python\n",
285
+ "# Interactive HTML\n",
286
+ "fig.write_html(\"plot.html\")\n",
287
+ "\n",
288
+ "# Static images (requires: pip install kaleido)\n",
289
+ "fig.write_image(\"plot.png\", scale=2)\n",
290
+ "fig.write_image(\"plot.svg\")\n",
291
+ "fig.write_image(\"plot.pdf\")\n",
292
+ "```"
293
+ ]
294
+ },
295
+ {
296
+ "cell_type": "markdown",
297
+ "metadata": {},
298
+ "source": [
299
+ "## More Resources\n",
300
+ "\n",
301
+ "- [Plotly Express API](https://plotly.com/python-api-reference/plotly.express.html)\n",
302
+ "- [Figure Reference](https://plotly.com/python/reference/)\n",
303
+ "- [Plotly Tutorials](https://plotly.com/python/)"
304
+ ]
305
+ }
306
+ ],
307
+ "metadata": {
308
+ "kernelspec": {
309
+ "display_name": "Python 3",
310
+ "language": "python",
311
+ "name": "python3"
312
+ },
313
+ "language_info": {
314
+ "name": "python",
315
+ "version": "3.12.0"
316
+ }
317
+ },
318
+ "nbformat": 4,
319
+ "nbformat_minor": 4
320
+ }