tesorotools-python 0.1.7__tar.gz → 0.1.9__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {tesorotools_python-0.1.7 → tesorotools_python-0.1.9}/PKG-INFO +1 -1
- {tesorotools_python-0.1.7 → tesorotools_python-0.1.9}/src/tesorotools/artists/_common.py +358 -11
- tesorotools_python-0.1.9/src/tesorotools/artists/_dates.py +238 -0
- {tesorotools_python-0.1.7 → tesorotools_python-0.1.9}/src/tesorotools/artists/box_plot.py +2 -0
- {tesorotools_python-0.1.7 → tesorotools_python-0.1.9}/src/tesorotools/artists/line_plot.py +54 -16
- {tesorotools_python-0.1.7 → tesorotools_python-0.1.9}/src/tesorotools/artists/plotly_backend.py +17 -0
- {tesorotools_python-0.1.7 → tesorotools_python-0.1.9}/src/tesorotools/artists/stacked.py +22 -7
- {tesorotools_python-0.1.7 → tesorotools_python-0.1.9}/src/tesorotools/assets/plots.yaml +27 -0
- {tesorotools_python-0.1.7 → tesorotools_python-0.1.9}/src/tesorotools/note/lint.py +33 -11
- {tesorotools_python-0.1.7 → tesorotools_python-0.1.9}/src/tesorotools/presentation/__init__.py +28 -1
- tesorotools_python-0.1.9/src/tesorotools/presentation/builder.py +2172 -0
- tesorotools_python-0.1.9/src/tesorotools/presentation/charts.py +101 -0
- {tesorotools_python-0.1.7 → tesorotools_python-0.1.9}/src/tesorotools/presentation/cli.py +3 -5
- tesorotools_python-0.1.9/src/tesorotools/presentation/fit.py +147 -0
- tesorotools_python-0.1.9/src/tesorotools/presentation/glossary.py +154 -0
- tesorotools_python-0.1.9/src/tesorotools/presentation/lint.py +790 -0
- {tesorotools_python-0.1.7 → tesorotools_python-0.1.9}/src/tesorotools/presentation/markdown.py +450 -45
- tesorotools_python-0.1.9/src/tesorotools/presentation/native.py +258 -0
- tesorotools_python-0.1.9/src/tesorotools/presentation/sections.py +71 -0
- {tesorotools_python-0.1.7 → tesorotools_python-0.1.9}/src/tesorotools/presentation/skeleton.md +2 -2
- {tesorotools_python-0.1.7 → tesorotools_python-0.1.9}/src/tesorotools/presentation/skill/SKILL.md +156 -30
- {tesorotools_python-0.1.7 → tesorotools_python-0.1.9}/src/tesorotools/presentation/skill/reglas.md +5 -3
- {tesorotools_python-0.1.7 → tesorotools_python-0.1.9}/src/tesorotools/providers/lseg.py +42 -14
- tesorotools_python-0.1.7/src/tesorotools/presentation/builder.py +0 -1175
- tesorotools_python-0.1.7/src/tesorotools/presentation/charts.py +0 -61
- tesorotools_python-0.1.7/src/tesorotools/presentation/lint.py +0 -183
- {tesorotools_python-0.1.7 → tesorotools_python-0.1.9}/.gitignore +0 -0
- {tesorotools_python-0.1.7 → tesorotools_python-0.1.9}/pyproject.toml +0 -0
- {tesorotools_python-0.1.7 → tesorotools_python-0.1.9}/src/tesorotools/__init__.py +0 -0
- {tesorotools_python-0.1.7 → tesorotools_python-0.1.9}/src/tesorotools/_build_context.py +0 -0
- {tesorotools_python-0.1.7 → tesorotools_python-0.1.9}/src/tesorotools/_registry.py +0 -0
- {tesorotools_python-0.1.7 → tesorotools_python-0.1.9}/src/tesorotools/artists/__init__.py +0 -0
- {tesorotools_python-0.1.7 → tesorotools_python-0.1.9}/src/tesorotools/artists/bar_line.py +0 -0
- {tesorotools_python-0.1.7 → tesorotools_python-0.1.9}/src/tesorotools/artists/barh_plot.py +0 -0
- {tesorotools_python-0.1.7 → tesorotools_python-0.1.9}/src/tesorotools/artists/compact.py +0 -0
- {tesorotools_python-0.1.7 → tesorotools_python-0.1.9}/src/tesorotools/artists/intraday_plot.py +0 -0
- {tesorotools_python-0.1.7 → tesorotools_python-0.1.9}/src/tesorotools/artists/matrix.py +0 -0
- {tesorotools_python-0.1.7 → tesorotools_python-0.1.9}/src/tesorotools/artists/sankey.py +0 -0
- {tesorotools_python-0.1.7 → tesorotools_python-0.1.9}/src/tesorotools/artists/scatter.py +0 -0
- {tesorotools_python-0.1.7 → tesorotools_python-0.1.9}/src/tesorotools/artists/sharing.py +0 -0
- {tesorotools_python-0.1.7 → tesorotools_python-0.1.9}/src/tesorotools/artists/shock_plot.py +0 -0
- {tesorotools_python-0.1.7 → tesorotools_python-0.1.9}/src/tesorotools/artists/tikz_backend.py +0 -0
- {tesorotools_python-0.1.7 → tesorotools_python-0.1.9}/src/tesorotools/artists/type_curve.py +0 -0
- {tesorotools_python-0.1.7 → tesorotools_python-0.1.9}/src/tesorotools/artists/vector_plot.py +0 -0
- {tesorotools_python-0.1.7 → tesorotools_python-0.1.9}/src/tesorotools/artists/waterfall.py +0 -0
- {tesorotools_python-0.1.7 → tesorotools_python-0.1.9}/src/tesorotools/assets/README.md +0 -0
- {tesorotools_python-0.1.7 → tesorotools_python-0.1.9}/src/tesorotools/assets/fonts/CabinetGrotesk-Black.otf +0 -0
- {tesorotools_python-0.1.7 → tesorotools_python-0.1.9}/src/tesorotools/assets/fonts/CabinetGrotesk-Bold.otf +0 -0
- {tesorotools_python-0.1.7 → tesorotools_python-0.1.9}/src/tesorotools/assets/fonts/CabinetGrotesk-Extrabold.otf +0 -0
- {tesorotools_python-0.1.7 → tesorotools_python-0.1.9}/src/tesorotools/assets/fonts/CabinetGrotesk-Extralight.otf +0 -0
- {tesorotools_python-0.1.7 → tesorotools_python-0.1.9}/src/tesorotools/assets/fonts/CabinetGrotesk-Light.otf +0 -0
- {tesorotools_python-0.1.7 → tesorotools_python-0.1.9}/src/tesorotools/assets/fonts/CabinetGrotesk-Medium.otf +0 -0
- {tesorotools_python-0.1.7 → tesorotools_python-0.1.9}/src/tesorotools/assets/fonts/CabinetGrotesk-Regular.otf +0 -0
- {tesorotools_python-0.1.7 → tesorotools_python-0.1.9}/src/tesorotools/assets/fonts/CabinetGrotesk-Thin.otf +0 -0
- {tesorotools_python-0.1.7 → tesorotools_python-0.1.9}/src/tesorotools/assets/fonts/README.md +0 -0
- {tesorotools_python-0.1.7 → tesorotools_python-0.1.9}/src/tesorotools/assets/nota_template.dotx +0 -0
- {tesorotools_python-0.1.7 → tesorotools_python-0.1.9}/src/tesorotools/assets/template.docx +0 -0
- {tesorotools_python-0.1.7 → tesorotools_python-0.1.9}/src/tesorotools/assets/tesoro.mplstyle +0 -0
- {tesorotools_python-0.1.7 → tesorotools_python-0.1.9}/src/tesorotools/data_sources/__init__.py +0 -0
- {tesorotools_python-0.1.7 → tesorotools_python-0.1.9}/src/tesorotools/data_sources/debug.py +0 -0
- {tesorotools_python-0.1.7 → tesorotools_python-0.1.9}/src/tesorotools/database/__init__.py +0 -0
- {tesorotools_python-0.1.7 → tesorotools_python-0.1.9}/src/tesorotools/database/local.py +0 -0
- {tesorotools_python-0.1.7 → tesorotools_python-0.1.9}/src/tesorotools/database/push.py +0 -0
- {tesorotools_python-0.1.7 → tesorotools_python-0.1.9}/src/tesorotools/database/shared.py +0 -0
- {tesorotools_python-0.1.7 → tesorotools_python-0.1.9}/src/tesorotools/dependencies/__init__.py +0 -0
- {tesorotools_python-0.1.7 → tesorotools_python-0.1.9}/src/tesorotools/dependencies/node.py +0 -0
- {tesorotools_python-0.1.7 → tesorotools_python-0.1.9}/src/tesorotools/dependencies/resolution.py +0 -0
- {tesorotools_python-0.1.7 → tesorotools_python-0.1.9}/src/tesorotools/driver.py +0 -0
- {tesorotools_python-0.1.7 → tesorotools_python-0.1.9}/src/tesorotools/latex/__init__.py +0 -0
- {tesorotools_python-0.1.7 → tesorotools_python-0.1.9}/src/tesorotools/latex/cli.py +0 -0
- {tesorotools_python-0.1.7 → tesorotools_python-0.1.9}/src/tesorotools/latex/scaffold.py +0 -0
- {tesorotools_python-0.1.7 → tesorotools_python-0.1.9}/src/tesorotools/latex/templates/article/main.tex.tmpl +0 -0
- {tesorotools_python-0.1.7 → tesorotools_python-0.1.9}/src/tesorotools/latex/templates/article/sections/01-introduccion.tex +0 -0
- {tesorotools_python-0.1.7 → tesorotools_python-0.1.9}/src/tesorotools/latex/templates/article/sections/02-analisis.tex +0 -0
- {tesorotools_python-0.1.7 → tesorotools_python-0.1.9}/src/tesorotools/latex/templates/article/sections/03-conclusiones.tex +0 -0
- {tesorotools_python-0.1.7 → tesorotools_python-0.1.9}/src/tesorotools/latex/templates/beamer/main.tex.tmpl +0 -0
- {tesorotools_python-0.1.7 → tesorotools_python-0.1.9}/src/tesorotools/latex/templates/beamer/slides/01-contexto.tex +0 -0
- {tesorotools_python-0.1.7 → tesorotools_python-0.1.9}/src/tesorotools/latex/templates/beamer/slides/02-analisis.tex +0 -0
- {tesorotools_python-0.1.7 → tesorotools_python-0.1.9}/src/tesorotools/latex/templates/beamer/slides/03-conclusiones.tex +0 -0
- {tesorotools_python-0.1.7 → tesorotools_python-0.1.9}/src/tesorotools/latex/templates/beamer/style/beamerthemeTesoro.sty +0 -0
- {tesorotools_python-0.1.7 → tesorotools_python-0.1.9}/src/tesorotools/latex/templates/book/appendices/a-fuentes.tex +0 -0
- {tesorotools_python-0.1.7 → tesorotools_python-0.1.9}/src/tesorotools/latex/templates/book/chapters/01-introduccion.tex +0 -0
- {tesorotools_python-0.1.7 → tesorotools_python-0.1.9}/src/tesorotools/latex/templates/book/chapters/02-analisis.tex +0 -0
- {tesorotools_python-0.1.7 → tesorotools_python-0.1.9}/src/tesorotools/latex/templates/book/frontmatter/resumen.tex +0 -0
- {tesorotools_python-0.1.7 → tesorotools_python-0.1.9}/src/tesorotools/latex/templates/book/main.tex.tmpl +0 -0
- {tesorotools_python-0.1.7 → tesorotools_python-0.1.9}/src/tesorotools/latex/templates/common/README.md.tmpl +0 -0
- {tesorotools_python-0.1.7 → tesorotools_python-0.1.9}/src/tesorotools/latex/templates/common/dot-gitignore +0 -0
- {tesorotools_python-0.1.7 → tesorotools_python-0.1.9}/src/tesorotools/latex/templates/common/dot-vscode/settings.json +0 -0
- {tesorotools_python-0.1.7 → tesorotools_python-0.1.9}/src/tesorotools/latex/templates/common/figures/dot-gitkeep +0 -0
- {tesorotools_python-0.1.7 → tesorotools_python-0.1.9}/src/tesorotools/latex/templates/common/fonts/README.md +0 -0
- {tesorotools_python-0.1.7 → tesorotools_python-0.1.9}/src/tesorotools/latex/templates/common/latexmkrc +0 -0
- {tesorotools_python-0.1.7 → tesorotools_python-0.1.9}/src/tesorotools/latex/templates/common/refs.bib +0 -0
- {tesorotools_python-0.1.7 → tesorotools_python-0.1.9}/src/tesorotools/latex/templates/common/style/tesoro.sty +0 -0
- {tesorotools_python-0.1.7 → tesorotools_python-0.1.9}/src/tesorotools/latex/templates/common/style/tesorobrand.sty.tmpl +0 -0
- {tesorotools_python-0.1.7 → tesorotools_python-0.1.9}/src/tesorotools/latex/templates/common/style/tesorolocal.sty.tmpl +0 -0
- {tesorotools_python-0.1.7 → tesorotools_python-0.1.9}/src/tesorotools/manifest.py +0 -0
- {tesorotools_python-0.1.7 → tesorotools_python-0.1.9}/src/tesorotools/note/__init__.py +0 -0
- {tesorotools_python-0.1.7 → tesorotools_python-0.1.9}/src/tesorotools/note/builder.py +0 -0
- {tesorotools_python-0.1.7 → tesorotools_python-0.1.9}/src/tesorotools/note/charts.py +0 -0
- {tesorotools_python-0.1.7 → tesorotools_python-0.1.9}/src/tesorotools/note/cli.py +0 -0
- {tesorotools_python-0.1.7 → tesorotools_python-0.1.9}/src/tesorotools/note/inline.py +0 -0
- {tesorotools_python-0.1.7 → tesorotools_python-0.1.9}/src/tesorotools/note/issues.py +0 -0
- {tesorotools_python-0.1.7 → tesorotools_python-0.1.9}/src/tesorotools/note/markdown.py +0 -0
- {tesorotools_python-0.1.7 → tesorotools_python-0.1.9}/src/tesorotools/note/pdf.py +0 -0
- {tesorotools_python-0.1.7 → tesorotools_python-0.1.9}/src/tesorotools/note/skeleton.md +0 -0
- {tesorotools_python-0.1.7 → tesorotools_python-0.1.9}/src/tesorotools/note/skill/SKILL.md +0 -0
- {tesorotools_python-0.1.7 → tesorotools_python-0.1.9}/src/tesorotools/note/skill/reglas.md +0 -0
- {tesorotools_python-0.1.7 → tesorotools_python-0.1.9}/src/tesorotools/note/template.py +0 -0
- {tesorotools_python-0.1.7 → tesorotools_python-0.1.9}/src/tesorotools/offsets/__init__.py +0 -0
- {tesorotools_python-0.1.7 → tesorotools_python-0.1.9}/src/tesorotools/offsets/offsets.py +0 -0
- {tesorotools_python-0.1.7 → tesorotools_python-0.1.9}/src/tesorotools/offsets/outliers.py +0 -0
- {tesorotools_python-0.1.7 → tesorotools_python-0.1.9}/src/tesorotools/orchestration.py +0 -0
- {tesorotools_python-0.1.7 → tesorotools_python-0.1.9}/src/tesorotools/palette.py +0 -0
- {tesorotools_python-0.1.7 → tesorotools_python-0.1.9}/src/tesorotools/pipeline/__init__.py +0 -0
- {tesorotools_python-0.1.7 → tesorotools_python-0.1.9}/src/tesorotools/pipeline/diagnose.py +0 -0
- {tesorotools_python-0.1.7 → tesorotools_python-0.1.9}/src/tesorotools/pipeline/engine.py +0 -0
- {tesorotools_python-0.1.7 → tesorotools_python-0.1.9}/src/tesorotools/pipeline/rules.py +0 -0
- {tesorotools_python-0.1.7 → tesorotools_python-0.1.9}/src/tesorotools/presentation/house.py +0 -0
- {tesorotools_python-0.1.7 → tesorotools_python-0.1.9}/src/tesorotools/presentation/pdf.py +0 -0
- {tesorotools_python-0.1.7 → tesorotools_python-0.1.9}/src/tesorotools/presentation/template.py +0 -0
- {tesorotools_python-0.1.7 → tesorotools_python-0.1.9}/src/tesorotools/providers/__init__.py +0 -0
- {tesorotools_python-0.1.7 → tesorotools_python-0.1.9}/src/tesorotools/providers/base.py +0 -0
- {tesorotools_python-0.1.7 → tesorotools_python-0.1.9}/src/tesorotools/providers/bde.py +0 -0
- {tesorotools_python-0.1.7 → tesorotools_python-0.1.9}/src/tesorotools/providers/ecb.py +0 -0
- {tesorotools_python-0.1.7 → tesorotools_python-0.1.9}/src/tesorotools/providers/fred.py +0 -0
- {tesorotools_python-0.1.7 → tesorotools_python-0.1.9}/src/tesorotools/providers/imf_irfcl.py +0 -0
- {tesorotools_python-0.1.7 → tesorotools_python-0.1.9}/src/tesorotools/py.typed +0 -0
- {tesorotools_python-0.1.7 → tesorotools_python-0.1.9}/src/tesorotools/render/__init__.py +0 -0
- {tesorotools_python-0.1.7 → tesorotools_python-0.1.9}/src/tesorotools/render/content/__init__.py +0 -0
- {tesorotools_python-0.1.7 → tesorotools_python-0.1.9}/src/tesorotools/render/content/content.py +0 -0
- {tesorotools_python-0.1.7 → tesorotools_python-0.1.9}/src/tesorotools/render/content/images.py +0 -0
- {tesorotools_python-0.1.7 → tesorotools_python-0.1.9}/src/tesorotools/render/content/section.py +0 -0
- {tesorotools_python-0.1.7 → tesorotools_python-0.1.9}/src/tesorotools/render/content/subtitle.py +0 -0
- {tesorotools_python-0.1.7 → tesorotools_python-0.1.9}/src/tesorotools/render/content/table.py +0 -0
- {tesorotools_python-0.1.7 → tesorotools_python-0.1.9}/src/tesorotools/render/content/text.py +0 -0
- {tesorotools_python-0.1.7 → tesorotools_python-0.1.9}/src/tesorotools/render/content/title.py +0 -0
- {tesorotools_python-0.1.7 → tesorotools_python-0.1.9}/src/tesorotools/render/document.py +0 -0
- {tesorotools_python-0.1.7 → tesorotools_python-0.1.9}/src/tesorotools/render/report.py +0 -0
- {tesorotools_python-0.1.7 → tesorotools_python-0.1.9}/src/tesorotools/scaffold.py +0 -0
- {tesorotools_python-0.1.7 → tesorotools_python-0.1.9}/src/tesorotools/testing/__init__.py +0 -0
- {tesorotools_python-0.1.7 → tesorotools_python-0.1.9}/src/tesorotools/testing/compare.py +0 -0
- {tesorotools_python-0.1.7 → tesorotools_python-0.1.9}/src/tesorotools/utils/__init__.py +0 -0
- {tesorotools_python-0.1.7 → tesorotools_python-0.1.9}/src/tesorotools/utils/config.py +0 -0
- {tesorotools_python-0.1.7 → tesorotools_python-0.1.9}/src/tesorotools/utils/format.py +0 -0
- {tesorotools_python-0.1.7 → tesorotools_python-0.1.9}/src/tesorotools/utils/globals.py +0 -0
- {tesorotools_python-0.1.7 → tesorotools_python-0.1.9}/src/tesorotools/utils/matplotlib.py +0 -0
- {tesorotools_python-0.1.7 → tesorotools_python-0.1.9}/src/tesorotools/utils/series.py +0 -0
- {tesorotools_python-0.1.7 → tesorotools_python-0.1.9}/src/tesorotools/utils/shortcuts.py +0 -0
- {tesorotools_python-0.1.7 → tesorotools_python-0.1.9}/src/tesorotools/utils/template.py +0 -0
- {tesorotools_python-0.1.7 → tesorotools_python-0.1.9}/src/tesorotools/web/__init__.py +0 -0
- {tesorotools_python-0.1.7 → tesorotools_python-0.1.9}/src/tesorotools/web/app.py +0 -0
- {tesorotools_python-0.1.7 → tesorotools_python-0.1.9}/src/tesorotools/web/brand.py +0 -0
- {tesorotools_python-0.1.7 → tesorotools_python-0.1.9}/src/tesorotools/web/cli.py +0 -0
- {tesorotools_python-0.1.7 → tesorotools_python-0.1.9}/src/tesorotools/web/components.py +0 -0
- {tesorotools_python-0.1.7 → tesorotools_python-0.1.9}/src/tesorotools/web/formatting.py +0 -0
- {tesorotools_python-0.1.7 → tesorotools_python-0.1.9}/src/tesorotools/web/launcher.py +0 -0
- {tesorotools_python-0.1.7 → tesorotools_python-0.1.9}/src/tesorotools/web/palette.py +0 -0
- {tesorotools_python-0.1.7 → tesorotools_python-0.1.9}/src/tesorotools/web/plotting.py +0 -0
- {tesorotools_python-0.1.7 → tesorotools_python-0.1.9}/src/tesorotools/web/scaffold.py +0 -0
- {tesorotools_python-0.1.7 → tesorotools_python-0.1.9}/src/tesorotools/web/templates/Dockerfile.tmpl +0 -0
- {tesorotools_python-0.1.7 → tesorotools_python-0.1.9}/src/tesorotools/web/templates/README.md.tmpl +0 -0
- {tesorotools_python-0.1.7 → tesorotools_python-0.1.9}/src/tesorotools/web/templates/dot-dockerignore.tmpl +0 -0
- {tesorotools_python-0.1.7 → tesorotools_python-0.1.9}/src/tesorotools/web/templates/dot-gitignore.tmpl +0 -0
- {tesorotools_python-0.1.7 → tesorotools_python-0.1.9}/src/tesorotools/web/templates/dot-streamlit/config.toml.tmpl +0 -0
- {tesorotools_python-0.1.7 → tesorotools_python-0.1.9}/src/tesorotools/web/templates/pyproject.toml.tmpl +0 -0
- {tesorotools_python-0.1.7 → tesorotools_python-0.1.9}/src/tesorotools/web/templates/src/$package/__init__.py.tmpl +0 -0
- {tesorotools_python-0.1.7 → tesorotools_python-0.1.9}/src/tesorotools/web/templates/src/$package/cli.py.tmpl +0 -0
- {tesorotools_python-0.1.7 → tesorotools_python-0.1.9}/src/tesorotools/web/templates/src/$package/main.py.tmpl +0 -0
- {tesorotools_python-0.1.7 → tesorotools_python-0.1.9}/src/tesorotools/web/templates/src/$package/sections.py.tmpl +0 -0
- {tesorotools_python-0.1.7 → tesorotools_python-0.1.9}/src/tesorotools/web/templates/tests/__init__.py.tmpl +0 -0
- {tesorotools_python-0.1.7 → tesorotools_python-0.1.9}/src/tesorotools/web/templates/tests/test_app.py.tmpl +0 -0
- {tesorotools_python-0.1.7 → tesorotools_python-0.1.9}/src/tesorotools/web/theme.py +0 -0
|
@@ -47,9 +47,21 @@ from matplotlib.dates import (
|
|
|
47
47
|
from matplotlib.figure import Figure
|
|
48
48
|
from matplotlib.font_manager import FontProperties
|
|
49
49
|
from matplotlib.legend import Legend as MplLegend
|
|
50
|
-
from matplotlib.
|
|
50
|
+
from matplotlib.text import Text
|
|
51
|
+
from matplotlib.ticker import (
|
|
52
|
+
FixedFormatter,
|
|
53
|
+
FixedLocator,
|
|
54
|
+
FuncFormatter,
|
|
55
|
+
MaxNLocator,
|
|
56
|
+
NullFormatter,
|
|
57
|
+
)
|
|
51
58
|
from yaml.nodes import MappingNode
|
|
52
59
|
|
|
60
|
+
from tesorotools.artists._dates import (
|
|
61
|
+
DateLayout,
|
|
62
|
+
date_layouts,
|
|
63
|
+
period_label,
|
|
64
|
+
)
|
|
53
65
|
from tesorotools.utils.matplotlib import (
|
|
54
66
|
PLOT_CONFIG,
|
|
55
67
|
apply_house_style,
|
|
@@ -110,6 +122,53 @@ def date_maxticks(density: str, *, compact: bool) -> int:
|
|
|
110
122
|
return int(table.get(density, fallback[density]))
|
|
111
123
|
|
|
112
124
|
|
|
125
|
+
# Most date labels an axis shows per date_density level, read from
|
|
126
|
+
# ``compact.date_max_labels`` / ``line.date_max_labels`` in ``plots.yaml``:
|
|
127
|
+
# the finest calendar step (every year, every month...) whose labels fit in
|
|
128
|
+
# the axis width AND stay within this count wins.
|
|
129
|
+
_DATE_MAX_LABELS_FALLBACK_COMPACT: dict[str, int] = {
|
|
130
|
+
"few": 4,
|
|
131
|
+
"default": 8,
|
|
132
|
+
"many": 14,
|
|
133
|
+
}
|
|
134
|
+
_DATE_MAX_LABELS_FALLBACK_LINE: dict[str, int] = {
|
|
135
|
+
"few": 6,
|
|
136
|
+
"default": 15,
|
|
137
|
+
"many": 30,
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
|
|
141
|
+
def date_max_labels(density: str, *, compact: bool) -> int:
|
|
142
|
+
"""Most date labels a ``date_density`` level allows on the x-axis.
|
|
143
|
+
|
|
144
|
+
Reads ``compact.date_max_labels`` / ``line.date_max_labels`` from
|
|
145
|
+
``plots.yaml``, falling back to the built-in table when the config
|
|
146
|
+
(or a level) is absent.
|
|
147
|
+
"""
|
|
148
|
+
cfg = COMPACT_CONFIG if compact else LINE_CONFIG
|
|
149
|
+
fallback = (
|
|
150
|
+
_DATE_MAX_LABELS_FALLBACK_COMPACT
|
|
151
|
+
if compact
|
|
152
|
+
else _DATE_MAX_LABELS_FALLBACK_LINE
|
|
153
|
+
)
|
|
154
|
+
table: dict[str, Any] = cfg.get("date_max_labels", fallback)
|
|
155
|
+
return int(table.get(density, fallback[density]))
|
|
156
|
+
|
|
157
|
+
|
|
158
|
+
def _date_axis_config(*, compact: bool) -> tuple[int, float, float]:
|
|
159
|
+
"""Calendar date-axis knobs of a mode, from ``plots.yaml``.
|
|
160
|
+
|
|
161
|
+
``(date_monthly_max_months, date_centered_min_fraction,
|
|
162
|
+
date_label_gap_em)``.
|
|
163
|
+
"""
|
|
164
|
+
cfg = COMPACT_CONFIG if compact else LINE_CONFIG
|
|
165
|
+
return (
|
|
166
|
+
int(cfg.get("date_monthly_max_months", 24)),
|
|
167
|
+
float(cfg.get("date_centered_min_fraction", 0.5)),
|
|
168
|
+
float(cfg.get("date_label_gap_em", 0.3)),
|
|
169
|
+
)
|
|
170
|
+
|
|
171
|
+
|
|
113
172
|
def dynamic_dpi(fig: Figure) -> int:
|
|
114
173
|
"""Save DPI adapted to the figure's physical size.
|
|
115
174
|
|
|
@@ -344,9 +403,18 @@ class Legend:
|
|
|
344
403
|
values work in compact and in full-size charts, so a
|
|
345
404
|
compact chart in a Word table can carry its labels under
|
|
346
405
|
the plot when the text above it already reads as a title.
|
|
406
|
+
``font_pt`` fixes the size of the legend text (a slide wants it
|
|
407
|
+
as big as the ticks) and ``min_entries=2`` drops the legend of a
|
|
408
|
+
chart with a single series.
|
|
347
409
|
"""
|
|
348
410
|
|
|
349
|
-
def __init__(
|
|
411
|
+
def __init__(
|
|
412
|
+
self,
|
|
413
|
+
ncol: int | None = None,
|
|
414
|
+
position: str = "auto",
|
|
415
|
+
font_pt: float | None = None,
|
|
416
|
+
min_entries: int = 1,
|
|
417
|
+
) -> None:
|
|
350
418
|
if position not in LEGEND_POSITIONS:
|
|
351
419
|
raise ValueError(
|
|
352
420
|
f"legend position must be one of {LEGEND_POSITIONS}: "
|
|
@@ -354,6 +422,12 @@ class Legend:
|
|
|
354
422
|
)
|
|
355
423
|
self.ncol = ncol
|
|
356
424
|
self.position = position
|
|
425
|
+
#: Size of the legend text (points); ``None`` keeps the house
|
|
426
|
+
#: size (``compact.legend_font_pt``, never above the ticks).
|
|
427
|
+
self.font_pt = font_pt
|
|
428
|
+
#: Fewer entries than this draw no legend: ``2`` leaves a chart
|
|
429
|
+
#: of a single series without one.
|
|
430
|
+
self.min_entries = min_entries
|
|
357
431
|
|
|
358
432
|
@classmethod
|
|
359
433
|
def from_yaml(cls, loader: TemplateLoader, node: MappingNode) -> Self:
|
|
@@ -804,6 +878,188 @@ def _date_label_ha(
|
|
|
804
878
|
return "right" if rotation > 0 else "left"
|
|
805
879
|
|
|
806
880
|
|
|
881
|
+
def _style_date_labels(ax: Axes, rot: float, *, minor: bool) -> None:
|
|
882
|
+
"""Tilt the date labels *rot* degrees with the matching anchor.
|
|
883
|
+
|
|
884
|
+
Rotation goes in the axis tick kwargs; ``ha`` and the anchor rotation
|
|
885
|
+
mode are per-label Text properties, so they are reapplied after every
|
|
886
|
+
draw or locator swap.
|
|
887
|
+
"""
|
|
888
|
+
ha = _date_label_ha(None if rot == 0 else rot)
|
|
889
|
+
ax.tick_params( # type: ignore[reportUnknownMemberType]
|
|
890
|
+
axis="x", which="minor" if minor else "major", rotation=rot
|
|
891
|
+
)
|
|
892
|
+
for label in ax.get_xticklabels(minor=minor):
|
|
893
|
+
label.set_horizontalalignment(ha)
|
|
894
|
+
if rot != 0:
|
|
895
|
+
label.set_rotation_mode("anchor")
|
|
896
|
+
|
|
897
|
+
|
|
898
|
+
def _layout_texts(layout: DateLayout, *, pin_start: bool) -> list[str]:
|
|
899
|
+
"""The label texts of *layout*, as the axis will print them."""
|
|
900
|
+
if layout.centered:
|
|
901
|
+
# centred: each label names its period
|
|
902
|
+
return [
|
|
903
|
+
period_label(p, layout.step, first=i == 0)
|
|
904
|
+
for i, p in enumerate(layout.periods)
|
|
905
|
+
]
|
|
906
|
+
values = [float(cast(Any, date2num(t))) for t in layout.ticks]
|
|
907
|
+
formatter = _tick_formatter(FixedLocator(values), values[0], pin_start)
|
|
908
|
+
return list(formatter.format_ticks(values))
|
|
909
|
+
|
|
910
|
+
|
|
911
|
+
def _tick_formatter(
|
|
912
|
+
locator: FixedLocator, start: float, pin_start: bool
|
|
913
|
+
) -> ConciseDateFormatter:
|
|
914
|
+
return (
|
|
915
|
+
_StartLabeledConcise(locator, start)
|
|
916
|
+
if pin_start
|
|
917
|
+
else ConciseDateFormatter(locator)
|
|
918
|
+
)
|
|
919
|
+
|
|
920
|
+
|
|
921
|
+
def calendar_date_axis(
|
|
922
|
+
ax: Axes,
|
|
923
|
+
layouts: list[DateLayout],
|
|
924
|
+
*,
|
|
925
|
+
pin_start: bool,
|
|
926
|
+
rotation: float | None = None,
|
|
927
|
+
auto_rotate: bool = False,
|
|
928
|
+
gap_em: float = 0.3,
|
|
929
|
+
) -> DateLayout:
|
|
930
|
+
"""Label every year (or month) the axis can hold.
|
|
931
|
+
|
|
932
|
+
*layouts* are the candidate calendar steps, finest first (see
|
|
933
|
+
:func:`tesorotools.artists._dates.date_layouts`). Each one's labels
|
|
934
|
+
are measured at their real size and position, and the first whose
|
|
935
|
+
labels do not overlap wins: a decade shows every year, thirty years
|
|
936
|
+
fall back to one every two or five. When none fits, the coarsest is
|
|
937
|
+
used. Returns the layout drawn.
|
|
938
|
+
|
|
939
|
+
With the start pinned, the first calendar tick gives way to the
|
|
940
|
+
(wider) start label when they collide and it lies under half a step
|
|
941
|
+
from the start: a series from October 2015 shows ``oct. 2015``,
|
|
942
|
+
2017, 2018... Further away, dropping it would read as a skipped
|
|
943
|
+
year, so that step counts as not fitting.
|
|
944
|
+
|
|
945
|
+
Centred layouts (``date_labels: centered``) put the tick marks on the
|
|
946
|
+
period boundaries and the labels, as minor ticks without a mark, in
|
|
947
|
+
the middle of each period.
|
|
948
|
+
|
|
949
|
+
``rotation`` tilts the labels. ``auto_rotate`` (ignored when a
|
|
950
|
+
rotation is given) tries 0, 30, 45, 60 and 90 degrees and keeps the
|
|
951
|
+
flattest tilt that reaches the finest step any of them reaches.
|
|
952
|
+
|
|
953
|
+
Neighbouring labels must stay *gap_em* ems apart
|
|
954
|
+
(``date_label_gap_em`` in ``plots.yaml``).
|
|
955
|
+
"""
|
|
956
|
+
fig = ax.get_figure()
|
|
957
|
+
assert fig is not None
|
|
958
|
+
# settle the layout so dates map to their final pixels
|
|
959
|
+
fig.canvas.draw() # type: ignore[reportUnknownMemberType]
|
|
960
|
+
renderer = fig.canvas.get_renderer() # type: ignore[reportUnknownMemberType]
|
|
961
|
+
font = ax.xaxis.get_major_ticks()[0].label1.get_fontproperties()
|
|
962
|
+
# a fixed share of an em between neighbouring labels, whatever the dpi
|
|
963
|
+
pad_px = (
|
|
964
|
+
float(
|
|
965
|
+
renderer.points_to_pixels(font.get_size_in_points()) # type: ignore[reportUnknownMemberType]
|
|
966
|
+
)
|
|
967
|
+
* gap_em
|
|
968
|
+
)
|
|
969
|
+
|
|
970
|
+
def _boxes(layout: DateLayout, rot: float) -> list[Any]:
|
|
971
|
+
texts = _layout_texts(layout, pin_start=pin_start)
|
|
972
|
+
ha = _date_label_ha(None if rot == 0 else rot)
|
|
973
|
+
boxes: list[Any] = []
|
|
974
|
+
for when, text in zip(layout.labels, texts):
|
|
975
|
+
x_px = float(
|
|
976
|
+
ax.transData.transform((float(cast(Any, date2num(when))), 0))[0]
|
|
977
|
+
)
|
|
978
|
+
probe = Text(
|
|
979
|
+
x_px,
|
|
980
|
+
0.0,
|
|
981
|
+
text,
|
|
982
|
+
fontproperties=font,
|
|
983
|
+
rotation=rot,
|
|
984
|
+
horizontalalignment=ha,
|
|
985
|
+
rotation_mode="anchor" if rot != 0 else "default",
|
|
986
|
+
)
|
|
987
|
+
probe.set_figure(fig)
|
|
988
|
+
boxes.append(probe.get_window_extent(renderer)) # type: ignore[reportUnknownArgumentType]
|
|
989
|
+
return boxes
|
|
990
|
+
|
|
991
|
+
def _fit(layout: DateLayout, rot: float) -> list[int] | None:
|
|
992
|
+
boxes = _boxes(layout, rot)
|
|
993
|
+
kept = list(range(len(boxes)))
|
|
994
|
+
centered = layout.centered
|
|
995
|
+
if pin_start and not centered and len(kept) > 2:
|
|
996
|
+
# the first calendar tick may give way to the (wider) start
|
|
997
|
+
# label only when it sits under half a step from the start:
|
|
998
|
+
# further away, dropping it would open a gap that reads as a
|
|
999
|
+
# skipped year, so the step does not fit instead
|
|
1000
|
+
when = [float(cast(Any, date2num(t))) for t in layout.ticks]
|
|
1001
|
+
near = when[1] - when[0] < (when[2] - when[1]) / 2
|
|
1002
|
+
if near and boxes[1].x0 <= boxes[0].x1 + pad_px:
|
|
1003
|
+
kept.pop(1)
|
|
1004
|
+
clear = all(
|
|
1005
|
+
boxes[b].x0 > boxes[a].x1 + pad_px for a, b in zip(kept, kept[1:])
|
|
1006
|
+
)
|
|
1007
|
+
return kept if clear else None
|
|
1008
|
+
|
|
1009
|
+
tilts = (
|
|
1010
|
+
(0.0, 30.0, 45.0, 60.0, 90.0)
|
|
1011
|
+
if auto_rotate and rotation is None
|
|
1012
|
+
else (0.0 if rotation is None else float(rotation),)
|
|
1013
|
+
)
|
|
1014
|
+
best: tuple[int, float, list[int]] | None = None
|
|
1015
|
+
for rot in tilts:
|
|
1016
|
+
for i, layout in enumerate(layouts):
|
|
1017
|
+
if best is not None and i >= best[0]:
|
|
1018
|
+
break
|
|
1019
|
+
kept = _fit(layout, rot)
|
|
1020
|
+
if kept is not None:
|
|
1021
|
+
best = (i, rot, kept)
|
|
1022
|
+
break
|
|
1023
|
+
if best is not None and best[0] == 0:
|
|
1024
|
+
break
|
|
1025
|
+
if best is None:
|
|
1026
|
+
coarsest = layouts[-1]
|
|
1027
|
+
best = (len(layouts) - 1, tilts[0], list(range(len(coarsest.labels))))
|
|
1028
|
+
index, rot, kept = best
|
|
1029
|
+
layout = layouts[index]
|
|
1030
|
+
centered = layout.centered
|
|
1031
|
+
|
|
1032
|
+
if centered:
|
|
1033
|
+
major = FixedLocator(
|
|
1034
|
+
[float(cast(Any, date2num(t))) for t in layout.ticks]
|
|
1035
|
+
)
|
|
1036
|
+
ax.xaxis.set_major_locator(major)
|
|
1037
|
+
ax.xaxis.set_major_formatter(NullFormatter())
|
|
1038
|
+
texts = _layout_texts(layout, pin_start=pin_start)
|
|
1039
|
+
ax.xaxis.set_minor_locator(
|
|
1040
|
+
FixedLocator(
|
|
1041
|
+
[float(cast(Any, date2num(layout.labels[k]))) for k in kept]
|
|
1042
|
+
)
|
|
1043
|
+
)
|
|
1044
|
+
ax.xaxis.set_minor_formatter(FixedFormatter([texts[k] for k in kept]))
|
|
1045
|
+
ax.tick_params( # type: ignore[reportUnknownMemberType]
|
|
1046
|
+
axis="x",
|
|
1047
|
+
which="minor",
|
|
1048
|
+
length=0,
|
|
1049
|
+
labelsize=ax.xaxis.get_major_ticks()[0].label1.get_fontsize(),
|
|
1050
|
+
)
|
|
1051
|
+
else:
|
|
1052
|
+
values = [float(cast(Any, date2num(layout.ticks[k]))) for k in kept]
|
|
1053
|
+
fixed = FixedLocator(values)
|
|
1054
|
+
ax.xaxis.set_major_locator(fixed)
|
|
1055
|
+
ax.xaxis.set_major_formatter(
|
|
1056
|
+
_tick_formatter(fixed, values[0], pin_start)
|
|
1057
|
+
)
|
|
1058
|
+
fig.canvas.draw() # type: ignore[reportUnknownMemberType]
|
|
1059
|
+
_style_date_labels(ax, rot, minor=centered)
|
|
1060
|
+
return layout
|
|
1061
|
+
|
|
1062
|
+
|
|
807
1063
|
def compact_date_axis(
|
|
808
1064
|
ax: Axes,
|
|
809
1065
|
data_min: Any,
|
|
@@ -813,8 +1069,22 @@ def compact_date_axis(
|
|
|
813
1069
|
rotation: float | None = None,
|
|
814
1070
|
pin_start: bool | None = None,
|
|
815
1071
|
auto_rotate: bool = False,
|
|
1072
|
+
max_labels: int | None = None,
|
|
1073
|
+
step: str = "auto",
|
|
1074
|
+
labels: str = "tick",
|
|
816
1075
|
) -> None:
|
|
817
|
-
"""Compact date x-axis:
|
|
1076
|
+
"""Compact date x-axis: as many calendar dates as fit the narrow figure.
|
|
1077
|
+
|
|
1078
|
+
The axis labels every year (or, over two years at most, every month)
|
|
1079
|
+
that fits in the width without overlapping and within *max_labels*
|
|
1080
|
+
(``None`` reads ``compact.date_max_labels`` for the default density),
|
|
1081
|
+
stepping to one every 2, 5, 10... years only when they do not fit;
|
|
1082
|
+
``step`` (``"1Y"``, ``"3M"``...) fixes the step instead, and
|
|
1083
|
+
``labels="centered"`` centres each year over its stretch. See
|
|
1084
|
+
:func:`calendar_date_axis`.
|
|
1085
|
+
|
|
1086
|
+
Spans too short for a calendar grid (a few weeks of daily data) keep
|
|
1087
|
+
the historical behaviour described below, with *maxticks* ticks.
|
|
818
1088
|
|
|
819
1089
|
Ticks are computed over the **data span** ``[data_min,
|
|
820
1090
|
data_max]`` and pinned with a ``FixedLocator``, so none
|
|
@@ -853,6 +1123,31 @@ def compact_date_axis(
|
|
|
853
1123
|
if pin_start is None
|
|
854
1124
|
else pin_start
|
|
855
1125
|
)
|
|
1126
|
+
monthly_max, min_fraction, gap_em = _date_axis_config(compact=True)
|
|
1127
|
+
layouts = date_layouts(
|
|
1128
|
+
data_min,
|
|
1129
|
+
data_max,
|
|
1130
|
+
step=step,
|
|
1131
|
+
max_labels=(
|
|
1132
|
+
date_max_labels("default", compact=True)
|
|
1133
|
+
if max_labels is None
|
|
1134
|
+
else max_labels
|
|
1135
|
+
),
|
|
1136
|
+
monthly_max_months=monthly_max,
|
|
1137
|
+
pin_start=show_start,
|
|
1138
|
+
centered=labels == "centered",
|
|
1139
|
+
min_fraction=min_fraction,
|
|
1140
|
+
)
|
|
1141
|
+
if layouts:
|
|
1142
|
+
calendar_date_axis(
|
|
1143
|
+
ax,
|
|
1144
|
+
layouts,
|
|
1145
|
+
pin_start=show_start,
|
|
1146
|
+
rotation=rotation,
|
|
1147
|
+
auto_rotate=auto_rotate,
|
|
1148
|
+
gap_em=gap_em,
|
|
1149
|
+
)
|
|
1150
|
+
return
|
|
856
1151
|
# same span-clipped ticks (and same pinned start) the non-compact axis
|
|
857
1152
|
# uses, so both modes answer "when does this start?" identically
|
|
858
1153
|
ticks: list[float] = date_span_ticks(
|
|
@@ -958,8 +1253,20 @@ def apply_date_axis(
|
|
|
958
1253
|
data_min: Any = None,
|
|
959
1254
|
data_max: Any = None,
|
|
960
1255
|
pin_start: bool = False,
|
|
1256
|
+
max_labels: int | None = None,
|
|
1257
|
+
step: str = "auto",
|
|
1258
|
+
labels: str = "tick",
|
|
961
1259
|
) -> None:
|
|
962
|
-
"""Native matplotlib date x-axis with
|
|
1260
|
+
"""Native matplotlib date x-axis with every year (or month) that fits.
|
|
1261
|
+
|
|
1262
|
+
Given the ``data_min`` / ``data_max`` span, the axis labels every year
|
|
1263
|
+
(or, over two years at most, every month) that fits in the width and
|
|
1264
|
+
within *max_labels* (``None`` reads ``line.date_max_labels`` for the
|
|
1265
|
+
default density), stepping to one every 2, 5, 10... years only when
|
|
1266
|
+
they do not; ``step`` fixes the step and ``labels="centered"`` centres
|
|
1267
|
+
each year over its stretch (see :func:`calendar_date_axis`). The rest
|
|
1268
|
+
of this docstring describes the fallback for spans too short for a
|
|
1269
|
+
calendar grid, or when no span is given.
|
|
963
1270
|
|
|
964
1271
|
The non-compact counterpart to :func:`compact_date_axis`: it takes over the
|
|
965
1272
|
x-axis (which must carry a **native** date axis, i.e. the series were
|
|
@@ -984,6 +1291,31 @@ def apply_date_axis(
|
|
|
984
1291
|
``rotation=None`` has to *actively* flatten them to keep the documented
|
|
985
1292
|
"horizontal unless asked" behaviour.
|
|
986
1293
|
"""
|
|
1294
|
+
if data_min is not None and data_max is not None:
|
|
1295
|
+
monthly_max, min_fraction, gap_em = _date_axis_config(compact=False)
|
|
1296
|
+
layouts = date_layouts(
|
|
1297
|
+
data_min,
|
|
1298
|
+
data_max,
|
|
1299
|
+
step=step,
|
|
1300
|
+
max_labels=(
|
|
1301
|
+
date_max_labels("default", compact=False)
|
|
1302
|
+
if max_labels is None
|
|
1303
|
+
else max_labels
|
|
1304
|
+
),
|
|
1305
|
+
monthly_max_months=monthly_max,
|
|
1306
|
+
pin_start=pin_start,
|
|
1307
|
+
centered=labels == "centered",
|
|
1308
|
+
min_fraction=min_fraction,
|
|
1309
|
+
)
|
|
1310
|
+
if layouts:
|
|
1311
|
+
calendar_date_axis(
|
|
1312
|
+
ax,
|
|
1313
|
+
layouts,
|
|
1314
|
+
pin_start=pin_start,
|
|
1315
|
+
rotation=rotation,
|
|
1316
|
+
gap_em=gap_em,
|
|
1317
|
+
)
|
|
1318
|
+
return
|
|
987
1319
|
if pin_start and data_min is not None and data_max is not None:
|
|
988
1320
|
ticks = date_span_ticks(
|
|
989
1321
|
data_min, data_max, maxticks=maxticks, pin_start=True
|
|
@@ -1226,8 +1558,9 @@ def style_compact_axes(
|
|
|
1226
1558
|
:func:`declutter_spines`.
|
|
1227
1559
|
"""
|
|
1228
1560
|
if font_pt is not None:
|
|
1561
|
+
# which="both": centred date labels live on the minor ticks
|
|
1229
1562
|
ax.tick_params( # type: ignore[reportUnknownMemberType]
|
|
1230
|
-
axis="both", labelsize=font_pt
|
|
1563
|
+
axis="both", which="both", labelsize=font_pt
|
|
1231
1564
|
)
|
|
1232
1565
|
if side == "right":
|
|
1233
1566
|
ax.yaxis.tick_right()
|
|
@@ -1244,6 +1577,8 @@ def place_legend(
|
|
|
1244
1577
|
ncol: int | None = None,
|
|
1245
1578
|
position: str = "auto",
|
|
1246
1579
|
entries: tuple[list[Any], list[str]] | None = None,
|
|
1580
|
+
font_pt: float | None = None,
|
|
1581
|
+
min_entries: int = 1,
|
|
1247
1582
|
) -> MplLegend | None:
|
|
1248
1583
|
"""Place the figure legend, compact-aware, from the labelled artists.
|
|
1249
1584
|
|
|
@@ -1257,17 +1592,21 @@ def place_legend(
|
|
|
1257
1592
|
``entries`` passes precomputed ``(handles, labels)`` instead of reading
|
|
1258
1593
|
them off *ax* -- for charts whose series span several axes (a
|
|
1259
1594
|
``twinx`` secondary y-axis), in the order the legend should list them.
|
|
1260
|
-
|
|
1595
|
+
``font_pt`` sets the size of the legend text. Returns ``None``
|
|
1596
|
+
(drawing nothing) when there are no labels, or fewer than
|
|
1597
|
+
``min_entries``.
|
|
1261
1598
|
"""
|
|
1262
1599
|
handles, labels = (
|
|
1263
1600
|
entries if entries is not None else ax.get_legend_handles_labels()
|
|
1264
1601
|
)
|
|
1265
|
-
if not labels:
|
|
1602
|
+
if not labels or len(labels) < min_entries:
|
|
1266
1603
|
return None
|
|
1267
1604
|
side = resolve_legend_position(position, compact=compact)
|
|
1268
1605
|
fig_w_px: float = fig.get_size_inches()[0] * fig.dpi
|
|
1269
1606
|
if compact:
|
|
1270
1607
|
legend_kw = compact_legend_kwargs(ax)
|
|
1608
|
+
if font_pt is not None:
|
|
1609
|
+
legend_kw["fontsize"] = font_pt
|
|
1271
1610
|
if ncol is None:
|
|
1272
1611
|
ncol = auto_ncol(
|
|
1273
1612
|
ax, labels, available_width_px=fig_w_px, **legend_kw
|
|
@@ -1285,8 +1624,12 @@ def place_legend(
|
|
|
1285
1624
|
**legend_kw,
|
|
1286
1625
|
)
|
|
1287
1626
|
if ncol is None:
|
|
1288
|
-
ncol = auto_ncol(
|
|
1289
|
-
|
|
1627
|
+
ncol = auto_ncol(
|
|
1628
|
+
ax, labels, available_width_px=fig_w_px, fontsize=font_pt
|
|
1629
|
+
)
|
|
1630
|
+
return legend_band(
|
|
1631
|
+
ax, handles, labels, ncol=ncol, position=side, fontsize=font_pt
|
|
1632
|
+
)
|
|
1290
1633
|
|
|
1291
1634
|
|
|
1292
1635
|
def legend_layout(legend: Legend | None) -> dict[str, Any]:
|
|
@@ -1302,9 +1645,13 @@ def legend_layout(legend: Legend | None) -> dict[str, Any]:
|
|
|
1302
1645
|
fig, ax, compact=self.compact, **legend_layout(self.legend)
|
|
1303
1646
|
)
|
|
1304
1647
|
"""
|
|
1648
|
+
if legend is None:
|
|
1649
|
+
return {"ncol": None, "position": "auto"}
|
|
1305
1650
|
return {
|
|
1306
|
-
"ncol": legend.ncol
|
|
1307
|
-
"position": legend.position
|
|
1651
|
+
"ncol": legend.ncol,
|
|
1652
|
+
"position": legend.position,
|
|
1653
|
+
"font_pt": legend.font_pt,
|
|
1654
|
+
"min_entries": legend.min_entries,
|
|
1308
1655
|
}
|
|
1309
1656
|
|
|
1310
1657
|
|