├── .gitignore ├── LICENSE ├── README.md ├── assets ├── dataviz-in-python.gif └── dataviz-in-python.mp4 ├── jupyter-panel-proxy.yml ├── pyproject.toml ├── setup.cfg ├── setup.py └── src └── dataviz_in_python ├── __init__.py ├── assets └── menu.html ├── config.py └── presentation ├── considerations.py ├── crossfiltering.py ├── introduction.py ├── lib_altair.py ├── lib_bokeh.py ├── lib_datashader.py ├── lib_deckgl.py ├── lib_echarts.py ├── lib_folium.py ├── lib_holoviews.py ├── lib_hvplot.py ├── lib_ipywidgets.py ├── lib_leaflet.py ├── lib_matplotlib.py ├── lib_plotly.py ├── lib_plotnine.py ├── lib_pydeck.py ├── lib_pyecharts.py ├── lib_pyvista.py ├── lib_seaborn.py ├── lib_vega.py ├── lib_vtk.py └── streaming.py /.gitignore: -------------------------------------------------------------------------------- 1 | # Byte-compiled / optimized / DLL files 2 | __pycache__/ 3 | *.py[cod] 4 | *$py.class 5 | 6 | # C extensions 7 | *.so 8 | 9 | # Distribution / packaging 10 | .Python 11 | build/ 12 | develop-eggs/ 13 | dist/ 14 | downloads/ 15 | eggs/ 16 | .eggs/ 17 | lib/ 18 | lib64/ 19 | parts/ 20 | sdist/ 21 | var/ 22 | wheels/ 23 | pip-wheel-metadata/ 24 | share/python-wheels/ 25 | *.egg-info/ 26 | .installed.cfg 27 | *.egg 28 | MANIFEST 29 | 30 | # PyInstaller 31 | # Usually these files are written by a python script from a template 32 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 33 | *.manifest 34 | *.spec 35 | 36 | # Installer logs 37 | pip-log.txt 38 | pip-delete-this-directory.txt 39 | 40 | # Unit test / coverage reports 41 | htmlcov/ 42 | .tox/ 43 | .nox/ 44 | .coverage 45 | .coverage.* 46 | .cache 47 | nosetests.xml 48 | coverage.xml 49 | *.cover 50 | *.py,cover 51 | .hypothesis/ 52 | .pytest_cache/ 53 | 54 | # Translations 55 | *.mo 56 | *.pot 57 | 58 | # Django stuff: 59 | *.log 60 | local_settings.py 61 | db.sqlite3 62 | db.sqlite3-journal 63 | 64 | # Flask stuff: 65 | instance/ 66 | .webassets-cache 67 | 68 | # Scrapy stuff: 69 | .scrapy 70 | 71 | # Sphinx documentation 72 | docs/_build/ 73 | 74 | # PyBuilder 75 | target/ 76 | 77 | # Jupyter Notebook 78 | .ipynb_checkpoints 79 | 80 | # IPython 81 | profile_default/ 82 | ipython_config.py 83 | 84 | # pyenv 85 | .python-version 86 | 87 | # pipenv 88 | # According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. 89 | # However, in case of collaboration, if having platform-specific dependencies or dependencies 90 | # having no cross-platform support, pipenv may install dependencies that don't work, or not 91 | # install all needed dependencies. 92 | #Pipfile.lock 93 | 94 | # PEP 582; used by e.g. github.com/David-OConnor/pyflow 95 | __pypackages__/ 96 | 97 | # Celery stuff 98 | celerybeat-schedule 99 | celerybeat.pid 100 | 101 | # SageMath parsed files 102 | *.sage.py 103 | 104 | # Environments 105 | .env 106 | .venv 107 | env/ 108 | venv/ 109 | ENV/ 110 | env.bak/ 111 | venv.bak/ 112 | 113 | # Spyder project settings 114 | .spyderproject 115 | .spyproject 116 | 117 | # Rope project settings 118 | .ropeproject 119 | 120 | # mkdocs documentation 121 | /site 122 | 123 | # mypy 124 | .mypy_cache/ 125 | .dmypy.json 126 | dmypy.json 127 | 128 | # Pyre type checker 129 | .pyre/ 130 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2022 Marc Skov Madsen 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ![Python Versions](https://img.shields.io/badge/python-3.9-blue) [![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](https://www.gnu.org/licenses/agpl-3.0) [![Binder](https://mybinder.org/badge_logo.svg)](https://mybinder.org/v2/gh/MarcSkovMadsen/dataviz-in-python/HEAD?urlpath=lab) [![Follow on Twitter](https://img.shields.io/twitter/follow/MarcSkovMadsen.svg?style=social)](https://twitter.com/MarcSkovMadsen) 2 | 3 | # An Introduction to dataviz in Python 4 | 5 | This repository contains an introduction to dataviz in Python with a focus on browser based, interactive dataviz. 6 | 7 | ![Tour](assets/dataviz-in-python.gif) 8 | 9 | ## Installation 10 | 11 | ```bash 12 | git clone https://github.com/MarcSkovMadsen/dataviz-in-python.git 13 | python -m venv .venv 14 | source .venv/Scripts/activate 15 | pip install -e .[all] 16 | ``` 17 | 18 | ### Serve the presentation 19 | 20 | ```bash 21 | panel serve src/dataviz_in_python/presentation/*.py 22 | ``` 23 | 24 | You can view the presentation at [http://localhost:5006/introduction](http://localhost:5006/introduction). 25 | -------------------------------------------------------------------------------- /assets/dataviz-in-python.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MarcSkovMadsen/dataviz-in-python/72c97c14752dde7a800e407b4a3cf48bae6b18a7/assets/dataviz-in-python.gif -------------------------------------------------------------------------------- /assets/dataviz-in-python.mp4: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MarcSkovMadsen/dataviz-in-python/72c97c14752dde7a800e407b4a3cf48bae6b18a7/assets/dataviz-in-python.mp4 -------------------------------------------------------------------------------- /jupyter-panel-proxy.yml: -------------------------------------------------------------------------------- 1 | --- 2 | apps: 3 | - src/dataviz_in_python/presentation/*.py -------------------------------------------------------------------------------- /pyproject.toml: -------------------------------------------------------------------------------- 1 | [build-system] 2 | requires = [ 3 | "setuptools>=42", 4 | "wheel" 5 | ] 6 | build-backend = "setuptools.build_meta" 7 | 8 | [tool.black] 9 | line-length = 100 10 | 11 | [tool.isort] 12 | profile = "black" 13 | 14 | [tool.pylint.design] 15 | max-attributes=12 16 | max-args=10 17 | 18 | # pyproject.toml 19 | [tool.pytest.ini_options] 20 | minversion = "6.0" 21 | testpaths = [ 22 | "tests", 23 | ] 24 | junit_family = "legacy" 25 | python_files = ["tests.py","test_*.py","*_tests.py"] 26 | -------------------------------------------------------------------------------- /setup.cfg: -------------------------------------------------------------------------------- 1 | [metadata] 2 | name = dataviz-in-python 3 | version = 0.0.1 4 | author = Marc Skov Madsen 5 | author_email = marc.skov.madsen@gmail.com 6 | description = An introduction to DataViz in Python 7 | home_page = https://github.com/MarcSkovMadsen/dataviz-in-python 8 | long_description = file: README.md 9 | long_description_content_type = text/markdown 10 | license = MIT 11 | license_file = LICENSE 12 | 13 | [options] 14 | package_dir = 15 | = src 16 | packages = find: 17 | python_requires = >=3.7 18 | include_package_data = True 19 | install_requires = 20 | altair 21 | autoflake 22 | black 23 | datashader 24 | folium 25 | holoviews 26 | hvplot 27 | invoke 28 | ipysheet==0.7.0 29 | ipywidgets_bokeh 30 | isort 31 | jupyter_bokeh 32 | jupyter-panel-proxy 33 | jupyterlab 34 | matplotlib 35 | mypy 36 | numpy<1.20 37 | panel 38 | plotly 39 | plotnine 40 | pooch 41 | pyarrow 42 | pydeck 43 | pyecharts 44 | pylint 45 | pytest 46 | pytest-cov 47 | pyvista 48 | scipy 49 | seaborn 50 | shapely 51 | twine 52 | vega_datasets 53 | vegafusion-python 54 | vtk 55 | watchdog 56 | xarray 57 | 58 | [options.packages.find] 59 | where = src -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | """This file enables `pip install -e .""" 2 | from setuptools import setup 3 | 4 | setup() 5 | -------------------------------------------------------------------------------- /src/dataviz_in_python/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MarcSkovMadsen/dataviz-in-python/72c97c14752dde7a800e407b4a3cf48bae6b18a7/src/dataviz_in_python/__init__.py -------------------------------------------------------------------------------- /src/dataviz_in_python/assets/menu.html: -------------------------------------------------------------------------------- 1 | 2 | 3 |

Introduction

{ COLLAPSED_ICON }{ EXPANDED_ICON } 4 | 8 |
9 | 10 |

Plotting libraries

{ COLLAPSED_ICON }{ EXPANDED_ICON } 11 | 31 |
32 | 33 |

Other

{ COLLAPSED_ICON }{ EXPANDED_ICON } 34 | 39 |
40 | 41 |

Resources

{ COLLAPSED_ICON }{ EXPANDED_ICON } 42 | 45 |
46 | 47 |

Marc Skov Madsen

{ COLLAPSED_ICON }{ EXPANDED_ICON } 48 | 59 |
60 |
61 | 62 | -------------------------------------------------------------------------------- /src/dataviz_in_python/config.py: -------------------------------------------------------------------------------- 1 | import pathlib 2 | from typing import Optional 3 | 4 | import panel as pn 5 | 6 | ACCENT_BASE_COLOR = "#4099da" 7 | 8 | ROOT = pathlib.Path(__file__).parent 9 | MENU_HTML_PATH = ROOT / "assets" / "menu.html" 10 | MENU_HTML = MENU_HTML_PATH.read_text() 11 | 12 | COLLAPSED_ICON = """ 13 | 14 | 15 | 16 | 17 | 18 | """ 19 | 20 | EXPANDED_ICON = """ 21 | 22 | 23 | 24 | 25 | """ 26 | 27 | TEXT_CLASS = "presentation-text" 28 | 29 | CSS = """ 30 | #menu .menu-item-active a { 31 | color: white !important; 32 | } 33 | div.bk.presentation-text.markdown div { 34 | font-size: var(--type-ramp-plus-3-font-size); 35 | } 36 | """ 37 | 38 | 39 | def get_menu(url, title): 40 | return ( 41 | MENU_HTML_PATH.read_text() 42 | .replace("{ COLLAPSED_ICON }", COLLAPSED_ICON) 43 | .replace("{ EXPANDED_ICON }", EXPANDED_ICON) 44 | .replace( 45 | f'
  • { title }
  • ', 46 | f'', 47 | ) 48 | ) 49 | 50 | 51 | def get_theme(): 52 | template = pn.state.template 53 | theme = "dark" if template.theme == pn.template.DarkTheme else "default" 54 | return theme 55 | 56 | 57 | def configure( 58 | *args, 59 | title: str, 60 | url: Optional[str] = None, 61 | sizing_mode="stretch_width", 62 | template="fast", 63 | accent_color=ACCENT_BASE_COLOR, 64 | site="DataViz in Python", 65 | ): 66 | """Runs pn.extension and pn.state.template.param.update with the specified 67 | arguments""" 68 | if not CSS in pn.config.raw_css: 69 | pn.config.raw_css.append(CSS) 70 | 71 | pn.extension(*args, sizing_mode=sizing_mode, template=template) 72 | 73 | if not url: 74 | url = title.lower().replace(" ", "_") 75 | menu = get_menu(url=url, title=title) 76 | 77 | pn.state.template.param.update( 78 | site=site, 79 | title=title, 80 | accent_base_color=accent_color, 81 | header_background=accent_color, 82 | sidebar_footer=menu, 83 | ) 84 | -------------------------------------------------------------------------------- /src/dataviz_in_python/presentation/considerations.py: -------------------------------------------------------------------------------- 1 | import panel as pn 2 | 3 | from dataviz_in_python import config 4 | 5 | config.configure(title="Considerations") 6 | 7 | TEXT = """ 8 | What data visualization tool or framework to pick depends on your **use case, experience, 9 | preferences, team and much more**. 10 | 11 | - Do you need something for quick data exploration, business communication or highly specialized use cases? 12 | - Will this be a part of an interactive data app built using Dash, Jupyter Voila, Panel, Streamlit, Angular etc.? 13 | - How will you deploy and share with users? 14 | 15 | I would always recommend to **start out with the [Pandas .plot api](https://pandas.pydata.org/docs/reference/api/pandas.DataFrame.plot.html)** or something very similar if possible. 16 | 17 | But visualization is not only plots. Its also interactive images, tables etc. And closely related are other formats like Audio, Video etc. 18 | """ 19 | 20 | pn.Column(pn.panel(TEXT, css_classes=[config.TEXT_CLASS])).servable() 21 | -------------------------------------------------------------------------------- /src/dataviz_in_python/presentation/crossfiltering.py: -------------------------------------------------------------------------------- 1 | import hvplot.pandas 2 | import holoviews as hv 3 | import panel as pn 4 | from bokeh.sampledata.iris import flowers 5 | from dataviz_in_python import config 6 | 7 | config.configure(title="Crossfiltering") 8 | 9 | TEXT = """ 10 | # Crossfiltering: Super powered zoom 11 | 12 | When you have multiple plots of the same data set or model your often want to link your plots. This 13 | is called *cross filtering* or *linked brushing*. 14 | 15 | To me crossfiltering is one of the most powerful features of Tableau. But you 16 | can also do this in Python using Altair, HoloViews or Plotly. Here I'm using Holoviews. 17 | 18 | [Source Code](https://github.com/MarcSkovMadsen/dataviz-in-python/blob/main/src/dataviz_in_python/presentation/crossfiltering.py) 19 | """ 20 | pn.panel(TEXT, css_classes=[config.TEXT_CLASS]).servable() 21 | 22 | accent_color = config.ACCENT_BASE_COLOR 23 | 24 | scatter = flowers.hvplot.scatter( 25 | x="sepal_length", y="sepal_width", c=accent_color, responsive=True, min_height=350 26 | ) 27 | hist = flowers.hvplot.hist("petal_width", c=accent_color, responsive=True, min_height=350) 28 | 29 | scatter.opts(size=10) 30 | 31 | selection_linker = hv.selection.link_selections.instance() 32 | 33 | scatter = selection_linker(scatter) 34 | hist = selection_linker(hist) 35 | 36 | scatter.opts(tools=["hover"], active_tools=["box_select"]) 37 | hist.opts(tools=["hover"], active_tools=["box_select"]) 38 | 39 | pn.Column( 40 | pn.pane.HoloViews(scatter, sizing_mode="stretch_both"), 41 | pn.pane.HoloViews(hist, sizing_mode="stretch_both"), 42 | sizing_mode="stretch_both", 43 | ).servable() 44 | -------------------------------------------------------------------------------- /src/dataviz_in_python/presentation/introduction.py: -------------------------------------------------------------------------------- 1 | import panel as pn 2 | 3 | from dataviz_in_python import config 4 | 5 | config.configure(title="Introduction") 6 | 7 | INTRODUCTION = """ 8 | ## DataViz in Python 9 | 10 | The Python visualization landscape can seem daunting at first. In this presentation I will give my 11 | take on the Python dataviz ecosystem. 12 | 13 | I will be focusing on **browser based, interactive** dataviz. 14 | 15 | For a general, up to date overview check out [PyViz.org](https://pyviz.org/). 16 | """ 17 | 18 | LANDSCAPE = "https://rougier.github.io/python-visualization-landscape/landscape-colors.png" 19 | 20 | pn.Column( 21 | pn.panel(INTRODUCTION, css_classes=[config.TEXT_CLASS]), 22 | pn.layout.Divider(), 23 | pn.panel(LANDSCAPE, height=700, align="center"), 24 | ).servable() 25 | -------------------------------------------------------------------------------- /src/dataviz_in_python/presentation/lib_altair.py: -------------------------------------------------------------------------------- 1 | import altair as alt 2 | import panel as pn 3 | from vega_datasets import data 4 | 5 | from dataviz_in_python import config 6 | 7 | config.configure("vega", url="lib_altair", title="Altair") 8 | 9 | TEXT = """ 10 | # Altair: Declarative Visualization in Python 11 | 12 | [Altair](https://altair-viz.github.io/) is a declarative statistical visualization library for Python, based on Vega and Vega-Lite. 13 | 14 | Traditionally Altair has been limited by data size. But [Vega Fusion](https://github.com/vegafusion/vegafusion/) is changing that. 15 | See also [Panel-VegaFusion](https://github.com/MarcSkovMadsen/panel-vegafusion). 16 | 17 | [Source Code](https://github.com/MarcSkovMadsen/dataviz-in-python/blob/main/src/dataviz_in_python/presentation/lib_altair.py) 18 | """ 19 | pn.panel(TEXT, css_classes=[config.TEXT_CLASS]).servable() 20 | 21 | 22 | def get_plot(theme="default"): 23 | if theme == "dark": 24 | alt.themes.enable("dark") 25 | else: 26 | alt.themes.enable("default") 27 | 28 | return ( 29 | alt.Chart(data.cars()) 30 | .mark_circle(size=60) 31 | .encode( 32 | x="Horsepower", 33 | y="Miles_per_Gallon", 34 | color="Origin", 35 | tooltip=["Name", "Origin", "Horsepower", "Miles_per_Gallon"], 36 | ) 37 | .properties( 38 | height="container", 39 | width="container", 40 | ) 41 | .interactive() 42 | ) 43 | 44 | 45 | plot = get_plot(theme=config.get_theme()) 46 | pn.pane.Vega(plot, height=700, sizing_mode="stretch_both").servable() 47 | -------------------------------------------------------------------------------- /src/dataviz_in_python/presentation/lib_bokeh.py: -------------------------------------------------------------------------------- 1 | import numpy as np 2 | import panel as pn 3 | 4 | from bokeh.plotting import figure 5 | from scipy.integrate import odeint 6 | 7 | from dataviz_in_python import config 8 | 9 | config.configure(title="Bokeh", url="lib_bokeh") 10 | 11 | TEXT = """ 12 | # Bokeh: Interactive Visualization in the Browser 13 | 14 | [Bokeh](https://docs.bokeh.org/en/latest/) is a Python library for creating interactive visualizations for modern web browsers. 15 | 16 | I think Bokeh appeals to users with specialized, scientific or streaming use cases. Bokeh has a lot of "specialized" tools for specialized use cases. 17 | 18 | [Source Code](https://github.com/MarcSkovMadsen/dataviz-in-python/blob/main/src/dataviz_in_python/presentation/lib_bokeh.py) 19 | """ 20 | pn.panel(TEXT, css_classes=[config.TEXT_CLASS]).servable() 21 | 22 | 23 | def get_plot(): 24 | sigma = 10 25 | rho = 28 26 | beta = 8.0 / 3 27 | theta = 3 * np.pi / 4 28 | 29 | def lorenz(xyz, t): 30 | x, y, z = xyz 31 | x_dot = sigma * (y - x) 32 | y_dot = x * rho - x * z - y 33 | z_dot = x * y - beta * z 34 | return [x_dot, y_dot, z_dot] 35 | 36 | initial = (-10, -7, 35) 37 | t = np.arange(0, 100, 0.006) 38 | 39 | solution = odeint(lorenz, initial, t) 40 | 41 | x = solution[:, 0] 42 | y = solution[:, 1] 43 | z = solution[:, 2] 44 | xprime = np.cos(theta) * x - np.sin(theta) * y 45 | 46 | colors = [ 47 | "#C6DBEF", 48 | "#9ECAE1", 49 | "#6BAED6", 50 | "#4292C6", 51 | "#2171B5", 52 | "#08519C", 53 | "#08306B", 54 | ] 55 | 56 | plot = figure(title="Lorenz attractor example", tools=["pan,wheel_zoom,box_zoom,reset,hover"]) 57 | 58 | plot.multi_line( 59 | np.array_split(xprime, 7), 60 | np.array_split(z, 7), 61 | line_color=colors, 62 | line_alpha=0.8, 63 | line_width=1.5, 64 | ) 65 | return plot 66 | 67 | 68 | plot = get_plot() 69 | pn.pane.Bokeh(plot, height=700, sizing_mode="stretch_both").servable() 70 | -------------------------------------------------------------------------------- /src/dataviz_in_python/presentation/lib_datashader.py: -------------------------------------------------------------------------------- 1 | import hvplot.xarray # noqa 2 | import panel as pn 3 | import xarray as xr 4 | from dataviz_in_python import config 5 | 6 | config.configure(url="lib_datashader", title="Datashader") 7 | 8 | TEXT = """ 9 | # Datashader: Accurately render even the largest data 10 | 11 | [Datashader](https://datashader.org/) is something unique in the Python ecosystem. It enables you 12 | to interactively explore big data of Terabyte or even Peta byte scale. For 13 | example for use cases at Cern, at intelligence Institutions. 14 | 15 | You would be using it in combination with [hvplot](lib_hvplot), 16 | [Holoviews](lib_holoviews) or [Plotly](lib_plotly). 17 | 18 | Rapids by NVidia bases their [CuxFilter](https://github.com/rapidsai/cuxfilter) 19 | framework on Datashader, HoloViews and Panel. For more inspiration see [Google Search](https://www.google.com/search?q=datashader+youtube) 20 | 21 | [Source Code](https://github.com/MarcSkovMadsen/dataviz-in-python/blob/main/src/dataviz_in_python/presentation/lib_datashader.py) 22 | """ 23 | pn.panel(TEXT, css_classes=[config.TEXT_CLASS]).servable() 24 | 25 | if not "air" in pn.state.cache: 26 | air = pn.state.cache["air"] = xr.tutorial.open_dataset("air_temperature").load().air 27 | else: 28 | air = pn.state.cache["air"] 29 | 30 | 31 | def get_plot(accent_base_color="blue"): 32 | plot = air.hvplot.scatter( 33 | "time", 34 | groupby=[], 35 | rasterize=True, 36 | dynspread=True, 37 | responsive=True, 38 | cmap="YlOrBr", 39 | colorbar=True, 40 | ) * air.mean(["lat", "lon"]).hvplot.line("time", color=accent_base_color, responsive=True) 41 | plot.opts(responsive=True, active_tools=["box_zoom"]) 42 | return plot 43 | 44 | 45 | plot = get_plot(accent_base_color=config.ACCENT_BASE_COLOR) 46 | 47 | pn.pane.HoloViews(plot, min_height=600, sizing_mode="stretch_both").servable() 48 | -------------------------------------------------------------------------------- /src/dataviz_in_python/presentation/lib_deckgl.py: -------------------------------------------------------------------------------- 1 | import panel as pn 2 | from dataviz_in_python import config 3 | 4 | config.configure("deckgl", url="lib_deckgl", title="DeckGl") 5 | 6 | THEME = config.get_theme() 7 | 8 | TEXT = """ 9 | # DeckGl: Declarative Visualization in Python 10 | 11 | [DeckGL](https://deck.gl/) is a WebGL-powered framework for visual exploratory data analysis of large datasets. 12 | 13 | For a more Pythonic interface see [PyDeck](lib_pydeck). 14 | 15 | [Source Code](https://github.com/MarcSkovMadsen/dataviz-in-python/blob/main/src/dataviz_in_python/presentation/lib_deckgl.py) 16 | """ 17 | pn.panel(TEXT, css_classes=[config.TEXT_CLASS]).servable() 18 | 19 | 20 | # Please create your own access token one your own Access tokens page 21 | # https://account.mapbox.com/access-tokens/ 22 | MAPBOX_KEY = ( 23 | "pk.eyJ1IjoicGFuZWxvcmciLCJhIjoiY2s1enA3ejhyMWhmZjNobjM1NXhtbWRrMyJ9.B_frQsAVepGIe-HiOJeqvQ" 24 | ) 25 | 26 | 27 | def get_plot(theme=THEME): 28 | if theme == "dark": 29 | deckgl_map_style = "mapbox://styles/mapbox/dark-v9" 30 | else: 31 | deckgl_map_style = "mapbox://styles/mapbox/light-v9" 32 | 33 | return { 34 | "initialViewState": { 35 | "bearing": -27.36, 36 | "latitude": 52.2323, 37 | "longitude": -1.415, 38 | "maxZoom": 15, 39 | "minZoom": 5, 40 | "pitch": 40.5, 41 | "zoom": 6, 42 | }, 43 | "layers": [ 44 | { 45 | "@@type": "HexagonLayer", 46 | "autoHighlight": True, 47 | "coverage": 1, 48 | "data": "https://raw.githubusercontent.com/uber-common/deck.gl-data/master/examples/3d-heatmap/heatmap-data.csv", 49 | "elevationRange": [0, 3000], 50 | "elevationScale": 50, 51 | "extruded": True, 52 | "getPosition": "@@=[lng, lat]", 53 | "id": "8a553b25-ef3a-489c-bbe2-e102d18a3211", 54 | "pickable": True, 55 | } 56 | ], 57 | "mapStyle": deckgl_map_style, 58 | "views": [{"@@type": "MapView", "controller": True}], 59 | } 60 | 61 | 62 | plot = get_plot() 63 | pn.pane.DeckGL(plot, mapbox_api_key=MAPBOX_KEY, sizing_mode="stretch_both", height=700).servable() 64 | -------------------------------------------------------------------------------- /src/dataviz_in_python/presentation/lib_echarts.py: -------------------------------------------------------------------------------- 1 | import panel as pn 2 | 3 | from dataviz_in_python import config 4 | 5 | config.configure("echarts", title="Echarts", url="lib_echarts") 6 | 7 | TEXT = """ 8 | # Echarts: Modern .js visualization in the browser 9 | 10 | [ECharts](https://echarts.apache.org/en/index.html) is a a very powerful and appealing open source javaScript visualization library. 11 | 12 | For a more Pythonic interface see [PyEcharts](lib_pyecharts). For another example see [awesome-panel.org/echarts](https://awesome-panel.org/echarts). 13 | 14 | [Source Code](https://github.com/MarcSkovMadsen/dataviz-in-python/blob/main/src/dataviz_in_python/presentation/lib_echarts.py) 15 | """ 16 | pn.panel(TEXT, css_classes=[config.TEXT_CLASS]).servable() 17 | 18 | 19 | def get_plot(): 20 | return { 21 | "xAxis": {"data": ["2017-10-24", "2017-10-25", "2017-10-26", "2017-10-27"]}, 22 | "yAxis": {}, 23 | "series": [ 24 | { 25 | "type": "k", 26 | "data": [ 27 | [20, 34, 10, 38], 28 | [40, 35, 30, 50], 29 | [31, 38, 33, 44], 30 | [38, 15, 5, 42], 31 | ], 32 | } 33 | ], 34 | "responsive": True, 35 | } 36 | 37 | 38 | plot = get_plot() 39 | pn.pane.ECharts(plot, min_height=700, sizing_mode="stretch_both").servable() 40 | -------------------------------------------------------------------------------- /src/dataviz_in_python/presentation/lib_folium.py: -------------------------------------------------------------------------------- 1 | import folium 2 | import panel as pn 3 | 4 | from html import escape # noqa 5 | 6 | from dataviz_in_python import config 7 | 8 | config.configure("vega", url="lib_folium", title="Folium") 9 | 10 | TEXT = """ 11 | # Folium: GeoViz using Python data wrangling and Leaflet.js 12 | 13 | [Folium](https://python-visualization.github.io/folium/) builds on the data wrangling strengths of the Python ecosystem and the mapping strengths of the leaflet.js library. Manipulate your data in Python, then visualize it in on a Leaflet map via folium 14 | 15 | Please note that Folium **does not support two-way events** like click etc. in your data apps. 16 | 17 | [Source Code](https://github.com/MarcSkovMadsen/dataviz-in-python/blob/main/src/dataviz_in_python/presentation/lib_folium.py) 18 | """ 19 | pn.panel(TEXT, css_classes=[config.TEXT_CLASS]).servable() 20 | 21 | 22 | def get_plot(): 23 | plot = folium.Map( 24 | location=[45.372, -121.6972], zoom_start=12, tiles="Stamen Terrain" 25 | ) # , width='100%', height="50%") 26 | 27 | folium.Marker( 28 | location=[45.3288, -121.6625], 29 | popup="Mt. Hood Meadows", 30 | icon=folium.Icon(icon="cloud"), 31 | ).add_to(plot) 32 | 33 | folium.Marker( 34 | location=[45.3311, -121.7113], 35 | popup="Timberline Lodge", 36 | icon=folium.Icon(color="green"), 37 | ).add_to(plot) 38 | 39 | folium.Marker( 40 | location=[45.3300, -121.6823], 41 | popup="Some Other Location", 42 | icon=folium.Icon(color="red", icon="info-sign"), 43 | ).add_to(plot) 44 | return plot 45 | 46 | 47 | plot = get_plot() 48 | 49 | 50 | def _get_properties(self): 51 | properties = pn.pane.HTML._get_properties(self) 52 | text = "" if self.object is None else self.object 53 | if hasattr(text, "_repr_html_"): 54 | text = text._repr_html_() 55 | before = '
    ' 56 | after = '
    ' 57 | text = text.replace(before, after) 58 | return dict(properties, text=escape(text)) 59 | 60 | 61 | # A Hack to be able to get responsive Folium plots 62 | pn.pane.plot.Folium._get_properties = _get_properties 63 | 64 | pn.pane.plot.Folium(plot, min_height=700, sizing_mode="stretch_both").servable() 65 | -------------------------------------------------------------------------------- /src/dataviz_in_python/presentation/lib_holoviews.py: -------------------------------------------------------------------------------- 1 | import holoviews as hv 2 | import numpy as np 3 | import panel as pn 4 | from holoviews import opts, streams 5 | from holoviews.plotting.links import DataLink 6 | 7 | from dataviz_in_python import config 8 | 9 | config.configure(url="lib_holoviews", title="HoloViews") 10 | 11 | TEXT = """ 12 | # HoloViews: Stop plotting your data - annotate your data and let it visualize itself. 13 | 14 | [HoloViews](http://holoviews.org/index.html) is an open-source Python library designed to make data analysis and visualization seamless and simple. With HoloViews, you can usually express what you want to do in very few lines of code, letting you focus on what you are trying to explore and convey, not on the process of plotting. 15 | 16 | You can use any of the backends [Bokeh](lib_bokeh), [Matplotlib](lib_matplotlib) and 17 | [Plotly](lib_plotly). Please note that Bokeh has the most comprehensive and robust support. 18 | 19 | HoloViews is much harder to learn than and less robust than Plotly. 20 | But it provides a lot of features for specialized use cases and a mental 21 | model for data visualiation. 22 | 23 | [Source Code](https://github.com/MarcSkovMadsen/dataviz-in-python/blob/main/src/dataviz_in_python/presentation/lib_holoviews.py) 24 | """ 25 | pn.panel(TEXT, css_classes=[config.TEXT_CLASS]).servable() 26 | 27 | 28 | def get_plot(theme="default", accent_base_color="blue"): 29 | curve = hv.Curve(np.random.randn(10).cumsum()).opts( 30 | min_height=600, 31 | responsive=True, 32 | line_width=6, 33 | color=accent_base_color, 34 | # https://github.com/holoviz/holoviews/issues/5058 35 | # active_tools=["point_draw"] 36 | ) 37 | if theme == "default": 38 | point_color = "black" 39 | else: 40 | point_color = "#E5E5E5" 41 | 42 | streams.CurveEdit(data=curve.columns(), source=curve, style={"color": point_color, "size": 10}) 43 | 44 | table = hv.Table(curve).opts(editable=True) 45 | DataLink(curve, table) 46 | 47 | return (curve + table).opts( 48 | opts.Table(editable=True), 49 | ) 50 | 51 | 52 | plot = get_plot(theme=config.get_theme(), accent_base_color=config.ACCENT_BASE_COLOR) 53 | pn.pane.HoloViews(plot, height=600, sizing_mode="stretch_both").servable() 54 | -------------------------------------------------------------------------------- /src/dataviz_in_python/presentation/lib_hvplot.py: -------------------------------------------------------------------------------- 1 | import hvplot.pandas # noqa 2 | import panel as pn 3 | from bokeh.sampledata.sprint import sprint 4 | 5 | from dataviz_in_python import config 6 | 7 | config.configure(url="lib_hvplot", title="hvplot") 8 | 9 | TEXT = """ 10 | # hvplot: A high-level plotting API for the PyData ecosystem built on HoloViews. 11 | 12 | [hvplot](https://hvplot.holoviz.org/) extends the [Pandas](https://pandas.pydata.org/) `.plot` api to other data frameworks like 13 | [Dask](https://docs.rapids.ai/api/cudf/stable/), 14 | [GeoPandas](https://geopandas.org/), 15 | [Intake](https://github.com/ContinuumIO/intake), 16 | [NetworkX](https://networkx.github.io/documentation/stable/). 17 | [Rapids cuDF](https://docs.rapids.ai/api/cudf/stable/), 18 | [Streamz](https://streamz.readthedocs.io/) and 19 | [XArray](https://xarray.pydata.org/), 20 | 21 | Currently the backend is [Bokeh](lib_bokeh). But that will soon be extended to 22 | [Matplotlib](lib_matplotlib) and [Plotly](lib_plotly). 23 | 24 | Personally I use `.hvplot` for simple data exploration and data apps. I like the simple api and the 25 | ability to combine plots with simple `*`, `+` and `/` operations. 26 | 27 | [Source Code](https://github.com/MarcSkovMadsen/dataviz-in-python/blob/main/src/dataviz_in_python/presentation/lib_hvplot.py) 28 | """ 29 | pn.panel(TEXT, css_classes=[config.TEXT_CLASS]).servable() 30 | 31 | 32 | def get_plot(): 33 | return sprint.hvplot.violin( 34 | y="Time", 35 | by="Medal", 36 | c="Medal", 37 | ylabel="Sprint Time", 38 | cmap=["gold", "silver", "brown"], 39 | legend=False, 40 | responsive=True, 41 | padding=0.4, 42 | ) 43 | 44 | 45 | plot = get_plot() 46 | pn.pane.HoloViews(plot, height=500, sizing_mode="stretch_both").servable() 47 | -------------------------------------------------------------------------------- /src/dataviz_in_python/presentation/lib_ipywidgets.py: -------------------------------------------------------------------------------- 1 | import panel as pn 2 | import ipywidgets as ipw 3 | import ipysheet 4 | 5 | from dataviz_in_python import config 6 | 7 | config.configure("ipywidgets", title="ipywidgets", url="lib_ipywidgets") 8 | 9 | TEXT = """ 10 | # Ipywidgets: Interactive Visualization in the Browser 11 | 12 | [Ipywidgets](https://ipywidgets.readthedocs.io/en/latest/) ipywidgets, also 13 | known as jupyter-widgets or simply widgets, are interactive HTML widgets for 14 | Jupyter notebooks, the IPython kernel and your 15 | [Panel](https://panel.holoviz.org) data app. 16 | 17 | A lot of data visualization widgets are built on top of ipywidgets. 18 | 19 | Here we are displaying [Ipysheet](https://github.com/QuantStack/ipysheet) 20 | 21 | [Source Code](https://github.com/MarcSkovMadsen/dataviz-in-python/blob/main/src/dataviz_in_python/presentation/lib_ipywidgets.py) 22 | """ 23 | pn.panel(TEXT, css_classes=[config.TEXT_CLASS]).servable() 24 | 25 | 26 | def get_widget(accent_base_color="blue"): 27 | slider = pn.widgets.FloatSlider(value=10, start=0, end=100) 28 | sheet = ipysheet.sheet() 29 | 30 | ipysheet.cell(1, 1, "Input") 31 | cell3 = ipysheet.cell(1, 2, 42.0) 32 | ipysheet.cell(2, 1, "Output") 33 | cell_sum = ipysheet.cell(2, 2, 52.0, read_only=True, background_color=accent_base_color) 34 | 35 | @pn.depends(slider, cell3, watch=True) 36 | def calculate(a, b): 37 | cell_sum.value = a + b 38 | 39 | return pn.Column(slider, sheet) 40 | 41 | 42 | widget = get_widget(accent_base_color=config.ACCENT_BASE_COLOR) 43 | pn.panel(widget, height=700, sizing_mode="stretch_both").servable() 44 | -------------------------------------------------------------------------------- /src/dataviz_in_python/presentation/lib_leaflet.py: -------------------------------------------------------------------------------- 1 | import numpy as np 2 | import panel as pn 3 | import holoviews as hv 4 | import xarray as xr 5 | import hvplot.xarray 6 | import param 7 | 8 | from dataviz_in_python import config 9 | 10 | config.configure(title="Leaflet", url="lib_leaflet") 11 | 12 | TEXT = """ 13 | # Leaflet: Mobile-friendly interactive maps 14 | 15 | [Leaflet](https://leafletjs.com/) is the leading open-source JavaScript library for mobile-friendly interactive maps. It has all the mapping features most developers ever need. 16 | 17 | You can use leaflet in Python via for example [IPyleaflet](https://ipyleaflet.readthedocs.io/en/latest/), Dash or as here via Panel. See also [Panel Ipyleaflet](https://discourse.holoviz.org/t/works-with-ipyleaflet/2755) 18 | 19 | [Source Code](https://github.com/MarcSkovMadsen/dataviz-in-python/blob/main/src/dataviz_in_python/presentation/lib_leaflet.py) 20 | """ 21 | pn.panel(TEXT, css_classes=[config.TEXT_CLASS]).servable() 22 | 23 | 24 | class LeafletMap(pn.reactive.ReactiveHTML): 25 | marker1_clicks = param.Integer() 26 | marker2_clicks = param.Integer() 27 | marker3_clicks = param.Integer() 28 | 29 | _template = """
    """ 30 | 31 | __css__ = ["https://unpkg.com/leaflet@1.7.1/dist/leaflet.css"] 32 | __javascript__ = ["https://unpkg.com/leaflet@1.7.1/dist/leaflet.js"] 33 | 34 | _scripts = { 35 | "render": """ 36 | var mymap = L.map(mapid).setView([50, 0], 6); 37 | state.map=mymap 38 | 39 | var tileLayer = L.tileLayer('https://api.mapbox.com/styles/v1/{id}/tiles/{z}/{x}/{y}?access_token={accessToken}', { 40 | attribution: 'Map data © OpenStreetMap contributors, Imagery © Mapbox', 41 | maxZoom: 18, 42 | id: 'mapbox/streets-v11', 43 | tileSize: 512, 44 | zoomOffset: -1, 45 | accessToken: 'pk.eyJ1IjoibWFyY3Nrb3ZtYWRzZW4iLCJhIjoiY2s1anMzcG5rMDYzazNvcm10NTFybTE4cSJ9.TV1XBgaMfR-iTLvAXM_Iew' 46 | }).addTo(mymap) 47 | 48 | var marker1 = L.marker([48, -2], name='marker1'); 49 | marker1.on('click', (e) => { data.marker1_clicks = data.marker1_clicks + 1 }) 50 | 51 | var marker2 = L.marker([50, 0], name='marker2'); 52 | marker2.on('click', (e) => { data.marker2_clicks = data.marker2_clicks + 1 }) 53 | 54 | var marker3 = L.marker([52, 2], name='marker3'); 55 | marker3.on('click', (e) => { data.marker3_clicks = data.marker3_clicks + 1 }) 56 | 57 | var layer1 = L.layerGroup([marker1, marker2, marker3]); 58 | layer1.addTo(mymap) 59 | L.control.layers({"tile1": tileLayer},{"layer1": layer1}).addTo(mymap); 60 | """, 61 | # Need to resize leaflet map to size of outer container 62 | "after_layout": """ 63 | state.map.invalidateSize();console.log("invalidated"); 64 | """, 65 | } 66 | 67 | 68 | map = LeafletMap(height=700) 69 | clicks = pn.Param( 70 | map, parameters=["marker1_clicks", "marker2_clicks", "marker3_clicks"], sizing_mode="fixed" 71 | ) 72 | 73 | pn.Row(map, clicks, sizing_mode="stretch_both").servable() 74 | -------------------------------------------------------------------------------- /src/dataviz_in_python/presentation/lib_matplotlib.py: -------------------------------------------------------------------------------- 1 | import matplotlib.pyplot as plt 2 | import numpy as np 3 | from matplotlib import cm 4 | from matplotlib.backends.backend_agg import FigureCanvas # not needed for mpl >= 3.1 5 | from matplotlib.figure import Figure 6 | import panel as pn 7 | 8 | from dataviz_in_python import config 9 | 10 | config.configure("vega", url="lib_matplotlib", title="Matplotlib") 11 | 12 | TEXT = """ 13 | # Matplotlib: Static plots in Python without limits 14 | 15 | [Matplotlib](https://matplotlib.org/) is a comprehensive library for creating static, animated, and interactive visualizations in Python. Matplotlib makes easy things easy and hard things possible. Matplotlib is the most used plotting library in Python. 16 | 17 | Matplotlib only supports interactivity to a limited extent compared to Altair, Bokeh, HoloViews and Plotly. So personally I normally don't use it. 18 | 19 | [Source Code](https://github.com/MarcSkovMadsen/dataviz-in-python/blob/main/src/dataviz_in_python/presentation/lib_matplotlib.py) 20 | """ 21 | pn.panel(TEXT, css_classes=[config.TEXT_CLASS]).servable() 22 | 23 | 24 | def get_plot(theme="default"): 25 | plt.style.use("default") 26 | if theme == "dark": 27 | plt.style.use("dark_background") 28 | Y, X = np.mgrid[-3:3:100j, -3:3:100j] 29 | U = -1 - X**2 + Y 30 | V = 1 + X - Y**2 31 | speed = np.sqrt(U * U + V * V) 32 | 33 | fig0 = Figure(figsize=(12, 6)) 34 | ax0 = fig0.subplots() 35 | FigureCanvas(fig0) # not needed for mpl >= 3.1 36 | 37 | strm = ax0.streamplot(X, Y, U, V, color=U, linewidth=2, cmap=cm.autumn) 38 | fig0.colorbar(strm.lines) 39 | return fig0 40 | 41 | 42 | plot = get_plot(theme=config.get_theme()) 43 | pn.pane.Matplotlib(plot, height=600, sizing_mode="scale_height").servable() 44 | -------------------------------------------------------------------------------- /src/dataviz_in_python/presentation/lib_plotly.py: -------------------------------------------------------------------------------- 1 | import pandas as pd 2 | import panel as pn 3 | 4 | import plotly.express as px 5 | 6 | from dataviz_in_python import config 7 | 8 | config.configure("plotly", url="lib_plotly", title="Plotly") 9 | 10 | TEXT = """ 11 | # Plotly: High Quality, Interactive Plots in Python 12 | 13 | [Plotly](https://plotly.com/python/)'s Python graphing library makes interactive, publication-quality graph. 14 | 15 | Plotly is one of the most popular plotting libraries in Python. It has high quality documentation and produces very appealing plots. 16 | 17 | One thing to note is that Plotly is a also a company that has a hard time finding funding for developing Plotly. 18 | Increasingly they focus on and market Dash the data app framework because there is a business case. 19 | 20 | [Source Code](https://github.com/MarcSkovMadsen/dataviz-in-python/blob/main/src/dataviz_in_python/presentation/lib_plotly.py) 21 | """ 22 | pn.panel(TEXT, css_classes=[config.TEXT_CLASS]).servable() 23 | 24 | 25 | def get_plot(theme="default", accent_base_color="blue"): 26 | data = pd.DataFrame( 27 | [ 28 | ("Monday", 7), 29 | ("Tuesday", 4), 30 | ("Wednesday", 9), 31 | ("Thursday", 4), 32 | ("Friday", 4), 33 | ("Saturday", 4), 34 | ("Sunay", 4), 35 | ], 36 | columns=["Day", "Orders"], 37 | ) 38 | 39 | if theme == "dark": 40 | plotly_template = "plotly_dark" 41 | else: 42 | plotly_template = "plotly" 43 | 44 | fig = px.line( 45 | data, 46 | x="Day", 47 | y="Orders", 48 | template=plotly_template, 49 | color_discrete_sequence=(accent_base_color,), 50 | ) 51 | fig.update_traces(mode="lines+markers", marker=dict(size=10), line=dict(width=4)) 52 | fig.layout.autosize = True 53 | return fig 54 | 55 | 56 | plot = get_plot(theme=config.get_theme(), accent_base_color=config.ACCENT_BASE_COLOR) 57 | pn.pane.Plotly(plot, config={"responsive": True}, sizing_mode="stretch_both", height=700).servable() 58 | -------------------------------------------------------------------------------- /src/dataviz_in_python/presentation/lib_plotnine.py: -------------------------------------------------------------------------------- 1 | import matplotlib.pyplot as plt 2 | import panel as pn 3 | 4 | from plotnine import aes, element_rect, facet_wrap, geom_point, ggplot, stat_smooth, themes 5 | from plotnine.data import mtcars 6 | from dataviz_in_python import config 7 | 8 | config.configure(url="lib_plotnine", title="Plotnine") 9 | 10 | TEXT = """ 11 | # Plotnine: A Grammar of Graphics for Python 12 | 13 | [Plotnine](https://plotnine.readthedocs.io/en/stable/) is an implementation of a grammar of graphics in Python, it is based on [ggplot2](https://ggplot2.tidyverse.org/). The grammar allows users to compose plots by explicitly mapping data to the visual objects that make up the plot. Its based on Matplotlib. 14 | 15 | [Source Code](https://github.com/MarcSkovMadsen/dataviz-in-python/blob/main/src/dataviz_in_python/presentation/lib_plotnine.py) 16 | """ 17 | pn.panel(TEXT, css_classes=[config.TEXT_CLASS]).servable() 18 | 19 | 20 | def get_plot(theme="default"): 21 | plt.style.use("default") 22 | if theme == "dark": 23 | plotnine_theme = themes.theme_dark() + themes.theme( 24 | plot_background=element_rect(fill="black", alpha=0) 25 | ) 26 | else: 27 | plotnine_theme = themes.theme_xkcd() 28 | 29 | plot = ( 30 | ( 31 | ggplot(mtcars, aes("wt", "mpg", color="factor(gear)")) 32 | + geom_point() 33 | + stat_smooth(method="lm") 34 | + facet_wrap("~gear") 35 | ) 36 | + plotnine_theme 37 | + themes.theme(figure_size=(16, 8)) 38 | ) 39 | return plot.draw() 40 | 41 | 42 | plot = get_plot(theme=config.get_theme()) 43 | 44 | pn.pane.Matplotlib(plot, height=700, sizing_mode="stretch_both").servable() 45 | -------------------------------------------------------------------------------- /src/dataviz_in_python/presentation/lib_pydeck.py: -------------------------------------------------------------------------------- 1 | import pydeck 2 | import panel as pn 3 | 4 | from dataviz_in_python import config 5 | 6 | config.configure("deckgl", url="lib_pydeck", title="PyDeck") 7 | 8 | TEXT = """ 9 | # PyDeck: High-scale spatial rendering in Python, powered by deck.gl. 10 | 11 | [PyDeck](https://deckgl.readthedocs.io/en/latest/) is a Python wrapper for [deck.gl](lib_deckgl). 12 | 13 | [Source Code](https://github.com/MarcSkovMadsen/dataviz-in-python/blob/main/src/dataviz_in_python/presentation/lib_deckgl.py) 14 | """ 15 | pn.panel(TEXT, css_classes=[config.TEXT_CLASS]).servable() 16 | 17 | 18 | def get_plot(theme="default", accent_base_color="blue"): 19 | LAND_COVER = [[[-123.0, 49.196], [-123.0, 49.324], [-123.306, 49.324], [-123.306, 49.196]]] 20 | polygon = pydeck.Layer( 21 | "PolygonLayer", 22 | LAND_COVER, 23 | stroked=False, 24 | # processes the data as a flat longitude-latitude pair 25 | get_polygon="-", 26 | get_fill_color=[0, 0, 0, 20], 27 | ) 28 | 29 | DATA_URL = "https://raw.githubusercontent.com/uber-common/deck.gl-data/master/examples/geojson/vancouver-blocks.json" 30 | geojson = pydeck.Layer( 31 | "GeoJsonLayer", 32 | DATA_URL, 33 | opacity=0.8, 34 | stroked=False, 35 | filled=True, 36 | extruded=True, 37 | wireframe=True, 38 | get_elevation="properties.valuePerSqm / 20", 39 | get_fill_color="[255, 255, properties.growth * 255]", 40 | get_line_color=[255, 255, 255], 41 | pickable=True, 42 | ) 43 | 44 | if theme == "dark": 45 | deckgl_map_style = "mapbox://styles/mapbox/dark-v9" 46 | else: 47 | deckgl_map_style = "mapbox://styles/mapbox/light-v9" 48 | MAPBOX_KEY = ( 49 | "pk.eyJ1IjoicGFuZWxvcmciLCJhIjoiY2s1enA3ejhyMWhmZjNobjM1NXhtbWRrMyJ9.B_frQsAVepGIe-HiOJeqvQ" 50 | ) 51 | INITIAL_VIEW_STATE = pydeck.ViewState( 52 | latitude=49.254, longitude=-123.13, zoom=11, max_zoom=16, pitch=45, bearing=0 53 | ) 54 | 55 | r = pydeck.Deck( 56 | api_keys={"mapbox": MAPBOX_KEY}, 57 | layers=[polygon, geojson], 58 | initial_view_state=INITIAL_VIEW_STATE, 59 | map_style=deckgl_map_style, 60 | ) 61 | 62 | # Tooltip (you can get the id directly from the layer object) 63 | geojson_tooltip = { 64 | "html": """ 65 | Value per Square meter: {properties.valuePerSqm}
    66 | Growth: {properties.growth} 67 | """, 68 | "style": {"backgroundColor": accent_base_color, "color": "white"}, 69 | } 70 | tooltips = {geojson.id: geojson_tooltip} 71 | return r, tooltips 72 | 73 | 74 | plot, tooltips = get_plot() 75 | 76 | pn.pane.DeckGL(plot, tooltips=tooltips, height=800, sizing_mode="stretch_both").servable() 77 | -------------------------------------------------------------------------------- /src/dataviz_in_python/presentation/lib_pyecharts.py: -------------------------------------------------------------------------------- 1 | import json 2 | import panel as pn 3 | 4 | from pyecharts.charts import Bar 5 | 6 | from dataviz_in_python import config 7 | 8 | config.configure("echarts", url="lib_pyecharts", title="PyECharts") 9 | 10 | TEXT = """ 11 | # PyEcharts: A Python wrapper for ECharts 12 | 13 | [PyEcharts](https://pyecharts.org/#/) is a Python wrapper for [ECharts](lib_echarts). 14 | 15 | Please note **currently I cannot recommend PyEcharts** as the documentation often seems out of date 16 | and the examples not working. But Echarts and thus PyEcharts is an Apache project and produces 17 | business plots of a higher quality than Plotly. So it worth monitoring. 18 | 19 | [Source Code](https://github.com/MarcSkovMadsen/dataviz-in-python/blob/main/src/dataviz_in_python/presentation/lib_pyecharts.py) 20 | """ 21 | pn.panel(TEXT, css_classes=[config.TEXT_CLASS]).servable() 22 | 23 | 24 | def get_plot(accent_base_color="blue"): 25 | plot = ( 26 | Bar() 27 | .add_xaxis(["Helicoptors", "Planes", "Air Ballons"]) 28 | .add_yaxis("Total In Flight", [50, 75, 25], color=accent_base_color) 29 | ) 30 | 31 | # Workaround to make plot responsive 32 | plot = json.loads(plot.dump_options()) 33 | plot["responsive"] = True 34 | return plot 35 | 36 | 37 | theme = config.get_theme() 38 | plot = get_plot(accent_base_color=config.ACCENT_BASE_COLOR) 39 | pn.pane.ECharts(plot, min_height=500, sizing_mode="stretch_both", theme=theme).servable() 40 | -------------------------------------------------------------------------------- /src/dataviz_in_python/presentation/lib_pyvista.py: -------------------------------------------------------------------------------- 1 | import json 2 | import panel as pn 3 | import pyvista as pv 4 | 5 | from dataviz_in_python import config 6 | 7 | config.configure("vtk", url="lib_vista", title="PyVista") 8 | 9 | TEXT = """ 10 | # PyVista: 3D plotting and mesh analysis through a streamlined interface for the Visualization Toolkit (VTK) 11 | 12 | [PyVista](https://docs.pyvista.org/) PyVista is a helper module for the Visualization Toolkit (VTK) 13 | that takes a different approach on interfacing with VTK through NumPy and direct array access 14 | 15 | [Source Code](https://github.com/MarcSkovMadsen/dataviz-in-python/blob/main/src/dataviz_in_python/presentation/lib_pyvista.py) 16 | """ 17 | pn.panel(TEXT, css_classes=[config.TEXT_CLASS]).servable() 18 | 19 | 20 | def get_plot(theme="default", accent_base_color="blue"): 21 | plotter = pv.Plotter() # we define a pyvista plotter 22 | if theme == "dark": 23 | plotter.background_color = (0.13, 0.13, 0.13) 24 | else: 25 | plotter.background_color = (0.97, 0.97, 0.97) 26 | 27 | # we create a `VTK` panel around the render window 28 | pvcylinder = pv.Cylinder(resolution=8, direction=(0, 1, 0)) 29 | cylinder_actor = plotter.add_mesh(pvcylinder, color=accent_base_color, smooth_shading=True) 30 | cylinder_actor.RotateX(30.0) 31 | cylinder_actor.RotateY(-45.0) 32 | plotter.add_mesh( 33 | pv.Sphere(theta_resolution=8, phi_resolution=8, center=(0.5, 0.5, 0.5)), 34 | color=accent_base_color, 35 | smooth_shading=True, 36 | ) 37 | return plotter.ren_win 38 | 39 | 40 | plot = get_plot(theme=config.get_theme(), accent_base_color=config.ACCENT_BASE_COLOR) 41 | pn.panel(plot, height=700, sizing_mode="stretch_both").servable() 42 | -------------------------------------------------------------------------------- /src/dataviz_in_python/presentation/lib_seaborn.py: -------------------------------------------------------------------------------- 1 | import matplotlib.pyplot as plt 2 | 3 | import panel as pn 4 | import seaborn as sns 5 | 6 | penguins = sns.load_dataset("penguins") 7 | 8 | from dataviz_in_python import config 9 | 10 | config.configure(url="lib_seaborn", title="Seaborn") 11 | 12 | TEXT = """ 13 | # Seaborn: Statistical data visualization 14 | 15 | [Seaborn](https://seaborn.pydata.org/) Seaborn is a Python data visualization 16 | library based on [matplotlib](lib_matplotlib). It provides a high-level 17 | interface for drawing attractive and informative statistical graphics. 18 | 19 | Please note the lead developer is tweeting about upcoming, major changes to 20 | the api. 21 | 22 | [Source Code](https://github.com/MarcSkovMadsen/dataviz-in-python/blob/main/src/dataviz_in_python/presentation/lib_seaborn.py) 23 | """ 24 | pn.panel(TEXT, css_classes=[config.TEXT_CLASS]).servable() 25 | 26 | 27 | def get_plot(theme="default", accent_base_color="blue"): 28 | if theme == "dark": 29 | sns.set_style("darkgrid") 30 | plt.style.use("dark_background") 31 | else: 32 | plt.style.use("default") 33 | sns.set_style("whitegrid") 34 | 35 | plot = sns.displot(penguins, x="flipper_length_mm", color=accent_base_color) 36 | fig0 = plot.fig 37 | fig0.set_size_inches(16, 8) 38 | return fig0 39 | 40 | 41 | plot = get_plot(theme=config.get_theme(), accent_base_color=config.ACCENT_BASE_COLOR) 42 | pn.pane.Matplotlib(plot, height=700, sizing_mode="stretch_both").servable() 43 | -------------------------------------------------------------------------------- /src/dataviz_in_python/presentation/lib_vega.py: -------------------------------------------------------------------------------- 1 | import panel as pn 2 | 3 | from dataviz_in_python import config 4 | 5 | config.configure(url="lib_vega", title="Vega & Vega-Lite") 6 | 7 | TEXT = """ 8 | # Vega: A Grammar of Graphics for Python 9 | 10 | [Vega](https://vega.github.io/) is a declarative format for creating, saving, and sharing 11 | visualization designs. With Vega, visualizations are described in JSON, and 12 | generate interactive views using either HTML5 Canvas or SVG. 13 | 14 | Please note that **you should be using the higher level Vega-Lite format.** 15 | 16 | [Source Code](https://github.com/MarcSkovMadsen/dataviz-in-python/blob/main/src/dataviz_in_python/presentation/lib_vega.py) 17 | """ 18 | pn.panel(TEXT, css_classes=[config.TEXT_CLASS]).servable() 19 | 20 | 21 | def get_plot(theme="default"): 22 | vegalite = { 23 | "$schema": "https://vega.github.io/schema/vega-lite/v3.json", 24 | "data": {"url": "https://raw.githubusercontent.com/vega/vega/master/docs/data/barley.json"}, 25 | "mark": {"type": "bar", "tooltip": True}, 26 | "width": "container", 27 | "height": "container", 28 | "encoding": { 29 | "x": {"aggregate": "sum", "field": "yield", "type": "quantitative"}, 30 | "y": {"field": "variety", "type": "nominal"}, 31 | "color": {"field": "site", "type": "nominal"}, 32 | }, 33 | } 34 | 35 | if theme == "dark": 36 | vegalite["config"] = { 37 | "background": "#333", 38 | "title": {"color": "#fff"}, 39 | "style": {"guide-label": {"fill": "#fff"}, "guide-title": {"fill": "#fff"}}, 40 | "axis": {"domainColor": "#fff", "gridColor": "#888", "tickColor": "#fff"}, 41 | } 42 | return vegalite 43 | 44 | 45 | plot = get_plot(theme=config.get_theme()) 46 | pn.pane.Vega(plot, height=700, sizing_mode="stretch_both").servable() 47 | -------------------------------------------------------------------------------- /src/dataviz_in_python/presentation/lib_vtk.py: -------------------------------------------------------------------------------- 1 | import panel as pn 2 | import vtk 3 | 4 | from vtk.util.colors import tomato 5 | 6 | from dataviz_in_python import config 7 | 8 | config.configure("vtk", url="lib_vtk", title="VTK") 9 | 10 | TEXT = """ 11 | # VTK: Process images and create 3D computer graphics with the Visualization Toolkit. 12 | 13 | [VTK](https://vtk.org/) is open source software for manipulating and 14 | displaying scientific data. It comes with state-of-the-art tools for 3D 15 | rendering, a suite of widgets for 3D interaction, and extensive 2D 16 | plotting capability. 17 | 18 | [Source Code](https://github.com/MarcSkovMadsen/dataviz-in-python/blob/main/src/dataviz_in_python/presentation/lib_vtk.py) 19 | """ 20 | pn.panel(TEXT, css_classes=[config.TEXT_CLASS]).servable() 21 | 22 | 23 | def get_plot(theme="default", accent_base_color="red"): 24 | # This creates a polygonal cylinder model with eight circumferential 25 | # facets. 26 | cylinder = vtk.vtkCylinderSource() 27 | cylinder.SetResolution(8) 28 | 29 | # The mapper is responsible for pushing the geometry into the graphics 30 | # library. It may also do color mapping, if scalars or other 31 | # attributes are defined. 32 | cylinderMapper = vtk.vtkPolyDataMapper() 33 | cylinderMapper.SetInputConnection(cylinder.GetOutputPort()) 34 | 35 | # The actor is a grouping mechanism: besides the geometry (mapper), it 36 | # also has a property, transformation matrix, and/or texture map. 37 | # Here we set its color and rotate it -22.5 degrees. 38 | cylinderActor = vtk.vtkActor() 39 | cylinderActor.SetMapper(cylinderMapper) 40 | cylinderActor.GetProperty().SetColor(tomato) 41 | # We must set ScalarVisibilty to 0 to use tomato Color 42 | cylinderMapper.SetScalarVisibility(0) 43 | cylinderActor.RotateX(30.0) 44 | cylinderActor.RotateY(-45.0) 45 | 46 | # Create the graphics structure. The renderer renders into the render 47 | # window. 48 | ren = vtk.vtkRenderer() 49 | renWin = vtk.vtkRenderWindow() 50 | renWin.AddRenderer(ren) 51 | 52 | # Add the actors to the renderer, set the background and size 53 | ren.AddActor(cylinderActor) 54 | if theme == "dark": 55 | ren.SetBackground(0.13, 0.13, 0.13) 56 | else: 57 | ren.SetBackground(0.97, 0.97, 0.97) 58 | return renWin 59 | 60 | 61 | plot = get_plot(theme=config.get_theme()) 62 | component = pn.pane.VTK(plot, height=700, sizing_mode="stretch_both").servable() 63 | -------------------------------------------------------------------------------- /src/dataviz_in_python/presentation/streaming.py: -------------------------------------------------------------------------------- 1 | import numpy as np 2 | import pandas as pd 3 | import panel as pn 4 | 5 | from dataviz_in_python import config 6 | 7 | config.configure(title="Streaming") 8 | 9 | TEXT = """ 10 | # Streaming: Live data or story telling with a time dimension 11 | 12 | [Source Code](https://github.com/MarcSkovMadsen/dataviz-in-python/blob/main/src/dataviz_in_python/presentation/streaming.py) 13 | """ 14 | pn.panel(TEXT, css_classes=[config.TEXT_CLASS]).servable() 15 | 16 | layout = pn.layout.FlexBox( 17 | *( 18 | pn.indicators.Trend( 19 | data={"x": list(range(10)), "y": np.random.randn(10).cumsum()}, 20 | width=200, 21 | height=150, 22 | plot_type=pn.indicators.Trend.param.plot_type.objects[i % 4], 23 | ) 24 | for i in range(36) 25 | ) 26 | ).servable() 27 | 28 | 29 | def stream(): 30 | for trend in layout: 31 | trend.stream( 32 | {"x": [trend.data["x"][-1] + 1], "y": [trend.data["y"][-1] + np.random.randn()]}, 33 | rollover=20, 34 | ) 35 | 36 | 37 | cb = pn.state.add_periodic_callback(stream, 500) 38 | --------------------------------------------------------------------------------