diff --git a/gapminders/anaconda-project-lock.yml b/gapminders/anaconda-project-lock.yml
index c1efb00c5..69f29e7bd 100644
--- a/gapminders/anaconda-project-lock.yml
+++ b/gapminders/anaconda-project-lock.yml
@@ -10,528 +10,595 @@
# Set to false to ignore locked versions.
#
locking_enabled: true
-
-#
-# A key goes in here for each env spec.
-#
env_specs:
- test:
- locked: true
- env_spec_hash: a8d706af59fa3a30148011cc332c2cc035881c84
- platforms:
- - linux-64
- - osx-64
- - win-64
- packages:
- all:
- - altair=3.3.0=py37_0
- - async_generator=1.10=py_0
- - attrs=21.2.0=pyhd8ed1ab_0
- - backcall=0.2.0=pyh9f0ad1d_0
- - backports.functools_lru_cache=1.6.4=pyhd8ed1ab_0
- - backports=1.0=py_2
- - bleach=3.3.1=pyhd8ed1ab_0
- - bokeh=2.2.1=py37hc8dfbb8_1
- - charset-normalizer=2.0.0=pyhd8ed1ab_0
- - colorcet=2.0.6=pyhd8ed1ab_0
- - cycler=0.10.0=py_2
- - decorator=5.0.9=pyhd8ed1ab_0
- - defusedxml=0.7.1=pyhd8ed1ab_0
- - entrypoints=0.3=pyhd8ed1ab_1003
- - holoviews=1.14.1=pyhd3deb0d_0
- - hvplot=0.7.0=pyhd3deb0d_0
- - idna=3.1=pyhd3deb0d_0
- - ipython_genutils=0.2.0=py_1
- - jinja2=3.0.1=pyhd8ed1ab_0
- - jsonschema=3.2.0=pyhd8ed1ab_3
- - jupyter_client=6.1.12=pyhd8ed1ab_0
- - jupyterlab_pygments=0.1.2=pyh9f0ad1d_0
- - markdown=3.3.4=pyhd8ed1ab_0
- - matplotlib-inline=0.1.2=pyhd8ed1ab_2
- - nbclient=0.5.3=pyhd8ed1ab_0
- - nbformat=5.1.3=pyhd8ed1ab_0
- - nest-asyncio=1.5.1=pyhd8ed1ab_0
- - notebook=5.7.8=py37_1
- - olefile=0.46=pyh9f0ad1d_1
- - packaging=21.0=pyhd8ed1ab_0
- - pandocfilters=1.4.2=py_1
- - panel=0.10.2=pyhd8ed1ab_0
- - param=1.10.0=py_0
- - parso=0.8.2=pyhd8ed1ab_0
- - pickleshare=0.7.5=py_1003
- - pip=21.2.1=pyhd8ed1ab_0
- - plotly=5.1.0=pyhd8ed1ab_1
- - prometheus_client=0.11.0=pyhd8ed1ab_0
- - prompt-toolkit=3.0.19=pyha770c72_0
- - pycparser=2.20=pyh9f0ad1d_2
- - pyct-core=0.4.6=py_0
- - pyct=0.4.6=py_0
- - pygments=2.9.0=pyhd8ed1ab_0
- - pyopenssl=20.0.1=pyhd8ed1ab_0
- - pyparsing=2.4.7=pyh9f0ad1d_0
- - python-dateutil=2.8.2=pyhd8ed1ab_0
- - python_abi=3.7=2_cp37m
- - pytz=2021.1=pyhd8ed1ab_0
- - pyviz_comms=2.1.0=pyhd8ed1ab_0
- - requests=2.26.0=pyhd8ed1ab_0
- - send2trash=1.7.1=pyhd8ed1ab_0
- - six=1.16.0=pyh6c4a22f_0
- - tenacity=8.0.1=pyhd8ed1ab_0
- - testpath=0.5.0=pyhd8ed1ab_0
- - toolz=0.11.1=py_0
- - tqdm=4.61.2=pyhd8ed1ab_1
- - traitlets=5.0.5=py_0
- - typing_extensions=3.10.0.0=pyha770c72_0
- - urllib3=1.26.6=pyhd8ed1ab_0
- - wcwidth=0.2.5=pyh9f0ad1d_2
- - webencodings=0.5.1=py_1
- - wheel=0.36.2=pyhd3deb0d_0
- - zipp=3.5.0=pyhd8ed1ab_0
- unix:
- - libblas=3.9.0=10_openblas
- - libcblas=3.9.0=10_openblas
- - liblapack=3.9.0=10_openblas
- - pexpect=4.8.0=pyh9f0ad1d_2
- - ptyprocess=0.7.0=pyhd3deb0d_0
- linux-64:
- - _libgcc_mutex=0.1=conda_forge
- - _openmp_mutex=4.5=1_gnu
- - brotlipy=0.7.0=py37h5e8e339_1001
- - ca-certificates=2021.5.30=ha878542_0
- - certifi=2021.5.30=py37h89c1867_0
- - cffi=1.14.6=py37hc58025e_0
- - chardet=4.0.0=py37h89c1867_1
- - cryptography=3.4.7=py37h5d9358c_0
- - freetype=2.10.4=h0708190_1
- - importlib-metadata=4.6.1=py37h89c1867_0
- - ipykernel=5.5.5=py37h085eea5_0
- - ipython=7.25.0=py37h085eea5_1
- - jbig=2.1=h7f98852_2003
- - jedi=0.18.0=py37h89c1867_2
- - jpeg=9d=h36c2ea0_0
- - jupyter_core=4.7.1=py37h89c1867_0
- - kiwisolver=1.3.1=py37h2527ec5_1
- - lcms2=2.12=hddcbb42_0
- - ld_impl_linux-64=2.36.1=hea4e1c9_2
- - lerc=2.2.1=h9c3ff4c_0
- - libdeflate=1.7=h7f98852_5
- - libffi=3.3=h58526e2_2
- - libgcc-ng=11.1.0=hc902ee8_4
- - libgfortran-ng=11.1.0=h69a702a_4
- - libgfortran5=11.1.0=h6c583b3_4
- - libgomp=11.1.0=hc902ee8_4
- - libopenblas=0.3.17=pthreads_h8fe5266_1
- - libpng=1.6.37=h21135ba_2
- - libsodium=1.0.18=h36c2ea0_1
- - libstdcxx-ng=11.1.0=h56837e0_4
- - libtiff=4.3.0=hf544144_1
- - libwebp-base=1.2.0=h7f98852_2
- - lz4-c=1.9.3=h9c3ff4c_0
- - markupsafe=2.0.1=py37h5e8e339_0
- - matplotlib-base=3.4.2=py37hdd32ed1_0
- - mistune=0.8.4=py37h5e8e339_1004
- - nbconvert=6.1.0=py37h89c1867_0
- - ncurses=6.2=h58526e2_4
- - numpy=1.21.1=py37h038b26d_0
- - openjpeg=2.4.0=hb52868f_1
- - openssl=1.1.1k=h7f98852_0
- - pandas=1.2.5=py37h219a48f_0
- - pandoc=2.14.1=h7f98852_0
- - pillow=8.3.1=py37h0f21c89_0
- - pyrsistent=0.17.3=py37h5e8e339_2
- - pysocks=1.7.1=py37h89c1867_3
- - python=3.7.10=hffdb5ce_100_cpython
- - pyyaml=5.4.1=py37h5e8e339_0
- - pyzmq=22.1.0=py37h336d617_0
- - readline=8.1=h46c0cb4_0
- - setuptools=49.6.0=py37h89c1867_3
- - sqlite=3.36.0=h9cd32fc_0
- - terminado=0.10.1=py37h89c1867_0
- - tk=8.6.10=h21135ba_1
- - tornado=6.1=py37h5e8e339_1
- - xz=5.2.5=h516909a_1
- - yaml=0.2.5=h516909a_0
- - zeromq=4.3.4=h9c3ff4c_0
- - zlib=1.2.11=h516909a_1010
- - zstd=1.5.0=ha95c52a_0
- osx-64:
- - appnope=0.1.2=py37hf985489_1
- - brotlipy=0.7.0=py37h271585c_1001
- - ca-certificates=2021.5.30=h033912b_0
- - certifi=2021.5.30=py37hf985489_0
- - cffi=1.14.6=py37hda351d9_0
- - chardet=4.0.0=py37hf985489_1
- - cryptography=3.4.7=py37hce4a858_0
- - freetype=2.10.4=h4cff582_1
- - importlib-metadata=4.6.1=py37hf985489_0
- - ipykernel=5.5.5=py37h85f7c60_0
- - ipython=7.25.0=py37h85f7c60_1
- - jbig=2.1=h0d85af4_2003
- - jedi=0.18.0=py37hf985489_2
- - jpeg=9d=hbcb3906_0
- - jupyter_core=4.7.1=py37hf985489_0
- - kiwisolver=1.3.1=py37h737db71_1
- - lcms2=2.12=h577c468_0
- - lerc=2.2.1=h046ec9c_0
- - libcxx=12.0.1=habf9029_0
- - libdeflate=1.7=h35c211d_5
- - libffi=3.3=h046ec9c_2
- - libgfortran5=9.3.0=h6c81a4c_23
- - libgfortran=5.0.0=9_3_0_h6c81a4c_23
- - libopenblas=0.3.17=openmp_h3351f45_1
- - libpng=1.6.37=h7cec526_2
- - libsodium=1.0.18=hbcb3906_1
- - libtiff=4.3.0=h1167814_1
- - libwebp-base=1.2.0=h0d85af4_2
- - llvm-openmp=12.0.1=hda6cdc1_1
- - lz4-c=1.9.3=h046ec9c_0
- - markupsafe=2.0.1=py37h271585c_0
- - matplotlib-base=3.4.2=py37hb018525_0
- - mistune=0.8.4=py37h271585c_1004
- - nbconvert=6.1.0=py37hf985489_0
- - ncurses=6.2=h2e338ed_4
- - numpy=1.21.1=py37h84c02c4_0
- - openjpeg=2.4.0=h6e7aa92_1
- - openssl=1.1.1k=h0d85af4_0
- - pandas=1.2.5=py37hb23ed4d_0
- - pandoc=2.14.1=h0d85af4_0
- - pillow=8.3.1=py37h76dc067_0
- - pyrsistent=0.17.3=py37h271585c_2
- - pysocks=1.7.1=py37hf985489_3
- - python=3.7.10=h7728216_100_cpython
- - pyyaml=5.4.1=py37hf967b71_0
- - pyzmq=22.1.0=py37h8f778e5_0
- - readline=8.1=h05e3726_0
- - setuptools=49.6.0=py37hf985489_3
- - sqlite=3.36.0=h23a322b_0
- - terminado=0.10.1=py37hf985489_0
- - tk=8.6.10=h0419947_1
- - tornado=6.1=py37h271585c_1
- - xz=5.2.5=haf1e3a3_1
- - yaml=0.2.5=haf1e3a3_0
- - zeromq=4.3.4=h1c7c35f_0
- - zlib=1.2.11=h7795811_1010
- - zstd=1.5.0=h582d3a0_0
- win-64:
- - brotlipy=0.7.0=py37hcc03f2d_1001
- - ca-certificates=2021.5.30=h5b45459_0
- - certifi=2021.5.30=py37h03978a9_0
- - cffi=1.14.6=py37hd8e9650_0
- - chardet=4.0.0=py37h03978a9_1
- - colorama=0.4.4=pyh9f0ad1d_0
- - cryptography=3.4.7=py37h20c650d_0
- - freetype=2.10.4=h546665d_1
- - importlib-metadata=4.6.1=py37h03978a9_0
- - intel-openmp=2021.3.0=h57928b3_3372
- - ipykernel=5.5.5=py37h7813e69_0
- - ipython=7.25.0=py37h7813e69_1
- - jbig=2.1=h8d14728_2003
- - jedi=0.18.0=py37h03978a9_2
- - jpeg=9d=h8ffe710_0
- - jupyter_core=4.7.1=py37h03978a9_0
- - kiwisolver=1.3.1=py37h8c56517_1
- - lcms2=2.12=h2a16943_0
- - lerc=2.2.1=h0e60522_0
- - libblas=3.9.0=10_mkl
- - libcblas=3.9.0=10_mkl
- - libdeflate=1.7=h8ffe710_5
- - liblapack=3.9.0=10_mkl
- - libpng=1.6.37=h1d00b33_2
- - libsodium=1.0.18=h8d14728_1
- - libtiff=4.3.0=h0c97f57_1
- - lz4-c=1.9.3=h8ffe710_0
- - markupsafe=2.0.1=py37hcc03f2d_0
- - matplotlib-base=3.4.2=py37h0d1fb12_0
- - mistune=0.8.4=py37hcc03f2d_1004
- - mkl=2021.3.0=hb70f87d_564
- - nbconvert=6.1.0=py37h03978a9_0
- - numpy=1.21.1=py37hcbcd69c_0
- - openjpeg=2.4.0=hb211442_1
- - openssl=1.1.1k=h8ffe710_0
- - pandas=1.2.5=py37h90003fb_0
- - pandoc=2.14.1=h8ffe710_0
- - pillow=8.3.1=py37hd7d9ad0_0
- - pyrsistent=0.17.3=py37hcc03f2d_2
- - pysocks=1.7.1=py37h03978a9_3
- - python=3.7.10=h7840368_100_cpython
- - pywin32=300=py37hcc03f2d_0
- - pywinpty=1.1.3=py37h7f67f24_0
- - pyyaml=5.4.1=py37hcc03f2d_0
- - pyzmq=22.1.0=py37hcce574b_0
- - setuptools=49.6.0=py37h03978a9_3
- - sqlite=3.36.0=h8ffe710_0
- - tbb=2021.3.0=h2d74725_0
- - terminado=0.10.1=py37h03978a9_0
- - tk=8.6.10=h8ffe710_1
- - tornado=6.1=py37hcc03f2d_1
- - ucrt=10.0.20348.0=h57928b3_0
- - vc=14.2=hb210afc_5
- - vs2015_runtime=14.29.30037=h902a5da_5
- - win_inet_pton=1.1.0=py37h03978a9_2
- - wincertstore=0.2=py37h03978a9_1006
- - winpty=0.4.3=4
- - xz=5.2.5=h62dcd97_1
- - yaml=0.2.5=he774522_0
- - zeromq=4.3.4=h0e60522_0
- - zlib=1.2.11=h62dcd97_1010
- - zstd=1.5.0=h6255e5f_0
default:
locked: true
- env_spec_hash: a8d706af59fa3a30148011cc332c2cc035881c84
+ env_spec_hash: 9e8077cd3d1eff25e335cea353a80d59eb4da2a2
platforms:
- linux-64
- osx-64
+ - osx-arm64
- win-64
packages:
all:
- - altair=3.3.0=py37_0
- - async_generator=1.10=py_0
- - attrs=21.2.0=pyhd8ed1ab_0
- - backcall=0.2.0=pyh9f0ad1d_0
- - backports.functools_lru_cache=1.6.4=pyhd8ed1ab_0
- - backports=1.0=py_2
- - bleach=3.3.1=pyhd8ed1ab_0
- - bokeh=2.2.1=py37hc8dfbb8_1
- - charset-normalizer=2.0.0=pyhd8ed1ab_0
- - colorcet=2.0.6=pyhd8ed1ab_0
- - cycler=0.10.0=py_2
- - decorator=5.0.9=pyhd8ed1ab_0
- - defusedxml=0.7.1=pyhd8ed1ab_0
- - entrypoints=0.3=pyhd8ed1ab_1003
- - holoviews=1.14.1=pyhd3deb0d_0
- - hvplot=0.7.0=pyhd3deb0d_0
- - idna=3.1=pyhd3deb0d_0
- - ipython_genutils=0.2.0=py_1
- - jinja2=3.0.1=pyhd8ed1ab_0
- - jsonschema=3.2.0=pyhd8ed1ab_3
- - jupyter_client=6.1.12=pyhd8ed1ab_0
- - jupyterlab_pygments=0.1.2=pyh9f0ad1d_0
- - markdown=3.3.4=pyhd8ed1ab_0
- - matplotlib-inline=0.1.2=pyhd8ed1ab_2
- - nbclient=0.5.3=pyhd8ed1ab_0
- - nbformat=5.1.3=pyhd8ed1ab_0
- - nest-asyncio=1.5.1=pyhd8ed1ab_0
- - notebook=5.7.8=py37_1
- - olefile=0.46=pyh9f0ad1d_1
- - packaging=21.0=pyhd8ed1ab_0
- - pandocfilters=1.4.2=py_1
- - panel=0.10.2=pyhd8ed1ab_0
- - param=1.10.0=py_0
- - parso=0.8.2=pyhd8ed1ab_0
- - pickleshare=0.7.5=py_1003
- - pip=21.2.1=pyhd8ed1ab_0
- - plotly=5.1.0=pyhd8ed1ab_1
- - prometheus_client=0.11.0=pyhd8ed1ab_0
- - prompt-toolkit=3.0.19=pyha770c72_0
- - pycparser=2.20=pyh9f0ad1d_2
- - pyct-core=0.4.6=py_0
- - pyct=0.4.6=py_0
- - pygments=2.9.0=pyhd8ed1ab_0
- - pyopenssl=20.0.1=pyhd8ed1ab_0
- - pyparsing=2.4.7=pyh9f0ad1d_0
- - python-dateutil=2.8.2=pyhd8ed1ab_0
- - python_abi=3.7=2_cp37m
- - pytz=2021.1=pyhd8ed1ab_0
- - pyviz_comms=2.1.0=pyhd8ed1ab_0
- - requests=2.26.0=pyhd8ed1ab_0
- - send2trash=1.7.1=pyhd8ed1ab_0
- - six=1.16.0=pyh6c4a22f_0
- - tenacity=8.0.1=pyhd8ed1ab_0
- - testpath=0.5.0=pyhd8ed1ab_0
- - toolz=0.11.1=py_0
- - tqdm=4.61.2=pyhd8ed1ab_1
- - traitlets=5.0.5=py_0
- - typing_extensions=3.10.0.0=pyha770c72_0
- - urllib3=1.26.6=pyhd8ed1ab_0
- - wcwidth=0.2.5=pyh9f0ad1d_2
- - webencodings=0.5.1=py_1
- - wheel=0.36.2=pyhd3deb0d_0
- - zipp=3.5.0=pyhd8ed1ab_0
+ - argon2-cffi=21.3.0=pyhd3eb1b0_0
+ - asttokens=2.0.5=pyhd3eb1b0_0
+ - backcall=0.2.0=pyhd3eb1b0_0
+ - bleach=4.1.0=pyhd3eb1b0_0
+ - charset-normalizer=2.0.4=pyhd3eb1b0_0
+ - cycler=0.11.0=pyhd3eb1b0_0
+ - decorator=5.1.1=pyhd3eb1b0_0
+ - defusedxml=0.7.1=pyhd3eb1b0_0
+ - executing=0.8.3=pyhd3eb1b0_0
+ - fonttools=4.25.0=pyhd3eb1b0_0
+ - ipython_genutils=0.2.0=pyhd3eb1b0_1
+ - jupyterlab_pygments=0.1.2=py_0
+ - munkres=1.1.4=py_0
+ - pandocfilters=1.5.0=pyhd3eb1b0_0
+ - parso=0.8.3=pyhd3eb1b0_0
+ - pickleshare=0.7.5=pyhd3eb1b0_1003
+ - pure_eval=0.2.2=pyhd3eb1b0_0
+ - pycparser=2.21=pyhd3eb1b0_0
+ - python-dateutil=2.8.2=pyhd3eb1b0_0
+ - python-tzdata=2023.3=pyhd3eb1b0_0
+ - send2trash=1.8.0=pyhd3eb1b0_1
+ - six=1.16.0=pyhd3eb1b0_1
+ - stack_data=0.2.0=pyhd3eb1b0_0
+ - tzdata=2023c=h04d1e81_0
+ - wcwidth=0.2.5=pyhd3eb1b0_0
unix:
- - libblas=3.9.0=10_openblas
- - libcblas=3.9.0=10_openblas
- - liblapack=3.9.0=10_openblas
- - pexpect=4.8.0=pyh9f0ad1d_2
- - ptyprocess=0.7.0=pyhd3deb0d_0
+ - pexpect=4.8.0=pyhd3eb1b0_3
+ - ptyprocess=0.7.0=pyhd3eb1b0_2
linux-64:
- - _libgcc_mutex=0.1=conda_forge
- - _openmp_mutex=4.5=1_gnu
- - brotlipy=0.7.0=py37h5e8e339_1001
- - ca-certificates=2021.5.30=ha878542_0
- - certifi=2021.5.30=py37h89c1867_0
- - cffi=1.14.6=py37hc58025e_0
- - chardet=4.0.0=py37h89c1867_1
- - cryptography=3.4.7=py37h5d9358c_0
- - freetype=2.10.4=h0708190_1
- - importlib-metadata=4.6.1=py37h89c1867_0
- - ipykernel=5.5.5=py37h085eea5_0
- - ipython=7.25.0=py37h085eea5_1
- - jbig=2.1=h7f98852_2003
- - jedi=0.18.0=py37h89c1867_2
- - jpeg=9d=h36c2ea0_0
- - jupyter_core=4.7.1=py37h89c1867_0
- - kiwisolver=1.3.1=py37h2527ec5_1
- - lcms2=2.12=hddcbb42_0
- - ld_impl_linux-64=2.36.1=hea4e1c9_2
- - lerc=2.2.1=h9c3ff4c_0
- - libdeflate=1.7=h7f98852_5
- - libffi=3.3=h58526e2_2
- - libgcc-ng=11.1.0=hc902ee8_4
- - libgfortran-ng=11.1.0=h69a702a_4
- - libgfortran5=11.1.0=h6c583b3_4
- - libgomp=11.1.0=hc902ee8_4
- - libopenblas=0.3.17=pthreads_h8fe5266_1
- - libpng=1.6.37=h21135ba_2
- - libsodium=1.0.18=h36c2ea0_1
- - libstdcxx-ng=11.1.0=h56837e0_4
- - libtiff=4.3.0=hf544144_1
- - libwebp-base=1.2.0=h7f98852_2
- - lz4-c=1.9.3=h9c3ff4c_0
- - markupsafe=2.0.1=py37h5e8e339_0
- - matplotlib-base=3.4.2=py37hdd32ed1_0
- - mistune=0.8.4=py37h5e8e339_1004
- - nbconvert=6.1.0=py37h89c1867_0
- - ncurses=6.2=h58526e2_4
- - numpy=1.21.1=py37h038b26d_0
- - openjpeg=2.4.0=hb52868f_1
- - openssl=1.1.1k=h7f98852_0
- - pandas=1.2.5=py37h219a48f_0
- - pandoc=2.14.1=h7f98852_0
- - pillow=8.3.1=py37h0f21c89_0
- - pyrsistent=0.17.3=py37h5e8e339_2
- - pysocks=1.7.1=py37h89c1867_3
- - python=3.7.10=hffdb5ce_100_cpython
- - pyyaml=5.4.1=py37h5e8e339_0
- - pyzmq=22.1.0=py37h336d617_0
- - readline=8.1=h46c0cb4_0
- - setuptools=49.6.0=py37h89c1867_3
- - sqlite=3.36.0=h9cd32fc_0
- - terminado=0.10.1=py37h89c1867_0
- - tk=8.6.10=h21135ba_1
- - tornado=6.1=py37h5e8e339_1
- - xz=5.2.5=h516909a_1
- - yaml=0.2.5=h516909a_0
- - zeromq=4.3.4=h9c3ff4c_0
- - zlib=1.2.11=h516909a_1010
- - zstd=1.5.0=ha95c52a_0
+ - _libgcc_mutex=0.1=main
+ - _openmp_mutex=5.1=1_gnu
+ - altair=5.0.1=py39h06a4308_0
+ - anyio=3.5.0=py39h06a4308_0
+ - argon2-cffi-bindings=21.2.0=py39h7f8727e_0
+ - attrs=23.1.0=py39h06a4308_0
+ - beautifulsoup4=4.12.2=py39h06a4308_0
+ - blas=1.0=mkl
+ - bokeh=3.2.1=py39h2f386ee_0
+ - bottleneck=1.3.5=py39h7deecbd_0
+ - brotli-bin=1.0.9=h5eee18b_7
+ - brotli-python=1.0.9=py39h6a678d5_7
+ - brotli=1.0.9=h5eee18b_7
+ - ca-certificates=2023.08.22=h06a4308_0
+ - certifi=2023.7.22=py39h06a4308_0
+ - cffi=1.15.1=py39h5eee18b_3
+ - colorcet=3.0.1=py39h06a4308_0
+ - comm=0.1.2=py39h06a4308_0
+ - contourpy=1.0.5=py39hdb19cb5_0
+ - cryptography=41.0.3=py39hdda0065_0
+ - debugpy=1.6.7=py39h6a678d5_0
+ - entrypoints=0.4=py39h06a4308_0
+ - exceptiongroup=1.0.4=py39h06a4308_0
+ - freetype=2.12.1=h4a9f257_0
+ - giflib=5.2.1=h5eee18b_3
+ - holoviews=1.17.1=py39h06a4308_0
+ - hvplot=0.9.0=py39h06a4308_0
+ - icu=73.1=h6a678d5_0
+ - idna=3.4=py39h06a4308_0
+ - importlib-metadata=6.0.0=py39h06a4308_0
+ - importlib_resources=6.1.0=py39h06a4308_0
+ - intel-openmp=2023.1.0=hdb19cb5_46305
+ - ipykernel=6.25.0=py39h2f386ee_0
+ - ipython=8.15.0=py39h06a4308_0
+ - jedi=0.18.1=py39h06a4308_1
+ - jinja2=3.1.2=py39h06a4308_0
+ - jpeg=9e=h5eee18b_1
+ - jsonschema=4.17.3=py39h06a4308_0
+ - jupyter_client=7.4.9=py39h06a4308_0
+ - jupyter_core=5.3.0=py39h06a4308_0
+ - jupyter_server=1.23.4=py39h06a4308_0
+ - kiwisolver=1.4.4=py39h6a678d5_0
+ - lcms2=2.12=h3be6417_0
+ - ld_impl_linux-64=2.38=h1181459_1
+ - lerc=3.0=h295c915_0
+ - libbrotlicommon=1.0.9=h5eee18b_7
+ - libbrotlidec=1.0.9=h5eee18b_7
+ - libbrotlienc=1.0.9=h5eee18b_7
+ - libdeflate=1.17=h5eee18b_1
+ - libffi=3.4.4=h6a678d5_0
+ - libgcc-ng=11.2.0=h1234567_1
+ - libgomp=11.2.0=h1234567_1
+ - libpng=1.6.39=h5eee18b_0
+ - libsodium=1.0.18=h7b6447c_0
+ - libstdcxx-ng=11.2.0=h1234567_1
+ - libtiff=4.5.1=h6a678d5_0
+ - libwebp-base=1.3.2=h5eee18b_0
+ - libwebp=1.3.2=h11a3e52_0
+ - libxml2=2.10.4=hf1b16e4_1
+ - libxslt=1.1.37=h5eee18b_1
+ - linkify-it-py=2.0.0=py39h06a4308_0
+ - lxml=4.9.3=py39hdbbb534_0
+ - lz4-c=1.9.4=h6a678d5_0
+ - markdown-it-py=2.2.0=py39h06a4308_1
+ - markdown=3.4.1=py39h06a4308_0
+ - markupsafe=2.1.1=py39h7f8727e_0
+ - matplotlib-base=3.8.0=py39h1128e8f_0
+ - matplotlib-inline=0.1.6=py39h06a4308_0
+ - mdit-py-plugins=0.3.0=py39h06a4308_0
+ - mdurl=0.1.0=py39h06a4308_0
+ - mistune=0.8.4=py39h27cfd23_1000
+ - mkl-service=2.4.0=py39h5eee18b_1
+ - mkl=2023.1.0=h213fc3f_46343
+ - mkl_fft=1.3.8=py39h5eee18b_0
+ - mkl_random=1.2.4=py39hdb19cb5_0
+ - nbclassic=0.5.5=py39h06a4308_0
+ - nbclient=0.5.13=py39h06a4308_0
+ - nbconvert=6.5.4=py39h06a4308_0
+ - nbformat=5.9.2=py39h06a4308_0
+ - ncurses=6.4=h6a678d5_0
+ - nest-asyncio=1.5.6=py39h06a4308_0
+ - notebook-shim=0.2.2=py39h06a4308_0
+ - notebook=6.5.4=py39h06a4308_1
+ - numexpr=2.8.7=py39h85018f9_0
+ - numpy-base=1.26.0=py39hb5e798b_0
+ - numpy=1.26.0=py39h5f9d8c6_0
+ - openjpeg=2.4.0=h3ad879b_0
+ - openssl=3.0.11=h7f8727e_2
+ - packaging=23.1=py39h06a4308_0
+ - pandas=2.1.1=py39h1128e8f_0
+ - panel=1.2.3=py39h06a4308_0
+ - param=1.13.0=py39h06a4308_0
+ - pillow=10.0.1=py39ha6cbd5a_0
+ - pip=23.3=py39h06a4308_0
+ - platformdirs=3.10.0=py39h06a4308_0
+ - plotly=5.9.0=py39h06a4308_0
+ - prometheus_client=0.14.1=py39h06a4308_0
+ - prompt-toolkit=3.0.36=py39h06a4308_0
+ - psutil=5.9.0=py39h5eee18b_0
+ - pyct=0.5.0=py39h06a4308_0
+ - pygments=2.15.1=py39h06a4308_1
+ - pyopenssl=23.2.0=py39h06a4308_0
+ - pyparsing=3.0.9=py39h06a4308_0
+ - pyrsistent=0.18.0=py39heee7806_0
+ - pysocks=1.7.1=py39h06a4308_0
+ - python-fastjsonschema=2.16.2=py39h06a4308_0
+ - python=3.9.18=h955ad1f_0
+ - pytz=2023.3.post1=py39h06a4308_0
+ - pyviz_comms=2.3.0=py39h06a4308_0
+ - pyyaml=6.0.1=py39h5eee18b_0
+ - pyzmq=23.2.0=py39h6a678d5_0
+ - readline=8.2=h5eee18b_0
+ - requests=2.31.0=py39h06a4308_0
+ - setuptools=68.0.0=py39h06a4308_0
+ - sniffio=1.2.0=py39h06a4308_1
+ - soupsieve=2.5=py39h06a4308_0
+ - sqlite=3.41.2=h5eee18b_0
+ - tbb=2021.8.0=hdb19cb5_0
+ - tenacity=8.2.2=py39h06a4308_0
+ - terminado=0.17.1=py39h06a4308_0
+ - tinycss2=1.2.1=py39h06a4308_0
+ - tk=8.6.12=h1ccaba5_0
+ - toolz=0.12.0=py39h06a4308_0
+ - tornado=6.3.3=py39h5eee18b_0
+ - tqdm=4.65.0=py39hb070fc8_0
+ - traitlets=5.7.1=py39h06a4308_0
+ - typing-extensions=4.7.1=py39h06a4308_0
+ - typing_extensions=4.7.1=py39h06a4308_0
+ - uc-micro-py=1.0.1=py39h06a4308_0
+ - urllib3=1.26.18=py39h06a4308_0
+ - webencodings=0.5.1=py39h06a4308_1
+ - websocket-client=0.58.0=py39h06a4308_4
+ - wheel=0.41.2=py39h06a4308_0
+ - xyzservices=2022.9.0=py39h06a4308_1
+ - xz=5.4.2=h5eee18b_0
+ - yaml=0.2.5=h7b6447c_0
+ - zeromq=4.3.4=h2531618_0
+ - zipp=3.11.0=py39h06a4308_0
+ - zlib=1.2.13=h5eee18b_0
+ - zstd=1.5.5=hc292b87_0
osx-64:
- - appnope=0.1.2=py37hf985489_1
- - brotlipy=0.7.0=py37h271585c_1001
- - ca-certificates=2021.5.30=h033912b_0
- - certifi=2021.5.30=py37hf985489_0
- - cffi=1.14.6=py37hda351d9_0
- - chardet=4.0.0=py37hf985489_1
- - cryptography=3.4.7=py37hce4a858_0
- - freetype=2.10.4=h4cff582_1
- - importlib-metadata=4.6.1=py37hf985489_0
- - ipykernel=5.5.5=py37h85f7c60_0
- - ipython=7.25.0=py37h85f7c60_1
- - jbig=2.1=h0d85af4_2003
- - jedi=0.18.0=py37hf985489_2
- - jpeg=9d=hbcb3906_0
- - jupyter_core=4.7.1=py37hf985489_0
- - kiwisolver=1.3.1=py37h737db71_1
- - lcms2=2.12=h577c468_0
- - lerc=2.2.1=h046ec9c_0
- - libcxx=12.0.1=habf9029_0
- - libdeflate=1.7=h35c211d_5
- - libffi=3.3=h046ec9c_2
- - libgfortran5=9.3.0=h6c81a4c_23
- - libgfortran=5.0.0=9_3_0_h6c81a4c_23
- - libopenblas=0.3.17=openmp_h3351f45_1
- - libpng=1.6.37=h7cec526_2
- - libsodium=1.0.18=hbcb3906_1
- - libtiff=4.3.0=h1167814_1
- - libwebp-base=1.2.0=h0d85af4_2
- - llvm-openmp=12.0.1=hda6cdc1_1
- - lz4-c=1.9.3=h046ec9c_0
- - markupsafe=2.0.1=py37h271585c_0
- - matplotlib-base=3.4.2=py37hb018525_0
- - mistune=0.8.4=py37h271585c_1004
- - nbconvert=6.1.0=py37hf985489_0
- - ncurses=6.2=h2e338ed_4
- - numpy=1.21.1=py37h84c02c4_0
- - openjpeg=2.4.0=h6e7aa92_1
- - openssl=1.1.1k=h0d85af4_0
- - pandas=1.2.5=py37hb23ed4d_0
- - pandoc=2.14.1=h0d85af4_0
- - pillow=8.3.1=py37h76dc067_0
- - pyrsistent=0.17.3=py37h271585c_2
- - pysocks=1.7.1=py37hf985489_3
- - python=3.7.10=h7728216_100_cpython
- - pyyaml=5.4.1=py37hf967b71_0
- - pyzmq=22.1.0=py37h8f778e5_0
- - readline=8.1=h05e3726_0
- - setuptools=49.6.0=py37hf985489_3
- - sqlite=3.36.0=h23a322b_0
- - terminado=0.10.1=py37hf985489_0
- - tk=8.6.10=h0419947_1
- - tornado=6.1=py37h271585c_1
- - xz=5.2.5=haf1e3a3_1
+ - altair=5.0.1=py39hecd8cb5_0
+ - anyio=3.5.0=py39hecd8cb5_0
+ - appnope=0.1.2=py39hecd8cb5_1001
+ - argon2-cffi-bindings=21.2.0=py39hca72f7f_0
+ - attrs=23.1.0=py39hecd8cb5_0
+ - beautifulsoup4=4.12.2=py39hecd8cb5_0
+ - blas=1.0=mkl
+ - bokeh=3.2.1=py39h20db666_0
+ - bottleneck=1.3.5=py39h67323c0_0
+ - brotli-bin=1.0.9=hca72f7f_7
+ - brotli-python=1.0.9=py39he9d5cce_7
+ - brotli=1.0.9=hca72f7f_7
+ - ca-certificates=2023.08.22=hecd8cb5_0
+ - certifi=2023.7.22=py39hecd8cb5_0
+ - cffi=1.15.1=py39h6c40b1e_3
+ - colorcet=3.0.1=py39hecd8cb5_0
+ - comm=0.1.2=py39hecd8cb5_0
+ - contourpy=1.0.5=py39haf03e11_0
+ - cryptography=41.0.3=py39h30e54ef_0
+ - debugpy=1.6.7=py39hcec6c5f_0
+ - entrypoints=0.4=py39hecd8cb5_0
+ - exceptiongroup=1.0.4=py39hecd8cb5_0
+ - freetype=2.12.1=hd8bbffd_0
+ - giflib=5.2.1=h6c40b1e_3
+ - holoviews=1.17.1=py39hecd8cb5_0
+ - hvplot=0.9.0=py39hecd8cb5_0
+ - icu=73.1=hcec6c5f_0
+ - idna=3.4=py39hecd8cb5_0
+ - importlib-metadata=6.0.0=py39hecd8cb5_0
+ - importlib_resources=6.1.0=py39hecd8cb5_0
+ - intel-openmp=2023.1.0=ha357a0b_43547
+ - ipykernel=6.25.0=py39h20db666_0
+ - ipython=8.15.0=py39hecd8cb5_0
+ - jedi=0.18.1=py39hecd8cb5_1
+ - jinja2=3.1.2=py39hecd8cb5_0
+ - jpeg=9e=h6c40b1e_1
+ - jsonschema=4.17.3=py39hecd8cb5_0
+ - jupyter_client=7.4.9=py39hecd8cb5_0
+ - jupyter_core=5.3.0=py39hecd8cb5_0
+ - jupyter_server=1.23.4=py39hecd8cb5_0
+ - kiwisolver=1.4.4=py39hcec6c5f_0
+ - lcms2=2.12=hf1fd2bf_0
+ - lerc=3.0=he9d5cce_0
+ - libbrotlicommon=1.0.9=hca72f7f_7
+ - libbrotlidec=1.0.9=hca72f7f_7
+ - libbrotlienc=1.0.9=hca72f7f_7
+ - libcxx=14.0.6=h9765a3e_0
+ - libdeflate=1.17=hb664fd8_1
+ - libffi=3.4.4=hecd8cb5_0
+ - libiconv=1.16=hca72f7f_2
+ - libpng=1.6.39=h6c40b1e_0
+ - libsodium=1.0.18=h1de35cc_0
+ - libtiff=4.5.1=hcec6c5f_0
+ - libwebp-base=1.3.2=h6c40b1e_0
+ - libwebp=1.3.2=hf6ce154_0
+ - libxml2=2.10.4=h1bd7e62_1
+ - libxslt=1.1.37=h6c40b1e_1
+ - linkify-it-py=2.0.0=py39hecd8cb5_0
+ - lxml=4.9.3=py39h946e0e5_0
+ - lz4-c=1.9.4=hcec6c5f_0
+ - markdown-it-py=2.2.0=py39hecd8cb5_1
+ - markdown=3.4.1=py39hecd8cb5_0
+ - markupsafe=2.1.1=py39hca72f7f_0
+ - matplotlib-base=3.8.0=py39hb47e01b_0
+ - matplotlib-inline=0.1.6=py39hecd8cb5_0
+ - mdit-py-plugins=0.3.0=py39hecd8cb5_0
+ - mdurl=0.1.0=py39hecd8cb5_0
+ - mistune=0.8.4=py39h9ed2024_1000
+ - mkl-service=2.4.0=py39h6c40b1e_1
+ - mkl=2023.1.0=h8e150cf_43559
+ - mkl_fft=1.3.8=py39h6c40b1e_0
+ - mkl_random=1.2.4=py39ha357a0b_0
+ - nbclassic=0.5.5=py39hecd8cb5_0
+ - nbclient=0.5.13=py39hecd8cb5_0
+ - nbconvert=6.5.4=py39hecd8cb5_0
+ - nbformat=5.9.2=py39hecd8cb5_0
+ - ncurses=6.4=hcec6c5f_0
+ - nest-asyncio=1.5.6=py39hecd8cb5_0
+ - notebook-shim=0.2.2=py39hecd8cb5_0
+ - notebook=6.5.4=py39hecd8cb5_1
+ - numexpr=2.8.7=py39h827a554_0
+ - numpy-base=1.26.0=py39ha186be2_0
+ - numpy=1.26.0=py39h827a554_0
+ - openjpeg=2.4.0=h66ea3da_0
+ - openssl=3.0.11=hca72f7f_2
+ - packaging=23.1=py39hecd8cb5_0
+ - pandas=2.1.1=py39h3ea8b11_0
+ - panel=1.2.3=py39hecd8cb5_0
+ - param=1.13.0=py39hecd8cb5_0
+ - pillow=10.0.1=py39h7d39338_0
+ - pip=23.3=py39hecd8cb5_0
+ - platformdirs=3.10.0=py39hecd8cb5_0
+ - plotly=5.9.0=py39hecd8cb5_0
+ - prometheus_client=0.14.1=py39hecd8cb5_0
+ - prompt-toolkit=3.0.36=py39hecd8cb5_0
+ - psutil=5.9.0=py39hca72f7f_0
+ - pyct=0.5.0=py39hecd8cb5_0
+ - pygments=2.15.1=py39hecd8cb5_1
+ - pyopenssl=23.2.0=py39hecd8cb5_0
+ - pyparsing=3.0.9=py39hecd8cb5_0
+ - pyrsistent=0.18.0=py39hca72f7f_0
+ - pysocks=1.7.1=py39hecd8cb5_0
+ - python-fastjsonschema=2.16.2=py39hecd8cb5_0
+ - python=3.9.18=h5ee71fb_0
+ - pytz=2023.3.post1=py39hecd8cb5_0
+ - pyviz_comms=2.3.0=py39hecd8cb5_0
+ - pyyaml=6.0.1=py39h6c40b1e_0
+ - pyzmq=23.2.0=py39he9d5cce_0
+ - readline=8.2=hca72f7f_0
+ - requests=2.31.0=py39hecd8cb5_0
+ - setuptools=68.0.0=py39hecd8cb5_0
+ - sniffio=1.2.0=py39hecd8cb5_1
+ - soupsieve=2.5=py39hecd8cb5_0
+ - sqlite=3.41.2=h6c40b1e_0
+ - tbb=2021.8.0=ha357a0b_0
+ - tenacity=8.2.2=py39hecd8cb5_0
+ - terminado=0.17.1=py39hecd8cb5_0
+ - tinycss2=1.2.1=py39hecd8cb5_0
+ - tk=8.6.12=h5d9f67b_0
+ - toolz=0.12.0=py39hecd8cb5_0
+ - tornado=6.3.3=py39h6c40b1e_0
+ - tqdm=4.65.0=py39h01d92e1_0
+ - traitlets=5.7.1=py39hecd8cb5_0
+ - typing-extensions=4.7.1=py39hecd8cb5_0
+ - typing_extensions=4.7.1=py39hecd8cb5_0
+ - uc-micro-py=1.0.1=py39hecd8cb5_0
+ - urllib3=1.26.18=py39hecd8cb5_0
+ - webencodings=0.5.1=py39hecd8cb5_1
+ - websocket-client=0.58.0=py39hecd8cb5_4
+ - wheel=0.41.2=py39hecd8cb5_0
+ - xyzservices=2022.9.0=py39hecd8cb5_1
+ - xz=5.4.2=h6c40b1e_0
- yaml=0.2.5=haf1e3a3_0
- - zeromq=4.3.4=h1c7c35f_0
- - zlib=1.2.11=h7795811_1010
- - zstd=1.5.0=h582d3a0_0
+ - zeromq=4.3.4=h23ab428_0
+ - zipp=3.11.0=py39hecd8cb5_0
+ - zlib=1.2.13=h4dc903c_0
+ - zstd=1.5.5=hc035e20_0
+ osx-arm64:
+ - altair=5.0.1=py39hca03da5_0
+ - anyio=3.5.0=py39hca03da5_0
+ - appnope=0.1.2=py39hca03da5_1001
+ - argon2-cffi-bindings=21.2.0=py39h1a28f6b_0
+ - attrs=23.1.0=py39hca03da5_0
+ - beautifulsoup4=4.12.2=py39hca03da5_0
+ - blas=1.0=openblas
+ - bokeh=3.2.1=py39h33ce5c2_0
+ - bottleneck=1.3.5=py39heec5a64_0
+ - brotli-bin=1.0.9=h1a28f6b_7
+ - brotli-python=1.0.9=py39hc377ac9_7
+ - brotli=1.0.9=h1a28f6b_7
+ - ca-certificates=2023.08.22=hca03da5_0
+ - certifi=2023.7.22=py39hca03da5_0
+ - cffi=1.15.1=py39h80987f9_3
+ - colorcet=3.0.1=py39hca03da5_0
+ - comm=0.1.2=py39hca03da5_0
+ - contourpy=1.0.5=py39h525c30c_0
+ - cryptography=41.0.3=py39hd4332d6_0
+ - debugpy=1.6.7=py39h313beb8_0
+ - entrypoints=0.4=py39hca03da5_0
+ - exceptiongroup=1.0.4=py39hca03da5_0
+ - freetype=2.12.1=h1192e45_0
+ - giflib=5.2.1=h80987f9_3
+ - holoviews=1.17.1=py39hca03da5_0
+ - hvplot=0.9.0=py39hca03da5_0
+ - icu=73.1=h313beb8_0
+ - idna=3.4=py39hca03da5_0
+ - importlib-metadata=6.0.0=py39hca03da5_0
+ - importlib_resources=6.1.0=py39hca03da5_0
+ - ipykernel=6.25.0=py39h33ce5c2_0
+ - ipython=8.15.0=py39hca03da5_0
+ - jedi=0.18.1=py39hca03da5_1
+ - jinja2=3.1.2=py39hca03da5_0
+ - jpeg=9e=h80987f9_1
+ - jsonschema=4.17.3=py39hca03da5_0
+ - jupyter_client=7.4.9=py39hca03da5_0
+ - jupyter_core=5.3.0=py39hca03da5_0
+ - jupyter_server=1.23.4=py39hca03da5_0
+ - kiwisolver=1.4.4=py39h313beb8_0
+ - lcms2=2.12=hba8e193_0
+ - lerc=3.0=hc377ac9_0
+ - libbrotlicommon=1.0.9=h1a28f6b_7
+ - libbrotlidec=1.0.9=h1a28f6b_7
+ - libbrotlienc=1.0.9=h1a28f6b_7
+ - libcxx=14.0.6=h848a8c0_0
+ - libdeflate=1.17=h80987f9_1
+ - libffi=3.4.4=hca03da5_0
+ - libgfortran5=11.3.0=h009349e_28
+ - libgfortran=5.0.0=11_3_0_hca03da5_28
+ - libiconv=1.16=h1a28f6b_2
+ - libopenblas=0.3.21=h269037a_0
+ - libpng=1.6.39=h80987f9_0
+ - libsodium=1.0.18=h1a28f6b_0
+ - libtiff=4.5.1=h313beb8_0
+ - libwebp-base=1.3.2=h80987f9_0
+ - libwebp=1.3.2=ha3663a8_0
+ - libxml2=2.10.4=h0dcf63f_1
+ - libxslt=1.1.37=h80987f9_1
+ - linkify-it-py=2.0.0=py39hca03da5_0
+ - llvm-openmp=14.0.6=hc6e5704_0
+ - lxml=4.9.3=py39h50ffb84_0
+ - lz4-c=1.9.4=h313beb8_0
+ - markdown-it-py=2.2.0=py39hca03da5_1
+ - markdown=3.4.1=py39hca03da5_0
+ - markupsafe=2.1.1=py39h1a28f6b_0
+ - matplotlib-base=3.8.0=py39h46d7db6_0
+ - matplotlib-inline=0.1.6=py39hca03da5_0
+ - mdit-py-plugins=0.3.0=py39hca03da5_0
+ - mdurl=0.1.0=py39hca03da5_0
+ - mistune=0.8.4=py39h1a28f6b_1000
+ - nbclassic=0.5.5=py39hca03da5_0
+ - nbclient=0.5.13=py39hca03da5_0
+ - nbconvert=6.5.4=py39hca03da5_0
+ - nbformat=5.9.2=py39hca03da5_0
+ - ncurses=6.4=h313beb8_0
+ - nest-asyncio=1.5.6=py39hca03da5_0
+ - notebook-shim=0.2.2=py39hca03da5_0
+ - notebook=6.5.4=py39hca03da5_1
+ - numexpr=2.8.7=py39hecc3335_0
+ - numpy-base=1.26.0=py39ha9811e2_0
+ - numpy=1.26.0=py39h3b2db8e_0
+ - openjpeg=2.3.0=h7a6adac_2
+ - openssl=3.0.11=h1a28f6b_2
+ - packaging=23.1=py39hca03da5_0
+ - pandas=2.1.1=py39h46d7db6_0
+ - panel=1.2.3=py39hca03da5_0
+ - param=1.13.0=py39hca03da5_0
+ - pillow=10.0.1=py39h3b245a6_0
+ - pip=23.3=py39hca03da5_0
+ - platformdirs=3.10.0=py39hca03da5_0
+ - plotly=5.9.0=py39hca03da5_0
+ - prometheus_client=0.14.1=py39hca03da5_0
+ - prompt-toolkit=3.0.36=py39hca03da5_0
+ - psutil=5.9.0=py39h1a28f6b_0
+ - pyct=0.5.0=py39hca03da5_0
+ - pygments=2.15.1=py39hca03da5_1
+ - pyopenssl=23.2.0=py39hca03da5_0
+ - pyparsing=3.0.9=py39hca03da5_0
+ - pyrsistent=0.18.0=py39h1a28f6b_0
+ - pysocks=1.7.1=py39hca03da5_0
+ - python-fastjsonschema=2.16.2=py39hca03da5_0
+ - python=3.9.18=hb885b13_0
+ - pytz=2023.3.post1=py39hca03da5_0
+ - pyviz_comms=2.3.0=py39hca03da5_0
+ - pyyaml=6.0.1=py39h80987f9_0
+ - pyzmq=23.2.0=py39hc377ac9_0
+ - readline=8.2=h1a28f6b_0
+ - requests=2.31.0=py39hca03da5_0
+ - setuptools=68.0.0=py39hca03da5_0
+ - sniffio=1.2.0=py39hca03da5_1
+ - soupsieve=2.5=py39hca03da5_0
+ - sqlite=3.41.2=h80987f9_0
+ - tenacity=8.2.2=py39hca03da5_0
+ - terminado=0.17.1=py39hca03da5_0
+ - tinycss2=1.2.1=py39hca03da5_0
+ - tk=8.6.12=hb8d0fd4_0
+ - toolz=0.12.0=py39hca03da5_0
+ - tornado=6.3.3=py39h80987f9_0
+ - tqdm=4.65.0=py39h86d0a89_0
+ - traitlets=5.7.1=py39hca03da5_0
+ - typing-extensions=4.7.1=py39hca03da5_0
+ - typing_extensions=4.7.1=py39hca03da5_0
+ - uc-micro-py=1.0.1=py39hca03da5_0
+ - urllib3=1.26.18=py39hca03da5_0
+ - webencodings=0.5.1=py39hca03da5_1
+ - websocket-client=0.58.0=py39hca03da5_4
+ - wheel=0.41.2=py39hca03da5_0
+ - xyzservices=2022.9.0=py39hca03da5_1
+ - xz=5.4.2=h80987f9_0
+ - yaml=0.2.5=h1a28f6b_0
+ - zeromq=4.3.4=hc377ac9_0
+ - zipp=3.11.0=py39hca03da5_0
+ - zlib=1.2.13=h5a0b063_0
+ - zstd=1.5.5=hd90d995_0
win-64:
- - brotlipy=0.7.0=py37hcc03f2d_1001
- - ca-certificates=2021.5.30=h5b45459_0
- - certifi=2021.5.30=py37h03978a9_0
- - cffi=1.14.6=py37hd8e9650_0
- - chardet=4.0.0=py37h03978a9_1
- - colorama=0.4.4=pyh9f0ad1d_0
- - cryptography=3.4.7=py37h20c650d_0
- - freetype=2.10.4=h546665d_1
- - importlib-metadata=4.6.1=py37h03978a9_0
- - intel-openmp=2021.3.0=h57928b3_3372
- - ipykernel=5.5.5=py37h7813e69_0
- - ipython=7.25.0=py37h7813e69_1
- - jbig=2.1=h8d14728_2003
- - jedi=0.18.0=py37h03978a9_2
- - jpeg=9d=h8ffe710_0
- - jupyter_core=4.7.1=py37h03978a9_0
- - kiwisolver=1.3.1=py37h8c56517_1
- - lcms2=2.12=h2a16943_0
- - lerc=2.2.1=h0e60522_0
- - libblas=3.9.0=10_mkl
- - libcblas=3.9.0=10_mkl
- - libdeflate=1.7=h8ffe710_5
- - liblapack=3.9.0=10_mkl
- - libpng=1.6.37=h1d00b33_2
- - libsodium=1.0.18=h8d14728_1
- - libtiff=4.3.0=h0c97f57_1
- - lz4-c=1.9.3=h8ffe710_0
- - markupsafe=2.0.1=py37hcc03f2d_0
- - matplotlib-base=3.4.2=py37h0d1fb12_0
- - mistune=0.8.4=py37hcc03f2d_1004
- - mkl=2021.3.0=hb70f87d_564
- - nbconvert=6.1.0=py37h03978a9_0
- - numpy=1.21.1=py37hcbcd69c_0
- - openjpeg=2.4.0=hb211442_1
- - openssl=1.1.1k=h8ffe710_0
- - pandas=1.2.5=py37h90003fb_0
- - pandoc=2.14.1=h8ffe710_0
- - pillow=8.3.1=py37hd7d9ad0_0
- - pyrsistent=0.17.3=py37hcc03f2d_2
- - pysocks=1.7.1=py37h03978a9_3
- - python=3.7.10=h7840368_100_cpython
- - pywin32=300=py37hcc03f2d_0
- - pywinpty=1.1.3=py37h7f67f24_0
- - pyyaml=5.4.1=py37hcc03f2d_0
- - pyzmq=22.1.0=py37hcce574b_0
- - setuptools=49.6.0=py37h03978a9_3
- - sqlite=3.36.0=h8ffe710_0
- - tbb=2021.3.0=h2d74725_0
- - terminado=0.10.1=py37h03978a9_0
- - tk=8.6.10=h8ffe710_1
- - tornado=6.1=py37hcc03f2d_1
- - ucrt=10.0.20348.0=h57928b3_0
- - vc=14.2=hb210afc_5
- - vs2015_runtime=14.29.30037=h902a5da_5
- - win_inet_pton=1.1.0=py37h03978a9_2
- - wincertstore=0.2=py37h03978a9_1006
+ - altair=5.0.1=py39haa95532_0
+ - anyio=3.5.0=py39haa95532_0
+ - argon2-cffi-bindings=21.2.0=py39h2bbff1b_0
+ - attrs=23.1.0=py39haa95532_0
+ - beautifulsoup4=4.12.2=py39haa95532_0
+ - blas=1.0=mkl
+ - bokeh=3.2.1=py39h9909e9c_0
+ - bottleneck=1.3.5=py39h080aedc_0
+ - brotli-bin=1.0.9=h2bbff1b_7
+ - brotli-python=1.0.9=py39hd77b12b_7
+ - brotli=1.0.9=h2bbff1b_7
+ - ca-certificates=2023.08.22=haa95532_0
+ - certifi=2023.7.22=py39haa95532_0
+ - cffi=1.15.1=py39h2bbff1b_3
+ - colorama=0.4.6=py39haa95532_0
+ - colorcet=3.0.1=py39haa95532_0
+ - comm=0.1.2=py39haa95532_0
+ - contourpy=1.0.5=py39h59b6b97_0
+ - cryptography=41.0.3=py39h89fc84f_0
+ - debugpy=1.6.7=py39hd77b12b_0
+ - entrypoints=0.4=py39haa95532_0
+ - exceptiongroup=1.0.4=py39haa95532_0
+ - freetype=2.12.1=ha860e81_0
+ - giflib=5.2.1=h8cc25b3_3
+ - holoviews=1.17.1=py39haa95532_0
+ - hvplot=0.9.0=py39haa95532_0
+ - idna=3.4=py39haa95532_0
+ - importlib-metadata=6.0.0=py39haa95532_0
+ - importlib_resources=6.1.0=py39haa95532_0
+ - intel-openmp=2023.1.0=h59b6b97_46319
+ - ipykernel=6.25.0=py39h9909e9c_0
+ - ipython=8.15.0=py39haa95532_0
+ - jedi=0.18.1=py39haa95532_1
+ - jinja2=3.1.2=py39haa95532_0
+ - jpeg=9e=h2bbff1b_1
+ - jsonschema=4.17.3=py39haa95532_0
+ - jupyter_client=7.4.9=py39haa95532_0
+ - jupyter_core=5.3.0=py39haa95532_0
+ - jupyter_server=1.23.4=py39haa95532_0
+ - kiwisolver=1.4.4=py39hd77b12b_0
+ - lerc=3.0=hd77b12b_0
+ - libbrotlicommon=1.0.9=h2bbff1b_7
+ - libbrotlidec=1.0.9=h2bbff1b_7
+ - libbrotlienc=1.0.9=h2bbff1b_7
+ - libdeflate=1.17=h2bbff1b_1
+ - libiconv=1.16=h2bbff1b_2
+ - libpng=1.6.39=h8cc25b3_0
+ - libsodium=1.0.18=h62dcd97_0
+ - libtiff=4.5.1=hd77b12b_0
+ - libwebp-base=1.3.2=h2bbff1b_0
+ - libwebp=1.3.2=hbc33d0d_0
+ - libxml2=2.10.4=h0ad7f3c_1
+ - libxslt=1.1.37=h2bbff1b_1
+ - linkify-it-py=2.0.0=py39haa95532_0
+ - lxml=4.9.3=py39h09808a7_0
+ - lz4-c=1.9.4=h2bbff1b_0
+ - markdown-it-py=2.2.0=py39haa95532_1
+ - markdown=3.4.1=py39haa95532_0
+ - markupsafe=2.1.1=py39h2bbff1b_0
+ - matplotlib-base=3.8.0=py39h4ed8f06_0
+ - matplotlib-inline=0.1.6=py39haa95532_0
+ - mdit-py-plugins=0.3.0=py39haa95532_0
+ - mdurl=0.1.0=py39haa95532_0
+ - mistune=0.8.4=py39h2bbff1b_1000
+ - mkl-service=2.4.0=py39h2bbff1b_1
+ - mkl=2023.1.0=h6b88ed4_46357
+ - mkl_fft=1.3.8=py39h2bbff1b_0
+ - mkl_random=1.2.4=py39h59b6b97_0
+ - nbclassic=0.5.5=py39haa95532_0
+ - nbclient=0.5.13=py39haa95532_0
+ - nbconvert=6.5.4=py39haa95532_0
+ - nbformat=5.9.2=py39haa95532_0
+ - nest-asyncio=1.5.6=py39haa95532_0
+ - notebook-shim=0.2.2=py39haa95532_0
+ - notebook=6.5.4=py39haa95532_1
+ - numexpr=2.8.7=py39h2cd9be0_0
+ - numpy-base=1.26.0=py39h65a83cf_0
+ - numpy=1.26.0=py39h055cbcc_0
+ - openjpeg=2.4.0=h4fc8c34_0
+ - openssl=3.0.11=h2bbff1b_2
+ - packaging=23.1=py39haa95532_0
+ - pandas=2.1.1=py39h4ed8f06_0
+ - panel=1.2.3=py39haa95532_0
+ - param=1.13.0=py39haa95532_0
+ - pillow=10.0.1=py39h045eedc_0
+ - pip=23.3=py39haa95532_0
+ - platformdirs=3.10.0=py39haa95532_0
+ - plotly=5.9.0=py39haa95532_0
+ - prometheus_client=0.14.1=py39haa95532_0
+ - prompt-toolkit=3.0.36=py39haa95532_0
+ - psutil=5.9.0=py39h2bbff1b_0
+ - pyct=0.5.0=py39haa95532_0
+ - pygments=2.15.1=py39haa95532_1
+ - pyopenssl=23.2.0=py39haa95532_0
+ - pyparsing=3.0.9=py39haa95532_0
+ - pyrsistent=0.18.0=py39h196d8e1_0
+ - pysocks=1.7.1=py39haa95532_0
+ - python-fastjsonschema=2.16.2=py39haa95532_0
+ - python=3.9.18=h1aa4202_0
+ - pytz=2023.3.post1=py39haa95532_0
+ - pyviz_comms=2.3.0=py39haa95532_0
+ - pywin32=305=py39h2bbff1b_0
+ - pywinpty=2.0.10=py39h5da7b33_0
+ - pyyaml=6.0.1=py39h2bbff1b_0
+ - pyzmq=23.2.0=py39hd77b12b_0
+ - requests=2.31.0=py39haa95532_0
+ - setuptools=68.0.0=py39haa95532_0
+ - sniffio=1.2.0=py39haa95532_1
+ - soupsieve=2.5=py39haa95532_0
+ - sqlite=3.41.2=h2bbff1b_0
+ - tbb=2021.8.0=h59b6b97_0
+ - tenacity=8.2.2=py39haa95532_0
+ - terminado=0.17.1=py39haa95532_0
+ - tinycss2=1.2.1=py39haa95532_0
+ - tk=8.6.12=h2bbff1b_0
+ - toolz=0.12.0=py39haa95532_0
+ - tornado=6.3.3=py39h2bbff1b_0
+ - tqdm=4.65.0=py39hd4e2768_0
+ - traitlets=5.7.1=py39haa95532_0
+ - typing-extensions=4.7.1=py39haa95532_0
+ - typing_extensions=4.7.1=py39haa95532_0
+ - uc-micro-py=1.0.1=py39haa95532_0
+ - urllib3=1.26.18=py39haa95532_0
+ - vc=14.2=h21ff451_1
+ - vs2015_runtime=14.27.29016=h5e58377_2
+ - webencodings=0.5.1=py39haa95532_1
+ - websocket-client=0.58.0=py39haa95532_4
+ - wheel=0.41.2=py39haa95532_0
+ - win_inet_pton=1.1.0=py39haa95532_0
- winpty=0.4.3=4
- - xz=5.2.5=h62dcd97_1
+ - xyzservices=2022.9.0=py39haa95532_1
+ - xz=5.4.2=h8cc25b3_0
- yaml=0.2.5=he774522_0
- - zeromq=4.3.4=h0e60522_0
- - zlib=1.2.11=h62dcd97_1010
- - zstd=1.5.0=h6255e5f_0
+ - zeromq=4.3.4=hd77b12b_0
+ - zipp=3.11.0=py39haa95532_0
+ - zlib=1.2.13=h8cc25b3_0
+ - zstd=1.5.5=hd43e919_0
diff --git a/gapminders/anaconda-project.yml b/gapminders/anaconda-project.yml
index e7c00e4ae..606198ba2 100644
--- a/gapminders/anaconda-project.yml
+++ b/gapminders/anaconda-project.yml
@@ -3,59 +3,46 @@ name: gapminders
description: Using four different plotting libraries for the Hans Rosling gapminder
example
examples_config:
+ created: 2018-01-01
maintainers:
- philippjfr
labels:
- panel
- - channel_conda-forge
+ deployments:
+ - command: dashboard
+ no_data_ingestion: true
user_fields: [examples_config]
channels:
-- conda-forge
-- defaults # Previously nodefaults attempt
+- defaults
packages: &pkgs
-- python=3.7
-- notebook=5.7.8
+- python=3.9
+- notebook
- plotly
-- holoviews=1.14.1
-- hvplot=0.7.0
-- panel=0.10.2
-- param=1.10.0
-- bokeh=2.2.1
-- altair=3
-- pandas = 1.2.5
+- holoviews
+- hvplot
+- panel >=1
+- param
+- bokeh >=3
+- altair
+- pandas
dependencies: *pkgs
commands:
dashboard:
- unix: panel serve gapminders.ipynb --show
+ unix: panel serve --rest-session-info --session-history -1 gapminders.ipynb --show
supports_http_options: true
notebook:
notebook: gapminders.ipynb
- test:
- unix: pytest --nbsmoke-run -k *.ipynb --ignore envs
- windows: pytest --nbsmoke-run -k *.ipynb --ignore envs
- env_spec: test
- lint:
- unix: pytest --nbsmoke-lint -k *.ipynb --ignore envs
- windows: pytest --nbsmoke-lint -k *.ipynb --ignore envs
- env_spec: test
variables: {}
downloads: {}
-env_specs:
- default: {}
- test:
- packages: &testpkgs
- - nbsmoke=0.2.8
- - pytest=4.4.1
- dependencies: *testpkgs
-
platforms:
- linux-64
- osx-64
- win-64
+- osx-arm64
diff --git a/gapminders/assets/app.jpg b/gapminders/assets/app.jpg
new file mode 100644
index 000000000..8590689fd
Binary files /dev/null and b/gapminders/assets/app.jpg differ
diff --git a/gapminders/gapminders.ipynb b/gapminders/gapminders.ipynb
index 85a706cb2..31e25bcde 100644
--- a/gapminders/gapminders.ipynb
+++ b/gapminders/gapminders.ipynb
@@ -4,24 +4,23 @@
"cell_type": "markdown",
"metadata": {},
"source": [
- "# Gapminders\n",
- "Written by Philipp Rudiger
\n",
- "Created: 2018
\n",
- "Last updated: January 14, 2021"
+ "# Gapminders"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
- ""
+ ""
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
- "The [Panel](http://panel.holoviz.org) library from [HoloViz](http://holoviz.org) lets you make widget-controlled apps and dashboards from a wide variety of [plotting libraries and data types](https://github.com/holoviz/panel/issues/2). Here let's set up four different plotting libraries controlled by a couple of widgets, for Hans Rosling's [gapminder](https://www.gapminder.org/tag/hans-rosling/) example."
+ "The [Panel](https://panel.holoviz.org) library from [HoloViz](https://holoviz.org) lets you make widget-controlled apps and dashboards from a wide variety of [plotting libraries and data types](https://panel.holoviz.org/reference/index.html#panes). \n",
+ "\n",
+ "**Here we set up four different plotting libraries controlled by a couple of widgets, for Hans Rosling's [gapminder](https://demo.bokeh.org/gapminder) example.**"
]
},
{
@@ -30,30 +29,93 @@
"metadata": {},
"outputs": [],
"source": [
- "import param\n",
+ "import warnings\n",
+ "\n",
"import numpy as np \n",
"import pandas as pd\n",
"import panel as pn\n",
- "import holoviews as hv\n",
"\n",
"import altair as alt\n",
"import plotly.graph_objs as go\n",
+ "import plotly.io as pio\n",
"import matplotlib.pyplot as plt\n",
+ "import hvplot.pandas # noqa\n",
"\n",
- "pn.extension('vega', 'plotly')\n",
+ "warnings.simplefilter('ignore')\n",
+ "pn.extension('vega', 'plotly', defer_load=True, sizing_mode=\"stretch_width\")"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "We need to define some configuration"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "XLABEL = 'GDP per capita (2000 dollars)'\n",
+ "YLABEL = 'Life expectancy (years)'\n",
+ "YLIM = (20, 90)\n",
+ "HEIGHT=500 # pixels\n",
+ "WIDTH=500 # pixels\n",
+ "ACCENT=\"#00A170\"\n",
"\n",
- "import hvplot.pandas # noqa: adds hvplot to pandas objects as a side effect\n",
+ "PERIOD = 1000 # miliseconds"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "## Extract the dataset\n",
"\n",
- "# Disable default plotly theme\n",
- "import plotly.io as pio\n",
- "pio.templates.default = None"
+ "First, we'll get the data into a Pandas dataframe. We use the [built in `cache`](https://panel.holoviz.org/user_guide/Performance_and_Debugging.html#caching) to speed up the app."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "@pn.cache\n",
+ "def get_dataset():\n",
+ " url = 'https://raw.githubusercontent.com/plotly/datasets/master/gapminderDataFiveYear.csv'\n",
+ " return pd.read_csv(url)"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "dataset = get_dataset()\n",
+ "dataset.sample(10)"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "YEARS = [int(year) for year in dataset.year.unique()]\n",
+ "YEARS"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
- "First, we'll get the data into a Pandas dataframe:"
+ "## Transform the dataset to plots\n",
+ "\n",
+ "Now let's define helper functions and functions to plot this dataset with Matplotlib, Plotly, Altair, and hvPlot (using HoloViews and Bokeh)."
]
},
{
@@ -62,15 +124,25 @@
"metadata": {},
"outputs": [],
"source": [
- "url = 'https://raw.githubusercontent.com/plotly/datasets/master/gapminderDataFiveYear.csv'\n",
- "dataset = pd.read_csv(url)"
+ "@pn.cache\n",
+ "def get_data(year):\n",
+ " df = dataset[(dataset.year==year) & (dataset.gdpPercap < 10000)].copy()\n",
+ " df['size'] = np.sqrt(df['pop']*2.666051223553066e-05)\n",
+ " df['size_hvplot'] = df['size']*6\n",
+ " return df\n",
+ "\n",
+ "def get_title(library, year):\n",
+ " return f\"{library}: Life expectancy vs. GDP, {year}\"\n",
+ "\n",
+ "def get_xlim(data):\n",
+ " return (data['gdpPercap'].min()-100,data['gdpPercap'].max()+1000)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
- "Now let's define a couple of user-settable parameters (the year and whether to show a legend), then write methods to plot this data with Matplotlib, Plotly, Altair (using Vega), and hvPlot (using HoloViews and Bokeh):"
+ "Let's define the **Matplotlib** plotting function."
]
},
{
@@ -79,93 +151,44 @@
"metadata": {},
"outputs": [],
"source": [
- "class Gapminder(param.Parameterized):\n",
- " year = param.ObjectSelector(default=1952, objects=list(dataset.year.unique()))\n",
- " show_legend = param.Boolean(default=True)\n",
- " \n",
- " title = 'Life expectancy vs. GDP, %s'\n",
- " xlabel = 'GDP per capita (2000 dollars)'\n",
- " ylabel = 'Life expectancy (years)'\n",
- " ylim = (20, 90)\n",
- " xlim = (200, 15000)\n",
+ "plt.rcParams.update({\n",
+ " \"savefig.facecolor\": (0.0, 0.0, 0.0, 0.0), \n",
+ "})\n",
"\n",
- " def get_data(self):\n",
- " df = dataset[(dataset.year==self.year) & (dataset.gdpPercap < 10000)].copy()\n",
- " df['size'] = np.sqrt(df['pop']*2.666051223553066e-05)\n",
- " return df\n",
- " \n",
- " def mpl_view(self):\n",
- " data = self.get_data()\n",
- " title = \"Matplotlib: \" + (self.title % self.year)\n",
- " \n",
- " plot = plt.figure(figsize=(7, 6))\n",
- " ax = plot.add_subplot(111)\n",
- " ax.set_xscale(\"log\", nonpositive='clip')\n",
- " ax.set_title(title)\n",
- " ax.set_xlabel(self.xlabel)\n",
- " ax.set_ylabel(self.ylabel)\n",
- " ax.set_ylim(self.ylim)\n",
- " ax.set_xlim(self.xlim)\n",
"\n",
- " for continent, df in data.groupby('continent'):\n",
- " ax.scatter(df.gdpPercap, y=df.lifeExp, s=df['size']*5,\n",
- " edgecolor='black', label=continent)\n",
- " \n",
- " if self.show_legend:\n",
- " ax.legend(loc=4)\n",
- " \n",
- " plt.close(plot)\n",
- " return plot\n",
- " \n",
- " def plotly_view(self):\n",
- " data = self.get_data()\n",
- " title = 'Plotly: ' + (self.title % self.year)\n",
+ "@pn.cache\n",
+ "def mpl_view(year=1952, show_legend=True):\n",
+ " data = get_data(year)\n",
+ " title = get_title(\"Matplotlib\", year)\n",
+ " xlim = get_xlim(data)\n",
"\n",
- " traces = []\n",
- " for continent, df in data.groupby('continent'):\n",
- " marker=dict(symbol='circle', sizemode='area', sizeref=0.1, size=df['size'], line=dict(width=2))\n",
- " traces.append(go.Scatter(x=df.gdpPercap, y=df.lifeExp, mode='markers', marker=marker, name=continent))\n",
- " \n",
- " axis_opts = dict(gridcolor='rgb(255, 255, 255)', zerolinewidth=1, ticklen=5, gridwidth=2)\n",
- " layout = go.Layout(title=title, showlegend=self.show_legend, width=550,\n",
- " xaxis=dict(title=self.xlabel, type='log', **axis_opts),\n",
- " yaxis=dict( title=self.ylabel, **axis_opts))\n",
- " \n",
- " return go.Figure(data=traces, layout=layout)\n",
- " \n",
- " def altair_view(self):\n",
- " data = self.get_data()\n",
- " title = \"Altair/Vega: \" + (self.title % self.year)\n",
- " legend= ({} if self.show_legend else {'legend': None})\n",
+ " plot = plt.figure(figsize=(10, 6), facecolor=(0, 0, 0, 0))\n",
+ " ax = plot.add_subplot(111)\n",
+ " ax.set_xscale(\"log\")\n",
+ " ax.set_title(title)\n",
+ " ax.set_xlabel(XLABEL)\n",
+ " ax.set_ylabel(YLABEL)\n",
+ " ax.set_ylim(YLIM)\n",
+ " ax.set_xlim(xlim)\n",
"\n",
- " plot = alt.Chart(data).mark_circle().encode(\n",
- " alt.X('gdpPercap:Q', scale=alt.Scale(type='log'), axis=alt.Axis(title=self.xlabel)),\n",
- " alt.Y('lifeExp:Q', scale=alt.Scale(zero=False, domain=self.ylim), axis=alt.Axis(title=self.ylabel)),\n",
- " size=alt.Size('pop:Q', scale=alt.Scale(type=\"log\"), legend=None),\n",
- " color=alt.Color('continent', scale=alt.Scale(scheme=\"category10\"), **legend))\\\n",
- " .properties(title=title).configure_axis(grid=False)\n",
- " return plot\n",
- " \n",
- " def hvplot_view(self):\n",
- " data = self.get_data()\n",
- " title = \"hvPlot/Bokeh: \" + (self.title % self.year)\n",
- " \n",
- " legend = 'bottom_right' if self.show_legend else None\n",
- " return data.hvplot.scatter(\n",
- " 'gdpPercap', 'lifeExp', by='continent', size=np.sqrt(hv.dim('size'))*3, line_color='black',\n",
- " logx=True, title=title, width=500, height=400, legend=legend,\n",
- " xlabel=self.xlabel, ylabel=self.ylabel, xticks=[500, 1000, 2000, 5000, 10000],\n",
- " ylim=self.ylim, xlim=(200, 12000)\n",
- " )\n",
+ " for continent, df in data.groupby('continent'):\n",
+ " ax.scatter(df.gdpPercap, y=df.lifeExp, s=df['size']*5,\n",
+ " edgecolor='black', label=continent)\n",
+ "\n",
+ " if show_legend:\n",
+ " ax.legend(loc=4)\n",
+ "\n",
+ " plt.close(plot)\n",
+ " return plot\n",
"\n",
- "gm = Gapminder(name='')"
+ "mpl_view(1952, True)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
- "We can now make a panel showing the parameters of this `gm` object along with the result of calling four different `_view()` methods:"
+ "Let's define the **Plotly** plotting function."
]
},
{
@@ -174,20 +197,36 @@
"metadata": {},
"outputs": [],
"source": [
- "pn.Column(\n",
- " gm.param,\n",
- " pn.Row(gm.hvplot_view, gm.altair_view),\n",
- " pn.Row(gm.mpl_view, gm.plotly_view)\n",
- ")"
+ "pio.templates.default = None\n",
+ "\n",
+ "@pn.cache\n",
+ "def plotly_view(year=1952, show_legend=True):\n",
+ " data = get_data(year)\n",
+ " title = get_title(\"Plotly\", year)\n",
+ "\n",
+ " traces = []\n",
+ " for continent, df in data.groupby('continent'):\n",
+ " marker=dict(symbol='circle', sizemode='area', sizeref=0.1, size=df['size'], line=dict(width=2))\n",
+ " traces.append(go.Scatter(x=df.gdpPercap, y=df.lifeExp, mode='markers', marker=marker, name=continent, text=df.country))\n",
+ "\n",
+ " axis_opts = dict(gridcolor='rgb(255, 255, 255)', zerolinewidth=1, ticklen=5, gridwidth=2)\n",
+ " layout = go.Layout(\n",
+ " title=title, showlegend=show_legend,\n",
+ " xaxis=dict(title=XLABEL, type='log', **axis_opts),\n",
+ " yaxis=dict(title=YLABEL, **axis_opts),\n",
+ " autosize=True, paper_bgcolor='rgba(0,0,0,0)',\n",
+ " )\n",
+ " \n",
+ " return go.Figure(data=traces, layout=layout)\n",
+ "\n",
+ "plotly_view()"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
- "Here the Matplotlib output is being rendered as PNG, but the rest are interactive, with hovering, zooming, and similar features (if allowed by the notebook viewer being used). Note that due to interactions between the various JS libraries used here, the Plotly output can get mixed up when resizing or reloading the page, but it should be reset if you adjust the year slider once you are done.\n",
- "\n",
- "We can also make these plots available as a standalone server, if we add `.servable()` to the panel object and call this notebook as `bokeh serve --show Panel_Gapminders.ipynb`. Before doing that, let's make another panel where we can add a logo and a title, so that it makes a nicer page layout when served separately. We'll also add an R ggplot2-based plot to fill out the page:"
+ "Let's define the **Altair** plotting function."
]
},
{
@@ -196,37 +235,90 @@
"metadata": {},
"outputs": [],
"source": [
- "logo = \"\"\"\n",
- " \"\"\"\n",
- "\n",
- "title = 'Plotting library comparison
'\n",
+ "@pn.cache\n",
+ "def altair_view(year=1952, show_legend=True, height=\"container\", width=\"container\"):\n",
+ " data = get_data(year)\n",
+ " title = get_title(\"Altair/ Vega\", year)\n",
+ " legend= ({} if show_legend else {'legend': None})\n",
+ " return (\n",
+ " alt.Chart(data)\n",
+ " .mark_circle().encode(\n",
+ " alt.X('gdpPercap:Q', scale=alt.Scale(type='log'), axis=alt.Axis(title=XLABEL)),\n",
+ " alt.Y('lifeExp:Q', scale=alt.Scale(zero=False, domain=YLIM), axis=alt.Axis(title=YLABEL)),\n",
+ " size=alt.Size('pop:Q', scale=alt.Scale(type=\"log\"), legend=None),\n",
+ " color=alt.Color('continent', scale=alt.Scale(scheme=\"category10\"), **legend),\n",
+ " tooltip=['continent','country'])\n",
+ " .configure_axis(grid=False)\n",
+ " .properties(title=title, height=height, width=width, background='rgba(0,0,0,0)') \n",
+ " .configure_view(fill=\"white\")\n",
+ " .interactive()\n",
+ " )\n",
"\n",
- "desc = pn.pane.HTML(\"\"\"\n",
- " The Panel library from PyViz \n",
- " lets you make widget-controlled apps and dashboards from a wide variety of \n",
- " plotting libraries and data types. Here you can try out five different plotting libraries\n",
- " controlled by a couple of widgets, for Hans Rosling's \n",
- " gapminder example.\"\"\", width=250)\n",
+ "altair_view(height=HEIGHT-100, width=1000)"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "Let's define the **hvPlot** plotting function. Please note that [hvPlot](https://hvplot.holoviz.org/) is the recommended entry point to the HoloViz plotting ecosystem."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "@pn.cache\n",
+ "def hvplot_view(year=1952, show_legend=True):\n",
+ " data = get_data(year)\n",
+ " title = get_title(\"hvPlot/ Bokeh\", year)\n",
+ " xlim = get_xlim(data)\n",
+ " return data.hvplot.scatter(\n",
+ " 'gdpPercap', 'lifeExp', by='continent', s='size_hvplot', alpha=0.8,\n",
+ " logx=True, title=title, responsive=True, legend='bottom_right',\n",
+ " hover_cols=['country'], ylim=YLIM, xlim=xlim, ylabel=YLABEL, xlabel=XLABEL\n",
+ " )\n",
"\n",
- "widgets = pn.Param(gm.param, widgets={'year': {'type': pn.widgets.DiscreteSlider, 'width': 250}}, margin=0)\n",
"\n",
- "pn.Row(\n",
- " pn.Column(logo, title, desc, widgets),\n",
- " pn.Column(\n",
- " pn.Row(gm.hvplot_view, gm.altair_view),\n",
- " pn.Row(gm.mpl_view, gm.plotly_view)\n",
- " )\n",
- ")"
+ "hvplot_view().opts(height=400)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
- "## Template\n",
+ "## Define the widgets"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "year = pn.widgets.DiscreteSlider(value=YEARS[-1], options=YEARS, name=\"Year\")\n",
+ "show_legend = pn.widgets.Checkbox(value=True, name=\"Show Legend\")"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "def play():\n",
+ " print(\"play\")\n",
+ " if year.value == YEARS[-1]:\n",
+ " year.value=YEARS[0]\n",
+ " return\n",
+ " \n",
+ " index = YEARS.index(year.value)\n",
+ " year.value = YEARS[index+1] \n",
"\n",
- "To make the application look more polished we can put our components into a template:"
+ "periodic_callback = pn.state.add_periodic_callback(play, start=False, period=PERIOD)\n",
+ "player = pn.widgets.Checkbox.from_param(periodic_callback.param.running, name=\"Autoplay\")"
]
},
{
@@ -235,44 +327,150 @@
"metadata": {},
"outputs": [],
"source": [
- "template = pn.template.MaterialTemplate(\n",
- " title='Gapminder - Plotting library comparison',\n",
+ "widgets = pn.Column(year, player, show_legend, margin=(0,15))\n",
+ "widgets"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "## Bind the plot functions to the widgets"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "mpl_view = pn.bind(mpl_view, year=year, show_legend=show_legend)\n",
+ "plotly_view = pn.bind(plotly_view, year=year, show_legend=show_legend)\n",
+ "altair_view = pn.bind(altair_view, year=year, show_legend=show_legend)\n",
+ "hvplot_view = pn.bind(hvplot_view, year=year, show_legend=show_legend)"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "## Layout the widgets"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {
+ "tags": []
+ },
+ "outputs": [],
+ "source": [
+ "logo = pn.pane.PNG(\n",
+ " \"https://panel.holoviz.org/_static/logo_stacked.png\",\n",
+ " link_url=\"https://panel.holoviz.org\", embed=False, width=150, align=\"center\"\n",
")\n",
+ " \n",
+ "desc = \"\"\"## 🎓 Info\n",
"\n",
- "template.sidebar.append(logo)\n",
- "template.sidebar.append(desc.clone(width=250, margin=(20, 5)))\n",
+ "The [Panel](http://panel.holoviz.org) library from [HoloViz](https://holoviz.org)\n",
+ "lets you make widget-controlled apps and dashboards from a wide variety of \n",
+ "plotting libraries and data types. Here you can try out four different plotting libraries\n",
+ "controlled by a couple of widgets, for Hans Rosling's \n",
+ "[gapminder](https://demo.bokeh.org/gapminder) example.\n",
+ "\"\"\"\n",
"\n",
- "template.sidebar.append(widgets)\n",
+ "settings = pn.Column(logo, \"## ⚙️ Settings\", widgets, desc)\n",
+ "settings"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "## Layout the plots\n",
"\n",
- "template.main.append(\n",
- " pn.Column(\n",
- " pn.Row(gm.hvplot_view, gm.altair_view),\n",
- " pn.Row(gm.mpl_view, gm.plotly_view)\n",
- " )\n",
+ "We layout the plots in a [Gridbox](https://panel.holoviz.org/reference/layouts/GridBox.html) with 2 columns. Please note Panel provides many other [layouts](https://panel.holoviz.org/reference/index.html#layouts) that might be perfect for your use case."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "plots = pn.layout.GridBox(\n",
+ " pn.pane.Matplotlib(mpl_view, format='png', sizing_mode='scale_both', tight=True, margin=10),\n",
+ " pn.pane.HoloViews(hvplot_view, sizing_mode='stretch_both', margin=10),\n",
+ " pn.pane.Plotly(plotly_view, sizing_mode='stretch_both', margin=10),\n",
+ " pn.pane.Vega(altair_view, sizing_mode='stretch_both', margin=10),\n",
+ " ncols=2,\n",
+ " sizing_mode=\"stretch_both\"\n",
")\n",
+ "plots"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "## Configure the template\n",
"\n",
- "template.servable();"
+ "Let us layout out the app in the nicely styled [FastListTemplate](https://panel.holoviz.org/reference/templates/FastListTemplate.html)."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "pn.template.FastListTemplate(\n",
+ " sidebar=[settings],\n",
+ " main=[plots],\n",
+ " site=\"Panel\",\n",
+ " site_url=\"https://panel.holoviz.org\",\n",
+ " title=\"Hans Rosling's Gapminder\",\n",
+ " header_background=ACCENT,\n",
+ " accent_base_color=ACCENT,\n",
+ " favicon=\"static/extensions/panel/images/favicon.ico\",\n",
+ " theme_toggle=False,\n",
+ ").servable(); # We add ; to avoid showing the app in the notebook"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
- "Once you run `panel serve` on this notebook , you should get a panel like the following in your web browser that you can explore or share with other users of your machine:"
+ "The final data app can be served via `panel serve gapminders.ipynb`.\n",
+ "\n",
+ "It will look something like."
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
- ""
+ ""
]
}
],
"metadata": {
+ "kernelspec": {
+ "display_name": "Python 3 (ipykernel)",
+ "language": "python",
+ "name": "python3"
+ },
"language_info": {
+ "codemirror_mode": {
+ "name": "ipython",
+ "version": 3
+ },
+ "file_extension": ".py",
+ "mimetype": "text/x-python",
"name": "python",
- "pygments_lexer": "ipython3"
+ "nbconvert_exporter": "python",
+ "pygments_lexer": "ipython3",
+ "version": "3.9.18"
}
},
"nbformat": 4,