tesorotools-python 0.1.11__tar.gz → 0.1.12__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.11 → tesorotools_python-0.1.12}/PKG-INFO +1 -1
- tesorotools_python-0.1.12/src/tesorotools/artists/_bars.py +231 -0
- {tesorotools_python-0.1.11 → tesorotools_python-0.1.12}/src/tesorotools/artists/_common.py +307 -48
- {tesorotools_python-0.1.11 → tesorotools_python-0.1.12}/src/tesorotools/artists/_dates.py +118 -5
- {tesorotools_python-0.1.11 → tesorotools_python-0.1.12}/src/tesorotools/artists/bar_line.py +139 -39
- {tesorotools_python-0.1.11 → tesorotools_python-0.1.12}/src/tesorotools/artists/barh_plot.py +137 -26
- {tesorotools_python-0.1.11 → tesorotools_python-0.1.12}/src/tesorotools/artists/intraday_plot.py +8 -4
- {tesorotools_python-0.1.11 → tesorotools_python-0.1.12}/src/tesorotools/artists/line_plot.py +232 -96
- {tesorotools_python-0.1.11 → tesorotools_python-0.1.12}/src/tesorotools/artists/matrix.py +2 -2
- {tesorotools_python-0.1.11 → tesorotools_python-0.1.12}/src/tesorotools/artists/plotly_backend.py +39 -19
- {tesorotools_python-0.1.11 → tesorotools_python-0.1.12}/src/tesorotools/artists/sankey.py +2 -2
- {tesorotools_python-0.1.11 → tesorotools_python-0.1.12}/src/tesorotools/artists/scatter.py +10 -7
- {tesorotools_python-0.1.11 → tesorotools_python-0.1.12}/src/tesorotools/artists/stacked.py +69 -34
- {tesorotools_python-0.1.11 → tesorotools_python-0.1.12}/src/tesorotools/artists/type_curve.py +98 -41
- {tesorotools_python-0.1.11 → tesorotools_python-0.1.12}/src/tesorotools/artists/vector_plot.py +10 -10
- {tesorotools_python-0.1.11 → tesorotools_python-0.1.12}/src/tesorotools/artists/waterfall.py +123 -57
- {tesorotools_python-0.1.11 → tesorotools_python-0.1.12}/src/tesorotools/assets/plots.yaml +48 -8
- {tesorotools_python-0.1.11 → tesorotools_python-0.1.12}/src/tesorotools/note/charts.py +15 -2
- {tesorotools_python-0.1.11 → tesorotools_python-0.1.12}/src/tesorotools/note/lint.py +2 -0
- {tesorotools_python-0.1.11 → tesorotools_python-0.1.12}/src/tesorotools/note/markdown.py +106 -22
- {tesorotools_python-0.1.11 → tesorotools_python-0.1.12}/src/tesorotools/note/skill/SKILL.md +4 -3
- {tesorotools_python-0.1.11 → tesorotools_python-0.1.12}/src/tesorotools/presentation/__init__.py +40 -0
- tesorotools_python-0.1.12/src/tesorotools/presentation/builder.py +5965 -0
- {tesorotools_python-0.1.11 → tesorotools_python-0.1.12}/src/tesorotools/presentation/charts.py +14 -3
- {tesorotools_python-0.1.11 → tesorotools_python-0.1.12}/src/tesorotools/presentation/cli.py +13 -0
- {tesorotools_python-0.1.11 → tesorotools_python-0.1.12}/src/tesorotools/presentation/fit.py +29 -1
- tesorotools_python-0.1.12/src/tesorotools/presentation/fonts.py +260 -0
- {tesorotools_python-0.1.11 → tesorotools_python-0.1.12}/src/tesorotools/presentation/house.py +255 -62
- {tesorotools_python-0.1.11 → tesorotools_python-0.1.12}/src/tesorotools/presentation/lint.py +279 -14
- tesorotools_python-0.1.12/src/tesorotools/presentation/markdown.py +2615 -0
- {tesorotools_python-0.1.11 → tesorotools_python-0.1.12}/src/tesorotools/presentation/skeleton.md +1 -1
- {tesorotools_python-0.1.11 → tesorotools_python-0.1.12}/src/tesorotools/presentation/skill/SKILL.md +241 -40
- {tesorotools_python-0.1.11 → tesorotools_python-0.1.12}/src/tesorotools/presentation/template.py +4 -2
- {tesorotools_python-0.1.11 → tesorotools_python-0.1.12}/src/tesorotools/utils/matplotlib.py +53 -1
- tesorotools_python-0.1.11/src/tesorotools/presentation/builder.py +0 -2535
- tesorotools_python-0.1.11/src/tesorotools/presentation/markdown.py +0 -1379
- {tesorotools_python-0.1.11 → tesorotools_python-0.1.12}/.gitignore +0 -0
- {tesorotools_python-0.1.11 → tesorotools_python-0.1.12}/pyproject.toml +0 -0
- {tesorotools_python-0.1.11 → tesorotools_python-0.1.12}/src/tesorotools/__init__.py +0 -0
- {tesorotools_python-0.1.11 → tesorotools_python-0.1.12}/src/tesorotools/_build_context.py +0 -0
- {tesorotools_python-0.1.11 → tesorotools_python-0.1.12}/src/tesorotools/_registry.py +0 -0
- {tesorotools_python-0.1.11 → tesorotools_python-0.1.12}/src/tesorotools/artists/__init__.py +0 -0
- {tesorotools_python-0.1.11 → tesorotools_python-0.1.12}/src/tesorotools/artists/box_plot.py +0 -0
- {tesorotools_python-0.1.11 → tesorotools_python-0.1.12}/src/tesorotools/artists/compact.py +0 -0
- {tesorotools_python-0.1.11 → tesorotools_python-0.1.12}/src/tesorotools/artists/sharing.py +0 -0
- {tesorotools_python-0.1.11 → tesorotools_python-0.1.12}/src/tesorotools/artists/shock_plot.py +0 -0
- {tesorotools_python-0.1.11 → tesorotools_python-0.1.12}/src/tesorotools/artists/tikz_backend.py +0 -0
- {tesorotools_python-0.1.11 → tesorotools_python-0.1.12}/src/tesorotools/assets/README.md +0 -0
- {tesorotools_python-0.1.11 → tesorotools_python-0.1.12}/src/tesorotools/assets/fonts/CabinetGrotesk-Black.otf +0 -0
- {tesorotools_python-0.1.11 → tesorotools_python-0.1.12}/src/tesorotools/assets/fonts/CabinetGrotesk-Bold.otf +0 -0
- {tesorotools_python-0.1.11 → tesorotools_python-0.1.12}/src/tesorotools/assets/fonts/CabinetGrotesk-Extrabold.otf +0 -0
- {tesorotools_python-0.1.11 → tesorotools_python-0.1.12}/src/tesorotools/assets/fonts/CabinetGrotesk-Extralight.otf +0 -0
- {tesorotools_python-0.1.11 → tesorotools_python-0.1.12}/src/tesorotools/assets/fonts/CabinetGrotesk-Light.otf +0 -0
- {tesorotools_python-0.1.11 → tesorotools_python-0.1.12}/src/tesorotools/assets/fonts/CabinetGrotesk-Medium.otf +0 -0
- {tesorotools_python-0.1.11 → tesorotools_python-0.1.12}/src/tesorotools/assets/fonts/CabinetGrotesk-Regular.otf +0 -0
- {tesorotools_python-0.1.11 → tesorotools_python-0.1.12}/src/tesorotools/assets/fonts/CabinetGrotesk-Thin.otf +0 -0
- {tesorotools_python-0.1.11 → tesorotools_python-0.1.12}/src/tesorotools/assets/fonts/README.md +0 -0
- {tesorotools_python-0.1.11 → tesorotools_python-0.1.12}/src/tesorotools/assets/nota_template.dotx +0 -0
- {tesorotools_python-0.1.11 → tesorotools_python-0.1.12}/src/tesorotools/assets/template.docx +0 -0
- {tesorotools_python-0.1.11 → tesorotools_python-0.1.12}/src/tesorotools/assets/tesoro.mplstyle +0 -0
- {tesorotools_python-0.1.11 → tesorotools_python-0.1.12}/src/tesorotools/data_sources/__init__.py +0 -0
- {tesorotools_python-0.1.11 → tesorotools_python-0.1.12}/src/tesorotools/data_sources/debug.py +0 -0
- {tesorotools_python-0.1.11 → tesorotools_python-0.1.12}/src/tesorotools/database/__init__.py +0 -0
- {tesorotools_python-0.1.11 → tesorotools_python-0.1.12}/src/tesorotools/database/local.py +0 -0
- {tesorotools_python-0.1.11 → tesorotools_python-0.1.12}/src/tesorotools/database/push.py +0 -0
- {tesorotools_python-0.1.11 → tesorotools_python-0.1.12}/src/tesorotools/database/shared.py +0 -0
- {tesorotools_python-0.1.11 → tesorotools_python-0.1.12}/src/tesorotools/dependencies/__init__.py +0 -0
- {tesorotools_python-0.1.11 → tesorotools_python-0.1.12}/src/tesorotools/dependencies/node.py +0 -0
- {tesorotools_python-0.1.11 → tesorotools_python-0.1.12}/src/tesorotools/dependencies/resolution.py +0 -0
- {tesorotools_python-0.1.11 → tesorotools_python-0.1.12}/src/tesorotools/driver.py +0 -0
- {tesorotools_python-0.1.11 → tesorotools_python-0.1.12}/src/tesorotools/latex/__init__.py +0 -0
- {tesorotools_python-0.1.11 → tesorotools_python-0.1.12}/src/tesorotools/latex/cli.py +0 -0
- {tesorotools_python-0.1.11 → tesorotools_python-0.1.12}/src/tesorotools/latex/scaffold.py +0 -0
- {tesorotools_python-0.1.11 → tesorotools_python-0.1.12}/src/tesorotools/latex/templates/article/main.tex.tmpl +0 -0
- {tesorotools_python-0.1.11 → tesorotools_python-0.1.12}/src/tesorotools/latex/templates/article/sections/01-introduccion.tex +0 -0
- {tesorotools_python-0.1.11 → tesorotools_python-0.1.12}/src/tesorotools/latex/templates/article/sections/02-analisis.tex +0 -0
- {tesorotools_python-0.1.11 → tesorotools_python-0.1.12}/src/tesorotools/latex/templates/article/sections/03-conclusiones.tex +0 -0
- {tesorotools_python-0.1.11 → tesorotools_python-0.1.12}/src/tesorotools/latex/templates/beamer/main.tex.tmpl +0 -0
- {tesorotools_python-0.1.11 → tesorotools_python-0.1.12}/src/tesorotools/latex/templates/beamer/slides/01-contexto.tex +0 -0
- {tesorotools_python-0.1.11 → tesorotools_python-0.1.12}/src/tesorotools/latex/templates/beamer/slides/02-analisis.tex +0 -0
- {tesorotools_python-0.1.11 → tesorotools_python-0.1.12}/src/tesorotools/latex/templates/beamer/slides/03-conclusiones.tex +0 -0
- {tesorotools_python-0.1.11 → tesorotools_python-0.1.12}/src/tesorotools/latex/templates/beamer/style/beamerthemeTesoro.sty +0 -0
- {tesorotools_python-0.1.11 → tesorotools_python-0.1.12}/src/tesorotools/latex/templates/book/appendices/a-fuentes.tex +0 -0
- {tesorotools_python-0.1.11 → tesorotools_python-0.1.12}/src/tesorotools/latex/templates/book/chapters/01-introduccion.tex +0 -0
- {tesorotools_python-0.1.11 → tesorotools_python-0.1.12}/src/tesorotools/latex/templates/book/chapters/02-analisis.tex +0 -0
- {tesorotools_python-0.1.11 → tesorotools_python-0.1.12}/src/tesorotools/latex/templates/book/frontmatter/resumen.tex +0 -0
- {tesorotools_python-0.1.11 → tesorotools_python-0.1.12}/src/tesorotools/latex/templates/book/main.tex.tmpl +0 -0
- {tesorotools_python-0.1.11 → tesorotools_python-0.1.12}/src/tesorotools/latex/templates/common/README.md.tmpl +0 -0
- {tesorotools_python-0.1.11 → tesorotools_python-0.1.12}/src/tesorotools/latex/templates/common/dot-gitignore +0 -0
- {tesorotools_python-0.1.11 → tesorotools_python-0.1.12}/src/tesorotools/latex/templates/common/dot-vscode/settings.json +0 -0
- {tesorotools_python-0.1.11 → tesorotools_python-0.1.12}/src/tesorotools/latex/templates/common/figures/dot-gitkeep +0 -0
- {tesorotools_python-0.1.11 → tesorotools_python-0.1.12}/src/tesorotools/latex/templates/common/fonts/README.md +0 -0
- {tesorotools_python-0.1.11 → tesorotools_python-0.1.12}/src/tesorotools/latex/templates/common/latexmkrc +0 -0
- {tesorotools_python-0.1.11 → tesorotools_python-0.1.12}/src/tesorotools/latex/templates/common/refs.bib +0 -0
- {tesorotools_python-0.1.11 → tesorotools_python-0.1.12}/src/tesorotools/latex/templates/common/style/tesoro.sty +0 -0
- {tesorotools_python-0.1.11 → tesorotools_python-0.1.12}/src/tesorotools/latex/templates/common/style/tesorobrand.sty.tmpl +0 -0
- {tesorotools_python-0.1.11 → tesorotools_python-0.1.12}/src/tesorotools/latex/templates/common/style/tesorolocal.sty.tmpl +0 -0
- {tesorotools_python-0.1.11 → tesorotools_python-0.1.12}/src/tesorotools/manifest.py +0 -0
- {tesorotools_python-0.1.11 → tesorotools_python-0.1.12}/src/tesorotools/note/__init__.py +0 -0
- {tesorotools_python-0.1.11 → tesorotools_python-0.1.12}/src/tesorotools/note/builder.py +0 -0
- {tesorotools_python-0.1.11 → tesorotools_python-0.1.12}/src/tesorotools/note/cli.py +0 -0
- {tesorotools_python-0.1.11 → tesorotools_python-0.1.12}/src/tesorotools/note/inline.py +0 -0
- {tesorotools_python-0.1.11 → tesorotools_python-0.1.12}/src/tesorotools/note/issues.py +0 -0
- {tesorotools_python-0.1.11 → tesorotools_python-0.1.12}/src/tesorotools/note/pdf.py +0 -0
- {tesorotools_python-0.1.11 → tesorotools_python-0.1.12}/src/tesorotools/note/skeleton.md +0 -0
- {tesorotools_python-0.1.11 → tesorotools_python-0.1.12}/src/tesorotools/note/skill/reglas.md +0 -0
- {tesorotools_python-0.1.11 → tesorotools_python-0.1.12}/src/tesorotools/note/template.py +0 -0
- {tesorotools_python-0.1.11 → tesorotools_python-0.1.12}/src/tesorotools/offsets/__init__.py +0 -0
- {tesorotools_python-0.1.11 → tesorotools_python-0.1.12}/src/tesorotools/offsets/offsets.py +0 -0
- {tesorotools_python-0.1.11 → tesorotools_python-0.1.12}/src/tesorotools/offsets/outliers.py +0 -0
- {tesorotools_python-0.1.11 → tesorotools_python-0.1.12}/src/tesorotools/orchestration.py +0 -0
- {tesorotools_python-0.1.11 → tesorotools_python-0.1.12}/src/tesorotools/palette.py +0 -0
- {tesorotools_python-0.1.11 → tesorotools_python-0.1.12}/src/tesorotools/pipeline/__init__.py +0 -0
- {tesorotools_python-0.1.11 → tesorotools_python-0.1.12}/src/tesorotools/pipeline/diagnose.py +0 -0
- {tesorotools_python-0.1.11 → tesorotools_python-0.1.12}/src/tesorotools/pipeline/engine.py +0 -0
- {tesorotools_python-0.1.11 → tesorotools_python-0.1.12}/src/tesorotools/pipeline/rules.py +0 -0
- {tesorotools_python-0.1.11 → tesorotools_python-0.1.12}/src/tesorotools/presentation/glossary.py +0 -0
- {tesorotools_python-0.1.11 → tesorotools_python-0.1.12}/src/tesorotools/presentation/native.py +0 -0
- {tesorotools_python-0.1.11 → tesorotools_python-0.1.12}/src/tesorotools/presentation/pdf.py +0 -0
- {tesorotools_python-0.1.11 → tesorotools_python-0.1.12}/src/tesorotools/presentation/sections.py +0 -0
- {tesorotools_python-0.1.11 → tesorotools_python-0.1.12}/src/tesorotools/presentation/skill/reglas.md +0 -0
- {tesorotools_python-0.1.11 → tesorotools_python-0.1.12}/src/tesorotools/providers/__init__.py +0 -0
- {tesorotools_python-0.1.11 → tesorotools_python-0.1.12}/src/tesorotools/providers/base.py +0 -0
- {tesorotools_python-0.1.11 → tesorotools_python-0.1.12}/src/tesorotools/providers/bde.py +0 -0
- {tesorotools_python-0.1.11 → tesorotools_python-0.1.12}/src/tesorotools/providers/ecb.py +0 -0
- {tesorotools_python-0.1.11 → tesorotools_python-0.1.12}/src/tesorotools/providers/fred.py +0 -0
- {tesorotools_python-0.1.11 → tesorotools_python-0.1.12}/src/tesorotools/providers/imf_irfcl.py +0 -0
- {tesorotools_python-0.1.11 → tesorotools_python-0.1.12}/src/tesorotools/providers/lseg.py +0 -0
- {tesorotools_python-0.1.11 → tesorotools_python-0.1.12}/src/tesorotools/py.typed +0 -0
- {tesorotools_python-0.1.11 → tesorotools_python-0.1.12}/src/tesorotools/render/__init__.py +0 -0
- {tesorotools_python-0.1.11 → tesorotools_python-0.1.12}/src/tesorotools/render/content/__init__.py +0 -0
- {tesorotools_python-0.1.11 → tesorotools_python-0.1.12}/src/tesorotools/render/content/content.py +0 -0
- {tesorotools_python-0.1.11 → tesorotools_python-0.1.12}/src/tesorotools/render/content/images.py +0 -0
- {tesorotools_python-0.1.11 → tesorotools_python-0.1.12}/src/tesorotools/render/content/section.py +0 -0
- {tesorotools_python-0.1.11 → tesorotools_python-0.1.12}/src/tesorotools/render/content/subtitle.py +0 -0
- {tesorotools_python-0.1.11 → tesorotools_python-0.1.12}/src/tesorotools/render/content/table.py +0 -0
- {tesorotools_python-0.1.11 → tesorotools_python-0.1.12}/src/tesorotools/render/content/text.py +0 -0
- {tesorotools_python-0.1.11 → tesorotools_python-0.1.12}/src/tesorotools/render/content/title.py +0 -0
- {tesorotools_python-0.1.11 → tesorotools_python-0.1.12}/src/tesorotools/render/document.py +0 -0
- {tesorotools_python-0.1.11 → tesorotools_python-0.1.12}/src/tesorotools/render/report.py +0 -0
- {tesorotools_python-0.1.11 → tesorotools_python-0.1.12}/src/tesorotools/scaffold.py +0 -0
- {tesorotools_python-0.1.11 → tesorotools_python-0.1.12}/src/tesorotools/testing/__init__.py +0 -0
- {tesorotools_python-0.1.11 → tesorotools_python-0.1.12}/src/tesorotools/testing/compare.py +0 -0
- {tesorotools_python-0.1.11 → tesorotools_python-0.1.12}/src/tesorotools/utils/__init__.py +0 -0
- {tesorotools_python-0.1.11 → tesorotools_python-0.1.12}/src/tesorotools/utils/config.py +0 -0
- {tesorotools_python-0.1.11 → tesorotools_python-0.1.12}/src/tesorotools/utils/format.py +0 -0
- {tesorotools_python-0.1.11 → tesorotools_python-0.1.12}/src/tesorotools/utils/globals.py +0 -0
- {tesorotools_python-0.1.11 → tesorotools_python-0.1.12}/src/tesorotools/utils/series.py +0 -0
- {tesorotools_python-0.1.11 → tesorotools_python-0.1.12}/src/tesorotools/utils/shortcuts.py +0 -0
- {tesorotools_python-0.1.11 → tesorotools_python-0.1.12}/src/tesorotools/utils/template.py +0 -0
- {tesorotools_python-0.1.11 → tesorotools_python-0.1.12}/src/tesorotools/web/__init__.py +0 -0
- {tesorotools_python-0.1.11 → tesorotools_python-0.1.12}/src/tesorotools/web/app.py +0 -0
- {tesorotools_python-0.1.11 → tesorotools_python-0.1.12}/src/tesorotools/web/brand.py +0 -0
- {tesorotools_python-0.1.11 → tesorotools_python-0.1.12}/src/tesorotools/web/cli.py +0 -0
- {tesorotools_python-0.1.11 → tesorotools_python-0.1.12}/src/tesorotools/web/components.py +0 -0
- {tesorotools_python-0.1.11 → tesorotools_python-0.1.12}/src/tesorotools/web/formatting.py +0 -0
- {tesorotools_python-0.1.11 → tesorotools_python-0.1.12}/src/tesorotools/web/launcher.py +0 -0
- {tesorotools_python-0.1.11 → tesorotools_python-0.1.12}/src/tesorotools/web/palette.py +0 -0
- {tesorotools_python-0.1.11 → tesorotools_python-0.1.12}/src/tesorotools/web/period.py +0 -0
- {tesorotools_python-0.1.11 → tesorotools_python-0.1.12}/src/tesorotools/web/plotting.py +0 -0
- {tesorotools_python-0.1.11 → tesorotools_python-0.1.12}/src/tesorotools/web/scaffold.py +0 -0
- {tesorotools_python-0.1.11 → tesorotools_python-0.1.12}/src/tesorotools/web/templates/Dockerfile.tmpl +0 -0
- {tesorotools_python-0.1.11 → tesorotools_python-0.1.12}/src/tesorotools/web/templates/README.md.tmpl +0 -0
- {tesorotools_python-0.1.11 → tesorotools_python-0.1.12}/src/tesorotools/web/templates/dot-dockerignore.tmpl +0 -0
- {tesorotools_python-0.1.11 → tesorotools_python-0.1.12}/src/tesorotools/web/templates/dot-gitignore.tmpl +0 -0
- {tesorotools_python-0.1.11 → tesorotools_python-0.1.12}/src/tesorotools/web/templates/dot-streamlit/config.toml.tmpl +0 -0
- {tesorotools_python-0.1.11 → tesorotools_python-0.1.12}/src/tesorotools/web/templates/pyproject.toml.tmpl +0 -0
- {tesorotools_python-0.1.11 → tesorotools_python-0.1.12}/src/tesorotools/web/templates/src/$package/__init__.py.tmpl +0 -0
- {tesorotools_python-0.1.11 → tesorotools_python-0.1.12}/src/tesorotools/web/templates/src/$package/cli.py.tmpl +0 -0
- {tesorotools_python-0.1.11 → tesorotools_python-0.1.12}/src/tesorotools/web/templates/src/$package/main.py.tmpl +0 -0
- {tesorotools_python-0.1.11 → tesorotools_python-0.1.12}/src/tesorotools/web/templates/src/$package/sections.py.tmpl +0 -0
- {tesorotools_python-0.1.11 → tesorotools_python-0.1.12}/src/tesorotools/web/templates/tests/__init__.py.tmpl +0 -0
- {tesorotools_python-0.1.11 → tesorotools_python-0.1.12}/src/tesorotools/web/templates/tests/test_app.py.tmpl +0 -0
- {tesorotools_python-0.1.11 → tesorotools_python-0.1.12}/src/tesorotools/web/theme.py +0 -0
|
@@ -0,0 +1,231 @@
|
|
|
1
|
+
"""Category x-axes of the vertical bar charts: labels that never collide.
|
|
2
|
+
|
|
3
|
+
Shared by :class:`~tesorotools.artists.stacked.StackedBarPlot`,
|
|
4
|
+
:class:`~tesorotools.artists.bar_line.BarLinePlot` and
|
|
5
|
+
:class:`~tesorotools.artists.waterfall.Waterfall`, whose bars sit one per
|
|
6
|
+
slot (``np.arange``) rather than on a date axis. A dated slot axis is
|
|
7
|
+
labelled on the calendar grid of :mod:`tesorotools.artists._dates` (every
|
|
8
|
+
year, quarter or month boundary that fits), and any label row turns only
|
|
9
|
+
when it does not fit flat. Measured on the drawn figure, like the date
|
|
10
|
+
axes of :mod:`tesorotools.artists._common`.
|
|
11
|
+
"""
|
|
12
|
+
|
|
13
|
+
from __future__ import annotations
|
|
14
|
+
|
|
15
|
+
from collections.abc import Callable, Sequence
|
|
16
|
+
from typing import Any
|
|
17
|
+
|
|
18
|
+
import pandas as pd
|
|
19
|
+
from matplotlib.axes import Axes
|
|
20
|
+
from matplotlib.figure import Figure
|
|
21
|
+
from matplotlib.text import Text
|
|
22
|
+
|
|
23
|
+
from tesorotools.artists._common import (
|
|
24
|
+
COMPACT_CONFIG,
|
|
25
|
+
LINE_CONFIG,
|
|
26
|
+
date_max_labels,
|
|
27
|
+
)
|
|
28
|
+
from tesorotools.artists._dates import (
|
|
29
|
+
data_frequency,
|
|
30
|
+
date_layouts,
|
|
31
|
+
month_label,
|
|
32
|
+
quarter_label,
|
|
33
|
+
)
|
|
34
|
+
from tesorotools.utils.matplotlib import PLOT_CONFIG
|
|
35
|
+
|
|
36
|
+
__all__ = [
|
|
37
|
+
"bar_date_ticks",
|
|
38
|
+
"fit_x_rotation",
|
|
39
|
+
"labels_collide",
|
|
40
|
+
]
|
|
41
|
+
|
|
42
|
+
BAR_AXIS_CONFIG: dict[str, Any] = PLOT_CONFIG.get("bar_axis", {})
|
|
43
|
+
|
|
44
|
+
# label texts tried, coarsest first, when no calendar step suits the
|
|
45
|
+
# bars (a few weeks of daily data): the first one that tells them apart.
|
|
46
|
+
# The house forms of the date axes: 2024, mar. 2024, 4 mar., 4 mar. 2024
|
|
47
|
+
_FALLBACK_LABELS: tuple[Callable[[pd.Timestamp], str], ...] = (
|
|
48
|
+
lambda moment: str(moment.year),
|
|
49
|
+
lambda moment: month_label(moment, year=True),
|
|
50
|
+
lambda moment: f"{moment.day} {month_label(moment)}",
|
|
51
|
+
lambda moment: f"{moment.day} {month_label(moment, year=True)}",
|
|
52
|
+
)
|
|
53
|
+
|
|
54
|
+
#: The calendar steps finer than a frequency, which would name each bar
|
|
55
|
+
#: by a month it only ends in (``dic.`` under a yearly bar).
|
|
56
|
+
_TOO_FINE: dict[str, tuple[str, ...]] = {
|
|
57
|
+
"Y": ("1M", "2M", "3M", "6M"),
|
|
58
|
+
"Q": ("1M", "2M"),
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
def _gap_px(fig: Figure, texts: Sequence[Text], *, compact: bool) -> float:
|
|
63
|
+
"""The air two neighbour labels need, ``date_label_gap_em`` of their size."""
|
|
64
|
+
cfg = COMPACT_CONFIG if compact else LINE_CONFIG
|
|
65
|
+
em = float(cfg.get("date_label_gap_em", 0.3))
|
|
66
|
+
size = max(float(t.get_fontsize()) for t in texts)
|
|
67
|
+
return em * size * fig.dpi / 72
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
def labels_collide(
|
|
71
|
+
fig: Figure, texts: Sequence[Text], *, compact: bool = False
|
|
72
|
+
) -> bool:
|
|
73
|
+
"""Whether two neighbouring *texts*, left to right, overlap or touch.
|
|
74
|
+
|
|
75
|
+
Empty texts are skipped; the gap two labels need is the date axes'
|
|
76
|
+
``date_label_gap_em`` (``compact.`` or ``line.`` in ``plots.yaml``).
|
|
77
|
+
"""
|
|
78
|
+
shown = [t for t in texts if t.get_visible() and t.get_text()]
|
|
79
|
+
if len(shown) < 2:
|
|
80
|
+
return False
|
|
81
|
+
fig.draw_without_rendering()
|
|
82
|
+
gap = _gap_px(fig, shown, compact=compact)
|
|
83
|
+
boxes = sorted((t.get_window_extent() for t in shown), key=lambda b: b.x0)
|
|
84
|
+
return any(
|
|
85
|
+
a.x1 + gap > b.x0 and a.y0 < b.y1 and b.y0 < a.y1
|
|
86
|
+
for a, b in zip(boxes, boxes[1:])
|
|
87
|
+
)
|
|
88
|
+
|
|
89
|
+
|
|
90
|
+
def _set_rotation(ax: Axes, rotation: float) -> None:
|
|
91
|
+
for label in ax.get_xticklabels():
|
|
92
|
+
label.set_rotation(rotation)
|
|
93
|
+
label.set_horizontalalignment("right" if rotation else "center")
|
|
94
|
+
label.set_rotation_mode("anchor" if rotation else "default")
|
|
95
|
+
|
|
96
|
+
|
|
97
|
+
def fit_x_rotation(
|
|
98
|
+
fig: Figure, ax: Axes, rotation: float | None, *, compact: bool = False
|
|
99
|
+
) -> None:
|
|
100
|
+
"""Turn the x labels only when they do not fit flat.
|
|
101
|
+
|
|
102
|
+
``rotation=None`` (auto) leaves the labels horizontal unless two of
|
|
103
|
+
them collide, and then turns them ``bar_axis.x_rotation`` degrees
|
|
104
|
+
(``plots.yaml``); an explicit *rotation* is applied as given.
|
|
105
|
+
"""
|
|
106
|
+
if rotation is None:
|
|
107
|
+
_set_rotation(ax, 0)
|
|
108
|
+
if not labels_collide(fig, ax.get_xticklabels(), compact=compact):
|
|
109
|
+
return
|
|
110
|
+
rotation = float(BAR_AXIS_CONFIG.get("x_rotation", 45))
|
|
111
|
+
_set_rotation(ax, rotation)
|
|
112
|
+
|
|
113
|
+
|
|
114
|
+
def _calendar_slots(
|
|
115
|
+
dates: pd.DatetimeIndex,
|
|
116
|
+
step: str,
|
|
117
|
+
ticks: Sequence[pd.Timestamp],
|
|
118
|
+
frequency: str = "M",
|
|
119
|
+
) -> tuple[list[int], list[str]]:
|
|
120
|
+
"""The first bar of each period of *step* that starts at a tick.
|
|
121
|
+
|
|
122
|
+
A period with no bar of its own (a gap in the data) gets no label, so
|
|
123
|
+
a label always names the bar under it and no two labels repeat. The
|
|
124
|
+
labels read as on the date axes of the line charts: the year where
|
|
125
|
+
a year starts, else the month (``mar.``), the first one with its year
|
|
126
|
+
(``may. 2024``). Quarterly bars (*frequency* ``"Q"``) on a month
|
|
127
|
+
step name their quarter instead, with its year on the first label and
|
|
128
|
+
on every first quarter (``1T 2024``, ``2T``, ``3T``).
|
|
129
|
+
"""
|
|
130
|
+
n = int(step[:-1])
|
|
131
|
+
span = (
|
|
132
|
+
pd.DateOffset(years=n)
|
|
133
|
+
if step.endswith("Y")
|
|
134
|
+
else pd.DateOffset(months=n)
|
|
135
|
+
)
|
|
136
|
+
positions: list[int] = []
|
|
137
|
+
texts: list[str] = []
|
|
138
|
+
for tick in ticks:
|
|
139
|
+
i = int(dates.searchsorted(tick))
|
|
140
|
+
if i < len(dates) and dates[i] < tick + span:
|
|
141
|
+
positions.append(i)
|
|
142
|
+
# the year on a year step and where a year starts, as the
|
|
143
|
+
# date axes do; the first month carries its year too
|
|
144
|
+
if step.endswith("Y"):
|
|
145
|
+
texts.append(str(dates[i].year))
|
|
146
|
+
elif frequency == "Q":
|
|
147
|
+
texts.append(
|
|
148
|
+
quarter_label(
|
|
149
|
+
dates[i], year=not texts or dates[i].quarter == 1
|
|
150
|
+
)
|
|
151
|
+
)
|
|
152
|
+
elif tick.month == 1:
|
|
153
|
+
texts.append(str(dates[i].year))
|
|
154
|
+
else:
|
|
155
|
+
texts.append(month_label(dates[i], year=not texts))
|
|
156
|
+
return positions, texts
|
|
157
|
+
|
|
158
|
+
|
|
159
|
+
def _fallback_slots(
|
|
160
|
+
dates: pd.DatetimeIndex, every: int
|
|
161
|
+
) -> tuple[list[int], list[str]]:
|
|
162
|
+
"""Every *every*-th bar, in the coarsest format that tells them apart."""
|
|
163
|
+
positions = list(range(0, len(dates), every))
|
|
164
|
+
rows = [[label(dates[i]) for i in positions] for label in _FALLBACK_LABELS]
|
|
165
|
+
return positions, next(
|
|
166
|
+
(texts for texts in rows if len(set(texts)) == len(texts)), rows[-1]
|
|
167
|
+
)
|
|
168
|
+
|
|
169
|
+
|
|
170
|
+
def bar_date_ticks(
|
|
171
|
+
fig: Figure,
|
|
172
|
+
ax: Axes,
|
|
173
|
+
dates: pd.DatetimeIndex,
|
|
174
|
+
*,
|
|
175
|
+
compact: bool = False,
|
|
176
|
+
rotation: float | None = None,
|
|
177
|
+
pin_start: bool = True,
|
|
178
|
+
) -> None:
|
|
179
|
+
"""Label one bar per calendar period, as many periods as fit.
|
|
180
|
+
|
|
181
|
+
The candidates are the calendar steps of the date axes (every year,
|
|
182
|
+
every 2, 5... years and, over spans up to ``date_monthly_max_months``,
|
|
183
|
+
every month or quarter), finest first, none finer than the bars
|
|
184
|
+
themselves (:func:`~tesorotools.artists._dates.data_frequency`):
|
|
185
|
+
yearly bars read ``2023 2024 2025`` and not ``dic. 2023 dic. dic.``,
|
|
186
|
+
quarterly ones ``1T 2024 2T 3T`` (over up to three times
|
|
187
|
+
``date_monthly_max_months``); each labels the first bar of
|
|
188
|
+
its period, and the finest whose labels fit across the axis (at
|
|
189
|
+
*rotation*, flat when ``None``) and stay within the mode's
|
|
190
|
+
``date_max_labels`` wins. When no calendar step suits the span, every
|
|
191
|
+
k-th bar is labelled instead, k as small as fits. With *pin_start*
|
|
192
|
+
the first bar is labelled too (its year, or month), as the date axes
|
|
193
|
+
of ``date_show_start`` do, so the reader sees when the series starts.
|
|
194
|
+
"""
|
|
195
|
+
cfg = COMPACT_CONFIG if compact else LINE_CONFIG
|
|
196
|
+
frequency = data_frequency(dates)
|
|
197
|
+
# a quarter is a third of the bars a month gives over the same span:
|
|
198
|
+
# quarterly bars name their quarters over three times the span
|
|
199
|
+
months = int(cfg.get("date_monthly_max_months", 24))
|
|
200
|
+
layouts = date_layouts(
|
|
201
|
+
dates.min(),
|
|
202
|
+
dates.max(),
|
|
203
|
+
step="auto",
|
|
204
|
+
max_labels=date_max_labels("default", compact=compact),
|
|
205
|
+
monthly_max_months=months * (3 if frequency == "Q" else 1),
|
|
206
|
+
pin_start=pin_start,
|
|
207
|
+
)
|
|
208
|
+
too_fine = _TOO_FINE.get(frequency, ())
|
|
209
|
+
candidates = [
|
|
210
|
+
_calendar_slots(dates, layout.step, layout.ticks, frequency)
|
|
211
|
+
for layout in layouts
|
|
212
|
+
if layout.step not in too_fine
|
|
213
|
+
]
|
|
214
|
+
candidates = [c for c in candidates if len(c[0]) >= 2]
|
|
215
|
+
# no calendar step suits the span, or none fits: every k-th bar, from
|
|
216
|
+
# the k that keeps within the label count up to a single label
|
|
217
|
+
most = date_max_labels("default", compact=compact)
|
|
218
|
+
first = max(1, -(-len(dates) // most)) # ceil
|
|
219
|
+
candidates += [
|
|
220
|
+
_fallback_slots(dates, every) for every in range(first, len(dates) + 1)
|
|
221
|
+
]
|
|
222
|
+
for positions, texts in candidates:
|
|
223
|
+
ax.set_xticks( # type: ignore[reportUnknownMemberType]
|
|
224
|
+
positions
|
|
225
|
+
)
|
|
226
|
+
ax.set_xticklabels( # type: ignore[reportUnknownMemberType]
|
|
227
|
+
texts
|
|
228
|
+
)
|
|
229
|
+
_set_rotation(ax, rotation or 0)
|
|
230
|
+
if not labels_collide(fig, ax.get_xticklabels(), compact=compact):
|
|
231
|
+
return
|