diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000000000000000000000000000000000000..a4070bc197b4427c2a1b05bb679116bf5be7e26e --- /dev/null +++ b/Dockerfile @@ -0,0 +1,18 @@ +# read the doc: https://huggingface.co/docs/hub/spaces-sdks-docker +# you will also find guides on how best to write your Dockerfile + +FROM python:3.9 + +RUN useradd -m -u 1000 user + +WORKDIR /app + +COPY --chown=user ./requirements.txt requirements.txt + +RUN pip install --no-cache-dir --upgrade -r requirements.txt + +COPY --chown=user . /app + +EXPOSE 7860 + +CMD ["gunicorn", "-w", "4", "-b", "0.0.0.0:7860", "app:server"] \ No newline at end of file diff --git a/LICENSE.txt b/LICENSE.txt new file mode 100644 index 0000000000000000000000000000000000000000..42383880001ed528599f4d0a66d4c82d6b34c8dd --- /dev/null +++ b/LICENSE.txt @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright (c) 2016 FT Interactive News + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/README.md b/README.md index 68b8d21f1aae642e7b0e13906ec2d7555d2c3eb8..8caf36cce0d1b916ec8081078a972226d207b43d 100644 --- a/README.md +++ b/README.md @@ -1,11 +1,103 @@ ---- -title: Demo Visual Vocabulary -emoji: 🌖 -colorFrom: indigo -colorTo: indigo -sdk: docker -pinned: false -license: apache-2.0 ---- - -Check out the configuration reference at https://huggingface.co/docs/hub/spaces-config-reference +# Vizro - Visual vocabulary + +This dashboard shows a gallery of charts. It includes guidance on when to use each chart type and sample Python code +to create them using [Plotly](https://plotly.com/python/) and [Vizro](https://github.com/mckinsey/vizro). + +**Created by:** + +- [Huong Li Nguyen](https://github.com/huong-li-nguyen) and [Antony Milne](https://github.com/antonymilne) + +- Images created by QuantumBlack + +**Inspired by:** + +- [The FT Visual Vocabulary](https://github.com/Financial-Times/chart-doctor/blob/main/visual-vocabulary/README.md): Alan Smith, Chris Campbell, Ian Bott, Liz Faunce, Graham Parrish, Billy Ehrenberg, Paul McCallum, + Martin Stabe. + +- [The Graphic Continuum](https://www.informationisbeautifulawards.com/showcase/611-the-graphic-continuum): Jon Swabish and Severino Ribecca + +**Credits, tutorials and resources:** + +- [Plotly](https://plotly.com/python/plotly-express/) +- [Guide to data chart mastery](https://www.atlassian.com/data/charts) + +## Chart types + +The dashboard is still in development. Below is an overview of the chart types for which a completed page is available. + +| Chart Type | Status | Category | +| --------------------- | ------ | ------------------------ | +| Arc | ❌ | Part-to-whole | +| Area | ✅ | Time | +| Bar | ✅ | Magnitude | +| Barcode | ❌ | Distribution | +| Beeswarm | ❌ | Distribution | +| Boxplot | ✅ | Distribution | +| Bubble | ✅ | Correlation | +| Bubble map | ❌ | Spatial | +| Bubble timeline | ❌ | Time | +| Bullet | ❌ | Magnitude | +| Bump | ❌ | Ranking | +| Butterfly | ✅ | Deviation, Distribution | +| Chord | ❌ | Flow | +| Choropleth | ✅ | Spatial | +| Column | ✅ | Magnitude, Time | +| Column and line | ✅ | Correlation, Time | +| Connected scatter | ✅ | Correlation, Time | +| Cumulative curve | ❌ | Distribution | +| Diverging bar | ❌ | Deviation | +| Diverging stacked bar | ❌ | Deviation | +| Donut | ✅ | Part-to-whole | +| Dot map | ❌ | Spatial | +| Dot plot | ❌ | Distribution | +| Fan | ❌ | Time | +| Flow map | ❌ | Spatial | +| Funnel | ✅ | Part-to-whole | +| Gantt | ❌ | Time | +| Gridplot | ❌ | Part-to-whole | +| Heatmap | ✅ | Time | +| Heatmap matrix | ❌ | Correlation | +| Histogram | ✅ | Distribution | +| Line | ✅ | Time | +| Lollipop | ❌ | Ranking, Magnitude | +| Marimekko | ❌ | Magnitude, Part-to-whole | +| Network | ❌ | Flow | +| Ordered bar | ✅ | Ranking | +| Ordered bubble | ❌ | Ranking | +| Ordered column | ✅ | Ranking | +| Paired bar | ✅ | Magnitude | +| Paired column | ✅ | Magnitude | +| Parallel coordinates | ✅ | Magnitude | +| Pictogram | ❌ | Magnitude | +| Pie | ✅ | Part-to-whole | +| Radar | ❌ | Magnitude | +| Radial | ❌ | Magnitude | +| Sankey | ✅ | Flow | +| Scatter | ✅ | Correlation | +| Scatter matrix | ✅ | Correlation | +| Slope | ❌ | Ranking, Time | +| Sparkline | ❌ | Time | +| Stacked bar | ✅ | Part-to-whole | +| Stacked column | ✅ | Part-to-whole | +| Stepped line | ✅ | Time | +| Surplus deficit line | ❌ | Deviation | +| Treemap | ✅ | Part-to-whole | +| Venn | ❌ | Part-to-whole | +| Violin | ✅ | Distribution | +| Waterfall | ❌ | Part-to-whole, Flow | + +To contribute a chart, follow the steps below: + +1. Place an `svg` file named after the chart type in the `assets` folder if it doesn't already exist. +2. Add the data set to `_pages_utils.py` if it doesn't already exist. +3. Create a new page for the chart type and add it to the relevant category `.py` file such as `correlation.py`, + `deviation.py`, `distribution.py`, etc. +4. Add a `.py` file containing a code example of the chart type in the `pages/examples` folder, for instance, `area.py` +5. Remove the `IncompletePage(..)` entry for that chart type in `chart_groups.py`. +6. Update this `README.md` with the new chart type. + +## How to run the example locally + +1. If you have `hatch` set up, run the example with the command `hatch run example _visual-vocabulary`. + Otherwise, with a virtual Python environment activated, run `pip install -r requirements.txt` and then `python app.py`. +2. You should now be able to access the app locally via http://127.0.0.1:8050/. diff --git a/app.py b/app.py new file mode 100644 index 0000000000000000000000000000000000000000..311d87b5f7ce41298b6ba2c8b34cad1039ad300f --- /dev/null +++ b/app.py @@ -0,0 +1,111 @@ +"""App configuration for dashboard.""" + +from typing import List, Union + +import vizro.models as vm +from chart_groups import ALL_CHART_GROUP, CHART_GROUPS, ChartGroup, IncompletePage +from custom_components import FlexContainer, Markdown +from vizro import Vizro + + +def make_chart_card(page: Union[vm.Page, IncompletePage]) -> vm.Card: + """Makes a card with svg icon, linked to the right page if page is complete. + + Args: + page: page to make card for + + Returns: card with svg icon, linked to the right page if page is complete. + + """ + # There's one SVG per chart title, so that e.g. pages distribution-butterfly and deviation-butterfly, which both + # have title "Butterfly", correspond to butterfly.svg. + # Incomplete pages have page.path = "" so won't be linked to here. + svg_name = page.title.lower().replace(" ", "-") + return vm.Card( + text=f""" + ![](assets/images/charts/{svg_name}.svg#chart-icon) + + #### {page.title} + """, + href=page.path, + ) + + +def make_homepage_container(chart_group: ChartGroup) -> vm.Container: + """Makes a container with cards for each completed and incomplete chart in chart_group. + + Args: + chart_group: group of charts to make container for. + + Returns: container with cards for each chart in chart_group. + + """ + # Pages are sorted in title's alphabetical order and deduplicated so that e.g. pages distribution-butterfly and + # deviation-butterfly, which both have title "Butterfly", correspond to a single card. + return vm.Container( + title=chart_group.name, + layout=vm.Layout(grid=[[0, 1, 1]], col_gap="40px"), + components=[ + Markdown(text=chart_group.intro_text, classname="intro-text"), + FlexContainer( + components=[ + make_chart_card(page) + for page in sorted( + _remove_duplicates(chart_group.pages + chart_group.incomplete_pages), + key=lambda page: page.title, + ) + ], + ), + ], + ) + + +def _remove_duplicates(pages: List[Union[vm.Page, IncompletePage]]) -> List[Union[vm.Page, IncompletePage]]: + # Deduplicate pages that have the same title. Using reversed means that the page that is kept is the first one + # in the dashboard. This will be the one that the card on the homepage links to. + return list({page.title: page for page in reversed(pages)}.values()) + + +def make_navlink(chart_group: ChartGroup) -> vm.NavLink: + """Makes a navlink with icon and links to every complete page within chart_group. + + Args: + chart_group: chart_group to make a navlink for. + + Returns: navlink for chart_group. + + """ + # Pages are sorted in alphabetical order within each chart group. + return vm.NavLink( + label=chart_group.name, + pages={chart_group.name: [page.id for page in sorted(chart_group.pages, key=lambda page: page.title)]}, + icon=chart_group.icon, + ) + + +homepage = vm.Page( + title="Overview", + components=[ + vm.Tabs(tabs=[make_homepage_container(chart_group) for chart_group in [ALL_CHART_GROUP, *CHART_GROUPS]]), + ], +) + +# TODO: consider whether each chart group should have its own individual homepage, +# e.g. at http://localhost:8050/deviation/. This could just repeat the content of the tab from the homepage and would +# work nicely with the hierarchical navigation. +dashboard = vm.Dashboard( + # ALL_CHART_GROUP.pages has duplicated pages, e.g. both distribution-butterfly and deviation-butterfly. + title="Vizro - Visual Vocabulary", + pages=[homepage, *ALL_CHART_GROUP.pages], + navigation=vm.Navigation( + nav_selector=vm.NavBar( + items=[ + vm.NavLink(label="Overview", pages=[homepage.id], icon="Home"), + ] + + [make_navlink(chart_group) for chart_group in CHART_GROUPS] + ) + ), +) + +if __name__ == "__main__": + Vizro().build(dashboard).run() diff --git a/assets/app.svg b/assets/app.svg new file mode 100644 index 0000000000000000000000000000000000000000..9d07d6372b2a023eafe9e1a7a408e89c93cbfe1e --- /dev/null +++ b/assets/app.svg @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/assets/css/custom.css b/assets/css/custom.css new file mode 100644 index 0000000000000000000000000000000000000000..3d441b1f9175f272bece23d601b67fc2dacbf653 --- /dev/null +++ b/assets/css/custom.css @@ -0,0 +1,71 @@ +#page-header { + padding-left: 8px; +} + +#left-main { + width: 288px; +} + +img[src*="#chart-icon"] { + width: 100%; +} + +.code-clipboard { + font-size: 20px; + position: absolute; + right: 14px; + top: 12px; +} + +.code-clipboard-container { + background: var(--surfaces-bg-card); + font-family: monospace; + max-height: 500px; + overflow: auto; + padding: 1rem; + position: relative; +} + +.code-clipboard-container::-webkit-scrollbar-thumb { + border-color: var(--surfaces-bg-card); +} + +.flex-container { + display: flex; + flex-wrap: wrap; + gap: 24px; + justify-content: flex-start; +} + +.flex-container h4 { + color: var(--text-secondary); + margin: 0; + padding-top: 12px; + text-align: center; +} + +.flex-container .card { + height: 232px; + margin: 0 0 auto; + opacity: 0.3; + width: 176px; +} + +.flex-container .card-nav { + opacity: 1; +} + +.flex-container .card img { + width: 100%; +} + +.intro-text { + border-left: 4px solid var(--text-secondary); + padding: 4px 12px; +} + +.intro-text p { + font-size: 16px; + line-height: 20px; + margin: 0; +} diff --git a/assets/favicon.ico b/assets/favicon.ico new file mode 100644 index 0000000000000000000000000000000000000000..240c9f541de1e47599bb047e63b008ccb268a4c3 Binary files /dev/null and b/assets/favicon.ico differ diff --git a/assets/images/charts/arc.svg b/assets/images/charts/arc.svg new file mode 100644 index 0000000000000000000000000000000000000000..35a5c81f874cbf3468c17b43b7a9e855383525e4 --- /dev/null +++ b/assets/images/charts/arc.svg @@ -0,0 +1,25 @@ + + + + Group 6 Copy 34 + Created with Sketch. + + + + + + + + + + + + + + + + + + + + diff --git a/assets/images/charts/area.svg b/assets/images/charts/area.svg new file mode 100644 index 0000000000000000000000000000000000000000..f51c7d036a13a466643ed417ccf0fc18b6941b89 --- /dev/null +++ b/assets/images/charts/area.svg @@ -0,0 +1 @@ + diff --git a/assets/images/charts/bar.svg b/assets/images/charts/bar.svg new file mode 100644 index 0000000000000000000000000000000000000000..b2491fb527eec5b90ceccdadab0b6503ed7b7f7f --- /dev/null +++ b/assets/images/charts/bar.svg @@ -0,0 +1 @@ + diff --git a/assets/images/charts/barcode.svg b/assets/images/charts/barcode.svg new file mode 100644 index 0000000000000000000000000000000000000000..cbe2ffc23c29ee65110482d93e24fb38808bd9d8 --- /dev/null +++ b/assets/images/charts/barcode.svg @@ -0,0 +1,136 @@ + + + + Group 6 Copy 14 + Created with Sketch. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/assets/images/charts/beeswarm.svg b/assets/images/charts/beeswarm.svg new file mode 100644 index 0000000000000000000000000000000000000000..7d29af852261286407ee69a390d69d2b774e3bed --- /dev/null +++ b/assets/images/charts/beeswarm.svg @@ -0,0 +1 @@ + diff --git a/assets/images/charts/boxplot.svg b/assets/images/charts/boxplot.svg new file mode 100644 index 0000000000000000000000000000000000000000..224deddd82bc003b1656c54942a761d94500e78d --- /dev/null +++ b/assets/images/charts/boxplot.svg @@ -0,0 +1,78 @@ + + + + Group 6 Copy 15 + Created with Sketch. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/assets/images/charts/bubble-map.svg b/assets/images/charts/bubble-map.svg new file mode 100644 index 0000000000000000000000000000000000000000..cade9887df283370b9a696b4fea04ab456de817d --- /dev/null +++ b/assets/images/charts/bubble-map.svg @@ -0,0 +1,467 @@ + + + + Group 6 Copy 4 + Created with Sketch. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/assets/images/charts/bubble-timeline.svg b/assets/images/charts/bubble-timeline.svg new file mode 100644 index 0000000000000000000000000000000000000000..b98e5af323d7cc656e15dc0f8cf8312252e163ed --- /dev/null +++ b/assets/images/charts/bubble-timeline.svg @@ -0,0 +1,35 @@ + + + + Group 6 Copy 22 + Created with Sketch. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/assets/images/charts/bubble.svg b/assets/images/charts/bubble.svg new file mode 100644 index 0000000000000000000000000000000000000000..a939659fc377cfb12eb1d228e5e93827ae90c54d --- /dev/null +++ b/assets/images/charts/bubble.svg @@ -0,0 +1,32 @@ + + + + Group 6 Copy 3 + Created with Sketch. + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/assets/images/charts/bullet.svg b/assets/images/charts/bullet.svg new file mode 100644 index 0000000000000000000000000000000000000000..d7f7081738e390a953bd210489b5b9e15f90f4d0 --- /dev/null +++ b/assets/images/charts/bullet.svg @@ -0,0 +1,41 @@ + + + + Group 6 Copy 33 + Created with Sketch. + + + + + + + + + + + + + + + + + + 0 + + + 2 + + + 4 + + + 6 + + + 8 + + + + + + diff --git a/assets/images/charts/bump.svg b/assets/images/charts/bump.svg new file mode 100644 index 0000000000000000000000000000000000000000..509f6cb0a2c796e652efca526bc9d64a4e1cda7a --- /dev/null +++ b/assets/images/charts/bump.svg @@ -0,0 +1 @@ + diff --git a/assets/images/charts/butterfly.svg b/assets/images/charts/butterfly.svg new file mode 100644 index 0000000000000000000000000000000000000000..5f0281a6071a0c55187ffa196ff7e97c192d1fc7 --- /dev/null +++ b/assets/images/charts/butterfly.svg @@ -0,0 +1,27 @@ + + + + Group 6 + Created with Sketch. + + + + + + + + + + + + + + + + + + + + + + diff --git a/assets/images/charts/chord.svg b/assets/images/charts/chord.svg new file mode 100644 index 0000000000000000000000000000000000000000..8eecba457b6c38324d9a6a8f64e7832568489472 --- /dev/null +++ b/assets/images/charts/chord.svg @@ -0,0 +1,23 @@ + + + + Group 6 Copy 29 + Created with Sketch. + + + + + + + + + + + + + + + + + + diff --git a/assets/images/charts/choropleth.svg b/assets/images/charts/choropleth.svg new file mode 100644 index 0000000000000000000000000000000000000000..6a00da7bd89829c455367ab37a4ae513777e7cc9 --- /dev/null +++ b/assets/images/charts/choropleth.svg @@ -0,0 +1,126 @@ + + + + Group 6 Copy 44 + Created with Sketch. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/assets/images/charts/column-and-line.svg b/assets/images/charts/column-and-line.svg new file mode 100644 index 0000000000000000000000000000000000000000..c4af7f626056858f06c3859ba5a8d748ad8cdecf --- /dev/null +++ b/assets/images/charts/column-and-line.svg @@ -0,0 +1,20 @@ + + + + Group 6 Copy 4 + Created with Sketch. + + + + + + + + + + + + + + + diff --git a/assets/images/charts/column.svg b/assets/images/charts/column.svg new file mode 100644 index 0000000000000000000000000000000000000000..ae4a75e22bc1072bb007063228ada935c3505a20 --- /dev/null +++ b/assets/images/charts/column.svg @@ -0,0 +1 @@ + diff --git a/assets/images/charts/connected-scatter.svg b/assets/images/charts/connected-scatter.svg new file mode 100644 index 0000000000000000000000000000000000000000..b3e46261af06a554be0df177d1fb016cb2122b9b --- /dev/null +++ b/assets/images/charts/connected-scatter.svg @@ -0,0 +1 @@ + diff --git a/assets/images/charts/cumulative-curve.svg b/assets/images/charts/cumulative-curve.svg new file mode 100644 index 0000000000000000000000000000000000000000..93cb42b5bfb38b83995bf95b835cdcea7409fc45 --- /dev/null +++ b/assets/images/charts/cumulative-curve.svg @@ -0,0 +1,18 @@ + + + + Group 6 Copy 17 + Created with Sketch. + + + + + + + + + + + + + diff --git a/assets/images/charts/diverging-bar.svg b/assets/images/charts/diverging-bar.svg new file mode 100644 index 0000000000000000000000000000000000000000..bb0b80977495142a3c4fb7a553162683f6854943 --- /dev/null +++ b/assets/images/charts/diverging-bar.svg @@ -0,0 +1,21 @@ + + + + Group 6 Copy + Created with Sketch. + + + + + + + + + + + + + + + + diff --git a/assets/images/charts/diverging-stacked-bar.svg b/assets/images/charts/diverging-stacked-bar.svg new file mode 100644 index 0000000000000000000000000000000000000000..42cbdadc7deb63c7d8037604b4e211409c87229c --- /dev/null +++ b/assets/images/charts/diverging-stacked-bar.svg @@ -0,0 +1 @@ + diff --git a/assets/images/charts/donut.svg b/assets/images/charts/donut.svg new file mode 100644 index 0000000000000000000000000000000000000000..df72ab3ca96c3f863190da2bd31eb380d0bb308f --- /dev/null +++ b/assets/images/charts/donut.svg @@ -0,0 +1,25 @@ + + + + Group 6 Copy 24 + Created with Sketch. + + + + + + + + + + + + + + + + + + + + diff --git a/assets/images/charts/dot-map.svg b/assets/images/charts/dot-map.svg new file mode 100644 index 0000000000000000000000000000000000000000..f733721059983419239d49c30323a18e19763733 --- /dev/null +++ b/assets/images/charts/dot-map.svg @@ -0,0 +1,265 @@ + + + + Group 6 Copy 5 + Created with Sketch. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/assets/images/charts/dot-plot.svg b/assets/images/charts/dot-plot.svg new file mode 100644 index 0000000000000000000000000000000000000000..6640e2813670b66e5dd39c3433f486404577972f --- /dev/null +++ b/assets/images/charts/dot-plot.svg @@ -0,0 +1,30 @@ + + + + Group 6 Copy 13 + Created with Sketch. + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/assets/images/charts/fan.svg b/assets/images/charts/fan.svg new file mode 100644 index 0000000000000000000000000000000000000000..cd0c938c3ba69ca8922ca685808bfe37545b7ce2 --- /dev/null +++ b/assets/images/charts/fan.svg @@ -0,0 +1,21 @@ + + + + Group 6 Copy 39 + Created with Sketch. + + + + + + + + + + + + + + + + diff --git a/assets/images/charts/flow-map.svg b/assets/images/charts/flow-map.svg new file mode 100644 index 0000000000000000000000000000000000000000..238505d2a5bbe38eb99a00ac3f30fcf3ee9c48d9 --- /dev/null +++ b/assets/images/charts/flow-map.svg @@ -0,0 +1,80 @@ + + + + Group 6 Copy 6 + Created with Sketch. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/assets/images/charts/funnel.svg b/assets/images/charts/funnel.svg new file mode 100644 index 0000000000000000000000000000000000000000..4d33728ad70c1c3ebc58b8905f7f4509c30cdf19 --- /dev/null +++ b/assets/images/charts/funnel.svg @@ -0,0 +1,19 @@ + + + + Group 6 Copy 36 + Created with Sketch. + + + + + + + + + + + + + + diff --git a/assets/images/charts/gantt.svg b/assets/images/charts/gantt.svg new file mode 100644 index 0000000000000000000000000000000000000000..543e0ed8553f2949f2a902aff0a5e3b947431c47 --- /dev/null +++ b/assets/images/charts/gantt.svg @@ -0,0 +1,20 @@ + + + + Group 6 Copy 21 + Created with Sketch. + + + + + + + + + + + + + + + diff --git a/assets/images/charts/gridplot.svg b/assets/images/charts/gridplot.svg new file mode 100644 index 0000000000000000000000000000000000000000..276deb3a1583b660a41cecd0d20336d9a14727c6 --- /dev/null +++ b/assets/images/charts/gridplot.svg @@ -0,0 +1 @@ + diff --git a/assets/images/charts/heatmap-matrix.svg b/assets/images/charts/heatmap-matrix.svg new file mode 100644 index 0000000000000000000000000000000000000000..84c13f73a8986b75f70019193a99c641b4acc317 --- /dev/null +++ b/assets/images/charts/heatmap-matrix.svg @@ -0,0 +1,179 @@ + + + + Group 6 Copy 40 + Created with Sketch. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + v1 + + + 0.1 + + + 0.5 + + + v1 + + + v2 + + + v3 + + + v4 + + + v5 + + + v2 + + + v3 + + + v4 + + + v5 + + + 1.0 + + + + + + diff --git a/assets/images/charts/heatmap.svg b/assets/images/charts/heatmap.svg new file mode 100644 index 0000000000000000000000000000000000000000..fb40db53834a703ef28d2f7f5e2bf2e04c1a1ed5 --- /dev/null +++ b/assets/images/charts/heatmap.svg @@ -0,0 +1,27 @@ + + + + Group 6 Copy 5 + Created with Sketch. + + + + + + + + + + + + + + + + + + + + + + diff --git a/assets/images/charts/histogram.svg b/assets/images/charts/histogram.svg new file mode 100644 index 0000000000000000000000000000000000000000..b61f86c45b090e81d4d4196851acc30ff32dfc2b --- /dev/null +++ b/assets/images/charts/histogram.svg @@ -0,0 +1,25 @@ + + + + Group 6 Copy 12 + Created with Sketch. + + + + + + + + + + + + + + + + + + + + diff --git a/assets/images/charts/line.svg b/assets/images/charts/line.svg new file mode 100644 index 0000000000000000000000000000000000000000..e6a042d249f9aa11a1e7c7dc5a544864092b8252 --- /dev/null +++ b/assets/images/charts/line.svg @@ -0,0 +1,17 @@ + + + + Group 6 Copy 19 + Created with Sketch. + + + + + + + + + + + + diff --git a/assets/images/charts/lollipop.svg b/assets/images/charts/lollipop.svg new file mode 100644 index 0000000000000000000000000000000000000000..f04bc8ed7ec991246cd691da7cc8b4a3ee407ec1 --- /dev/null +++ b/assets/images/charts/lollipop.svg @@ -0,0 +1,26 @@ + + + + Group 6 Copy 8 + Created with Sketch. + + + + + + + + + + + + + + + + + + + + + diff --git a/assets/images/charts/marimekko.svg b/assets/images/charts/marimekko.svg new file mode 100644 index 0000000000000000000000000000000000000000..38fb0de7796da695242ab565fdde7936bf5f78c5 --- /dev/null +++ b/assets/images/charts/marimekko.svg @@ -0,0 +1,27 @@ + + + + Group 6 Copy 26 + Created with Sketch. + + + + + + + + + + + + + + + + + + + + + + diff --git a/assets/images/charts/network.svg b/assets/images/charts/network.svg new file mode 100644 index 0000000000000000000000000000000000000000..604485ef08c4bd704035ae6f09d6fbc720eb987e --- /dev/null +++ b/assets/images/charts/network.svg @@ -0,0 +1,44 @@ + + + + Group 6 Copy 32 + Created with Sketch. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/assets/images/charts/ordered-bar.svg b/assets/images/charts/ordered-bar.svg new file mode 100644 index 0000000000000000000000000000000000000000..342907fe412aa8264e4e6ee636f99dec8f1ef678 --- /dev/null +++ b/assets/images/charts/ordered-bar.svg @@ -0,0 +1,20 @@ + + + + Group 6 Copy 10 + Created with Sketch. + + + + + + + + + + + + + + + diff --git a/assets/images/charts/ordered-bubble.svg b/assets/images/charts/ordered-bubble.svg new file mode 100644 index 0000000000000000000000000000000000000000..bef4e69296b10faae39c3a6a5450d17074b9c15e --- /dev/null +++ b/assets/images/charts/ordered-bubble.svg @@ -0,0 +1,20 @@ + + + + Group 6 Copy 7 + Created with Sketch. + + + + + + + + + + + + + + + diff --git a/assets/images/charts/ordered-column.svg b/assets/images/charts/ordered-column.svg new file mode 100644 index 0000000000000000000000000000000000000000..fca0509d637f095c2d881f6496610c55bb6abd6a --- /dev/null +++ b/assets/images/charts/ordered-column.svg @@ -0,0 +1,20 @@ + + + + Group 6 Copy 11 + Created with Sketch. + + + + + + + + + + + + + + + diff --git a/assets/images/charts/paired-bar.svg b/assets/images/charts/paired-bar.svg new file mode 100644 index 0000000000000000000000000000000000000000..2f75406e7fb3d6815083a92a05600922b0069ab1 --- /dev/null +++ b/assets/images/charts/paired-bar.svg @@ -0,0 +1 @@ + diff --git a/assets/images/charts/paired-column.svg b/assets/images/charts/paired-column.svg new file mode 100644 index 0000000000000000000000000000000000000000..925d74f7e00ed697586234bb43e88263ec594206 --- /dev/null +++ b/assets/images/charts/paired-column.svg @@ -0,0 +1 @@ + diff --git a/assets/images/charts/parallel-coordinates.svg b/assets/images/charts/parallel-coordinates.svg new file mode 100644 index 0000000000000000000000000000000000000000..16c16d68c11e4ca810be7dfec3cfab177ec80477 --- /dev/null +++ b/assets/images/charts/parallel-coordinates.svg @@ -0,0 +1,26 @@ + + + + Group 6 Copy 43 + Created with Sketch. + + + + + + + + + + + + + + + + + + + + + diff --git a/assets/images/charts/pictogram.svg b/assets/images/charts/pictogram.svg new file mode 100644 index 0000000000000000000000000000000000000000..c7a787b0a5ee2e44631a19da9d1e98ee2c24f932 --- /dev/null +++ b/assets/images/charts/pictogram.svg @@ -0,0 +1,63 @@ + + + + Group 6 Copy 42 + Created with Sketch. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/assets/images/charts/pie.svg b/assets/images/charts/pie.svg new file mode 100644 index 0000000000000000000000000000000000000000..c32e7c8b10e7aa6afae2b73e8c6ac298afa82a55 --- /dev/null +++ b/assets/images/charts/pie.svg @@ -0,0 +1,29 @@ + + + + Group 6 Copy 23 + Created with Sketch. + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/assets/images/charts/radar.svg b/assets/images/charts/radar.svg new file mode 100644 index 0000000000000000000000000000000000000000..5906b0740b38318e67eb4deb5e485232c24c0ee3 --- /dev/null +++ b/assets/images/charts/radar.svg @@ -0,0 +1,33 @@ + + + + Group 6 Copy 38 + Created with Sketch. + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/assets/images/charts/radial.svg b/assets/images/charts/radial.svg new file mode 100644 index 0000000000000000000000000000000000000000..0b8f96a90767250d7acab6669b769c18021e8fa8 --- /dev/null +++ b/assets/images/charts/radial.svg @@ -0,0 +1,18 @@ + + + + Group 6 Copy 37 + Created with Sketch. + + + + + + + + + + + + + diff --git a/assets/images/charts/sankey.svg b/assets/images/charts/sankey.svg new file mode 100644 index 0000000000000000000000000000000000000000..9e9ad9d6837437b13125754c0f171ec0b8e90fa4 --- /dev/null +++ b/assets/images/charts/sankey.svg @@ -0,0 +1,20 @@ + + + + Group 6 Copy 30 + Created with Sketch. + + + + + + + + + + + + + + + diff --git a/assets/images/charts/scatter-matrix.svg b/assets/images/charts/scatter-matrix.svg new file mode 100644 index 0000000000000000000000000000000000000000..add1a0995ea6a7b32e8999c994b365efe5809677 --- /dev/null +++ b/assets/images/charts/scatter-matrix.svg @@ -0,0 +1,176 @@ + + + + Group 6 Copy 41 + Created with Sketch. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + v1 + + + v2 + + + v3 + + + + + + diff --git a/assets/images/charts/scatter.svg b/assets/images/charts/scatter.svg new file mode 100644 index 0000000000000000000000000000000000000000..3993f505f707a84c91f91fbeac0310a693dc7e3e --- /dev/null +++ b/assets/images/charts/scatter.svg @@ -0,0 +1,56 @@ + + + + Group 6 Copy 2 + Created with Sketch. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/assets/images/charts/slope.svg b/assets/images/charts/slope.svg new file mode 100644 index 0000000000000000000000000000000000000000..01b770af55c148ac2a2dba1a3d9f7788eb50f8b3 --- /dev/null +++ b/assets/images/charts/slope.svg @@ -0,0 +1,32 @@ + + + + Group 6 Copy 9 + Created with Sketch. + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/assets/images/charts/sparkline.svg b/assets/images/charts/sparkline.svg new file mode 100644 index 0000000000000000000000000000000000000000..ed269cd5380c4b8e9c09d243034cadbdc7ddf89f --- /dev/null +++ b/assets/images/charts/sparkline.svg @@ -0,0 +1,33 @@ + + + + Group 6 Copy 35 + Created with Sketch. + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/assets/images/charts/stacked-bar.svg b/assets/images/charts/stacked-bar.svg new file mode 100644 index 0000000000000000000000000000000000000000..432a2e4a6bd1c3d6cd04f17dd8a1d12d72848859 --- /dev/null +++ b/assets/images/charts/stacked-bar.svg @@ -0,0 +1,27 @@ + + + + Group 6 Copy 25 + Created with Sketch. + + + + + + + + + + + + + + + + + + + + + + diff --git a/assets/images/charts/stacked-column.svg b/assets/images/charts/stacked-column.svg new file mode 100644 index 0000000000000000000000000000000000000000..7afbe494c9fbfab991898a8e5b35f77729e19b28 --- /dev/null +++ b/assets/images/charts/stacked-column.svg @@ -0,0 +1,27 @@ + + + + Group 6 Copy 25 + Created with Sketch. + + + + + + + + + + + + + + + + + + + + + + diff --git a/assets/images/charts/stepped-line.svg b/assets/images/charts/stepped-line.svg new file mode 100644 index 0000000000000000000000000000000000000000..5bb9c361e40c4da3e413035bd39f2f3e00269651 --- /dev/null +++ b/assets/images/charts/stepped-line.svg @@ -0,0 +1,19 @@ + + + + Group 6 Copy 18 + Created with Sketch. + + + + + + + + + + + + + + diff --git a/assets/images/charts/surplus-deficit-filled-line.svg b/assets/images/charts/surplus-deficit-filled-line.svg new file mode 100644 index 0000000000000000000000000000000000000000..5f6baaf234ca44f999a6179c4d51a6582772909d --- /dev/null +++ b/assets/images/charts/surplus-deficit-filled-line.svg @@ -0,0 +1,29 @@ + + + + Group 6 Copy 6 + Created with Sketch. + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/assets/images/charts/treemap.svg b/assets/images/charts/treemap.svg new file mode 100644 index 0000000000000000000000000000000000000000..a09929e943478c2ed60f06df24bacfaafb2ae218 --- /dev/null +++ b/assets/images/charts/treemap.svg @@ -0,0 +1,24 @@ + + + + Group 6 Copy 27 + Created with Sketch. + + + + + + + + + + + + + + + + + + + diff --git a/assets/images/charts/venn.svg b/assets/images/charts/venn.svg new file mode 100644 index 0000000000000000000000000000000000000000..753928cf7cd1491e3e16c7d4bd2845583111c0b1 --- /dev/null +++ b/assets/images/charts/venn.svg @@ -0,0 +1,18 @@ + + + + Group 6 Copy 28 + Created with Sketch. + + + + + + + + + + + + + diff --git a/assets/images/charts/violin.svg b/assets/images/charts/violin.svg new file mode 100644 index 0000000000000000000000000000000000000000..537b0ecfef641bec3129cc243f0283f9fff6f0d2 --- /dev/null +++ b/assets/images/charts/violin.svg @@ -0,0 +1,131 @@ + + + + Group 6 Copy 16 + Created with Sketch. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/assets/images/charts/waterfall.svg b/assets/images/charts/waterfall.svg new file mode 100644 index 0000000000000000000000000000000000000000..3b90721897daf8203fd643ba57351e7a14cb39ab --- /dev/null +++ b/assets/images/charts/waterfall.svg @@ -0,0 +1,20 @@ + + + + Group 6 Copy 31 + Created with Sketch. + + + + + + + + + + + + + + + diff --git a/assets/logo.svg b/assets/logo.svg new file mode 100644 index 0000000000000000000000000000000000000000..0904b87dea0bc5507a56061c9c6aec6c1c89446b --- /dev/null +++ b/assets/logo.svg @@ -0,0 +1,3 @@ + + + diff --git a/chart_groups.py b/chart_groups.py new file mode 100644 index 0000000000000000000000000000000000000000..1c78f98580bda1716991a545001642e603a13ef4 --- /dev/null +++ b/chart_groups.py @@ -0,0 +1,261 @@ +"""Defines chart groups.""" + +import itertools +from dataclasses import dataclass +from typing import List + +import pages.correlation +import pages.deviation +import pages.distribution +import pages.flow +import pages.magnitude +import pages.part_to_whole +import pages.ranking +import pages.spatial +import pages.time +import vizro.models as vm + + +class IncompletePage: + """Fake vm.Page-like class. + + This has the properties required to make it function sufficiently like a page when generating the navigation cards. + Only the title is configurable; path is fixed to "". + """ + + def __init__(self, title): # noqa: D107 + self.title = title + + @property + def path(self): # noqa: D102 + return "" + + +@dataclass +class ChartGroup: + """Represents a group of charts like "Deviation".""" + + name: str + pages: List[vm.Page] + incomplete_pages: List[IncompletePage] + intro_text: str + icon: str = "" # ALL_CHART_GROUP is the only one that doesn't require an icon. + + +deviation_intro_text = """ +Deviation enables you to draw attention to variations (+/-) from a fixed reference point. +Often this reference point is zero, but you might also show a target or a long term average. +You can also use deviation to express a positive, neutral or negative sentiment. +""" +deviation_chart_group = ChartGroup( + name="Deviation", + pages=pages.deviation.pages, + incomplete_pages=[ + IncompletePage(title="Diverging bar"), + IncompletePage("Diverging stacked bar"), + IncompletePage(title="Surplus deficit filled line"), + ], + icon="Contrast Square", + intro_text=deviation_intro_text, +) + + +correlation_intro_text = """ +Correlation helps you show the relationship between two or more variables. It is important that you +make it clear to your audience whether or not the relationship is causal, i.e., whether one causes the +other. +""" +correlation_chart_group = ChartGroup( + name="Correlation", + pages=pages.correlation.pages, + incomplete_pages=[ + IncompletePage("Heatmap matrix"), + ], + icon="Bubble Chart", + intro_text=correlation_intro_text, +) + + +ranking_intro_text = """ +Ranking enables you to present items in an ordered list. Use this when you want to highlight the +position of an item rather than its absolute or relative value. You might want to emphasize the most +interesting points with highlighting or labels to ensure the reader understands what matters most. +""" +ranking_chart_group = ChartGroup( + name="Ranking", + pages=pages.ranking.pages, + incomplete_pages=[ + IncompletePage("Ordered bubble"), + IncompletePage("Slope"), + IncompletePage("Lollipop"), + IncompletePage("Bump"), + ], + icon="Stacked Bar Chart", + intro_text=ranking_intro_text, +) + + +distribution_intro_text = """ +Distribution helps you to present all the possible values (or intervals) of your data and how often they +occur. You can organize the data to show the number or percentage of items in a specified group, what shape +the group takes, where the center lies, and how much variability there is in the data. This shape +(or _skew_) of a distribution can be a powerful way for you to highlight either the existence or lack of +uniformity or equality in the data. +""" +distribution_chart_group = ChartGroup( + name="Distribution", + pages=pages.distribution.pages, + incomplete_pages=[ + IncompletePage("Dot plot"), + IncompletePage("Barcode"), + IncompletePage("Cumulative curve"), + IncompletePage("Beeswarm"), + ], + icon="Waterfall Chart", + intro_text=distribution_intro_text, +) + +magnitude_intro_text = """ +Magnitude allows you to emphasize size comparisons of **counted** items in your data set. You can show +relative comparisons (whether something is larger or smaller) or absolute differences (where the nuances +are most interesting). Typically, you will use magnitude for actual numbers versus calculated rates or +percentages. +""" +magnitude_chart_group = ChartGroup( + name="Magnitude", + pages=pages.magnitude.pages, + incomplete_pages=[ + IncompletePage("Marimekko"), + IncompletePage("Lollipop"), + IncompletePage("Radar"), + IncompletePage("Pictogram"), + IncompletePage("Bullet"), + IncompletePage("Radial"), + ], + icon="Bar Chart", + intro_text=magnitude_intro_text, +) + +time_intro_text = """ +Time helps you draw attention to important trends emerging over a specified period. The time period you +select could be as short as seconds or as long as centuries. What matters most is selecting the correct +period of time to best show your audience the message they need to take away. +""" +time_chart_group = ChartGroup( + name="Time", + pages=pages.time.pages, + incomplete_pages=[ + IncompletePage("Gantt"), + IncompletePage("Slope"), + IncompletePage("Fan"), + IncompletePage("Bubble timeline"), + IncompletePage("Sparkline"), + ], + icon="Timeline", + intro_text=time_intro_text, +) + + +part_to_whole_intro_text = """ +Part-to-whole helps you show how one whole item breaks down into its component parts. If you consider the +size of the parts to be most important, a magnitude chart may be more appropriate. +""" +part_to_whole_chart_group = ChartGroup( + name="Part-to-whole", + pages=pages.part_to_whole.pages, + incomplete_pages=[ + IncompletePage("Marimekko"), + IncompletePage("Arc"), + IncompletePage("Gridplot"), + IncompletePage("Venn"), + IncompletePage("Waterfall"), + ], + icon="Donut Small", + intro_text=part_to_whole_intro_text, +) + +flow_intro_text = """ +With flow charts, you can highlight the quantity or the intensity of the movement between more than one +state or condition. The flow might be steps in a logical sequence or movement between different geographical +locations. +""" +flow_chart_group = ChartGroup( + name="Flow", + pages=pages.flow.pages, + incomplete_pages=[ + IncompletePage("Waterfall"), + IncompletePage("Chord"), + IncompletePage("Network"), + ], + icon="Air", + intro_text=flow_intro_text, +) + +spatial_intro_text = """ +Spatial charts allow you to demonstrate precise locations or geographical patterns in your data. +""" +spatial_chart_group = ChartGroup( + name="Spatial", + pages=pages.spatial.pages, + incomplete_pages=[ + IncompletePage("Dot map"), + IncompletePage("Flow map"), + IncompletePage("Bubble map"), + ], + icon="Map", + intro_text=spatial_intro_text, +) + + +CHART_GROUPS = [ + deviation_chart_group, + correlation_chart_group, + ranking_chart_group, + distribution_chart_group, + magnitude_chart_group, + time_chart_group, + part_to_whole_chart_group, + flow_chart_group, + spatial_chart_group, +] + +all_intro_text = """ +This dashboard shows a gallery of charts. It includes guidance on when to use each chart type and sample Python code +to create them using [Plotly](https://plotly.com/python/) and [Vizro](https://github.com/mckinsey/vizro). + +  + +  + +  + +**Created by:** +- [Huong Li Nguyen](https://github.com/huong-li-nguyen) and [Antony Milne](https://github.com/antonymilne) + +- Images created by QuantumBlack + +**Inspired by:** +- [The FT Visual Vocabulary](https://github.com/Financial-Times/chart-doctor/blob/main/visual-vocabulary/README.md): +Alan Smith, Chris Campbell, Ian Bott, Liz Faunce, Graham Parrish, Billy Ehrenberg, Paul McCallum,Martin Stabe. + +- [The Graphic Continuum](https://www.informationisbeautifulawards.com/showcase/611-the-graphic-continuum): +Jon Swabish and Severino Ribecca + +  + +**Credits and sources:** + +- [Plotly](https://plotly.com/python/plotly-express/) +- [Guide to data chart mastery](https://www.atlassian.com/data/charts) + +""" + + +# This contains all pages used across all chart groups, without de-duplicating. De-duplication is done where required +# by remove_duplicates. +ALL_CHART_GROUP = ChartGroup( + name="All", + pages=list(itertools.chain(*(chart_group.pages for chart_group in CHART_GROUPS))), + incomplete_pages=list(itertools.chain(*(chart_group.incomplete_pages for chart_group in CHART_GROUPS))), + intro_text=all_intro_text, +) diff --git a/custom_charts.py b/custom_charts.py new file mode 100644 index 0000000000000000000000000000000000000000..976bfea8e312f9ef589abc584a14e07256d30369 --- /dev/null +++ b/custom_charts.py @@ -0,0 +1,144 @@ +"""Contains custom charts used inside the dashboard.""" + +from typing import List + +import pandas as pd +import vizro.plotly.express as px +from plotly import graph_objects as go +from plotly.subplots import make_subplots +from vizro.models.types import capture + + +# TODO: consider how this should be represented in the code example files. Since the code is copy and pasted +# it can get out of sync. But probably we don't want the docstrings in the short code snippet. +# Ultimately these charts will probably move to vizro.charts anyway. +@capture("graph") +def butterfly(data_frame: pd.DataFrame, x1: str, x2: str, y: str) -> go.Figure: + """Creates a custom butterfly chart using Plotly's go.Figure. + + A butterfly chart is a type of bar chart where two sets of bars are displayed back-to-back, often used to compare + two sets of data. + + Args: + data_frame (pd.DataFrame): The data source for the chart. + x1 (str): The name of the column in the data frame for the first set of bars (negative values). + x2 (str): The name of the column in the data frame for the second set of bars (positive values). + y (str): The name of the column in the data frame for the y-axis (categories). + + Returns: + go.Figure: A Plotly Figure object representing the butterfly chart. + + """ + fig = go.Figure() + fig.add_trace( + go.Bar( + x=-data_frame[x1], + y=data_frame[y], + orientation="h", + name=x1, + ) + ) + fig.add_trace( + go.Bar( + x=data_frame[x2], + y=data_frame[y], + orientation="h", + name=x2, + ) + ) + fig.update_layout(barmode="relative") + return fig + + +@capture("graph") +def sankey(data_frame: pd.DataFrame, source: str, target: str, value: str, labels: List[str]) -> go.Figure: + """Creates a custom sankey chart using Plotly's `go.Sankey`. + + A Sankey chart is a type of flow diagram where the width of the arrows is proportional to the flow rate. + It is used to visualize the flow of resources or data between different stages or categories. + + Args: + data_frame (pd.DataFrame): The data source for the chart. + source (str): The name of the column in the data frame for the source nodes. + target (str): The name of the column in the data frame for the target nodes. + value (str): The name of the column in the data frame for the values representing the flow between nodes. + labels (List[str]): A list of labels for the nodes. + + Returns: + go.Figure: A Plotly Figure object representing the Sankey chart. + + For detailed information on additional parameters and customization, refer to the Plotly documentation: + https://plotly.com/python/reference/sankey/ + + """ + fig = go.Figure( + data=[ + go.Sankey( + node={ + "pad": 16, + "thickness": 16, + "label": labels, + }, + link={ + "source": data_frame[source], + "target": data_frame[target], + "value": data_frame[value], + "label": labels, + "color": "rgba(205, 209, 228, 0.4)", + }, + ) + ] + ) + fig.update_layout(barmode="relative") + return fig + + +@capture("graph") +def column_and_line(data_frame: pd.DataFrame, x: str, y_column: str, y_line: str) -> go.Figure: + """Creates a combined column and line chart using Plotly. + + This function generates a chart with a bar graph for one variable (y-axis 1) and a line graph for another variable + (y-axis 2), sharing the same x-axis. The y-axes for the bar and line graphs are synchronized and overlaid. + + Args: + data_frame (pd.DataFrame): The data source for the chart. + x (str): The column name to be used for the x-axis. + y_column (str): The column name to be used for the y-axis 1, representing the column chart. + y_line (str): The column name to be used for the y-axis 2, representing the line chart. + + Returns: + go.Figure: : A Plotly Figure object representing the combined column and line chart. + + """ + fig = make_subplots(specs=[[{"secondary_y": True}]]) + + fig.add_trace( + go.Bar(x=data_frame[x], y=data_frame[y_column], name=y_column), + secondary_y=False, + ) + + fig.add_trace( + go.Scatter(x=data_frame[x], y=data_frame[y_line], name=y_line), + secondary_y=True, + ) + + fig.update_layout( + xaxis={"type": "category", "title": x}, + yaxis={"tickmode": "sync", "title": y_column}, + yaxis2={"tickmode": "sync", "overlaying": "y", "title": y_line}, + ) + + return fig + + +@capture("graph") +def categorical_column(data_frame: pd.DataFrame, x: str, y: str): + """Creates a column chart where the x-axis values are converted to category type.""" + fig = px.bar( + data_frame, + x=x, + y=y, + ) + # So ticks are aligned with bars when xaxes values are numbers (e.g. years) + fig.update_xaxes(type="category") + return fig diff --git a/custom_components.py b/custom_components.py new file mode 100644 index 0000000000000000000000000000000000000000..26ec8e0e4a9d1a7a55e2378bae2c500e407cfe7a --- /dev/null +++ b/custom_components.py @@ -0,0 +1,71 @@ +"""Contains custom components used inside the dashboard.""" + +from typing import Literal + +import dash_bootstrap_components as dbc +import vizro.models as vm +from dash import dcc, html + +try: + from pydantic.v1 import Field +except ImportError: # pragma: no cov + from pydantic import Field + + +class CodeClipboard(vm.VizroBaseModel): + """Code snippet with a copy to clipboard button.""" + + type: Literal["code_clipboard"] = "code_clipboard" + code: str + language: str = "" + + def build(self): + """Returns the code clipboard component inside an accordion.""" + markdown_code = "\n".join([f"```{self.language}", self.code, "```"]) + return dbc.Accordion( + [ + dbc.AccordionItem( + html.Div( + [ + dcc.Markdown(markdown_code, id=self.id), + dcc.Clipboard(target_id=self.id, className="code-clipboard"), + ], + className="code-clipboard-container", + ), + title="SHOW CODE", + ) + ], + start_collapsed=False, + ) + + +class Markdown(vm.VizroBaseModel): + """Markdown component.""" + + type: Literal["markdown"] = "markdown" + text: str = Field( + ..., description="Markdown string to create card title/text that should adhere to the CommonMark Spec." + ) + classname: str = "" + + def build(self): + """Returns a markdown component with an optional classname.""" + return dcc.Markdown(id=self.id, children=self.text, dangerously_allow_html=False, className=self.classname) + + +class FlexContainer(vm.Container): + """Custom flex `Container`.""" + + type: Literal["flex_container"] = "flex_container" + title: str = None # Title exists in vm.Container but we don't want to use it here. + + def build(self): + """Returns a flex container.""" + return html.Div( + id=self.id, children=[component.build() for component in self.components], className="flex-container" + ) + + +vm.Container.add_type("components", FlexContainer) +vm.Container.add_type("components", Markdown) +vm.Page.add_type("components", CodeClipboard) diff --git a/pages/__init__.py b/pages/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..cc121d915ec5e666e2ce087b9740ee41c5f54664 --- /dev/null +++ b/pages/__init__.py @@ -0,0 +1,5 @@ +# TODO: eventually deduplicate page generation into a function rather than copying and pasting across files? +# TODO: think about the best way to do code examples, e.g. +# - do we want full dashboard example or plot-only example? +# - or both? Could be done using a toggle switch or multiple tabs. +# - a link to py.cafe showing the dashboard code? diff --git a/pages/_factories.py b/pages/_factories.py new file mode 100644 index 0000000000000000000000000000000000000000..f4b6b4ffdcab18369f915ad4458ebd523fe9f368 --- /dev/null +++ b/pages/_factories.py @@ -0,0 +1,112 @@ +"""Contains reusable page functions to create identical content with a different `id`. + +Note: Since each page can only belong to one navigation group, we need a new page with a unique ID for +each chart type used in different groups. +""" + +import vizro.models as vm +import vizro.plotly.express as px +from custom_charts import butterfly, column_and_line + +from pages._pages_utils import PAGE_GRID, ages, gapminder, make_code_clipboard_from_py_file + + +def butterfly_factory(group: str): + """Reusable function to create the page content for the butterfly chart with a unique ID.""" + return vm.Page( + id=f"{group}-butterfly", + path=f"{group}/butterfly", + title="Butterfly", + layout=vm.Layout(grid=PAGE_GRID), + components=[ + vm.Card( + text=""" + + #### What is a butterfly chart? + + A butterfly chart (also called a tornado chart) is a bar chart for displaying two sets of data series + side by side. + +   + + #### When should I use it? + + Use a butterfly chart when you wish to emphasize the comparison between two data sets sharing the same + parameters. Sharing this chart with your audience will help them see at a glance how two groups differ + within the same parameters. You can also **stack** two bars on each side if you wish to divide your + categories. + """ + ), + vm.Graph(figure=butterfly(ages, x1="Male", x2="Female", y="Age")), + make_code_clipboard_from_py_file("butterfly.py"), + ], + ) + + +def connected_scatter_factory(group: str): + """Reusable function to create the page content for the column chart with a unique ID.""" + return vm.Page( + id=f"{group}-connected-scatter", + path=f"{group}/connected-scatter", + title="Connected scatter", + layout=vm.Layout(grid=PAGE_GRID), + components=[ + vm.Card( + text=""" + #### What is a connected scatter chart? + + A connected scatter chart visualizes two variables (x and y) using dots, with lines connecting the dots + in the order of the data points. One variable is plotted along the x-axis and the other along the + y-axis, showing both the relationship and a sequence of the data. + +   + + #### When should I use it? + + Use connected scatter charts to show the relationship between two variables and the sequence of data + points. They are ideal for paired numerical data, helping to reveal trends and patterns over time or in + a specific order. Remember, correlation is not causation, so ensure your audience understands this to + avoid misinterpretation. + """ + ), + vm.Graph(figure=px.line(gapminder.query("country == 'Australia'"), x="year", y="lifeExp", markers=True)), + make_code_clipboard_from_py_file("connected_scatter.py"), + ], + ) + + +def column_and_line_factory(group: str): + """Reusable function to create the page content for the column+line chart with a unique ID.""" + return vm.Page( + id=f"{group}-column-and-line", + path=f"{group}/column-and-line", + title="Column and line", + layout=vm.Layout(grid=PAGE_GRID), + components=[ + vm.Card( + text=""" + #### What is a column and line chart? + + A combined column and line chart helps you demonstrate the relationship between an amount + (displayed in columns) and a trend or rate (displayed as a line running across the columns). + +   + + #### When should I use it? + + Use this type of chart when you wish to compare quantities of one item with changes in another item. + It's ideal for showing patterns over time (e.g., monthly sales and growth rates) but can also be used + for other types of data comparisons. + """ + ), + vm.Graph( + figure=column_and_line( + gapminder.query("country == 'Vietnam'"), + y_column="gdpPercap", + y_line="lifeExp", + x="year", + ) + ), + make_code_clipboard_from_py_file("column_and_line.py"), + ], + ) diff --git a/pages/_pages_utils.py b/pages/_pages_utils.py new file mode 100644 index 0000000000000000000000000000000000000000..1121c657862b3a5b3b82eb2b0ccaebd0e9f1e9c5 --- /dev/null +++ b/pages/_pages_utils.py @@ -0,0 +1,57 @@ +"""Contains reusable data sets and constants.""" + +import logging +from pathlib import Path + +import black +import pandas as pd +import vizro.plotly.express as px +from custom_components import CodeClipboard + +# To disable logging info messages caused by black.format_str: https://github.com/psf/black/issues/2058 +logging.getLogger("blib2to3").setLevel(logging.ERROR) + + +def make_code_clipboard_from_py_file(filepath: str): + # Black doesn't yet have a Python API, so format_str might not work at some point in the future. + # https://black.readthedocs.io/en/stable/faq.html#does-black-have-an-api + filepath = Path(__file__).parents[1] / "pages/examples" / filepath + return CodeClipboard( + code=black.format_str(filepath.read_text(encoding="utf-8"), mode=black.Mode(line_length=80)), + language="python", + ) + + +PAGE_GRID = [[0, 0, 0, 0, 0, 0, 0]] * 2 + [[1, 1, 1, 1, 2, 2, 2]] * 5 + +# DATA -------------------------------------------------------------- +gapminder = px.data.gapminder() +iris = px.data.iris() +stocks = px.data.stocks() +tips = px.data.tips() + +ages = pd.DataFrame( + { + "Age": ["0-19", "20-29", "30-39", "40-49", "50-59", ">=60"], + "Male": [800, 2000, 4200, 5000, 2100, 800], + "Female": [1000, 3000, 3500, 3800, 3600, 700], + } +) +sankey_data = pd.DataFrame( + { + "Origin": [0, 1, 0, 2, 3, 3], + "Destination": [2, 3, 3, 4, 4, 5], + "Value": [8, 4, 2, 8, 4, 2], + } +) + +funnel_data = pd.DataFrame( + {"Stage": ["Leads", "Sales calls", "Follow-up", "Conversion", "Sales"], "Value": [10, 7, 4, 2, 1]} +) + +stepped_line_data = pd.DataFrame( + { + "year": [1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003], + "rate": [0.10, 0.12, 0.15, 0.13, 0.14, 0.13, 0.14, 0.16, 0.15], + } +) diff --git a/pages/correlation.py b/pages/correlation.py new file mode 100644 index 0000000000000000000000000000000000000000..199042261b69f346cf40da233c4186336d43e5c0 --- /dev/null +++ b/pages/correlation.py @@ -0,0 +1,100 @@ +"""Correlation charts.""" + +import vizro.models as vm +import vizro.plotly.express as px + +from pages._factories import column_and_line_factory, connected_scatter_factory +from pages._pages_utils import PAGE_GRID, gapminder, iris, make_code_clipboard_from_py_file + +scatter = vm.Page( + title="Scatter", + path="correlation/scatter", + layout=vm.Layout(grid=PAGE_GRID), + components=[ + vm.Card( + text=""" + + #### What is a scatter chart? + + A scatter plot is a two-dimensional data visualization using dots to represent the values obtained for two + different variables - one plotted along the x-axis and the other plotted along the y-axis. + +   + + #### When should I use it? + + Use scatter plots when you want to show the relationship between two variables. Scatter plots are sometimes + called _Correlation plots_ because they show how two variables are correlated. Scatter plots are ideal when + you have paired numerical data and you want to see if one variable impacts the other. However, do remember + that correlation is not causation. Make sure your audience does not draw the wrong conclusions. + """ + ), + vm.Graph(figure=px.scatter(iris, x="sepal_width", y="sepal_length", color="species")), + make_code_clipboard_from_py_file("scatter.py"), + ], +) + +connected_scatter = connected_scatter_factory("correlation") + +scatter_matrix = vm.Page( + title="Scatter matrix", + path="correlation/scatter-matrix", + layout=vm.Layout(grid=PAGE_GRID), + components=[ + vm.Card( + text=""" + + #### What is a scatter matrix? + + A scatter matrix, also known as a SPLOM chart, is a multi-dimensional data visualization that displays + scatter plots for every pair of variables in a dataset. Each scatter plot is positioned in a matrix format, + where rows and columns represent different variables. + +   + + #### When should I use it? + + Use a scatter matrix when you want to explore relationships between multiple pairs of variables + simultaneously. They are particularly useful for identifying correlations, patterns, and potential outliers + within a dataset containing multiple numerical variables. Carefully select the most relevant variables to + ensure clarity and readability of the chart. + """ + ), + vm.Graph( + figure=px.scatter_matrix(iris, dimensions=["sepal_length", "sepal_width", "petal_length", "petal_width"]) + ), + make_code_clipboard_from_py_file("scatter_matrix.py"), + ], +) + +bubble = vm.Page( + title="Bubble", + path="correlation/bubble", + layout=vm.Layout(grid=PAGE_GRID), + components=[ + vm.Card( + text=""" + + #### What is a bubble chart? + + A bubble chart is a type of data visualization that displays three dimensions of data. Each point on the + chart is represented by a bubble, where the x-axis and y-axis denote two of the data dimensions, and the + size of the bubble represents the third dimension. + +   + + #### When should I use it? + + Use a bubble chart when you want to explore and compare relationships between three variables + simultaneously. They are particularly useful for identifying patterns, trends, and outliers in + multi-dimensional data. Bubble charts can help you visualize the impact of a third variable, + providing deeper insights than a standard scatter plot. + """ + ), + vm.Graph(figure=px.scatter(gapminder.query("year==2007"), x="gdpPercap", y="lifeExp", size="pop", size_max=60)), + make_code_clipboard_from_py_file("bubble.py"), + ], +) + +column_and_line = column_and_line_factory("correlation") +pages = [scatter, connected_scatter, scatter_matrix, bubble, column_and_line] diff --git a/pages/deviation.py b/pages/deviation.py new file mode 100644 index 0000000000000000000000000000000000000000..93b4f731f6ede284de68d812e0d2bb09fcb79d33 --- /dev/null +++ b/pages/deviation.py @@ -0,0 +1,7 @@ +"""Deviation charts.""" + +from pages._factories import butterfly_factory + +butterfly = butterfly_factory("deviation") + +pages = [butterfly] diff --git a/pages/distribution.py b/pages/distribution.py new file mode 100644 index 0000000000000000000000000000000000000000..91c0116cdb6397cad5b0249aa20d569c3ad956b4 --- /dev/null +++ b/pages/distribution.py @@ -0,0 +1,110 @@ +"""Distribution charts.""" + +import vizro.models as vm +import vizro.plotly.express as px + +from pages._factories import butterfly_factory +from pages._pages_utils import PAGE_GRID, make_code_clipboard_from_py_file, tips + +violin = vm.Page( + title="Violin", + path="distribution/violin", + layout=vm.Layout(grid=PAGE_GRID), + components=[ + vm.Card( + text=""" + + #### What is a violin chart? + + A violin chart is similar to a box plot, but works better for visualizing more complex distributions and + their probability density at different values. + +   + + #### When should I use it? + + Use this chart to go beyond the simple box plot and show the distribution shape of the data, the + inter-quartile range, the confidence intervals and the median. + """ + ), + vm.Graph( + figure=px.violin( + tips, + y="total_bill", + x="day", + color="day", + box=True, + ) + ), + make_code_clipboard_from_py_file("violin.py"), + ], +) + +boxplot = vm.Page( + title="Boxplot", + path="distribution/boxplot", + layout=vm.Layout(grid=PAGE_GRID), + components=[ + vm.Card( + text=""" + + #### What is a boxplot? + + A box plot (also known as whisker plot) provides a visual display of multiple datasets, + indicating the median (center) and the range of the data for each. + +   + + #### When should I use it? + + Choose a box plot when you need to summarize distributions between many groups or datasets. It takes up + less space than many other charts. + + Create boxes to display the median, and the upper and lower quartiles. Add whiskers to highlight + variability outside the upper and lower quartiles. You can add outliers as dots beyond, but in line with + the whiskers. + """ + ), + vm.Graph( + figure=px.box( + tips, + y="total_bill", + x="day", + color="day", + ) + ), + make_code_clipboard_from_py_file("boxplot.py"), + ], +) + +butterfly = butterfly_factory("distribution") + +histogram = vm.Page( + title="Histogram", + path="distribution/histogram", + layout=vm.Layout(grid=PAGE_GRID), + components=[ + vm.Card( + text=""" + #### What is a histogram? + + A histogram organizes numerical data into columns, with the size of each column representing how frequently + values fall within specified ranges. It visualizes data across a continuous interval. + +   + + #### When should I use it? + + A histogram is useful for showing your audience where specific values are concentrated, identifying the + extremes, and spotting any gaps or outliers. It can also help you visualize a rough probability + distribution. Ensure that the gaps between columns are minimal to make the 'shape' of your data + immediately clear. + """ + ), + vm.Graph(figure=px.histogram(tips, x="total_bill")), + make_code_clipboard_from_py_file("histogram.py"), + ], +) + + +pages = [violin, boxplot, butterfly, histogram] diff --git a/pages/examples/__init__.py b/pages/examples/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..b1cac5fb954bf52640acde29fbe7f6ab8a851a66 --- /dev/null +++ b/pages/examples/__init__.py @@ -0,0 +1 @@ +"""Contains code examples inserted into `CodeClipboard`.""" diff --git a/pages/examples/area.py b/pages/examples/area.py new file mode 100644 index 0000000000000000000000000000000000000000..50d0d3ae27ffce1ca1946d655476c7e83414b421 --- /dev/null +++ b/pages/examples/area.py @@ -0,0 +1,13 @@ +import vizro.models as vm +import vizro.plotly.express as px +from vizro import Vizro + +stocks = px.data.stocks() + +page = vm.Page( + title="Area", + components=[vm.Graph(figure=px.area(stocks, x="date", y="GOOG"))], +) + +dashboard = vm.Dashboard(pages=[page]) +Vizro().build(dashboard).run() diff --git a/pages/examples/bar.py b/pages/examples/bar.py new file mode 100644 index 0000000000000000000000000000000000000000..6afdd3040341e4f441255a958700483334bb140c --- /dev/null +++ b/pages/examples/bar.py @@ -0,0 +1,24 @@ +import vizro.models as vm +import vizro.plotly.express as px +from vizro import Vizro + +gapminder = px.data.gapminder() + +page = vm.Page( + title="Bar", + components=[ + vm.Graph( + figure=px.bar( + gapminder.query( + "year == 2007 and country.isin(['United States', 'Pakistan', 'India', 'China', 'Indonesia'])" + ), + x="pop", + y="country", + orientation="h", + ) + ) + ], +) + +dashboard = vm.Dashboard(pages=[page]) +Vizro().build(dashboard).run() diff --git a/pages/examples/boxplot.py b/pages/examples/boxplot.py new file mode 100644 index 0000000000000000000000000000000000000000..ce9c79e836824c35e47f1df11c225f2683d1b89c --- /dev/null +++ b/pages/examples/boxplot.py @@ -0,0 +1,15 @@ +import vizro.models as vm +import vizro.plotly.express as px +from vizro import Vizro + +tips = px.data.tips() + +page = vm.Page( + title="Boxplot", + components=[ + vm.Graph(figure=px.boxplot(tips, y="total_bill", x="day", color="day")), + ], +) + +dashboard = vm.Dashboard(pages=[page]) +Vizro().build(dashboard).run() diff --git a/pages/examples/bubble.py b/pages/examples/bubble.py new file mode 100644 index 0000000000000000000000000000000000000000..6da0aa8166486b037c304cbef70eba2607000149 --- /dev/null +++ b/pages/examples/bubble.py @@ -0,0 +1,15 @@ +import vizro.models as vm +import vizro.plotly.express as px +from vizro import Vizro + +gapminder = px.data.gapminder() + +page = vm.Page( + title="Bubble", + components=[ + vm.Graph(figure=px.scatter(gapminder.query("year==2007"), x="gdpPercap", y="lifeExp", size="pop", size_max=60)) + ], +) + +dashboard = vm.Dashboard(pages=[page]) +Vizro().build(dashboard).run() diff --git a/pages/examples/butterfly.py b/pages/examples/butterfly.py new file mode 100644 index 0000000000000000000000000000000000000000..69c66ed8db82c96d0452d57bf7f2027988fe9912 --- /dev/null +++ b/pages/examples/butterfly.py @@ -0,0 +1,47 @@ +import pandas as pd +import plotly.graph_objects as go +import vizro.models as vm +from vizro import Vizro +from vizro.models.types import capture + +ages = pd.DataFrame( + { + "Age": ["0-19", "20-29", "30-39", "40-49", "50-59", ">=60"], + "Male": [800, 2000, 4200, 5000, 2100, 800], + "Female": [1000, 3000, 3500, 3800, 3600, 700], + } +) + + +@capture("graph") +def butterfly(data_frame: pd.DataFrame, x1: str, x2: str, y: str): + fig = go.Figure() + fig.add_trace( + go.Bar( + x=-data_frame[x1], + y=data_frame[y], + orientation="h", + name=x1, + ) + ) + fig.add_trace( + go.Bar( + x=data_frame[x2], + y=data_frame[y], + orientation="h", + name=x2, + ) + ) + fig.update_layout(barmode="relative") + return fig + + +dashboard = vm.Dashboard( + pages=[ + vm.Page( + title="Butterfly", + components=[vm.Graph(figure=butterfly(ages, x1="Male", x2="Female", y="Age"))], + ) + ] +) +Vizro().build(dashboard).run() diff --git a/pages/examples/choropleth.py b/pages/examples/choropleth.py new file mode 100644 index 0000000000000000000000000000000000000000..95c5cac5f7104e62fe0968db549d770f3ac28923 --- /dev/null +++ b/pages/examples/choropleth.py @@ -0,0 +1,19 @@ +import vizro.models as vm +import vizro.plotly.express as px +from vizro import Vizro + +gapminder = px.data.gapminder() + +page = vm.Page( + title="Choropleth", + components=[ + vm.Graph( + figure=px.choropleth( + gapminder.query("year == 2007"), locations="iso_alpha", color="lifeExp", hover_name="country" + ) + ) + ], +) + +dashboard = vm.Dashboard(pages=[page]) +Vizro().build(dashboard).run() diff --git a/pages/examples/column_and_line.py b/pages/examples/column_and_line.py new file mode 100644 index 0000000000000000000000000000000000000000..85a5fb11688f7303dafaadbd546348d8ca38de75 --- /dev/null +++ b/pages/examples/column_and_line.py @@ -0,0 +1,53 @@ +import pandas as pd +import plotly.graph_objects as go +import vizro.models as vm +import vizro.plotly.express as px +from plotly.subplots import make_subplots +from vizro import Vizro +from vizro.models.types import capture + +gapminder = px.data.gapminder() + + +@capture("graph") +def column_and_line(data_frame: pd.DataFrame, x: str, y_column: str, y_line: str) -> go.Figure: + """Creates a combined column and line chart using Plotly.""" + fig = make_subplots(specs=[[{"secondary_y": True}]]) + + fig.add_trace( + go.Bar(x=data_frame[x], y=data_frame[y_column], name=y_column), + secondary_y=False, + ) + + fig.add_trace( + go.Scatter(x=data_frame[x], y=data_frame[y_line], name=y_line), + secondary_y=True, + ) + + fig.update_layout( + xaxis={"type": "category", "title": x}, + yaxis={"tickmode": "sync", "title": y_column}, + yaxis2={"tickmode": "sync", "overlaying": "y", "title": y_line}, + ) + + return fig + + +dashboard = vm.Dashboard( + pages=[ + vm.Page( + title="Column and line", + components=[ + vm.Graph( + figure=column_and_line( + gapminder.query("country == 'Vietnam'"), + y_column="gdpPercap", + y_line="lifeExp", + x="year", + ) + ) + ], + ) + ] +) +Vizro().build(dashboard).run() diff --git a/pages/examples/connected_scatter.py b/pages/examples/connected_scatter.py new file mode 100644 index 0000000000000000000000000000000000000000..68da341c940f1c9d988364279b5d8d44a1870353 --- /dev/null +++ b/pages/examples/connected_scatter.py @@ -0,0 +1,15 @@ +import vizro.models as vm +import vizro.plotly.express as px +from vizro import Vizro + +gapminder = px.data.gapminder() + +page = vm.Page( + title="Connected scatter", + components=[ + vm.Graph(figure=px.line(gapminder.query("country == 'Australia'"), x="year", y="lifeExp", markers=True)) + ], +) + +dashboard = vm.Dashboard(pages=[page]) +Vizro().build(dashboard).run() diff --git a/pages/examples/donut.py b/pages/examples/donut.py new file mode 100644 index 0000000000000000000000000000000000000000..3005c2fdcd8332d50e8625c125c6650ecf42c41b --- /dev/null +++ b/pages/examples/donut.py @@ -0,0 +1,13 @@ +import vizro.models as vm +import vizro.plotly.express as px +from vizro import Vizro + +tips = px.data.tips() + +page = vm.Page( + title="Donut", + components=[vm.Graph(figure=px.pie(tips, values="tip", names="day", hole=0.4))], +) + +dashboard = vm.Dashboard(pages=[page]) +Vizro().build(dashboard).run() diff --git a/pages/examples/funnel.py b/pages/examples/funnel.py new file mode 100644 index 0000000000000000000000000000000000000000..221430cbf654f51e848437a4239da61058e0bf72 --- /dev/null +++ b/pages/examples/funnel.py @@ -0,0 +1,16 @@ +import pandas as pd +import vizro.models as vm +import vizro.plotly.express as px +from vizro import Vizro + +funnel_data = pd.DataFrame( + {"Stage": ["Leads", "Sales calls", "Follow-up", "Conversion", "Sales"], "Value": [10, 7, 4, 2, 1]} +) + +page = vm.Page( + title="Funnel", + components=[vm.Graph(figure=px.funnel_area(funnel_data, names="Stage", values="Value"))], +) + +dashboard = vm.Dashboard(pages=[page]) +Vizro().build(dashboard).run() diff --git a/pages/examples/heatmap.py b/pages/examples/heatmap.py new file mode 100644 index 0000000000000000000000000000000000000000..350e9e8234ab7c790bf63c1c8aaecb5caa8110b8 --- /dev/null +++ b/pages/examples/heatmap.py @@ -0,0 +1,15 @@ +import vizro.models as vm +import vizro.plotly.express as px +from vizro import Vizro + +tips = px.data.tips() + +page = vm.Page( + title="Heatmap", + components=[ + vm.Graph(figure=px.density_heatmap(tips, x="day", y="size", z="tip", histfunc="avg", text_auto="$.2f")), + ], +) + +dashboard = vm.Dashboard(pages=[page]) +Vizro().build(dashboard).run() diff --git a/pages/examples/histogram.py b/pages/examples/histogram.py new file mode 100644 index 0000000000000000000000000000000000000000..dc5b3856d2db70b2b887dd200fa2c696a7769e6b --- /dev/null +++ b/pages/examples/histogram.py @@ -0,0 +1,13 @@ +import vizro.models as vm +import vizro.plotly.express as px +from vizro import Vizro + +tips = px.data.tips() + +page = vm.Page( + title="Histogram", + components=[vm.Graph(px.histogram(tips, x="total_bill"))], +) + +dashboard = vm.Dashboard(pages=[page]) +Vizro().build(dashboard).run() diff --git a/pages/examples/line.py b/pages/examples/line.py new file mode 100644 index 0000000000000000000000000000000000000000..c442a89949f229fe3383f0ce41bbf11a2198e441 --- /dev/null +++ b/pages/examples/line.py @@ -0,0 +1,13 @@ +import vizro.models as vm +import vizro.plotly.express as px +from vizro import Vizro + +stocks = px.data.stocks() + +page = vm.Page( + title="Line", + components=[vm.Graph(figure=px.line(stocks, x="date", y="GOOG"))], +) + +dashboard = vm.Dashboard(pages=[page]) +Vizro().build(dashboard).run() diff --git a/pages/examples/magnitude_column.py b/pages/examples/magnitude_column.py new file mode 100644 index 0000000000000000000000000000000000000000..04635ca85ac5d491db67586d96bb6a5c6dbbccf6 --- /dev/null +++ b/pages/examples/magnitude_column.py @@ -0,0 +1,23 @@ +import vizro.models as vm +import vizro.plotly.express as px +from vizro import Vizro + +gapminder = px.data.gapminder() + +page = vm.Page( + title="Column", + components=[ + vm.Graph( + figure=px.bar( + gapminder.query( + "year == 2007 and country.isin(['United States', 'Pakistan', 'India', 'China', 'Indonesia'])" + ), + y="pop", + x="country", + ) + ) + ], +) + +dashboard = vm.Dashboard(pages=[page]) +Vizro().build(dashboard).run() diff --git a/pages/examples/ordered_bar.py b/pages/examples/ordered_bar.py new file mode 100644 index 0000000000000000000000000000000000000000..316b0b031a838e2cfc36a6b936dea9ace50763f5 --- /dev/null +++ b/pages/examples/ordered_bar.py @@ -0,0 +1,25 @@ +import vizro.models as vm +import vizro.plotly.express as px +from vizro import Vizro + +gapminder = px.data.gapminder() + + +page = vm.Page( + title="Ordered bar", + components=[ + vm.Graph( + figure=px.bar( + gapminder.query( + "year == 2007 and country.isin(['United States', 'Pakistan', 'India', 'China', 'Indonesia'])" + ).sort_values("pop"), + x="pop", + y="country", + orientation="h", + ) + ) + ], +) + +dashboard = vm.Dashboard(pages=[page]) +Vizro().build(dashboard).run() diff --git a/pages/examples/ordered_column.py b/pages/examples/ordered_column.py new file mode 100644 index 0000000000000000000000000000000000000000..07bb37fe77c3b5b72c520e02fc9025fc3e485a6d --- /dev/null +++ b/pages/examples/ordered_column.py @@ -0,0 +1,24 @@ +import vizro.models as vm +import vizro.plotly.express as px +from vizro import Vizro + +gapminder = px.data.gapminder() + + +page = vm.Page( + title="Ordered column", + components=[ + vm.Graph( + figure=px.bar( + gapminder.query( + "year == 2007 and country.isin(['United States', 'Pakistan', 'India', 'China', 'Indonesia'])" + ).sort_values("pop"), + y="pop", + x="country", + ) + ) + ], +) + +dashboard = vm.Dashboard(pages=[page]) +Vizro().build(dashboard).run() diff --git a/pages/examples/paired_bar.py b/pages/examples/paired_bar.py new file mode 100644 index 0000000000000000000000000000000000000000..0d15e480c0156f981aa4a906a9bb5b124a9e4720 --- /dev/null +++ b/pages/examples/paired_bar.py @@ -0,0 +1,26 @@ +import vizro.models as vm +import vizro.plotly.express as px +from vizro import Vizro + +tips = px.data.tips() + + +page = vm.Page( + title="Paired bar", + components=[ + vm.Graph( + figure=px.histogram( + tips, + y="day", + x="total_bill", + color="sex", + barmode="group", + orientation="h", + category_orders={"day": ["Thur", "Fri", "Sat", "Sun"]}, + ), + ) + ], +) + +dashboard = vm.Dashboard(pages=[page]) +Vizro().build(dashboard).run() diff --git a/pages/examples/paired_column.py b/pages/examples/paired_column.py new file mode 100644 index 0000000000000000000000000000000000000000..b6292c14c20878b070ba848ab743312cd7899e7b --- /dev/null +++ b/pages/examples/paired_column.py @@ -0,0 +1,25 @@ +import vizro.models as vm +import vizro.plotly.express as px +from vizro import Vizro + +tips = px.data.tips() + + +page = vm.Page( + title="Paired column", + components=[ + vm.Graph( + figure=px.histogram( + tips, + x="day", + y="total_bill", + color="sex", + barmode="group", + category_orders={"day": ["Thur", "Fri", "Sat", "Sun"]}, + ), + ) + ], +) + +dashboard = vm.Dashboard(pages=[page]) +Vizro().build(dashboard).run() diff --git a/pages/examples/parallel_coordinates.py b/pages/examples/parallel_coordinates.py new file mode 100644 index 0000000000000000000000000000000000000000..c372ec9a775e08c5ed4b17bb79f3b0bccf0373ba --- /dev/null +++ b/pages/examples/parallel_coordinates.py @@ -0,0 +1,19 @@ +import vizro.models as vm +import vizro.plotly.express as px +from vizro import Vizro + +iris = px.data.iris() + +page = vm.Page( + title="Parallel coordinates", + components=[ + vm.Graph( + figure=px.parallel_coordinates( + iris, color="species_id", dimensions=["sepal_width", "sepal_length", "petal_width", "petal_length"] + ) + ) + ], +) + +dashboard = vm.Dashboard(pages=[page]) +Vizro().build(dashboard).run() diff --git a/pages/examples/pie.py b/pages/examples/pie.py new file mode 100644 index 0000000000000000000000000000000000000000..596abb2ff2dc87fb167c0d848bf7888ee9c86897 --- /dev/null +++ b/pages/examples/pie.py @@ -0,0 +1,13 @@ +import vizro.models as vm +import vizro.plotly.express as px +from vizro import Vizro + +tips = px.data.tips() + +page = vm.Page( + title="Pie", + components=[vm.Graph(figure=px.pie(tips, values="tip", names="day"))], +) + +dashboard = vm.Dashboard(pages=[page]) +Vizro().build(dashboard).run() diff --git a/pages/examples/sankey.py b/pages/examples/sankey.py new file mode 100644 index 0000000000000000000000000000000000000000..21789018a898fffd11dbd2bdd4f2ca248151c072 --- /dev/null +++ b/pages/examples/sankey.py @@ -0,0 +1,64 @@ +from typing import List + +import pandas as pd +import plotly.graph_objects as go +import vizro.models as vm +from vizro import Vizro +from vizro.models.types import capture + +sankey_data = pd.DataFrame( + { + "Origin": [0, 1, 0, 2, 3, 3], # indices inside labels + "Destination": [2, 3, 3, 4, 4, 5], # indices inside labels + "Value": [8, 4, 2, 8, 4, 2], + } +) + + +@capture("graph") +def sankey( + data_frame: pd.DataFrame, + source: str, + target: str, + value: str, + labels: List[str], +): + fig = go.Figure( + data=[ + go.Sankey( + node={ + "pad": 16, + "thickness": 16, + "label": labels, + }, + link={ + "source": data_frame[source], + "target": data_frame[target], + "value": data_frame[value], + "label": labels, + "color": "rgba(205, 209, 228, 0.4)", + }, + ) + ] + ) + fig.update_layout(barmode="relative") + return fig + + +page = vm.Page( + title="Sankey", + components=[ + vm.Graph( + figure=sankey( + sankey_data, + labels=["A1", "A2", "B1", "B2", "C1", "C2"], + source="Origin", + target="Destination", + value="Value", + ), + ), + ], +) + +dashboard = vm.Dashboard(pages=[page]) +Vizro().build(dashboard).run() diff --git a/pages/examples/scatter.py b/pages/examples/scatter.py new file mode 100644 index 0000000000000000000000000000000000000000..c82c95cc644fc658b9a81403ff9f6755018e55de --- /dev/null +++ b/pages/examples/scatter.py @@ -0,0 +1,13 @@ +import vizro.models as vm +import vizro.plotly.express as px +from vizro import Vizro + +iris = px.data.iris() + +page = vm.Page( + title="Scatter", + components=[vm.Graph(figure=px.scatter(iris, x="sepal_width", y="sepal_length", color="species"))], +) + +dashboard = vm.Dashboard(pages=[page]) +Vizro().build(dashboard).run() diff --git a/pages/examples/scatter_matrix.py b/pages/examples/scatter_matrix.py new file mode 100644 index 0000000000000000000000000000000000000000..01b355945a5737d6f2430289c97b56874150f6e8 --- /dev/null +++ b/pages/examples/scatter_matrix.py @@ -0,0 +1,17 @@ +import vizro.models as vm +import vizro.plotly.express as px +from vizro import Vizro + +iris = px.data.iris() + +page = vm.Page( + title="Scatter matrix", + components=[ + vm.Graph( + figure=px.scatter_matrix(iris, dimensions=["sepal_length", "sepal_width", "petal_length", "petal_width"]) + ) + ], +) + +dashboard = vm.Dashboard(pages=[page]) +Vizro().build(dashboard).run() diff --git a/pages/examples/stacked_bar.py b/pages/examples/stacked_bar.py new file mode 100644 index 0000000000000000000000000000000000000000..81b38ac1f6baeb8f22563bd757de8e1bc324898a --- /dev/null +++ b/pages/examples/stacked_bar.py @@ -0,0 +1,14 @@ +import vizro.models as vm +import vizro.plotly.express as px +from vizro import Vizro + +tips = px.data.tips() + + +page = vm.Page( + title="Stacked bar", + components=[vm.Graph(figure=px.histogram(tips, y="sex", x="total_bill", color="day", orientation="h"))], +) + +dashboard = vm.Dashboard(pages=[page]) +Vizro().build(dashboard).run() diff --git a/pages/examples/stacked_column.py b/pages/examples/stacked_column.py new file mode 100644 index 0000000000000000000000000000000000000000..68bb9eddc8bd064bbe0bd5edc0a59a1eb89bea16 --- /dev/null +++ b/pages/examples/stacked_column.py @@ -0,0 +1,14 @@ +import vizro.models as vm +import vizro.plotly.express as px +from vizro import Vizro + +tips = px.data.tips() + + +page = vm.Page( + title="Stacked column", + components=[vm.Graph(figure=px.histogram(tips, x="sex", y="total_bill", color="day"))], +) + +dashboard = vm.Dashboard(pages=[page]) +Vizro().build(dashboard).run() diff --git a/pages/examples/stepped_line.py b/pages/examples/stepped_line.py new file mode 100644 index 0000000000000000000000000000000000000000..3b97d4d4febcd30e79016f9a2181273bead9718d --- /dev/null +++ b/pages/examples/stepped_line.py @@ -0,0 +1,27 @@ +import pandas as pd +import vizro.models as vm +import vizro.plotly.express as px +from vizro import Vizro + +stepped_line_data = pd.DataFrame( + { + "year": [1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003], + "rate": [0.10, 0.12, 0.15, 0.13, 0.14, 0.13, 0.14, 0.16, 0.15], + } +) + +page = vm.Page( + title="Stepped line", + components=[ + vm.Graph( + figure=px.line( + data_frame=stepped_line_data, + x="year", + y="rate", + line_shape="vh", + ), + ) + ], +) +dashboard = vm.Dashboard(pages=[page]) +Vizro().build(dashboard).run() diff --git a/pages/examples/time_column.py b/pages/examples/time_column.py new file mode 100644 index 0000000000000000000000000000000000000000..dac0a57cafb91835fca7cae0349ef3ec090b32a6 --- /dev/null +++ b/pages/examples/time_column.py @@ -0,0 +1,36 @@ +import pandas as pd +import vizro.models as vm +import vizro.plotly.express as px +from vizro import Vizro +from vizro.models.types import capture + +gapminder = px.data.gapminder() + + +@capture("graph") +def categorical_column(data_frame: pd.DataFrame, x: str, y: str): + fig = px.bar( + data_frame, + x=x, + y=y, + ) + # So ticks are aligned with bars when xaxes values are numbers (e.g. years) + fig.update_xaxes(type="category") + return fig + + +page = vm.Page( + title="Column", + components=[ + vm.Graph( + figure=categorical_column( + gapminder.query("country == 'Nigeria' and year > 1970"), + y="lifeExp", + x="year", + ) + ) + ], +) + +dashboard = vm.Dashboard(pages=[page]) +Vizro().build(dashboard).run() diff --git a/pages/examples/treemap.py b/pages/examples/treemap.py new file mode 100644 index 0000000000000000000000000000000000000000..ce94bdb07f113118b919db0614112f7cae1ef3f0 --- /dev/null +++ b/pages/examples/treemap.py @@ -0,0 +1,22 @@ +import vizro.models as vm +import vizro.plotly.express as px +from vizro import Vizro + +gapminder = px.data.gapminder() + +page = vm.Page( + title="Treemap", + components=[ + vm.Graph( + figure=px.treemap( + gapminder.query("year == 2007"), + path=[px.Constant("world"), "continent", "country"], + values="pop", + color="lifeExp", + ) + ), + ], +) + +dashboard = vm.Dashboard(pages=[page]) +Vizro().build(dashboard).run() diff --git a/pages/examples/violin.py b/pages/examples/violin.py new file mode 100644 index 0000000000000000000000000000000000000000..f3edcffe515de04cbc8da3cac5279016e27c5964 --- /dev/null +++ b/pages/examples/violin.py @@ -0,0 +1,15 @@ +import vizro.models as vm +import vizro.plotly.express as px +from vizro import Vizro + +tips = px.data.tips() + +page = vm.Page( + title="Violin", + components=[ + vm.Graph(figure=px.violin(tips, y="total_bill", x="day", color="day", box=True)), + ], +) + +dashboard = vm.Dashboard(pages=[page]) +Vizro().build(dashboard).run() diff --git a/pages/flow.py b/pages/flow.py new file mode 100644 index 0000000000000000000000000000000000000000..4ceaed6e1c6bf77ae0d12a3313949c00a34526c1 --- /dev/null +++ b/pages/flow.py @@ -0,0 +1,47 @@ +"""Flow charts.""" + +import vizro.models as vm +from custom_charts import sankey + +from pages._pages_utils import PAGE_GRID, make_code_clipboard_from_py_file, sankey_data + +sankey = vm.Page( + title="Sankey", + path="flow/sankey", + layout=vm.Layout(grid=PAGE_GRID), + components=[ + vm.Card( + text=""" + #### What is a sankey chart? + + A Sankey chart is a type of flow diagram that illustrates how resources or values move between different + stages or entities. The width of the arrows in the chart is proportional to the quantity of the flow, + making it easy to see where the largest movements occur. + +   + + #### When should I use it? + + Use a Sankey chart when you want to visualize the flow of resources, energy, money, or other values from + one point to another. It is particularly useful for showing distributions and transfers within a system, + such as energy usage, cost breakdowns, or material flows. + + Be mindful that Sankey charts can become cluttered if there are too many nodes or flows. + To maintain clarity, focus on highlighting the most significant flows and keep the chart as simple as + possible. + """ + ), + vm.Graph( + figure=sankey( + sankey_data, + labels=["A1", "A2", "B1", "B2", "C1", "C2"], + source="Origin", + target="Destination", + value="Value", + ), + ), + make_code_clipboard_from_py_file("sankey.py"), + ], +) + +pages = [sankey] diff --git a/pages/magnitude.py b/pages/magnitude.py new file mode 100644 index 0000000000000000000000000000000000000000..a720a189358db10eff4fc1af3f9563bc18f10aa6 --- /dev/null +++ b/pages/magnitude.py @@ -0,0 +1,193 @@ +"""Magnitude charts.""" + +import vizro.models as vm +import vizro.plotly.express as px + +from pages._pages_utils import PAGE_GRID, gapminder, iris, make_code_clipboard_from_py_file, tips + +bar = vm.Page( + title="Bar", + path="magnitude/bar", + layout=vm.Layout(grid=PAGE_GRID), + components=[ + vm.Card( + text=""" + + #### What is a bar chart? + + A bar chart displays bars with lengths proportional to the values they represent. One axis shows the + categories to compare, and the other provides a value scale starting from zero. + +   + + #### When should I use it? + + Use a bar chart to help your audience compare sizes and identify patterns in categorical data, such as + **how many?** in each category. Arrange the bars in any order to fit the message you want to emphasize. + Ensure clear labeling, especially with many bars, and consider using a legend or abbreviations with fuller + descriptions below. + """ + ), + vm.Graph( + figure=px.bar( + gapminder.query( + "year == 2007 and country.isin(['United States', 'Pakistan', 'India', 'China', 'Indonesia'])" + ), + x="pop", + y="country", + orientation="h", + ) + ), + make_code_clipboard_from_py_file("bar.py"), + ], +) + +# Note: Code example for magnitude/column differs from time/column. The text description is the same. +column = vm.Page( + id="magnitude-column", + path="magnitude/column", + title="Column", + layout=vm.Layout(grid=PAGE_GRID), + components=[ + vm.Card( + text=""" + #### What is a column chart? + + A column chart is a type of bar chart where data is represented with vertical columns. Each + column's height corresponds to the value it represents, with the y-axis starting from zero. + +   + + #### When should I use it? + + Use a column chart to compare sizes and identify patterns in categorical data, including time-based + data. Arrange columns to fit your message, and for time-based data, order them chronologically to + highlight trends. Ensure clear labeling, especially with many categories, and consider using a legend + or abbreviations with fuller descriptions below. + """ + ), + vm.Graph( + figure=px.bar( + gapminder.query( + "year == 2007 and country.isin(['United States', 'Pakistan', 'India', 'China', 'Indonesia'])" + ), + y="pop", + x="country", + ) + ), + make_code_clipboard_from_py_file("magnitude_column.py"), + ], +) + +paired_bar = vm.Page( + title="Paired bar", + path="magnitude/paired-bar", + layout=vm.Layout(grid=PAGE_GRID), + components=[ + vm.Card( + text=""" + + #### What is a paired bar chart? + + A paired bar chart, also known as a grouped bar chart, displays bars grouped together in pairs for each + category, with the lengths of the bars proportional to the values they represent. One axis shows the + categories to compare, while the other provides a value scale starting from zero. + +   + + #### When should I use it? + + Use a paired bar chart to compare multiple sets of data within the same categories. This type of chart is + particularly useful for highlighting differences and similarities between groups, such as **how many?** in + each category across different groups. Arrange the paired bars clearly to fit the message you want to + emphasize. Ensure clear labeling, especially with many bars, and consider using a legend or abbreviations + with fuller descriptions below. + """ + ), + vm.Graph( + figure=px.histogram( + tips, + y="day", + x="total_bill", + color="sex", + barmode="group", + orientation="h", + category_orders={"day": ["Thur", "Fri", "Sat", "Sun"]}, + ), + ), + make_code_clipboard_from_py_file("paired_bar.py"), + ], +) + +paired_column = vm.Page( + title="Paired column", + path="magnitude/paired-column", + layout=vm.Layout(grid=PAGE_GRID), + components=[ + vm.Card( + text=""" + + #### What is a paired column chart? + + A paired column chart, also known as a grouped column chart, displays columns grouped together in pairs for + each category, with the heights of the columns proportional to the values they represent. One axis shows the + categories to compare, while the other provides a value scale starting from zero. + +   + + #### When should I use it? + + Use a paired column chart to compare multiple sets of data within the same categories. This type of chart is + particularly useful for highlighting differences and similarities between groups, such as **how many?** in + each category across different groups. Arrange the paired columns clearly to fit the message you want to + emphasize. Ensure clear labeling, especially with many columns, and consider using a legend or abbreviations + with fuller descriptions below. + """ + ), + vm.Graph( + figure=px.histogram( + tips, + x="day", + y="total_bill", + color="sex", + barmode="group", + category_orders={"day": ["Thur", "Fri", "Sat", "Sun"]}, + ), + ), + make_code_clipboard_from_py_file("paired_column.py"), + ], +) + +parallel_coordinates = vm.Page( + path="magnitude/parallel-coordinates ", + title="Parallel coordinates", + layout=vm.Layout(grid=PAGE_GRID), + components=[ + vm.Card( + text=""" + #### What is a parallel coordinates chart? + + A parallel coordinates chart is a type of data visualization used to plot multivariate numerical data. + Each axis represents a different variable, and lines connecting the axes indicate the values of + individual data points across these variables. + +   + + #### When should I use it? + + Use a parallel coordinates chart to explore relationships and patterns in high-dimensional data. + This chart is particularly useful for comparing multiple variables simultaneously and identifying + correlations or clusters within the data. Ensure clear labeling of each axis and consider using color + coding to distinguish between different data points or groups. + """ + ), + vm.Graph( + figure=px.parallel_coordinates( + iris, color="species_id", dimensions=["sepal_width", "sepal_length", "petal_width", "petal_length"] + ) + ), + make_code_clipboard_from_py_file("parallel_coordinates.py"), + ], +) + +pages = [bar, column, paired_bar, paired_column, parallel_coordinates] diff --git a/pages/part_to_whole.py b/pages/part_to_whole.py new file mode 100644 index 0000000000000000000000000000000000000000..19541b8eebc04efe5e425d67e19e2a1af7f266f7 --- /dev/null +++ b/pages/part_to_whole.py @@ -0,0 +1,207 @@ +"""Part-to-whole charts.""" + +import vizro.models as vm +import vizro.plotly.express as px + +from pages._pages_utils import PAGE_GRID, funnel_data, gapminder, make_code_clipboard_from_py_file, tips + +pie = vm.Page( + title="Pie", + path="part-to-whole/pie", + layout=vm.Layout(grid=PAGE_GRID), + components=[ + vm.Card( + text=""" + + #### What is a pie chart? + + A pie chart is a circular chart divided into segments to show proportions and percentages between + categories. The circle represents the whole. + +   + + #### When should I use it? + + Use the pie chart when you need to show your audience a quick view of how data is distributed + proportionately, with percentages highlighted. The different values you present must add up to a total and + equal 100%. + + The downsides are that pie charts tend to occupy more space than other charts, they don't + work well with more than a few values because labeling small segments is challenging, and it can be + difficult to accurately compare the sizes of the segments. + """ + ), + vm.Graph( + figure=px.pie( + tips, + values="tip", + names="day", + ) + ), + make_code_clipboard_from_py_file("pie.py"), + ], +) + +donut = vm.Page( + title="Donut", + path="part-to-whole/donut", + layout=vm.Layout(grid=PAGE_GRID), + components=[ + vm.Card( + text=""" + + #### What is a donut chart? + + A donut chart looks like a pie chart, but has a blank space in the center which may contain additional + information. + +   + + #### When should I use it? + + A donut chart can be used in place of a pie chart, particularly when you are short of space or have extra + information you would like to share about the data. It may also be more effective if you wish your audience + to focus on the length of the arcs of the sections instead of the proportions of the segment sizes. + """ + ), + vm.Graph( + figure=px.pie( + tips, + values="tip", + names="day", + hole=0.4, + ) + ), + make_code_clipboard_from_py_file("donut.py"), + ], +) + +treemap = vm.Page( + title="Treemap", + path="part-to-whole/treemap", + layout=vm.Layout(grid=PAGE_GRID), + components=[ + vm.Card( + text=""" + + #### What is a treemap? + + A treemap shows hierarchical data arranged as a set of nested rectangles: rectangles are sized + proportionately to the quantity they represent, combined together to form larger parent category + rectangles. + +   + + #### When should I use it? + + It's helpful to use a treemap when you wish to display hierarchical part-to-whole relationships. You can + compare groups and single elements nested within them. Consider using them instead of Pie charts when + you have a higher number of categories. Treemaps are very compact and allow audiences to get a quick + overview of the data. + """ + ), + vm.Graph( + figure=px.treemap( + gapminder.query("year == 2007"), + path=[px.Constant("world"), "continent", "country"], + values="pop", + color="lifeExp", + ) + ), + make_code_clipboard_from_py_file("treemap.py"), + ], +) + +stacked_bar = vm.Page( + title="Stacked bar", + path="part-to-whole/stacked-bar", + layout=vm.Layout(grid=PAGE_GRID), + components=[ + vm.Card( + text=""" + + #### What is a stacked bar chart? + + A stacked bar chart displays bars divided into segments, with each segment's length proportional to the + value it represents. One axis shows the categories being compared, while the other provides a value scale + starting from zero. The segments within each bar are stacked on top of each other, allowing for a cumulative + comparison. + +   + + #### When should I use it? + + Use a stacked bar chart to help your audience compare the total sizes of categories as well as the + individual components within those categories. This chart type is ideal for visualizing part-to-whole + relationships and identifying patterns within categorical data. Ensure clear labeling for each segment, + especially when there are many categories, and consider using a legend or abbreviations with fuller + descriptions below. + """ + ), + vm.Graph(figure=px.histogram(tips, y="sex", x="total_bill", color="day", orientation="h")), + make_code_clipboard_from_py_file("stacked_bar.py"), + ], +) + +stacked_column = vm.Page( + title="Stacked column", + path="part-to-whole/stacked-column", + layout=vm.Layout(grid=PAGE_GRID), + components=[ + vm.Card( + text=""" + + #### What is a stacked column chart? + + A stacked column chart displays columns divided into segments, with each segment's height proportional to + the value it represents. One axis shows the categories being compared, while the other provides a value + scale starting from zero. The segments within each column are stacked on top of each other, allowing for a + cumulative comparison. + +   + + #### When should I use it? + + Use a stacked column chart to help your audience compare the total sizes of categories as well as the + individual components within those categories. This chart type is ideal for visualizing part-to-whole + relationships and identifying patterns within categorical data. Ensure clear labeling for each segment, + especially when there are many categories, and consider using a legend or abbreviations with fuller + descriptions below. + """ + ), + vm.Graph(figure=px.histogram(tips, x="sex", y="total_bill", color="day")), + make_code_clipboard_from_py_file("stacked_bar.py"), + ], +) + +funnel = vm.Page( + title="Funnel", + path="part-to-whole/funnel", + layout=vm.Layout(grid=PAGE_GRID), + components=[ + vm.Card( + text=""" + + #### What is a funnel chart? + + A funnel area chart is a type of data visualization that represents stages in a process, with the size of + each area proportional to its value. The chart typically narrows as it progresses, visually depicting the + reduction in numbers through each stage. One axis represents the stages of the process, while the other axis + indicates the values or quantities at each stage. + +   + + #### When should I use it? + + Use a funnel area chart to help your audience understand and compare the progression of data through + different stages of a process. This chart type is particularly effective for visualizing conversion rates, + sales processes, or any sequential data where you want to highlight drop-offs or reductions between stages. + """ + ), + vm.Graph(figure=px.funnel_area(funnel_data, names="Stage", values="Value")), + make_code_clipboard_from_py_file("funnel.py"), + ], +) + + +pages = [donut, pie, treemap, stacked_bar, stacked_column, funnel] diff --git a/pages/ranking.py b/pages/ranking.py new file mode 100644 index 0000000000000000000000000000000000000000..745a5d01f8f2bfab29e268b237da33029fa54d54 --- /dev/null +++ b/pages/ranking.py @@ -0,0 +1,83 @@ +"""Ranking charts.""" + +import vizro.models as vm +import vizro.plotly.express as px + +from pages._pages_utils import PAGE_GRID, gapminder, make_code_clipboard_from_py_file + +ordered_bar = vm.Page( + title="Ordered bar", + path="ranking/ordered-bar", + layout=vm.Layout(grid=PAGE_GRID), + components=[ + vm.Card( + text=""" + + #### What is an ordered bar chart? + + An ordered bar chart displays bars with lengths proportional to their values, arranged in descending or + ascending order. One axis shows the categories, and the other provides a value scale starting from zero. + +   + + #### When should I use it? + + Use an ordered bar chart to help your audience compare sizes and identify patterns in categorical data, + emphasizing the order of categories. This is ideal for showing rankings or priorities. + Ensure clear labeling, especially with many bars, and consider using a legend or abbreviations with fuller + descriptions below. + """ + ), + vm.Graph( + figure=px.bar( + gapminder.query( + "year == 2007 and country.isin(['United States', 'Pakistan', 'India', 'China', 'Indonesia'])" + ).sort_values("pop"), + x="pop", + y="country", + orientation="h", + ) + ), + make_code_clipboard_from_py_file("ordered_bar.py"), + ], +) + +ordered_column = vm.Page( + title="Ordered column", + path="ranking/ordered-column", + layout=vm.Layout(grid=PAGE_GRID), + components=[ + vm.Card( + text=""" + + #### What is an ordered column chart? + + An ordered column chart is a vertical bar chart where columns are arranged in descending or ascending order + based on their values. The column lengths vary according to the categorical value they represent, with the + scale on the y-axis starting from zero. + +   + + #### When should I use it? + + Use an ordered column chart to help your audience compare sizes and identify patterns in categorical data, + emphasizing the order of categories. This is ideal for showing rankings or progressions. Ensure clear + labeling, especially with many columns, and consider using a legend or abbreviations with fuller + descriptions below. + """ + ), + vm.Graph( + figure=px.bar( + gapminder.query( + "year == 2007 and country.isin(['United States', 'Pakistan', 'India', 'China', 'Indonesia'])" + ).sort_values("pop"), + y="pop", + x="country", + ) + ), + make_code_clipboard_from_py_file("ordered_column.py"), + ], +) + + +pages = [ordered_bar, ordered_column] diff --git a/pages/spatial.py b/pages/spatial.py new file mode 100644 index 0000000000000000000000000000000000000000..880150c1193c18a457cdcadf238704e77e555bc1 --- /dev/null +++ b/pages/spatial.py @@ -0,0 +1,45 @@ +"""Spatial charts.""" + +import vizro.models as vm +import vizro.plotly.express as px + +from pages._pages_utils import PAGE_GRID, gapminder, make_code_clipboard_from_py_file + +choropleth = vm.Page( + title="Choropleth", + path="spatial/choropleth", + layout=vm.Layout(grid=PAGE_GRID), + components=[ + vm.Card( + text=""" + #### What is a choropleth map? + + A choropleth map is a map in which geographical areas are colored, shaded or patterned in relation to a + specific data variable. + +   + + #### When should I use it? + + Use a chloropleth map when you wish to show how a measurement varies across a geographic area, or to show + variability or patterns within a region. Typically, you will blend one color into another, take a color + shade from light to dark, or introduce patterns to depict the variation in the data. + + Be aware that it may be difficult for your audience to accurately read or compare values on the map + depicted by color. + + """ + ), + vm.Graph( + figure=px.choropleth( + gapminder.query("year == 2007"), + locations="iso_alpha", + color="lifeExp", + hover_name="country", + ) + ), + make_code_clipboard_from_py_file("choropleth.py"), + ], +) + +pages = [choropleth] diff --git a/pages/time.py b/pages/time.py new file mode 100644 index 0000000000000000000000000000000000000000..2d43bfd6ce1a265d3be31af202a2bf39368e101a --- /dev/null +++ b/pages/time.py @@ -0,0 +1,163 @@ +"""Time charts.""" + +import vizro.models as vm +import vizro.plotly.express as px +from custom_charts import categorical_column + +from pages._factories import column_and_line_factory, connected_scatter_factory +from pages._pages_utils import PAGE_GRID, gapminder, make_code_clipboard_from_py_file, stepped_line_data, stocks, tips + +line = vm.Page( + title="Line", + path="time/line", + layout=vm.Layout(grid=PAGE_GRID), + components=[ + vm.Card( + text=""" + + #### What is a line chart? + + A line chart presents a series of data points over a continuous interval or time period, joined together + with straight lines. + +   + + #### When should I use it? + + You should select a line chart when you want to show trends over time. Usually, your y-axis will show a + quantitative value and your x-axis will be marked as a timescale or a sequence of intervals. You can also + display negative values below the x-axis. If you wish to group several lines (different data series) in the + same chart, try to limit yourself to 3-4 to avoid cluttering up your chart. + """ + ), + vm.Graph(figure=px.line(stocks, x="date", y="GOOG")), + make_code_clipboard_from_py_file("line.py"), + ], +) + +# Note: Code example for magnitude/column differs from time/column. The text description is the same. +column = vm.Page( + id="time-column", + path="time/column", + title="Column", + layout=vm.Layout(grid=PAGE_GRID), + components=[ + vm.Card( + text=""" + #### What is a column chart? + + A column chart is a type of bar chart where data is represented with vertical columns. Each + column's height corresponds to the value it represents, with the y-axis starting from zero. + +   + + #### When should I use it? + + Use a column chart to compare sizes and identify patterns in categorical data, including time-based + data. Arrange columns to fit your message, and for time-based data, order them chronologically to + highlight trends. Ensure clear labeling, especially with many categories, and consider using a legend + or abbreviations with fuller descriptions below. + """ + ), + vm.Graph( + figure=categorical_column( + gapminder.query("country == 'Nigeria' and year > 1970"), + y="gdpPercap", + x="year", + ) + ), + make_code_clipboard_from_py_file("time_column.py"), + ], +) + +area = vm.Page( + title="Area", + path="time/area", + layout=vm.Layout(grid=PAGE_GRID), + components=[ + vm.Card( + text=""" + + #### What is an area chart? + + An area chart displays data points over a continuous interval or time period, with the area between the line + and the axis filled in to emphasize the magnitude of the values. + +   + + #### When should I use it? + + An area chart is ideal for showing trends over time and emphasizing the volume of data. Typically, + the y-axis represents a quantitative value, while the x-axis is marked with a timescale or sequence of + intervals. Area charts can also display negative values below the x-axis. If you need to compare multiple + data series in the same chart, try to limit yourself to 3-4 to maintain clarity and avoid clutter. + """ + ), + vm.Graph(figure=px.area(stocks, x="date", y="GOOG")), + make_code_clipboard_from_py_file("area.py"), + ], +) + +connected_scatter = connected_scatter_factory("time") +column_and_line = column_and_line_factory("time") + +stepped_line = vm.Page( + title="Stepped line", + path="time/stepped-line", + layout=vm.Layout(grid=PAGE_GRID), + components=[ + vm.Card( + text=""" + + #### What is a stepped line chart? + + A stepped line chart is much like a standard line chart but, instead of connecting two points with the + shortest line, the line forms a series of steps between data points. + +   + + #### When should I use it? + + You should use a stepped line chart when you wish to draw attention to changes occurring at specific points. + By contrast, a line chart would suggest that changes occur gradually. + """ + ), + vm.Graph( + figure=px.line( + data_frame=stepped_line_data, + x="year", + y="rate", + line_shape="vh", + ), + ), + make_code_clipboard_from_py_file("stepped_line.py"), + ], +) + +heatmap = vm.Page( + title="Heatmap", + path="time/heatmap", + layout=vm.Layout(grid=PAGE_GRID), + components=[ + vm.Card( + text=""" + #### What is a heatmap? + A heatmap chart depicts values for a main variable of interest across two axis variables as a grid of + colored squares. The color intensity of each cell represents the value of the main variable within a + specific range. + +   + + #### When should I use it? + + Use a heatmap chart to visualize time patterns and identify trends between two variables. + Typically, the x-axis represents time intervals (e.g., hours, days, months), while the y-axis represents + categories or different variables. By observing color changes across the grid, you can easily spot + patterns and correlations. + """ + ), + vm.Graph(figure=px.density_heatmap(tips, x="day", y="size", z="tip", histfunc="avg", text_auto="$.2f")), + make_code_clipboard_from_py_file("heatmap.py"), + ], +) +pages = [line, column, area, connected_scatter, column_and_line, stepped_line, heatmap] diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 0000000000000000000000000000000000000000..736f3e23a7ed18ec2c83234d644c23075d2e94b6 --- /dev/null +++ b/requirements.txt @@ -0,0 +1,3 @@ +black==24.4.2 +vizro +gunicorn