diff --git a/docs/img/clusters/cluster_density.drawio.png b/docs/img/clusters/cluster_density.drawio.png
new file mode 100644
index 0000000000..73b1705281
Binary files /dev/null and b/docs/img/clusters/cluster_density.drawio.png differ
diff --git a/docs/img/clusters/cluster_size.drawio.png b/docs/img/clusters/cluster_size.drawio.png
new file mode 100644
index 0000000000..d204682d78
Binary files /dev/null and b/docs/img/clusters/cluster_size.drawio.png differ
diff --git a/docs/img/clusters/is_bridge.drawio.png b/docs/img/clusters/is_bridge.drawio.png
new file mode 100644
index 0000000000..03743a6759
Binary files /dev/null and b/docs/img/clusters/is_bridge.drawio.png differ
diff --git a/docs/topic_guides/evaluation/clusters.md b/docs/topic_guides/evaluation/clusters.md
deleted file mode 100644
index aae5690d1a..0000000000
--- a/docs/topic_guides/evaluation/clusters.md
+++ /dev/null
@@ -1,3 +0,0 @@
-# Cluster Evaluation
-
-This page is under construction - check back soon!
\ No newline at end of file
diff --git a/docs/topic_guides/evaluation/clusters/graph_metrics.md b/docs/topic_guides/evaluation/clusters/graph_metrics.md
new file mode 100644
index 0000000000..2bf641fdde
--- /dev/null
+++ b/docs/topic_guides/evaluation/clusters/graph_metrics.md
@@ -0,0 +1,141 @@
+# Graph metrics
+
+Graph metrics quantify the characteristics of a graph. A simple example of a graph metric is [cluster size](#cluster-size), which is the number of nodes within a cluster.
+
+For data linking with Splink, it is useful to sort graph metrics into three categories:
+
+* [Node metrics](#node-metrics)
+* [Edge metrics](#edge-metrics)
+* [Cluster metrics](#cluster-metrics)
+
+Each of these are defined below together with examples and explanations of how they can be applied to linked data to evaluate cluster quality. The examples cover all metrics currently available in Splink.
+
+!!! note
+
+ It is important to bear in mind that whilst graph metrics can be very useful for assessing linkage quality, they are rarely definitive, especially when taken in isolation. A more comprehensive picture can be built by considering various metrics in conjunction with one another.
+
+ It is also important to consider metrics within the context of their distribution and the underlying dataset. For example: a cluster density (see below) of 0.4 might seem low but could actually be above average for the dataset in question; a cluster of size 80 might be suspiciously large for one dataset but not for another.
+
+
+## :purple_circle: Node metrics
+
+Node metrics quantify the properties of the nodes which live within clusters.
+
+### Node Degree
+
+##### Definition
+
+Node degree is the **number of edges connected to a node**.
+
+##### Example
+
+In the cluster below A has a node degree of 1, whereas D has a node degree of 3.
+
+![Basic Graph - Records](../../../img/clusters/basic_graph_records.drawio.png){:width="80%"}
+
+##### Application in Data Linkage
+
+High node degree is generally considered good as it means there are many edges in support of records in a cluster being linked. Nodes with low node degree could indicate links being missed (false negatives) or be the result of a small number of false links (false positives).
+
+However, erroneous links (false positives) could also be the reason for _high_ node degree, so it can be useful to validate the edges of highly connected nodes.
+
+It is important to consider [cluster size](#cluster-size) when looking at node degree. By definition, larger clusters contain more nodes to form links between, allowing nodes within them to attain higher degrees compared to those in smaller clusters. Consequently, low node degree within larger clusters can carry greater significance.
+
+Bear in mind, that the degree of a single node in a cluster isn't necessarily representative of the overall connectedness of a cluster. This is where [cluster centralisation](#cluster-centralisation) can help.
+
+
+
+## :link: Edge metrics
+
+Edge metrics quantify the properties of the edges within a cluster.
+
+### 'is bridge'
+
+##### Definition
+
+An edge is classified as a 'bridge' if its **removal splits a cluster into two smaller clusters**.
+
+##### Example
+
+For example, the removal of the link labelled "Bridge" below would break this cluster of 9 nodes into two clusters of 5 and 4 nodes, respectively.
+
+![](../../../img/clusters/is_bridge.drawio.png){:width="70%"}
+
+##### Application in Data Linkage
+
+Bridges can be signalers of false positives in linked data, especially when joining two highly connected sub-clusters. Examining bridges can shed light on issues with the linking process leading to the formation of false positive links.
+
+
+
+## :fontawesome-solid-circle-nodes: Cluster metrics
+
+Cluster metrics refer to the characteristics of a cluster as a whole, rather than the individual nodes and edges it contains.
+
+### Cluster Size
+
+##### Definition
+
+Cluster size refers to the **number of nodes within a cluster**.
+
+##### Example
+
+The cluster below is of size 5.
+
+![](../../../img/clusters/cluster_size.drawio.png){:width="30%"}
+
+##### Application in Data Linkage
+
+When thinking about cluster size, it is often useful to consider the biggest clusters produced and ask yourself if the sizes seem reasonable for the dataset being linked. For example when linking people, does it make sense that an individual is appearing hundreds of times in the linked data resulting in a cluster of over 100 nodes? If the answer is no, then false positives links are probably being formed.
+
+If you don't have an intuition of what seems reasonable, then it is worth inspecting a sample of the largest clusters in Splink's [Cluster Studio Dashboard](../../../charts/cluster_studio_dashboard.ipynb) to validate (or invalidate) links. From there you can develop an understanding of what maximum cluster size to expect for your linkage. Bear in mind that a large and highly dense cluster is usually less suspicious than a large low-density cluster.
+
+There also might be a lower bound on cluster size. For example, when linking two datasets in which you know people appear at least once in each, the minimum expected size of cluster will be 2. Clusters smaller than the minimum size indicate links have been missed.
+
+### Cluster Density
+
+##### Definition
+
+The density of a cluster is given by the **number of edges it contains divided by the maximum possible number of edges**. Density ranges from 0 to 1. A density of 1 means that all nodes are connected to all other nodes in a cluster.
+
+##### Example
+
+The left cluster below has links between all nodes (giving a density of 1), whereas the right cluster has the minimum number of edges (4) to link 5 nodes together (giving a density of 0.4).
+
+![](../../../img/clusters/cluster_density.drawio.png){:width="80%"}
+
+##### Application in Data Linkage
+
+When evaluating clusters, a high density (closer to 1) is generally considered good as it means there are many edges in support of the records in a cluster being linked.
+
+A low density could indicate links being missed. This could happen, for example, if blocking rules are too tight or the clustering threshold is too high.
+
+A sample of low density clusters can be inspected in Splink's [Cluster Studio Dashboard](../../../charts/cluster_studio_dashboard.ipynb) via the option `sampling_method = "lowest_density_clusters_by_size"`, which performs stratified sampling across different cluster sizes. When inspecting a cluster, ask yourself the question: why aren't more links being formed between record nodes?
+
+
+### Cluster Centralisation
+
+!!! info "Work in Progress"
+
+ We are still working out where Cluster Centralisation can be best used in the context of record linkage. At this stage, we do not have clear recommendations or guidance on the best places to use it - so if you have any expertise in this area we would love to [hear from you](https://github.com/moj-analytical-services/splink/discussions)!
+
+ We will update this guidance as and when we have clearer strategies in this space.
+
+##### Definition
+
+[Cluster centralisation](https://en.wikipedia.org/wiki/Centrality#Degree_centrality) is defined as the deviation from maximum [node degree](#node-degree) normalised with respect to the maximum possible value. In other words, cluster centralisation tells us about the concentration of edges in a cluster. Centralisation ranges from 0 to 1.
+
+##### Example
+
+Coming Soon
+
+##### Application in Data Linkage
+
+A high cluster centralisation (closer to 1) indicates that a few nodes are home to significantly more connections compared to the rest of the nodes in a cluster. This can help identify clusters containing nodes with a lower number of connections (low node degree) relative to what is possible for that cluster.
+
+Low centralisation suggests that edges are more evenly distributed amongst nodes in a cluster. This can be good if all nodes within a clusters enjoy many connections. However, low centralisation could also indicate that most nodes are not as highly connected as they could be. To check for this, look at low centralisation in conjunction with low [density](#cluster-density).
+
+
+
+A guide on [how to compute graph metrics](./how_to_compute_metrics.ipynb) mentioned above with Splink is given in the next chapter.
+
+Please note, this topic guide is a work in progress and we welcome any feedback.
diff --git a/docs/topic_guides/evaluation/clusters/how_to_compute_metrics.ipynb b/docs/topic_guides/evaluation/clusters/how_to_compute_metrics.ipynb
new file mode 100644
index 0000000000..549e04bf26
--- /dev/null
+++ b/docs/topic_guides/evaluation/clusters/how_to_compute_metrics.ipynb
@@ -0,0 +1,378 @@
+{
+ "cells": [
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "# How to compute graph metrics with Splink"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "## Introduction to the `compute_graph_metrics()` method"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "To enable users to calculate a variety of graph metrics for their linked data, Splink provides the `compute_graph_metrics()` method.\n",
+ "\n",
+ "The method is called on the `linker` like so:\n",
+ "\n",
+ "```\n",
+ "linker.computer_graph_metrics(df_predict, df_clustered, threshold_match_probability=0.95)\n",
+ "```\n",
+ "with arguments\n",
+ "\n",
+ " Args:\n",
+ " df_predict (SplinkDataFrame): The results of `linker.predict()`\n",
+ " df_clustered (SplinkDataFrame): The outputs of\n",
+ " `linker.cluster_pairwise_predictions_at_threshold()`\n",
+ " threshold_match_probability (float): Filter the pairwise match predictions\n",
+ " to include only pairwise comparisons with a match_probability at or\n",
+ " above this threshold.\n",
+ "\n",
+ "!!! warning\n",
+ "\n",
+ " `threshold_match_probability` should be the same as the clustering threshold passed to `cluster_pairwise_predictions_at_threshold()`. If this information is available to Splink then it will be passed automatically, otherwise the user will have to provide it themselves and take care to ensure that threshold values align.\n",
+ "\n",
+ "The method generates tables containing graph metrics (for nodes, edges and clusters), and returns a data class of [Splink dataframes](../../../SplinkDataFrame.md). The individual Splink dataframes containing node, edge and cluster metrics can be accessed as follows:\n",
+ "\n",
+ "```\n",
+ "compute_graph_metrics.nodes for node metrics\n",
+ "compute_graph_metrics.edges for edge metrics\n",
+ "compute_graph_metrics.clusters for cluster metrics\n",
+ "```\n",
+ "\n",
+ "The metrics computed by `compute_graph_metrics()` include all those mentioned in the [Graph metrics](./graph_metrics.md) chapter, namely:\n",
+ "\n",
+ "* Node degree\n",
+ "* 'Is bridge'\n",
+ "* Cluster size\n",
+ "* Cluster density\n",
+ "* Cluster centrality\n",
+ "\n",
+ "All of these metrics are calculated by default. If you are unable to install the `igraph` package required for 'is bridge', this metric won't be calculated, however all other metrics will still be generated.\n",
+ "\n",
+ "This topic guide is a work in progress and we welcome any feedback."
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "## Full code example"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "This code snippet computes graph metrics for a simple Splink dedupe model. A pandas dataframe of cluster metrics is displayed as the final output."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 1,
+ "metadata": {},
+ "outputs": [
+ {
+ "name": "stderr",
+ "output_type": "stream",
+ "text": [
+ "/var/folders/nd/c3xr518x3txg5kcqp1h7zwc80000gp/T/ipykernel_13654/2355919473.py:39: SplinkDeprecated: target_rows is deprecated; use max_pairs\n",
+ " linker.estimate_u_using_random_sampling(target_rows=1e6)\n",
+ "----- Estimating u probabilities using random sampling -----\n",
+ "\n",
+ "Estimated u probabilities using random sampling\n",
+ "\n",
+ "Your model is not yet fully trained. Missing estimates for:\n",
+ " - first_name (no m values are trained).\n",
+ " - surname (no m values are trained).\n",
+ " - postcode_fake (no m values are trained).\n",
+ "\n",
+ "----- Starting EM training session -----\n",
+ "\n",
+ "Estimating the m probabilities of the model by blocking on:\n",
+ "(l.\"first_name\" = r.\"first_name\") AND (l.\"surname\" = r.\"surname\")\n",
+ "\n",
+ "Parameter estimates will be made for the following comparison(s):\n",
+ " - postcode_fake\n",
+ "\n",
+ "Parameter estimates cannot be made for the following comparison(s) since they are used in the blocking rules: \n",
+ " - first_name\n",
+ " - surname\n",
+ "\n",
+ "Iteration 1: Largest change in params was -0.352 in probability_two_random_records_match\n",
+ "Iteration 2: Largest change in params was 0.108 in the m_probability of postcode_fake, level `All other comparisons`\n",
+ "Iteration 3: Largest change in params was 0.019 in the m_probability of postcode_fake, level `All other comparisons`\n",
+ "Iteration 4: Largest change in params was 0.00276 in the m_probability of postcode_fake, level `All other comparisons`\n",
+ "Iteration 5: Largest change in params was 0.000388 in the m_probability of postcode_fake, level `All other comparisons`\n",
+ "Iteration 6: Largest change in params was 5.44e-05 in the m_probability of postcode_fake, level `All other comparisons`\n",
+ "\n",
+ "EM converged after 6 iterations\n",
+ "\n",
+ "Your model is not yet fully trained. Missing estimates for:\n",
+ " - first_name (no m values are trained).\n",
+ " - surname (no m values are trained).\n",
+ "\n",
+ "----- Starting EM training session -----\n",
+ "\n",
+ "Estimating the m probabilities of the model by blocking on:\n",
+ "(l.\"dob\" = r.\"dob\") AND (SUBSTR(l.\"postcode_fake\", 1, 3) = SUBSTR(r.\"postcode_fake\", 1, 3))\n",
+ "\n",
+ "Parameter estimates will be made for the following comparison(s):\n",
+ " - first_name\n",
+ " - surname\n",
+ "\n",
+ "Parameter estimates cannot be made for the following comparison(s) since they are used in the blocking rules: \n",
+ " - postcode_fake\n",
+ "\n",
+ "Iteration 1: Largest change in params was 0.508 in probability_two_random_records_match\n",
+ "Iteration 2: Largest change in params was 0.0868 in probability_two_random_records_match\n",
+ "Iteration 3: Largest change in params was 0.0212 in probability_two_random_records_match\n",
+ "Iteration 4: Largest change in params was 0.00704 in probability_two_random_records_match\n",
+ "Iteration 5: Largest change in params was 0.00306 in probability_two_random_records_match\n",
+ "Iteration 6: Largest change in params was 0.00149 in probability_two_random_records_match\n",
+ "Iteration 7: Largest change in params was 0.000761 in probability_two_random_records_match\n",
+ "Iteration 8: Largest change in params was 0.000395 in probability_two_random_records_match\n",
+ "Iteration 9: Largest change in params was 0.000206 in probability_two_random_records_match\n",
+ "Iteration 10: Largest change in params was 0.000108 in probability_two_random_records_match\n",
+ "Iteration 11: Largest change in params was 5.66e-05 in probability_two_random_records_match\n",
+ "\n",
+ "EM converged after 11 iterations\n",
+ "\n",
+ "Your model is fully trained. All comparisons have at least one estimate for their m and u values\n",
+ "Completed iteration 1, root rows count 316\n",
+ "Completed iteration 2, root rows count 63\n",
+ "Completed iteration 3, root rows count 12\n",
+ "Completed iteration 4, root rows count 0\n"
+ ]
+ },
+ {
+ "data": {
+ "text/html": [
+ "\n",
+ "\n",
+ "
\n",
+ " \n",
+ " \n",
+ " | \n",
+ " cluster_id | \n",
+ " n_nodes | \n",
+ " n_edges | \n",
+ " density | \n",
+ " cluster_centralisation | \n",
+ "
\n",
+ " \n",
+ " \n",
+ " \n",
+ " 0 | \n",
+ " Q98761652-1 | \n",
+ " 5 | \n",
+ " 8.0 | \n",
+ " 0.800000 | \n",
+ " 0.333333 | \n",
+ "
\n",
+ " \n",
+ " 1 | \n",
+ " Q10307857-1 | \n",
+ " 11 | \n",
+ " 35.0 | \n",
+ " 0.636364 | \n",
+ " 0.200000 | \n",
+ "
\n",
+ " \n",
+ " 2 | \n",
+ " Q18910925-1 | \n",
+ " 20 | \n",
+ " 172.0 | \n",
+ " 0.905263 | \n",
+ " 0.105263 | \n",
+ "
\n",
+ " \n",
+ " 3 | \n",
+ " Q13530025-1 | \n",
+ " 11 | \n",
+ " 32.0 | \n",
+ " 0.581818 | \n",
+ " 0.266667 | \n",
+ "
\n",
+ " \n",
+ " 4 | \n",
+ " Q15966633-11 | \n",
+ " 3 | \n",
+ " 3.0 | \n",
+ " 1.000000 | \n",
+ " 0.000000 | \n",
+ "
\n",
+ " \n",
+ " ... | \n",
+ " ... | \n",
+ " ... | \n",
+ " ... | \n",
+ " ... | \n",
+ " ... | \n",
+ "
\n",
+ " \n",
+ " 21530 | \n",
+ " Q5006750-7 | \n",
+ " 1 | \n",
+ " 0.0 | \n",
+ " NaN | \n",
+ " NaN | \n",
+ "
\n",
+ " \n",
+ " 21531 | \n",
+ " Q5166888-13 | \n",
+ " 1 | \n",
+ " 0.0 | \n",
+ " NaN | \n",
+ " NaN | \n",
+ "
\n",
+ " \n",
+ " 21532 | \n",
+ " Q5546247-8 | \n",
+ " 1 | \n",
+ " 0.0 | \n",
+ " NaN | \n",
+ " NaN | \n",
+ "
\n",
+ " \n",
+ " 21533 | \n",
+ " Q6698372-5 | \n",
+ " 1 | \n",
+ " 0.0 | \n",
+ " NaN | \n",
+ " NaN | \n",
+ "
\n",
+ " \n",
+ " 21534 | \n",
+ " Q7794499-6 | \n",
+ " 1 | \n",
+ " 0.0 | \n",
+ " NaN | \n",
+ " NaN | \n",
+ "
\n",
+ " \n",
+ "
\n",
+ "
21535 rows × 5 columns
\n",
+ "
"
+ ],
+ "text/plain": [
+ " cluster_id n_nodes n_edges density cluster_centralisation\n",
+ "0 Q98761652-1 5 8.0 0.800000 0.333333\n",
+ "1 Q10307857-1 11 35.0 0.636364 0.200000\n",
+ "2 Q18910925-1 20 172.0 0.905263 0.105263\n",
+ "3 Q13530025-1 11 32.0 0.581818 0.266667\n",
+ "4 Q15966633-11 3 3.0 1.000000 0.000000\n",
+ "... ... ... ... ... ...\n",
+ "21530 Q5006750-7 1 0.0 NaN NaN\n",
+ "21531 Q5166888-13 1 0.0 NaN NaN\n",
+ "21532 Q5546247-8 1 0.0 NaN NaN\n",
+ "21533 Q6698372-5 1 0.0 NaN NaN\n",
+ "21534 Q7794499-6 1 0.0 NaN NaN\n",
+ "\n",
+ "[21535 rows x 5 columns]"
+ ]
+ },
+ "execution_count": 1,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "import splink.duckdb.comparison_library as cl\n",
+ "from splink.datasets import splink_datasets\n",
+ "from splink.duckdb.blocking_rule_library import block_on\n",
+ "from splink.duckdb.linker import DuckDBLinker\n",
+ "\n",
+ "import ssl\n",
+ "\n",
+ "ssl._create_default_https_context = ssl._create_unverified_context\n",
+ "\n",
+ "df = splink_datasets.historical_50k\n",
+ "\n",
+ "settings_dict = {\n",
+ " \"link_type\": \"dedupe_only\",\n",
+ " \"blocking_rules_to_generate_predictions\": [\n",
+ " block_on([\"postcode_fake\", \"first_name\"]),\n",
+ " block_on([\"first_name\", \"surname\"]),\n",
+ " block_on([\"dob\", \"substr(postcode_fake,1,2)\"]),\n",
+ " block_on([\"postcode_fake\", \"substr(dob,1,3)\"]),\n",
+ " block_on([\"postcode_fake\", \"substr(dob,4,5)\"]),\n",
+ " ],\n",
+ " \"comparisons\": [\n",
+ " cl.exact_match(\n",
+ " \"first_name\",\n",
+ " term_frequency_adjustments=True,\n",
+ " ),\n",
+ " cl.jaro_winkler_at_thresholds(\n",
+ " \"surname\", distance_threshold_or_thresholds=[0.9, 0.8]\n",
+ " ),\n",
+ " cl.levenshtein_at_thresholds(\n",
+ " \"postcode_fake\", distance_threshold_or_thresholds=[1, 2]\n",
+ " ),\n",
+ " ],\n",
+ " \"retain_intermediate_calculation_columns\": True,\n",
+ "}\n",
+ "\n",
+ "\n",
+ "linker = DuckDBLinker(df, settings_dict)\n",
+ "\n",
+ "linker.estimate_u_using_random_sampling(target_rows=1e6)\n",
+ "\n",
+ "linker.estimate_parameters_using_expectation_maximisation(\n",
+ " block_on([\"first_name\", \"surname\"])\n",
+ ")\n",
+ "\n",
+ "linker.estimate_parameters_using_expectation_maximisation(\n",
+ " block_on([\"dob\", \"substr(postcode_fake, 1,3)\"])\n",
+ ")\n",
+ "\n",
+ "df_predict = linker.predict()\n",
+ "df_clustered = linker.cluster_pairwise_predictions_at_threshold(df_predict, 0.95)\n",
+ "\n",
+ "graph_metrics = linker.compute_graph_metrics(df_predict, df_clustered)\n",
+ "\n",
+ "graph_metrics.clusters.as_pandas_dataframe()"
+ ]
+ }
+ ],
+ "metadata": {
+ "kernelspec": {
+ "display_name": "splink-bxsLLt4m",
+ "language": "python",
+ "name": "python3"
+ },
+ "language_info": {
+ "codemirror_mode": {
+ "name": "ipython",
+ "version": 3
+ },
+ "file_extension": ".py",
+ "mimetype": "text/x-python",
+ "name": "python",
+ "nbconvert_exporter": "python",
+ "pygments_lexer": "ipython3",
+ "version": "3.9.6"
+ }
+ },
+ "nbformat": 4,
+ "nbformat_minor": 2
+}
diff --git a/docs/topic_guides/evaluation/clusters/overview.md b/docs/topic_guides/evaluation/clusters/overview.md
new file mode 100644
index 0000000000..650d8652ef
--- /dev/null
+++ b/docs/topic_guides/evaluation/clusters/overview.md
@@ -0,0 +1,39 @@
+# Cluster Evaluation
+
+Graphs provide a natural way to think about linked data (see the ["Linked data as graphs" guide](../../theory/linked_data_as_graphs.md) for a refresher). Visualising linked data as a graph and employing graph metrics are powerful ways to evaluate linkage quality.
+
+![Basic Cluster](../../../img/clusters/basic_graph_cluster.drawio.png){:width="80%"}
+
+Graph metrics help to give a big-picture view of the clusters generated by a Splink model. Through metric distributions and statistics, we can gauge the quality of clusters and monitor how adjustments to models impact results.
+
+Graph metrics can also help us home in on problematic clusters, such as those containing inaccurate links (false positives). Spot-checking can be performed with Splink’s [Cluster Studio Dashboard](../../../charts/cluster_studio_dashboard.ipynb) which enables users to visualise individual clusters and interrogate the links between their member records.
+
+## Evaluating cluster quality
+
+### What is a high quality cluster?
+
+When it comes to data linking, the highest quality clusters will be those containing all possible true matches (there will be no missed links a.k.a. false negatives) and no false matches (no false positives). In other words, clusters only containing precisely those nodes corresponding to records about the same entity.
+
+Generating clusters which all adhere to this ideal is rare in practice. For example,
+
+* Blocking rules, necessary to make computations tractable, can prevent record comparisons between some true matches ever being made
+* Data limitations can place an upper bound on the level of quality achievable
+
+Despite this, graph metrics can help us get closer to a satisfactory level of quality as well as monitor it going forward.
+
+### What does cluster quality look like for you?
+
+The extent of cluster evaluation efforts and what is considered 'good enough' will vary greatly with linkage use-case. You might already have labelled data or quality assured outputs from another model which define a clear benchmark for cluster quality.
+
+Domain knowledge can also set expectations of what is deemed reasonable or good. For example, you might already know that a large cluster (containing say 100 nodes) is suspicious for your deduplicated dataset.
+
+However, you may currently have little or no knowledge about the data or no a clear idea of what good quality clusters look like for your linkage.
+
+Whatever the starting point, this topic guide is designed to help users develop a better understanding of their clusters and help focus quality assurance efforts to get the best out of their linkage models.
+
+## What this topic guide contains
+
+* An introduction to the [graph metrics](./graph_metrics.md) currently available in Splink and how to apply them to linked data
+* Instructions on [how to compute graph metrics](./how_to_compute_metrics.ipynb) with Splink
+
+Please note, this topic guide is a work in progress and we welcome any feedback.
\ No newline at end of file
diff --git a/docs/topic_guides/evaluation/edge_overview.md b/docs/topic_guides/evaluation/edge_overview.md
index 24bab33432..83531091d8 100644
--- a/docs/topic_guides/evaluation/edge_overview.md
+++ b/docs/topic_guides/evaluation/edge_overview.md
@@ -51,4 +51,4 @@ Evaluating the edges (links) of a linkage model depends on your use case. Defini
Your desired metric should help give an initial estimation for a linkage threshold, then you can use spot checking to help settle on a final threshold.
-In general, the links between pairs of records are not the final output of linkage pipeline. Most use-cases use these links to group records together into clusters. In this instance, evaluating the links themselves is not sufficient, you have to [evaluate the resulting clusters as well](./clusters.md).
\ No newline at end of file
+In general, the links between pairs of records are not the final output of linkage pipeline. Most use-cases use these links to group records together into clusters. In this instance, evaluating the links themselves is not sufficient, you have to [evaluate the resulting clusters as well](./clusters/overview.md).
\ No newline at end of file
diff --git a/docs/topic_guides/evaluation/overview.md b/docs/topic_guides/evaluation/overview.md
index 9688d50b99..4452e9a8bb 100644
--- a/docs/topic_guides/evaluation/overview.md
+++ b/docs/topic_guides/evaluation/overview.md
@@ -18,7 +18,7 @@ Once you have trained a model, you will use it to predict the probability of lin
### :fontawesome-solid-circle-nodes: Cluster Evaluation
-Once you have chosen a linkage threshold, the edges are used to generate clusters of records. To see how to evaluate these clusters, check out the [Cluster Evaluation guide](./clusters.md).
+Once you have chosen a linkage threshold, the edges are used to generate clusters of records. To see how to evaluate these clusters, check out the [Cluster Evaluation guide](./clusters/overview.md).
diff --git a/is_bridge.drawio.png b/is_bridge.drawio.png
new file mode 100644
index 0000000000..03743a6759
Binary files /dev/null and b/is_bridge.drawio.png differ
diff --git a/mkdocs.yml b/mkdocs.yml
index 4080841fbd..912a44e5a2 100644
--- a/mkdocs.yml
+++ b/mkdocs.yml
@@ -169,7 +169,10 @@ nav:
- Overview: "topic_guides/evaluation/edge_overview.md"
- Edge Metrics: "topic_guides/evaluation/edge_metrics.md"
- Clerical Labelling: "topic_guides/evaluation/labelling.md"
- - Clusters: "topic_guides/evaluation/clusters.md"
+ - Clusters:
+ - Overview: "topic_guides/evaluation/clusters/overview.md"
+ - Graph metrics: "topic_guides/evaluation/clusters/graph_metrics.md"
+ - How to compute graph metrics: "topic_guides/evaluation/clusters/how_to_compute_metrics.ipynb"
- Performance:
- Run times, performance and linking large data: "topic_guides/performance/drivers_of_performance.md"
- Spark Performance:
diff --git a/scripts/pyspelling/custom_dictionary.txt b/scripts/pyspelling/custom_dictionary.txt
index 9e2b30d3c4..f320c0506d 100644
--- a/scripts/pyspelling/custom_dictionary.txt
+++ b/scripts/pyspelling/custom_dictionary.txt
@@ -135,6 +135,7 @@ comparator
comparators
conda
config
+connectedness
csv
customisable
customizations
@@ -207,6 +208,7 @@ runtime
scalable
schemas
sdt
+signalers
subclassed
subdistricts
substring