afcharts 1.0.0a0__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.
- afcharts-1.0.0a0/LICENSE.md +21 -0
- afcharts-1.0.0a0/PKG-INFO +103 -0
- afcharts-1.0.0a0/README.md +80 -0
- afcharts-1.0.0a0/pyproject.toml +103 -0
- afcharts-1.0.0a0/setup.cfg +4 -0
- afcharts-1.0.0a0/src/afcharts/__init__.py +1 -0
- afcharts-1.0.0a0/src/afcharts/af_colours.py +259 -0
- afcharts-1.0.0a0/src/afcharts/afcharts.mplstyle +42 -0
- afcharts-1.0.0a0/src/afcharts/assets/__init__.py +0 -0
- afcharts-1.0.0a0/src/afcharts/config/af_colours.yaml +4 -0
- afcharts-1.0.0a0/src/afcharts/pio_template.py +142 -0
- afcharts-1.0.0a0/src/afcharts.egg-info/PKG-INFO +103 -0
- afcharts-1.0.0a0/src/afcharts.egg-info/SOURCES.txt +14 -0
- afcharts-1.0.0a0/src/afcharts.egg-info/dependency_links.txt +1 -0
- afcharts-1.0.0a0/src/afcharts.egg-info/requires.txt +4 -0
- afcharts-1.0.0a0/src/afcharts.egg-info/top_level.txt +1 -0
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
# MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025 Crown Copyright
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: afcharts
|
|
3
|
+
Version: 1.0.0a0
|
|
4
|
+
Summary: A python package to produce charts following UK Government Analysis Function guidance
|
|
5
|
+
Author: Crown Copyright
|
|
6
|
+
Maintainer-email: Nick Henden <Nick.Henden@ukhsa.gov.uk>, Tom Farley <tom.farley@ukhsa.gov.uk>, Stelios Georgiou <Stelios.Georgiou@ukhsa.gov.uk>, Frederico Rodrigues <fred.rodrigues@defra.gov.uk>, Peter Brohan <peter.brohan@communities.gov.uk>, Fahim Hossain <fahim.hossain@ukhsa.gov.uk>
|
|
7
|
+
Project-URL: Homepage, https://github.com/best-practice-and-impact/afcharts-py
|
|
8
|
+
Project-URL: Documentation, https://best-practice-and-impact.github.io/afcharts-py/
|
|
9
|
+
Project-URL: Source, https://github.com/best-practice-and-impact/afcharts-py
|
|
10
|
+
Project-URL: Issue Tracker, https://github.com/best-practice-and-impact/afcharts-py/issues
|
|
11
|
+
Classifier: Programming Language :: Python :: 3
|
|
12
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
13
|
+
Classifier: Operating System :: OS Independent
|
|
14
|
+
Classifier: Development Status :: 3 - Alpha
|
|
15
|
+
Requires-Python: >=3.10
|
|
16
|
+
Description-Content-Type: text/markdown
|
|
17
|
+
License-File: LICENSE.md
|
|
18
|
+
Requires-Dist: matplotlib>=3.10.3
|
|
19
|
+
Requires-Dist: plotly>=6.1.2
|
|
20
|
+
Requires-Dist: typeguard>=4.4.4
|
|
21
|
+
Requires-Dist: PyYAML>=5.0
|
|
22
|
+
Dynamic: license-file
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
# afcharts-py <img src="docs/images/logo.svg" alt="afcharts logo" align="right" height="150"/>
|
|
26
|
+
|
|
27
|
+
## Overview
|
|
28
|
+
|
|
29
|
+
The afcharts python package helps make accessible Matplotlib and Plotly charts following [Government Analysis Function Data Visualisation guidance](https://analysisfunction.civilservice.gov.uk/policy-store/data-visualisation-charts/):
|
|
30
|
+
|
|
31
|
+
- **Automatic chart formatting** with pre-built style sheets for Matplotlib and Plotly
|
|
32
|
+
- **Chart colours** from the Analysis Function [accessible colour palettes](https://analysisfunction.civilservice.gov.uk/policy-store/data-visualisation-colours-in-charts/#section-4)
|
|
33
|
+
- **Example code** for common chart types in the [cookbook](https://best-practice-and-impact.github.io/afcharts-py/)
|
|
34
|
+
|
|
35
|
+
Looking for the R version? Check out the [afcharts R package](https://best-practice-and-impact.github.io/afcharts/).
|
|
36
|
+
|
|
37
|
+
<img src="docs/images/example_charts/bar_chart-matplotlib_afcharts.png" width="28.1%" alt="A grouped bar chart in afcharts style showing life expectancy in 1967 and 2007 for four countries. Bars use Analysis Function palette: dark blue for 1967, orange for 2007."/> <img src="docs/images/example_charts/scatterplot-matplotlib_afcharts.png" width="30%" alt="A scatterplot in afcharts style showing life expectancy against GDP per capita for 142 countries in 2007."/> <img src="docs/images/example_charts/line_chart-matplotlib_afcharts.png" width="37.5%" alt="A line chart in afcharts style showing life expectancy in China and the UK from 1952 to 2007"/>
|
|
38
|
+
|
|
39
|
+
## Installation
|
|
40
|
+
|
|
41
|
+
afcharts is available at the Python Package Index (PyPI):
|
|
42
|
+
```bash
|
|
43
|
+
pip install afcharts
|
|
44
|
+
```
|
|
45
|
+
or see the [alternative installation](https://best-practice-and-impact.github.io/afcharts-py/getting-started.html) instructions.
|
|
46
|
+
|
|
47
|
+
## Usage
|
|
48
|
+
|
|
49
|
+
Format any Matplotlib or Plotly chart in the Analysis Function style using the built-in style sheets.
|
|
50
|
+
|
|
51
|
+
See the [Getting Started](https://best-practice-and-impact.github.io/afcharts-py/getting-started.html) guide for more options and the [cookbook](https://best-practice-and-impact.github.io/afcharts-py/) for extensive examples.
|
|
52
|
+
|
|
53
|
+
### Matplotlib
|
|
54
|
+
|
|
55
|
+
```python
|
|
56
|
+
import matplotlib.pyplot as plt
|
|
57
|
+
|
|
58
|
+
# Apply the afcharts style to all Matplotlib plots
|
|
59
|
+
plt.style.use('afcharts.afcharts')
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
Example: A [Matplotlib bar chart](https://best-practice-and-impact.github.io/afcharts-py/01-matplotlib-usage.html#grouped-bar-chart) with afcharts (left) and without (right)
|
|
63
|
+
|
|
64
|
+
<img src="docs/images/example_charts/bar_chart-matplotlib_afcharts.png" width="35%" alt="Grouped bar chart (afcharts style) showing life expectancy in 1967 and 2007 for four countries. Bars use Analysis Function palette: dark blue for 1967, orange for 2007."/> <img src="docs/images/example_charts/bar_chart-matplotlib_default.png" width="34.7%" alt="Grouped bar chart (default Matplotlib) showing life expectancy in 1967 and 2007 for four countries. Bars: blue for 1967, orange for 2007."/>
|
|
65
|
+
|
|
66
|
+
### Plotly
|
|
67
|
+
|
|
68
|
+
```python
|
|
69
|
+
from afcharts.pio_template import pio
|
|
70
|
+
|
|
71
|
+
# Apply the afcharts style to all Plotly plots
|
|
72
|
+
pio.templates.default = "afcharts"
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
Example: A [Plotly bar chart](https://best-practice-and-impact.github.io/afcharts-py/03-plotly-usage.html#grouped-bar-chart) with afcharts (left) and without (right)
|
|
76
|
+
|
|
77
|
+
<img src="docs/images/example_charts/bar_chart-plotly_afcharts.png" width="35%" alt="Grouped bar chart (afcharts style) showing life expectancy in 1967 and 2007 for four countries. Bars use Analysis Function palette: dark blue for 1967, orange for 2007."/> <img src="docs/images/example_charts/bar_chart-plotly_default.png" width="35%" alt="Grouped bar chart (default Plotly) showing life expectancy in 1967 and 2007 for four countries. Bars: blue for 1967, red for 2007."/>
|
|
78
|
+
|
|
79
|
+
### Colours
|
|
80
|
+
|
|
81
|
+
Easily return a list of colours from any of the Analysis Function [accessible colour palettes](https://analysisfunction.civilservice.gov.uk/policy-store/data-visualisation-colours-in-charts/#section-4) with the `get_af_colours()` function:
|
|
82
|
+
|
|
83
|
+
```python
|
|
84
|
+
from afcharts.af_colours import get_af_colours
|
|
85
|
+
|
|
86
|
+
# Get the duo colour palette hex codes
|
|
87
|
+
duo = get_af_colours("duo")
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
## Getting help
|
|
91
|
+
If you encounter a bug, please file a minimal reproducible example on [Github Issues](https://github.com/best-practice-and-impact/afcharts-py/issues). For questions and other discussion, please start a [discussion](https://github.com/best-practice-and-impact/afcharts-py/discussions).
|
|
92
|
+
|
|
93
|
+
## Contributing
|
|
94
|
+
Interested in contributing? Check out the [contributing guidelines](CONTRIBUTING.md).
|
|
95
|
+
|
|
96
|
+
## Acknowledgments
|
|
97
|
+
The afcharts python package is based on the
|
|
98
|
+
[afcharts](https://github.com/best-practice-and-impact/afcharts.git) R package and the [py-af-colours](https://github.com/best-practice-and-impact/py-af-colours) package.
|
|
99
|
+
|
|
100
|
+
## License
|
|
101
|
+
Unless stated otherwise, the codebase is released under [the MIT License](LICENSE.md). This covers both the codebase and any sample code in the documentation.
|
|
102
|
+
|
|
103
|
+
The documentation is [© Crown copyright](https://www.nationalarchives.gov.uk/information-management/re-using-public-sector-information/uk-government-licensing-framework/crown-copyright/) and available under the terms of the [Open Government 3.0](https://www.nationalarchives.gov.uk/doc/open-government-licence/version/3/) licence.
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
|
|
2
|
+
# afcharts-py <img src="docs/images/logo.svg" alt="afcharts logo" align="right" height="150"/>
|
|
3
|
+
|
|
4
|
+
## Overview
|
|
5
|
+
|
|
6
|
+
The afcharts python package helps make accessible Matplotlib and Plotly charts following [Government Analysis Function Data Visualisation guidance](https://analysisfunction.civilservice.gov.uk/policy-store/data-visualisation-charts/):
|
|
7
|
+
|
|
8
|
+
- **Automatic chart formatting** with pre-built style sheets for Matplotlib and Plotly
|
|
9
|
+
- **Chart colours** from the Analysis Function [accessible colour palettes](https://analysisfunction.civilservice.gov.uk/policy-store/data-visualisation-colours-in-charts/#section-4)
|
|
10
|
+
- **Example code** for common chart types in the [cookbook](https://best-practice-and-impact.github.io/afcharts-py/)
|
|
11
|
+
|
|
12
|
+
Looking for the R version? Check out the [afcharts R package](https://best-practice-and-impact.github.io/afcharts/).
|
|
13
|
+
|
|
14
|
+
<img src="docs/images/example_charts/bar_chart-matplotlib_afcharts.png" width="28.1%" alt="A grouped bar chart in afcharts style showing life expectancy in 1967 and 2007 for four countries. Bars use Analysis Function palette: dark blue for 1967, orange for 2007."/> <img src="docs/images/example_charts/scatterplot-matplotlib_afcharts.png" width="30%" alt="A scatterplot in afcharts style showing life expectancy against GDP per capita for 142 countries in 2007."/> <img src="docs/images/example_charts/line_chart-matplotlib_afcharts.png" width="37.5%" alt="A line chart in afcharts style showing life expectancy in China and the UK from 1952 to 2007"/>
|
|
15
|
+
|
|
16
|
+
## Installation
|
|
17
|
+
|
|
18
|
+
afcharts is available at the Python Package Index (PyPI):
|
|
19
|
+
```bash
|
|
20
|
+
pip install afcharts
|
|
21
|
+
```
|
|
22
|
+
or see the [alternative installation](https://best-practice-and-impact.github.io/afcharts-py/getting-started.html) instructions.
|
|
23
|
+
|
|
24
|
+
## Usage
|
|
25
|
+
|
|
26
|
+
Format any Matplotlib or Plotly chart in the Analysis Function style using the built-in style sheets.
|
|
27
|
+
|
|
28
|
+
See the [Getting Started](https://best-practice-and-impact.github.io/afcharts-py/getting-started.html) guide for more options and the [cookbook](https://best-practice-and-impact.github.io/afcharts-py/) for extensive examples.
|
|
29
|
+
|
|
30
|
+
### Matplotlib
|
|
31
|
+
|
|
32
|
+
```python
|
|
33
|
+
import matplotlib.pyplot as plt
|
|
34
|
+
|
|
35
|
+
# Apply the afcharts style to all Matplotlib plots
|
|
36
|
+
plt.style.use('afcharts.afcharts')
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
Example: A [Matplotlib bar chart](https://best-practice-and-impact.github.io/afcharts-py/01-matplotlib-usage.html#grouped-bar-chart) with afcharts (left) and without (right)
|
|
40
|
+
|
|
41
|
+
<img src="docs/images/example_charts/bar_chart-matplotlib_afcharts.png" width="35%" alt="Grouped bar chart (afcharts style) showing life expectancy in 1967 and 2007 for four countries. Bars use Analysis Function palette: dark blue for 1967, orange for 2007."/> <img src="docs/images/example_charts/bar_chart-matplotlib_default.png" width="34.7%" alt="Grouped bar chart (default Matplotlib) showing life expectancy in 1967 and 2007 for four countries. Bars: blue for 1967, orange for 2007."/>
|
|
42
|
+
|
|
43
|
+
### Plotly
|
|
44
|
+
|
|
45
|
+
```python
|
|
46
|
+
from afcharts.pio_template import pio
|
|
47
|
+
|
|
48
|
+
# Apply the afcharts style to all Plotly plots
|
|
49
|
+
pio.templates.default = "afcharts"
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
Example: A [Plotly bar chart](https://best-practice-and-impact.github.io/afcharts-py/03-plotly-usage.html#grouped-bar-chart) with afcharts (left) and without (right)
|
|
53
|
+
|
|
54
|
+
<img src="docs/images/example_charts/bar_chart-plotly_afcharts.png" width="35%" alt="Grouped bar chart (afcharts style) showing life expectancy in 1967 and 2007 for four countries. Bars use Analysis Function palette: dark blue for 1967, orange for 2007."/> <img src="docs/images/example_charts/bar_chart-plotly_default.png" width="35%" alt="Grouped bar chart (default Plotly) showing life expectancy in 1967 and 2007 for four countries. Bars: blue for 1967, red for 2007."/>
|
|
55
|
+
|
|
56
|
+
### Colours
|
|
57
|
+
|
|
58
|
+
Easily return a list of colours from any of the Analysis Function [accessible colour palettes](https://analysisfunction.civilservice.gov.uk/policy-store/data-visualisation-colours-in-charts/#section-4) with the `get_af_colours()` function:
|
|
59
|
+
|
|
60
|
+
```python
|
|
61
|
+
from afcharts.af_colours import get_af_colours
|
|
62
|
+
|
|
63
|
+
# Get the duo colour palette hex codes
|
|
64
|
+
duo = get_af_colours("duo")
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
## Getting help
|
|
68
|
+
If you encounter a bug, please file a minimal reproducible example on [Github Issues](https://github.com/best-practice-and-impact/afcharts-py/issues). For questions and other discussion, please start a [discussion](https://github.com/best-practice-and-impact/afcharts-py/discussions).
|
|
69
|
+
|
|
70
|
+
## Contributing
|
|
71
|
+
Interested in contributing? Check out the [contributing guidelines](CONTRIBUTING.md).
|
|
72
|
+
|
|
73
|
+
## Acknowledgments
|
|
74
|
+
The afcharts python package is based on the
|
|
75
|
+
[afcharts](https://github.com/best-practice-and-impact/afcharts.git) R package and the [py-af-colours](https://github.com/best-practice-and-impact/py-af-colours) package.
|
|
76
|
+
|
|
77
|
+
## License
|
|
78
|
+
Unless stated otherwise, the codebase is released under [the MIT License](LICENSE.md). This covers both the codebase and any sample code in the documentation.
|
|
79
|
+
|
|
80
|
+
The documentation is [© Crown copyright](https://www.nationalarchives.gov.uk/information-management/re-using-public-sector-information/uk-government-licensing-framework/crown-copyright/) and available under the terms of the [Open Government 3.0](https://www.nationalarchives.gov.uk/doc/open-government-licence/version/3/) licence.
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["setuptools>=61.0", "wheel"]
|
|
3
|
+
build-backend = "setuptools.build_meta"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "afcharts"
|
|
7
|
+
version = "1.0.0-alpha"
|
|
8
|
+
description = "A python package to produce charts following UK Government Analysis Function guidance"
|
|
9
|
+
readme = "README.md"
|
|
10
|
+
license = { file = "LICENSE" }
|
|
11
|
+
authors = [{ name = "Crown Copyright" }]
|
|
12
|
+
maintainers = [
|
|
13
|
+
{ name = "Nick Henden", email = "Nick.Henden@ukhsa.gov.uk" },
|
|
14
|
+
{ name = "Tom Farley", email = "tom.farley@ukhsa.gov.uk" },
|
|
15
|
+
{ name = "Stelios Georgiou", email = "Stelios.Georgiou@ukhsa.gov.uk" },
|
|
16
|
+
{ name = "Frederico Rodrigues", email = "fred.rodrigues@defra.gov.uk" },
|
|
17
|
+
{ name = "Peter Brohan", email = "peter.brohan@communities.gov.uk"},
|
|
18
|
+
{ name = "Fahim Hossain", email = "fahim.hossain@ukhsa.gov.uk"}
|
|
19
|
+
# Other contributors, please add your details here
|
|
20
|
+
]
|
|
21
|
+
classifiers = [
|
|
22
|
+
"Programming Language :: Python :: 3",
|
|
23
|
+
"License :: OSI Approved :: MIT License",
|
|
24
|
+
"Operating System :: OS Independent",
|
|
25
|
+
"Development Status :: 3 - Alpha",
|
|
26
|
+
]
|
|
27
|
+
requires-python = ">=3.10"
|
|
28
|
+
|
|
29
|
+
dependencies = [ # install package dependencies with $ uv sync
|
|
30
|
+
"matplotlib>=3.10.3",
|
|
31
|
+
"plotly>=6.1.2",
|
|
32
|
+
"typeguard>=4.4.4",
|
|
33
|
+
'PyYAML >= 5.0',
|
|
34
|
+
]
|
|
35
|
+
|
|
36
|
+
[dependency-groups]
|
|
37
|
+
dev = [ # package developer dependencies are installed by default with $ uv sync
|
|
38
|
+
"ipython>=8.26.0", # Installing ipython will enable colourised console output/tracebacks
|
|
39
|
+
"mypy>=1.15.0", # Mypy static type checker/linter
|
|
40
|
+
"types-PyYAML", # type stub package used by mypy to check code that uses PyYAML
|
|
41
|
+
"pre-commit>=3.8.0", # Used to run pre-commit hook linting and formatting checks
|
|
42
|
+
"ruff>=0.6.2", # Python linting/formatting tool combines black, flake8 etc. Run `ruff check --fix`/`ruff format`
|
|
43
|
+
"pandas>=2.2.2", # Used to manipulate tabular data
|
|
44
|
+
"afcharts", # Install current package in developer mode as workspace package
|
|
45
|
+
"ipykernel", # For rendering Quarto
|
|
46
|
+
"nbclient", # For rendering Quarto
|
|
47
|
+
]
|
|
48
|
+
test = [ # package testing dependencies are installed by default with $ uv sync
|
|
49
|
+
"pandas>=2.2.2", # Used to manipulate tabular data
|
|
50
|
+
"pytest>=8.2.2", # Unit/regression testing etc.
|
|
51
|
+
"pytest-cov>=5.0.0", # Test code coverage
|
|
52
|
+
"pytest-mock", # Mock wrapper for unit tests
|
|
53
|
+
"pytest-sugar>=1.0.0", # Prettier pytest terminal output
|
|
54
|
+
]
|
|
55
|
+
|
|
56
|
+
[project.urls]
|
|
57
|
+
"Homepage" = "https://github.com/best-practice-and-impact/afcharts-py"
|
|
58
|
+
"Documentation" = "https://best-practice-and-impact.github.io/afcharts-py/"
|
|
59
|
+
"Source" = "https://github.com/best-practice-and-impact/afcharts-py"
|
|
60
|
+
"Issue Tracker" = "https://github.com/best-practice-and-impact/afcharts-py/issues"
|
|
61
|
+
|
|
62
|
+
[tool.uv]
|
|
63
|
+
default-groups = [
|
|
64
|
+
"dev",
|
|
65
|
+
"test",
|
|
66
|
+
] # `uv sync` will also install dev and test dependencies by default
|
|
67
|
+
|
|
68
|
+
[tool.uv.sources]
|
|
69
|
+
afcharts = { workspace = true } # Install as editable package (developer mode like pip install -e .)
|
|
70
|
+
|
|
71
|
+
[tool.setuptools]
|
|
72
|
+
package-dir = { "" = "src" } # Maps package names to their actual file paths.
|
|
73
|
+
|
|
74
|
+
[tool.setuptools.packages.find]
|
|
75
|
+
where = ["src"] # Packages are located in src/ top level directory
|
|
76
|
+
exclude = ["docs*", "Documentation*", "build"]
|
|
77
|
+
|
|
78
|
+
[tool.setuptools.package-data]
|
|
79
|
+
afcharts = ["*.mplstyle", "config/af_colours.yaml"]
|
|
80
|
+
|
|
81
|
+
[tool.ruff]
|
|
82
|
+
line-length = 120
|
|
83
|
+
target-version = "py313"
|
|
84
|
+
lint.select = [
|
|
85
|
+
"E", # pycodestyle
|
|
86
|
+
"W", # pycodestyle
|
|
87
|
+
"F", # pyflakes
|
|
88
|
+
"I", # isort
|
|
89
|
+
"B", # flake8-bugbear
|
|
90
|
+
"C90", # mccabe cyclomatic complexity
|
|
91
|
+
"G", # flake8-logging-format
|
|
92
|
+
]
|
|
93
|
+
exclude = ["migrations/versions/"]
|
|
94
|
+
lint.ignore = []
|
|
95
|
+
|
|
96
|
+
[tool.ruff.lint.pydocstyle]
|
|
97
|
+
convention = "google" # Accepts one of {google,numpy,pep257}
|
|
98
|
+
|
|
99
|
+
[tool.mypy] # Config for mypy static type checking
|
|
100
|
+
python_version = "3.13"
|
|
101
|
+
check_untyped_defs = true
|
|
102
|
+
ignore_missing_imports = true
|
|
103
|
+
warn_unused_ignores = true
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
|
|
@@ -0,0 +1,259 @@
|
|
|
1
|
+
# Government Analysis Function (AF) colours and palettes
|
|
2
|
+
# Source: https://analysisfunction.civilservice.gov.uk/policy-store/
|
|
3
|
+
# data-visualisation-colours-in-charts/
|
|
4
|
+
# Py-af-colours source: https://github.com/best-practice-and-impact/py-af-colours
|
|
5
|
+
|
|
6
|
+
from pathlib import Path
|
|
7
|
+
|
|
8
|
+
import yaml
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
def get_af_colours(palette: str, colour_format="hex", number_of_colours=6, config_path=None):
|
|
12
|
+
"""
|
|
13
|
+
get_af_colours() is the top level function in af_colours. This returns
|
|
14
|
+
the chosen Analysis Function colour palette in hex or rgb format.
|
|
15
|
+
For the categorical palette, this can be a chosen number of colours
|
|
16
|
+
up to 6.
|
|
17
|
+
|
|
18
|
+
Parameters
|
|
19
|
+
----------
|
|
20
|
+
palette : string
|
|
21
|
+
Type of palette required, with accepted values of "duo",
|
|
22
|
+
"focus", "categorical", and "sequential".
|
|
23
|
+
|
|
24
|
+
colour_format : string, optional
|
|
25
|
+
Colour format required, with accepted values of "hex" or "rgb".
|
|
26
|
+
|
|
27
|
+
number_of_colours : int, optional
|
|
28
|
+
Number of colours required (categorical palette only). Takes
|
|
29
|
+
values between 2 and 6. Returns 2 colours by default. If a
|
|
30
|
+
palette other than categorical is chosen, any value passed
|
|
31
|
+
is ignored.
|
|
32
|
+
|
|
33
|
+
config_path : NoneType, optional
|
|
34
|
+
Takes the default value None, inside the function this is
|
|
35
|
+
mapped to the relative path independent of operating system.
|
|
36
|
+
Should not require changing.
|
|
37
|
+
|
|
38
|
+
Raises
|
|
39
|
+
------
|
|
40
|
+
ValueError
|
|
41
|
+
If palette is not "categorical", "duo", "sequential", or "focus".
|
|
42
|
+
Or if colour_format is not "hex" or "rgb".
|
|
43
|
+
|
|
44
|
+
Returns
|
|
45
|
+
-------
|
|
46
|
+
list
|
|
47
|
+
chosen_colours_list
|
|
48
|
+
|
|
49
|
+
"""
|
|
50
|
+
if config_path is None:
|
|
51
|
+
parent_dir = Path(__file__).parent
|
|
52
|
+
config_path = parent_dir.joinpath("config", "af_colours.yaml")
|
|
53
|
+
|
|
54
|
+
with open(config_path) as file:
|
|
55
|
+
config = yaml.load(file, Loader=yaml.BaseLoader)
|
|
56
|
+
|
|
57
|
+
categorical_hex_list = config["categorical_hex_list"]
|
|
58
|
+
duo_hex_list = config["duo_hex_list"]
|
|
59
|
+
sequential_hex_list = config["sequential_hex_list"]
|
|
60
|
+
focus_hex_list = config["focus_hex_list"]
|
|
61
|
+
|
|
62
|
+
if palette not in ["categorical", "duo", "sequential", "focus"]:
|
|
63
|
+
raise ValueError("palette must be one of 'categorical', 'duo', 'sequential' " + f"or 'focus', not {palette}.")
|
|
64
|
+
if colour_format not in ["hex", "rgb"]:
|
|
65
|
+
raise ValueError(f"colour_format must be 'hex' or 'rgb', not {colour_format}.")
|
|
66
|
+
|
|
67
|
+
if number_of_colours < 1:
|
|
68
|
+
raise ValueError("number_of_colours must be greater than 0.")
|
|
69
|
+
|
|
70
|
+
elif palette == "sequential":
|
|
71
|
+
chosen_colours_list = sequential_colours(sequential_hex_list, colour_format)
|
|
72
|
+
|
|
73
|
+
elif palette == "focus":
|
|
74
|
+
chosen_colours_list = focus_colours(focus_hex_list, colour_format)
|
|
75
|
+
|
|
76
|
+
elif palette == "duo":
|
|
77
|
+
chosen_colours_list = duo_colours(duo_hex_list, colour_format)
|
|
78
|
+
|
|
79
|
+
elif palette == "categorical":
|
|
80
|
+
chosen_colours_list = categorical_colours(categorical_hex_list, duo_hex_list, colour_format, number_of_colours)
|
|
81
|
+
|
|
82
|
+
return chosen_colours_list
|
|
83
|
+
|
|
84
|
+
|
|
85
|
+
def categorical_colours(categorical_hex_list, duo_hex_list, colour_format="hex", number_of_colours=2):
|
|
86
|
+
"""
|
|
87
|
+
Return the Analysis Function categorical colour palette as a list
|
|
88
|
+
in hex or rgb format for up to 6 colours. If number_of_colours is
|
|
89
|
+
2, the function returns the duo palette.
|
|
90
|
+
|
|
91
|
+
Parameters
|
|
92
|
+
----------
|
|
93
|
+
categorical_hex_list : list
|
|
94
|
+
List of categorical colours as a hex list, stored in the config.
|
|
95
|
+
|
|
96
|
+
duo_hex_list : list
|
|
97
|
+
List of duo hex codes, stored in the config. This is needed for the
|
|
98
|
+
case of number_of_colours = 2.
|
|
99
|
+
|
|
100
|
+
colour_format : string
|
|
101
|
+
Colour format required, with accepted values of "hex" or "rgb".
|
|
102
|
+
|
|
103
|
+
number_of_colours : int
|
|
104
|
+
Number of colours required, with accepted values between 2 and 6
|
|
105
|
+
inclusive. Returns 2 colours if no value given.
|
|
106
|
+
|
|
107
|
+
Raises
|
|
108
|
+
------
|
|
109
|
+
ValueError
|
|
110
|
+
If number_of_colours is greater than 6.
|
|
111
|
+
|
|
112
|
+
Returns
|
|
113
|
+
-------
|
|
114
|
+
list
|
|
115
|
+
categorical_colours_list
|
|
116
|
+
|
|
117
|
+
"""
|
|
118
|
+
|
|
119
|
+
if number_of_colours > 6:
|
|
120
|
+
raise ValueError("number_of_colours must not be more than 6 for the categorical palette.")
|
|
121
|
+
|
|
122
|
+
if number_of_colours == 2:
|
|
123
|
+
categorical_colours_list = duo_colours(duo_hex_list, colour_format)
|
|
124
|
+
return categorical_colours_list
|
|
125
|
+
|
|
126
|
+
elif colour_format == "hex":
|
|
127
|
+
full_categorical_colours_list = categorical_hex_list
|
|
128
|
+
|
|
129
|
+
elif colour_format == "rgb":
|
|
130
|
+
full_categorical_colours_list = hex_to_rgb(categorical_hex_list)
|
|
131
|
+
|
|
132
|
+
else:
|
|
133
|
+
raise ValueError(f"colour_format must be 'hex' or 'rgb', not {colour_format}.")
|
|
134
|
+
|
|
135
|
+
categorical_colours_list = full_categorical_colours_list[0:number_of_colours]
|
|
136
|
+
|
|
137
|
+
return categorical_colours_list
|
|
138
|
+
|
|
139
|
+
|
|
140
|
+
def duo_colours(duo_hex_list, colour_format="hex"):
|
|
141
|
+
"""
|
|
142
|
+
Return the Analysis Function duo colour palette as a list of 2
|
|
143
|
+
colours in hex or rgb format. This function is also called by
|
|
144
|
+
sequential_colours() if number_of_colours is equal to 2.
|
|
145
|
+
|
|
146
|
+
Parameters
|
|
147
|
+
----------
|
|
148
|
+
duo_hex_list : list
|
|
149
|
+
List of duo colours hex codes, stored in the config. This is needed for the
|
|
150
|
+
case of number_of_colours = 2.
|
|
151
|
+
|
|
152
|
+
colour_format : string
|
|
153
|
+
Colour format required, with accepted values of "hex" or "rgb".
|
|
154
|
+
|
|
155
|
+
Returns
|
|
156
|
+
-------
|
|
157
|
+
list
|
|
158
|
+
duo_colours_list
|
|
159
|
+
|
|
160
|
+
"""
|
|
161
|
+
|
|
162
|
+
if colour_format == "hex":
|
|
163
|
+
duo_colours_list = duo_hex_list
|
|
164
|
+
elif colour_format == "rgb":
|
|
165
|
+
duo_colours_list = hex_to_rgb(duo_hex_list)
|
|
166
|
+
else:
|
|
167
|
+
raise ValueError(f"colour_format must be 'hex' or 'rgb', not {colour_format}.")
|
|
168
|
+
|
|
169
|
+
return duo_colours_list
|
|
170
|
+
|
|
171
|
+
|
|
172
|
+
def sequential_colours(sequential_hex_list, colour_format="hex"):
|
|
173
|
+
"""
|
|
174
|
+
Return the Analysis Function sequential colour palette as a list
|
|
175
|
+
of 3 colours in hex or rgb format.
|
|
176
|
+
|
|
177
|
+
Parameters
|
|
178
|
+
----------
|
|
179
|
+
sequential_hex_list : list
|
|
180
|
+
List of sequential colours hex codes, stored in the config.
|
|
181
|
+
|
|
182
|
+
colour_format : string
|
|
183
|
+
Colour format required, with accepted values of "hex" or "rgb".
|
|
184
|
+
|
|
185
|
+
Returns
|
|
186
|
+
-------
|
|
187
|
+
list
|
|
188
|
+
sequential_colours_list
|
|
189
|
+
|
|
190
|
+
"""
|
|
191
|
+
|
|
192
|
+
if colour_format == "hex":
|
|
193
|
+
sequential_colours_list = sequential_hex_list
|
|
194
|
+
elif colour_format == "rgb":
|
|
195
|
+
sequential_colours_list = hex_to_rgb(sequential_hex_list)
|
|
196
|
+
else:
|
|
197
|
+
raise ValueError(f"colour_format must be 'hex' or 'rgb', not {colour_format}.")
|
|
198
|
+
|
|
199
|
+
return sequential_colours_list
|
|
200
|
+
|
|
201
|
+
|
|
202
|
+
def focus_colours(focus_hex_list, colour_format="hex"):
|
|
203
|
+
"""
|
|
204
|
+
Return the Analysis Function focus colour palette as a list of 2
|
|
205
|
+
colours in hex or rgb format.
|
|
206
|
+
|
|
207
|
+
Parameters
|
|
208
|
+
----------
|
|
209
|
+
focus_hex_list : list
|
|
210
|
+
List of focus colours hex codes, stored in the config.
|
|
211
|
+
|
|
212
|
+
colour_format : string
|
|
213
|
+
Colour format required, with accepted values of "hex" or "rgb".
|
|
214
|
+
|
|
215
|
+
Returns
|
|
216
|
+
-------
|
|
217
|
+
list
|
|
218
|
+
focus_colours_list
|
|
219
|
+
|
|
220
|
+
"""
|
|
221
|
+
|
|
222
|
+
if colour_format == "hex":
|
|
223
|
+
focus_colours_list = focus_hex_list
|
|
224
|
+
elif colour_format == "rgb":
|
|
225
|
+
focus_colours_list = hex_to_rgb(focus_hex_list)
|
|
226
|
+
else:
|
|
227
|
+
raise ValueError(f"colour_format must be 'hex' or 'rgb', not {colour_format}.")
|
|
228
|
+
|
|
229
|
+
return focus_colours_list
|
|
230
|
+
|
|
231
|
+
|
|
232
|
+
def hex_to_rgb(hex_colours):
|
|
233
|
+
"""
|
|
234
|
+
Convert a list of hex codes to a list of rgb colours.
|
|
235
|
+
|
|
236
|
+
Parameters
|
|
237
|
+
----------
|
|
238
|
+
hex_colours : list
|
|
239
|
+
The hex colours to be converted as a list of strings, with or
|
|
240
|
+
without # at the beginning.
|
|
241
|
+
|
|
242
|
+
Raises
|
|
243
|
+
------
|
|
244
|
+
TypeError
|
|
245
|
+
If hex_colours is not a list.
|
|
246
|
+
|
|
247
|
+
Returns
|
|
248
|
+
-------
|
|
249
|
+
list
|
|
250
|
+
converted_list
|
|
251
|
+
|
|
252
|
+
"""
|
|
253
|
+
if type(hex_colours) is not list:
|
|
254
|
+
raise TypeError("hex_colours must be a list.")
|
|
255
|
+
|
|
256
|
+
hex_colours_new = [i.lstrip("#") for i in hex_colours]
|
|
257
|
+
|
|
258
|
+
converted_list = [(tuple(int(value[i : i + 2], 16) for i in (0, 2, 4))) for value in hex_colours_new]
|
|
259
|
+
return converted_list
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
# TEXT
|
|
2
|
+
font.size : 14
|
|
3
|
+
font.family : sans-serif
|
|
4
|
+
font.sans-serif : Arial, Helvetica, DejaVu Sans, Bitstream Vera Sans, Computer Modern Sans Serif, Lucida Grande, Verdana, Geneva, Lucid, Avant Garde, sans-serif
|
|
5
|
+
text.color : black
|
|
6
|
+
axes.titlelocation: left
|
|
7
|
+
|
|
8
|
+
# AXES
|
|
9
|
+
axes.edgecolor : (0.8,0.8,0.8)
|
|
10
|
+
axes.labelcolor : (0.1,0.1,0.1)
|
|
11
|
+
axes.linewidth : 1
|
|
12
|
+
axes.spines.top : False
|
|
13
|
+
axes.spines.right : False
|
|
14
|
+
axes.spines.bottom : True
|
|
15
|
+
axes.spines.left : False
|
|
16
|
+
axes.axisbelow : True
|
|
17
|
+
|
|
18
|
+
# GRID
|
|
19
|
+
axes.grid : True
|
|
20
|
+
axes.grid.axis : y
|
|
21
|
+
axes.grid.which : major
|
|
22
|
+
grid.color : '0.8'
|
|
23
|
+
grid.linestyle : -
|
|
24
|
+
grid.linewidth : 0.6
|
|
25
|
+
|
|
26
|
+
# LINES
|
|
27
|
+
axes.prop_cycle: cycler('color', ["#12436D","#28A197","#801650", "#F46A25","#3D3D3D","#A285D1"])
|
|
28
|
+
lines.linewidth : 2
|
|
29
|
+
|
|
30
|
+
# TICKS
|
|
31
|
+
xtick.color : (0.8,0.8,0.8)
|
|
32
|
+
xtick.labelcolor : (0.1,0.1,0.1)
|
|
33
|
+
xtick.direction : out
|
|
34
|
+
ytick.color : (0.8,0.8,0.8)
|
|
35
|
+
ytick.labelcolor : (0.1,0.1,0.1)
|
|
36
|
+
ytick.direction : out
|
|
37
|
+
|
|
38
|
+
# LEGEND
|
|
39
|
+
legend.frameon : False
|
|
40
|
+
|
|
41
|
+
# FIGURE OUTPUT
|
|
42
|
+
figure.figsize : 6.4, 4.8
|
|
File without changes
|
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
import plotly.graph_objects as go
|
|
2
|
+
import plotly.io as pio
|
|
3
|
+
|
|
4
|
+
from afcharts.af_colours import get_af_colours
|
|
5
|
+
|
|
6
|
+
# References:
|
|
7
|
+
# https://plotly.com/python/templates/
|
|
8
|
+
# https://plotly.com/python-api-reference/generated/plotly.graph_objects.layout.template.html
|
|
9
|
+
|
|
10
|
+
# Use built in sans font
|
|
11
|
+
afcharts_font = "Sans-serif" # consider using a different font?
|
|
12
|
+
|
|
13
|
+
# R package ggplot uses pt as dimmentions whereas plotly uses px
|
|
14
|
+
# 1 pt ≈ 1.33 px
|
|
15
|
+
# So a 14 pt font ≈ 18.62 px in Plotly
|
|
16
|
+
base_size = 14 * 1.33
|
|
17
|
+
base_line_size = base_size / 24
|
|
18
|
+
base_rect_size = base_size / 24
|
|
19
|
+
|
|
20
|
+
# The half-line (base_size / 2) sets up the basic vertical
|
|
21
|
+
# rhythm of the theme. Most margins will be set to this value.
|
|
22
|
+
# However, when we work with relative sizes, we may want to multiply
|
|
23
|
+
# `half_line` with the appropriate relative size. This applies in
|
|
24
|
+
# particular for axis tick sizes. And also, for axis ticks and
|
|
25
|
+
# axis titles, `half_size` is too large a distance, and we use `half_size/2`
|
|
26
|
+
# instead.
|
|
27
|
+
half_line = base_size / 2
|
|
28
|
+
|
|
29
|
+
af_chart_feature_colour = "#D6D6D6"
|
|
30
|
+
|
|
31
|
+
pio.templates["afcharts"] = go.layout.Template(
|
|
32
|
+
layout={
|
|
33
|
+
"autosize": True, # Automatically adjusts the scale of the plot based on it's content
|
|
34
|
+
"annotationdefaults": {
|
|
35
|
+
"font": {"size": base_size},
|
|
36
|
+
"showarrow": False,
|
|
37
|
+
}, # Sets default font size for annotation
|
|
38
|
+
"bargap": 0.15,
|
|
39
|
+
"bargroupgap": 0.1,
|
|
40
|
+
"coloraxis": {
|
|
41
|
+
"colorbar": { # Bar chart colours
|
|
42
|
+
"outlinewidth": 0, # Width of the outline around the color bar
|
|
43
|
+
"tickcolor": af_chart_feature_colour, # Bar chart tick colour
|
|
44
|
+
"ticklen": half_line / 2, # Bar chart tick length
|
|
45
|
+
"ticks": "outside", # Bar chart tick position
|
|
46
|
+
}
|
|
47
|
+
},
|
|
48
|
+
"colorscale": {
|
|
49
|
+
"sequential": get_af_colours("sequential")[
|
|
50
|
+
::-1
|
|
51
|
+
] # reverse to make high = dark # Sequential colour scale for low to high ranges
|
|
52
|
+
},
|
|
53
|
+
"colorway": get_af_colours("categorical"), # Sequence of colours to be used in plots
|
|
54
|
+
"font": {
|
|
55
|
+
"color": "black", # Text colour
|
|
56
|
+
"family": afcharts_font, # Font
|
|
57
|
+
"size": base_size,
|
|
58
|
+
}, # Text size
|
|
59
|
+
"legend_title": None, # Removes legend title
|
|
60
|
+
"legend": {
|
|
61
|
+
"borderwidth": 0,
|
|
62
|
+
"title": {"text": None}, # Removes legend title
|
|
63
|
+
"font": {"size": base_size}, # Legend font size
|
|
64
|
+
"bgcolor": "rgba(0,0,0,0)", # Makes legend background transparent
|
|
65
|
+
"orientation": "v", # Legend orientation
|
|
66
|
+
"x": 1, # Positions legend (0,0 is the bottom left)
|
|
67
|
+
"y": 0.5,
|
|
68
|
+
"indentation": 0,
|
|
69
|
+
"itemclick": "toggleothers", # Change behaviour from hiding trace to showing only this trace
|
|
70
|
+
"itemwidth": 30,
|
|
71
|
+
"traceorder": "normal",
|
|
72
|
+
},
|
|
73
|
+
"hoverlabel": {
|
|
74
|
+
"align": "left", # Align hover label text to the left
|
|
75
|
+
"font_size": base_size * 0.9, # Text size of hover
|
|
76
|
+
"bgcolor": "white", # Hover box background
|
|
77
|
+
},
|
|
78
|
+
"hovermode": "x unified", # How hovering affects the display
|
|
79
|
+
# x unified shows info for all the data at that point in the x-axis
|
|
80
|
+
"margin": { # Set margins around the plot area in pixels
|
|
81
|
+
"l": half_line, # Left margin
|
|
82
|
+
"r": half_line, # Right margin
|
|
83
|
+
"t": half_line, # Top margin
|
|
84
|
+
"b": half_line, # Bottom margin
|
|
85
|
+
"pad": 0, # Padding between grid lines and the tick labels
|
|
86
|
+
},
|
|
87
|
+
"uniformtext_minsize": 8, # Minimum font size for text elements in the plot
|
|
88
|
+
"uniformtext_mode": "hide", # Controls visibility of text based on size then the
|
|
89
|
+
# text will be hidden - hide means that if a text element's size falls below the "uniformtext_minsize"
|
|
90
|
+
"title": {
|
|
91
|
+
"text": None,
|
|
92
|
+
"font": {
|
|
93
|
+
"size": base_size * 1.6,
|
|
94
|
+
}, # Title font size and colour
|
|
95
|
+
"x": 0,
|
|
96
|
+
"xref": "paper", # Title alignment
|
|
97
|
+
"pad": {
|
|
98
|
+
"t": half_line,
|
|
99
|
+
"l": 0,
|
|
100
|
+
"r": half_line,
|
|
101
|
+
"b": half_line,
|
|
102
|
+
}, # Padding above and below title
|
|
103
|
+
},
|
|
104
|
+
"xaxis": { # Configures the x-axis
|
|
105
|
+
"automargin": True, # Automatically adjust margins on axes to fit the content
|
|
106
|
+
"gridcolor": af_chart_feature_colour, # Grid lines colours
|
|
107
|
+
"linecolor": af_chart_feature_colour, # Axes line colour
|
|
108
|
+
"linewidth": 1,
|
|
109
|
+
"tickcolor": af_chart_feature_colour, # Tick mark colours
|
|
110
|
+
"tickfont": {
|
|
111
|
+
"size": base_size,
|
|
112
|
+
}, # Tick label font size
|
|
113
|
+
"tickwidth": 1,
|
|
114
|
+
"ticks": "outside", # Removes tick marks
|
|
115
|
+
"title": { # Axes title
|
|
116
|
+
"text": None, # Removes axes title
|
|
117
|
+
"standoff": half_line / 2, # Position from axes
|
|
118
|
+
},
|
|
119
|
+
"fixedrange": True, # Disables zoom and pan, keeps range fixed
|
|
120
|
+
"zeroline": True, # Makes zeroline visible
|
|
121
|
+
"zerolinecolor": af_chart_feature_colour, # Zero line colour
|
|
122
|
+
},
|
|
123
|
+
"yaxis": { # Configures the y-axis (as with the x-axis above)
|
|
124
|
+
"automargin": True,
|
|
125
|
+
"gridcolor": af_chart_feature_colour,
|
|
126
|
+
"linecolor": af_chart_feature_colour,
|
|
127
|
+
"linewidth": 1,
|
|
128
|
+
"tickcolor": af_chart_feature_colour,
|
|
129
|
+
"tickfont": {"size": base_size},
|
|
130
|
+
"tickwidth": 1,
|
|
131
|
+
"ticks": "outside",
|
|
132
|
+
"title": {
|
|
133
|
+
"text": None,
|
|
134
|
+
"standoff": half_line / 2, # Position from axes
|
|
135
|
+
},
|
|
136
|
+
"fixedrange": True,
|
|
137
|
+
"zeroline": True,
|
|
138
|
+
"zerolinecolor": af_chart_feature_colour,
|
|
139
|
+
},
|
|
140
|
+
},
|
|
141
|
+
data={"scatter": [{"marker": {"size": 12}, "line": {"width": 2.5}}]},
|
|
142
|
+
)
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: afcharts
|
|
3
|
+
Version: 1.0.0a0
|
|
4
|
+
Summary: A python package to produce charts following UK Government Analysis Function guidance
|
|
5
|
+
Author: Crown Copyright
|
|
6
|
+
Maintainer-email: Nick Henden <Nick.Henden@ukhsa.gov.uk>, Tom Farley <tom.farley@ukhsa.gov.uk>, Stelios Georgiou <Stelios.Georgiou@ukhsa.gov.uk>, Frederico Rodrigues <fred.rodrigues@defra.gov.uk>, Peter Brohan <peter.brohan@communities.gov.uk>, Fahim Hossain <fahim.hossain@ukhsa.gov.uk>
|
|
7
|
+
Project-URL: Homepage, https://github.com/best-practice-and-impact/afcharts-py
|
|
8
|
+
Project-URL: Documentation, https://best-practice-and-impact.github.io/afcharts-py/
|
|
9
|
+
Project-URL: Source, https://github.com/best-practice-and-impact/afcharts-py
|
|
10
|
+
Project-URL: Issue Tracker, https://github.com/best-practice-and-impact/afcharts-py/issues
|
|
11
|
+
Classifier: Programming Language :: Python :: 3
|
|
12
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
13
|
+
Classifier: Operating System :: OS Independent
|
|
14
|
+
Classifier: Development Status :: 3 - Alpha
|
|
15
|
+
Requires-Python: >=3.10
|
|
16
|
+
Description-Content-Type: text/markdown
|
|
17
|
+
License-File: LICENSE.md
|
|
18
|
+
Requires-Dist: matplotlib>=3.10.3
|
|
19
|
+
Requires-Dist: plotly>=6.1.2
|
|
20
|
+
Requires-Dist: typeguard>=4.4.4
|
|
21
|
+
Requires-Dist: PyYAML>=5.0
|
|
22
|
+
Dynamic: license-file
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
# afcharts-py <img src="docs/images/logo.svg" alt="afcharts logo" align="right" height="150"/>
|
|
26
|
+
|
|
27
|
+
## Overview
|
|
28
|
+
|
|
29
|
+
The afcharts python package helps make accessible Matplotlib and Plotly charts following [Government Analysis Function Data Visualisation guidance](https://analysisfunction.civilservice.gov.uk/policy-store/data-visualisation-charts/):
|
|
30
|
+
|
|
31
|
+
- **Automatic chart formatting** with pre-built style sheets for Matplotlib and Plotly
|
|
32
|
+
- **Chart colours** from the Analysis Function [accessible colour palettes](https://analysisfunction.civilservice.gov.uk/policy-store/data-visualisation-colours-in-charts/#section-4)
|
|
33
|
+
- **Example code** for common chart types in the [cookbook](https://best-practice-and-impact.github.io/afcharts-py/)
|
|
34
|
+
|
|
35
|
+
Looking for the R version? Check out the [afcharts R package](https://best-practice-and-impact.github.io/afcharts/).
|
|
36
|
+
|
|
37
|
+
<img src="docs/images/example_charts/bar_chart-matplotlib_afcharts.png" width="28.1%" alt="A grouped bar chart in afcharts style showing life expectancy in 1967 and 2007 for four countries. Bars use Analysis Function palette: dark blue for 1967, orange for 2007."/> <img src="docs/images/example_charts/scatterplot-matplotlib_afcharts.png" width="30%" alt="A scatterplot in afcharts style showing life expectancy against GDP per capita for 142 countries in 2007."/> <img src="docs/images/example_charts/line_chart-matplotlib_afcharts.png" width="37.5%" alt="A line chart in afcharts style showing life expectancy in China and the UK from 1952 to 2007"/>
|
|
38
|
+
|
|
39
|
+
## Installation
|
|
40
|
+
|
|
41
|
+
afcharts is available at the Python Package Index (PyPI):
|
|
42
|
+
```bash
|
|
43
|
+
pip install afcharts
|
|
44
|
+
```
|
|
45
|
+
or see the [alternative installation](https://best-practice-and-impact.github.io/afcharts-py/getting-started.html) instructions.
|
|
46
|
+
|
|
47
|
+
## Usage
|
|
48
|
+
|
|
49
|
+
Format any Matplotlib or Plotly chart in the Analysis Function style using the built-in style sheets.
|
|
50
|
+
|
|
51
|
+
See the [Getting Started](https://best-practice-and-impact.github.io/afcharts-py/getting-started.html) guide for more options and the [cookbook](https://best-practice-and-impact.github.io/afcharts-py/) for extensive examples.
|
|
52
|
+
|
|
53
|
+
### Matplotlib
|
|
54
|
+
|
|
55
|
+
```python
|
|
56
|
+
import matplotlib.pyplot as plt
|
|
57
|
+
|
|
58
|
+
# Apply the afcharts style to all Matplotlib plots
|
|
59
|
+
plt.style.use('afcharts.afcharts')
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
Example: A [Matplotlib bar chart](https://best-practice-and-impact.github.io/afcharts-py/01-matplotlib-usage.html#grouped-bar-chart) with afcharts (left) and without (right)
|
|
63
|
+
|
|
64
|
+
<img src="docs/images/example_charts/bar_chart-matplotlib_afcharts.png" width="35%" alt="Grouped bar chart (afcharts style) showing life expectancy in 1967 and 2007 for four countries. Bars use Analysis Function palette: dark blue for 1967, orange for 2007."/> <img src="docs/images/example_charts/bar_chart-matplotlib_default.png" width="34.7%" alt="Grouped bar chart (default Matplotlib) showing life expectancy in 1967 and 2007 for four countries. Bars: blue for 1967, orange for 2007."/>
|
|
65
|
+
|
|
66
|
+
### Plotly
|
|
67
|
+
|
|
68
|
+
```python
|
|
69
|
+
from afcharts.pio_template import pio
|
|
70
|
+
|
|
71
|
+
# Apply the afcharts style to all Plotly plots
|
|
72
|
+
pio.templates.default = "afcharts"
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
Example: A [Plotly bar chart](https://best-practice-and-impact.github.io/afcharts-py/03-plotly-usage.html#grouped-bar-chart) with afcharts (left) and without (right)
|
|
76
|
+
|
|
77
|
+
<img src="docs/images/example_charts/bar_chart-plotly_afcharts.png" width="35%" alt="Grouped bar chart (afcharts style) showing life expectancy in 1967 and 2007 for four countries. Bars use Analysis Function palette: dark blue for 1967, orange for 2007."/> <img src="docs/images/example_charts/bar_chart-plotly_default.png" width="35%" alt="Grouped bar chart (default Plotly) showing life expectancy in 1967 and 2007 for four countries. Bars: blue for 1967, red for 2007."/>
|
|
78
|
+
|
|
79
|
+
### Colours
|
|
80
|
+
|
|
81
|
+
Easily return a list of colours from any of the Analysis Function [accessible colour palettes](https://analysisfunction.civilservice.gov.uk/policy-store/data-visualisation-colours-in-charts/#section-4) with the `get_af_colours()` function:
|
|
82
|
+
|
|
83
|
+
```python
|
|
84
|
+
from afcharts.af_colours import get_af_colours
|
|
85
|
+
|
|
86
|
+
# Get the duo colour palette hex codes
|
|
87
|
+
duo = get_af_colours("duo")
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
## Getting help
|
|
91
|
+
If you encounter a bug, please file a minimal reproducible example on [Github Issues](https://github.com/best-practice-and-impact/afcharts-py/issues). For questions and other discussion, please start a [discussion](https://github.com/best-practice-and-impact/afcharts-py/discussions).
|
|
92
|
+
|
|
93
|
+
## Contributing
|
|
94
|
+
Interested in contributing? Check out the [contributing guidelines](CONTRIBUTING.md).
|
|
95
|
+
|
|
96
|
+
## Acknowledgments
|
|
97
|
+
The afcharts python package is based on the
|
|
98
|
+
[afcharts](https://github.com/best-practice-and-impact/afcharts.git) R package and the [py-af-colours](https://github.com/best-practice-and-impact/py-af-colours) package.
|
|
99
|
+
|
|
100
|
+
## License
|
|
101
|
+
Unless stated otherwise, the codebase is released under [the MIT License](LICENSE.md). This covers both the codebase and any sample code in the documentation.
|
|
102
|
+
|
|
103
|
+
The documentation is [© Crown copyright](https://www.nationalarchives.gov.uk/information-management/re-using-public-sector-information/uk-government-licensing-framework/crown-copyright/) and available under the terms of the [Open Government 3.0](https://www.nationalarchives.gov.uk/doc/open-government-licence/version/3/) licence.
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
LICENSE.md
|
|
2
|
+
README.md
|
|
3
|
+
pyproject.toml
|
|
4
|
+
src/afcharts/__init__.py
|
|
5
|
+
src/afcharts/af_colours.py
|
|
6
|
+
src/afcharts/afcharts.mplstyle
|
|
7
|
+
src/afcharts/pio_template.py
|
|
8
|
+
src/afcharts.egg-info/PKG-INFO
|
|
9
|
+
src/afcharts.egg-info/SOURCES.txt
|
|
10
|
+
src/afcharts.egg-info/dependency_links.txt
|
|
11
|
+
src/afcharts.egg-info/requires.txt
|
|
12
|
+
src/afcharts.egg-info/top_level.txt
|
|
13
|
+
src/afcharts/assets/__init__.py
|
|
14
|
+
src/afcharts/config/af_colours.yaml
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
afcharts
|