from py_af_colours import af_colours
py_af_colours
py_af_colours aims to support the creation of accessible python graphs across the UK Government Analysis Function by bringing the accessble colour palettes to creators. The colour palettes have been designed to support visually impaired people interpret data visualisations by featuring distinct colours with suitable contrasts.
The examples in these docs include other best practices for creating accessible graphs, with the full guidance available on the Analysis Function website.
The data used throughout these examples is provided in the required format on the py_af_colours
repository.
Requirements
To use py_af_colours
, your system requires the following:
- Python 3
- the
PyYAML
package - the py_af_colours package
Installing py_af_colours
py_af_colours is on PyPI. To install it:
- Open a python terminal, such as Anaconda shell
- Run:
pip install py_af_colours
Getting the colours
Import the af_colours function:
The af_colours
function takes four arguments: palette
, colour_format
, number_of_colours
, and config_path
.
palette
is a required parameter, chosen by the user based on the Analysis Function colour guidance. It takes one of four possible string values corresponding to the options:- “duo”
- “focus”
- “sequential”
- “categorical”
- “duo”
colour_format
is an optional parameter. It takes one of two possible string values:- “hex”; meaning hexadecimal colour code, for example #12436D. “hex” is the default value of colour_format if none is specified.
- “rgb”; meaning red green blue colour code, for example (18, 67, 109).
number_of_colours
is an optional parameter for the categorical palette. It takes an integer value up to 6.config_path
is an optional parameter that links to where the palette values are stored. It is not recommended for the user to specify a value for this parameter (see Basic usage). It may be needed if the user moves the config file, or if they require custom values stored elsewhere.
af_colours()
returns hex codes as a list of strings, and rgb codes as a list of tuples.
Basic usage
To return the duo colour palette hex codes:
"duo") af_colours(
['#12436D', '#F46A25']
To return a five colour categorical palette as rgb codes:
"categorical", "rgb", 5) af_colours(
C:\Users\rosetg\AppData\Local\Temp\ipykernel_13232\4202599074.py:1: UserWarning: It is best practice to limit graphs to four categories where possible to avoid graphs becoming cluttered.
af_colours("categorical", "rgb", 5)
[(18, 67, 109), (40, 161, 151), (128, 22, 80), (244, 106, 37), (61, 61, 61)]
See also that warnings are given to highlight other accessibility best practice - in this case, to avoid graphs becoming cluttered.
This lists can be specified as values for colour parameters with graphing packages such as plotly
and seaborn
.
Examples in graphs
Categorical
This palette is for data which can be divided into groups or categories by using names or labels.
Colour name | Hex code | RGB code | Colour fill |
---|---|---|---|
Dark blue | #12436D | (18, 67, 109) | |
Turquoise | #28A197 | (40, 161, 151) | |
Dark pink | #801650 | (128, 22, 80) | |
Orange | #F46A25 | (244, 106, 37) | |
Dark grey | #3D3D3D | (61, 61, 61) | |
Light purple | #A285D1 | (162, 133, 209) |
Figure 1: Bar chart using the Categorical colour palette
This clustered bar chart uses data based on the world population dataset. Each cluster corresponds to a year. Each of the four bars within each cluster represent a country, denoted a distinct colour. The bars are outlined in white to aid with contrast between the bars, with additional whitespace between the clusters. The legend is presented in the same order as the bars in the clusters.
import pandas as pd
import seaborn as sns
import matplotlib.pyplot as plt
from py_af_colours import af_colours
= pd.read_csv("data\world_population_in_billions.csv")
world_population = world_population[0:4]
top_four = top_four.set_index("Country/Territory")
top_four_data = top_four_data.columns.tolist()[-4:]
years_of_interest
= sns.color_palette(af_colours("categorical", "hex", 4))
categorical
= pd.melt(top_four_data.reset_index(),
population_over_time = "Country/Territory",
id_vars = years_of_interest,
value_vars = "Year",
var_name = "Population")
value_name
= plt.subplots()
fig, ax
= "Year", y = "Population",
sns.barplot(x = population_over_time,
data = 2, hue = "Country/Territory",
zorder = "white",
edgecolor = categorical)
palette
False)
plt.box(
"Population (Billion)", rotation = 0)
ax.set_ylabel(-0.15, 1.05)
ax.yaxis.set_label_coords(= True, which = "both", axis = "y", color = "#D6D6D6")
plt.grid(visible = "#D6D6D6")
ax.tick_params(color = -0.01, top = 1.6)
ax.set_ylim(bottom = False, loc = "upper left", ncol = 4,
plt.legend(frameon = 0.7, bbox_to_anchor = (0, 0.62, 0.5, 0.5))
handlelength
plt.show()
Figure 2: Pie chart using the Categorical colour palette
This a pie chart showing the proportional populations of the four most populous countries. Each country is represented by a distinct colour. Each slice is outlined in white, and labelled with a sans serif font in black. This data is from the world population dataset.
import pandas as pd
import seaborn as sns
import numpy as np
import matplotlib.pyplot as plt
from py_af_colours import af_colours
= pd.read_csv("data\world_population_in_billions.csv")
world_population = world_population[0:4]
top_four =top_four.set_index("Country/Territory")
top_four_data = top_four_data.iloc[:,-1:]
most_recent = most_recent.reset_index()
data = top_four["Country/Territory"].values
countries
= af_colours("categorical", "hex", 4)
colours_list = sns.color_palette(colours_list)
categorical
= []
percentage_labels for n in np.arange(most_recent.shape[0]):
= 100 * ((most_recent.iloc[n].values.item())
percentage /(most_recent.values.sum().item()))
= round(percentage, 1)
value = str(data["Country/Territory"].iloc[n])
country + " " + str(value) + "%"))
percentage_labels.append((country
= most_recent.plot.pie(x = "Country/Territory", y = "2022",
ax = percentage_labels,
labels = False,
legend = categorical,
colors = 1,
pctdistance = False,
counterclock = 90,
startangle = {"edgecolor": "white",
wedgeprops "linewidth": 1.5,
"antialiased": True})
None)
ax.set_ylabel("Population of Four Most Populous Countries in 2022")
ax.set_title(
plt.show()
Duo
This palette is for categorical data when there are two categories.
Colour name | Hex code | RGB code | Colour fill |
---|---|---|---|
Dark blue | #12436D | (18, 67, 109) | |
Orange | #F46A25 | (244, 106, 37) |
Figure 3: Line chart using the Duo colour palette
This line chart has two lines, one dark blue and one orange. Each is labelled with black text in a sans serif font. This line chart uses data based on the world population dataset.
import pandas as pd
import seaborn as sns
import matplotlib.pyplot as plt
from py_af_colours import af_colours
= pd.read_csv("data\world_population_in_billions.csv")
world_population = world_population.set_index("Country/Territory")[0:2]
top_two_data
= sns.color_palette(af_colours("duo"))
duo
= plt.subplots()
fig, ax
= (top_two_data.transpose()),
sns.lineplot(data = duo,
palette = False,
dashes = False)
legend
"Year")
ax.set_xlabel("Population (Billion)", rotation = 0)
ax.set_ylabel(-0.15, 1.05)
ax.yaxis.set_label_coords(
= True, which = "both", axis = "y", color = "#BEBEBE")
ax.grid(visible True)
ax.set_axisbelow(False)
ax.set_frame_on(= "#D6D6D6")
ax.tick_params(color
= len(top_two_data.columns) - 1
x_label_coord = top_two_data.values[0][-1] + 0.03
y_label_coord1 = top_two_data.values[1][-1] - 0.05
y_label_coord2
" China", xy = (x_label_coord, y_label_coord1))
ax.annotate(" India", xy = (x_label_coord, y_label_coord2))
ax.annotate(
= 0, right = len(top_two_data.columns) - 1)
ax.set_xlim(left = -0.01, top = 1.6)
ax.set_ylim(bottom
plt.tight_layout() plt.show()
Sequential
This palette is for data where the order of the data is meaningful, such as for age groups.
Colour name | Hex code | RGB code | Colour fill |
---|---|---|---|
Dark blue | #12436D | (18, 67, 109) | |
Mid blue | #2073BC | (32, 115, 188) | |
Light blue | #6BACE6 | (107, 172, 230) |
Figure 4: Bar chart using the Sequential colour palette
This clustered bar chart uses data based on the population by age dataset and shows the population of three age groups for five countries. Each country is labelled along the x axis. The bars within each cluster use the gradient of the sequential palette to follow the order of age bars going from young to older. The legend is presented at the top left of the graph in the same order as the age-group bars in the bar clusters. Each bar has a dark blue border and white space in between.
Distinct from previous examples, this example shows how to use an rgb list returned by af_colours. It is generally easier to use hex codes, but this example is included for completeness. The end result is the same as using hex codes.
import numpy as np
import pandas as pd
import seaborn as sns
import matplotlib.pyplot as plt
from py_af_colours import af_colours
= pd.read_csv("data\population_by_age.csv")
world_population_by_age = world_population_by_age[0:5]
top_five_age = top_five_age.set_index(top_five_age["Country"])
top_five_age_data = top_five_age["Country"].values
countries
= plt.subplots()
fig, ax
= np.arange(top_five_age.shape[0])
x_spacing = af_colours("sequential", "rgb")
colours = [tuple(t / 255 for t in x) for x in colours]
sequential
- 0.25, top_five_age["Under 25"].values,
plt.bar(x_spacing = 0.19, zorder = 2, edgecolor = sequential[0],
width = "0 to 25", color = sequential[0])
label "25-64 years"].values,
plt.bar(x_spacing, top_five_age[= 0.19, zorder = 2, edgecolor = sequential[0],
width = "25 to 64", color=sequential[1])
label + 0.25, top_five_age["65+"].values,
plt.bar(x_spacing = 0.19, zorder = 2, edgecolor = sequential[0],
width = "65+", color = sequential[2])
label
False)
plt.box(= True, which = "both", axis = "y", color = "#D6D6D6")
plt.grid(visible "Country"].values)
plt.xticks(x_spacing, top_five_age[= "white")
plt.tick_params(color "Country")
ax.set_xlabel("Population (Billion)", rotation = 0)
ax.set_ylabel(-0.15, 1.05)
ax.yaxis.set_label_coords(= 0.9)
ax.set_ylim(top
= (0.025, 0.62, 0.5, 0.5), ncol = 4,
plt.legend(bbox_to_anchor = 0.7, frameon = False)
handlelength plt.show()
Focus
This palette should be used when you want to highlight specific elements to help users understand the information.
Colour name | Hex code | RGB code | Colour fill |
---|---|---|---|
Dark blue | #12436D | (18, 67, 109) | |
Grey | #BFBFBF | (191, 191, 191) |
Figure 5: Line chart using the Focus colour palette
This line chart uses data based on the world population dataset. It shows the change in population of various countries over time as five lines, of which four are a uniform light grey, and one is a dark blue, serving to highlight the data.
import pandas as pd
import numpy as np
import seaborn as sns
import matplotlib.pyplot as plt
from py_af_colours import af_colours
= pd.read_csv("data\world_population_in_billions.csv")
world_population = world_population[0:5]
top_five = top_five.set_index("Country/Territory")
top_five_data
= "India"
focus_country = af_colours("focus")
focus_colours = top_five.index[top_five["Country/Territory"]
focus_index == focus_country].tolist()
= int(top_five_data.shape[0]) * [focus_colours[1]]
focus_palette 0]] = focus_colours[0]
focus_palette[focus_index[= sns.color_palette(focus_palette)
focus
= plt.subplots()
fig, ax
= top_five_data.transpose(),
sns.lineplot(data = focus,
palette = False,
dashes = False)
legend
"Year")
ax.set_xlabel("Population (Billion)", rotation = 0)
ax.set_ylabel(-0.15, 1.05)
ax.yaxis.set_label_coords(
= True, which = "both", axis = "y", color = "#D6D6D6")
ax.grid(visible True)
ax.set_axisbelow(False)
ax.set_frame_on(= "#D6D6D6")
ax.tick_params(color
= len(top_five.columns) - 2
x_coord = []
y_coord for n in np.arange(top_five_data.shape[0]):
= top_five_data.values[n][-1]
y
y_coord.append(y)
" China", xy = (x_coord, y_coord[0]))
ax.annotate(" India", xy = (x_coord, y_coord[1] - 0.04))
ax.annotate(" United States", xy = (x_coord, y_coord[2] + 0.01))
ax.annotate(" Indonesia", xy = (x_coord, y_coord[3]))
ax.annotate(" Pakistan", xy = (x_coord, y_coord[4] - 0.02))
ax.annotate(
= 0, right = (len(top_five.columns) - 2))
ax.set_xlim(left = -0.01, top = 1.6)
ax.set_ylim(bottom
plt.tight_layout() plt.show()
Maintenance
In the event of future updates to the colours, they will be updated in the config file.
This page will undergo changes as the package is updated.