Why put Strictly in an ontology?#
I have pulled 22 years of BBC Strictly Come Dancing into a Microsoft Fabric IQ ontology project. It covers all 23 previous UK series and the start of the 24th series, with 339 couples, 3,210 routines and 10,913 individual judges’ marks. The project can now answer questions like “How does Craig mark a pro’s partners on the Paso Doble, compared with the rest of the panel?” — turning natural language into a walk through named things, rather than joining five data tables into a result set. As series 24 has just started, I plan to update this as we go.
Data tables store facts, whereas ontologies store meaning: a Couple is a Celebrity partnered with a Professional in a Series, and a JudgeScore is one Judge marking one Performance. Once those words and links are defined, analysts, Power BI and AI agents can all ask questions in the same vocabulary.
Strictly is a good test bed for ontologies because everyone understands Strictly and can reason about how its data is connected. In Strictly people change roles too: Anton Du Beke partnered a celebrity in each of series 1–18, guest-judged in series 18, and has been on the panel since series 19 — so his history needs to be split into two based on when he changed roles. I’m building a Strictly Predictor, which needs clean, leak-free history about every celebrity, pro and judge, which the same model will also underpin.
What is an ontology, in plain English?#
An ontology is a shared dictionary and map of your world. It is built from three simple ideas:
- Things (entity types): Celebrity, Professional, Judge, Couple, Performance, Dance.
- Facts about things (properties): a celebrity’s age at launch, a routine’s music, a judge’s mark.
- Links between things (relationships): a celebrity competes as a couple, a judge awards a score, a score scores a performance.
Once the ontology is connected to data, every row becomes a real thing you can point at, and every link becomes a path you can walk or a dancefloor to traverse.
Here is one small corner of the Strictly graph:
flowchart LR K["Karen Carney (Celebrity)"] -->|competesAs| C["Karen and Carlos (Couple, series 23)"] G["Carlos Gu (Professional)"] -->|partnersIn| C C -->|competesIn| S["Series 23 (2025)"] C -->|performs| P["Week 2 Tango (Performance)"] J["Craig Revel Horwood (Judge)"] -->|awards| M["Mark of 4 (JudgeScore)"] M -->|scores| P
Read it like a sentence: Karen Carney competes as a couple with Carlos Gu, in series 23. That couple performed a week 2 Tango, and Craig awarded it a 4. In a database the same facts sit in five tables joined on ID columns. In the ontology they are one connected picture that anyone can read, and a question is answered by walking the links.
For comparison, here are the same facts as a simple star schema: one fact table of judges’ marks, surrounded by four dimension tables.
erDiagram
JUDGE ||--o{ JUDGE_SCORE : "judge_id"
COUPLE ||--o{ JUDGE_SCORE : "couple_id"
DANCE ||--o{ JUDGE_SCORE : "dance_id"
SERIES ||--o{ JUDGE_SCORE : "series_id"
JUDGE_SCORE {
string judge_id FK
string couple_id FK
string dance_id FK
int series_id FK
int week
int score
}
JUDGE {
string judge_id PK
string name
}
COUPLE {
string couple_id PK
string celebrity
string professional
}
DANCE {
string dance_id PK
string name
}
SERIES {
int series_id PK
int year
}
| Table | The row for this example |
|---|---|
| JUDGE_SCORE (fact) | judge_id = jud_craig_revel_horwoodcouple_id = s23_karen_carneydance_id = dan_tangoseries_id = 23, week = 2, score = 4 |
| JUDGE | jud_craig_revel_horwood → Craig Revel Horwood |
| COUPLE | s23_karen_carney → Karen Carney with Carlos Gu |
| DANCE | dan_tango → Tango |
| SERIES | 23 → 2025 |
The star schema is built for counting and averaging: every question starts from the fact table and joins out to the dimensions by ID. The ontology holds the same facts, but it names each link once (a judge awards a score, a couple performs a routine). A question can then start from any thing and walk to any other, without knowing which ID column joins to which.
Designing the ontology: 13 entity types, 22 relationships#
The core design choice was to split every person into who they are and who they were in a given series. A Celebrity has a birth date; a CelebrityProfile records their age, career and dance background at that series’ launch. A ProfessionalProfile records a pro’s record before that series only, so nothing from the series being studied leaks in.
flowchart TD Celebrity([Celebrity]) -->|competesAs| Couple[Couple] Professional([Professional]) -->|partnersIn| Couple Judge([Judge]) -->|awards| JudgeScore[JudgeScore] Couple -->|competesIn| Series[Series] Couple -->|performs| Performance[Performance] JudgeScore -->|scores| Performance Performance -->|isDance| Dance[Dance] Performance -->|inWeek| Week[Week] Celebrity -.->|hasProfile| CelebrityProfile[CelebrityProfile] Professional -.->|hasSeasonProfile| ProfessionalProfile[ProfessionalProfile] ProfessionalProfile -->|hasDanceHistory| ProDanceHistory[ProDanceHistory] ProDanceHistory --> Dance Judge -.->|hasDanceStats| JudgeDanceStats[JudgeDanceStats] JudgeDanceStats --> Dance
The diagram illustrates how the core entities, point-in-time profiles, and weekly competition elements connect. Profiles capture state prior to each series so predictive models avoid data leakage, while relationships like wonBy, onPanel, and isDance allow graph traversals across all 24 series.
| Entity type | Key example | What one instance is |
|---|---|---|
| Series | 23 | One series: dates, panel, head judge, winner |
| Celebrity | cel_karen_carney | A competing celebrity across all appearances |
| Professional | pro_carlos_gu | A professional dancer across all appearances |
| Judge | jud_craig_revel_horwood | A judge across all appearances |
| Couple | s23_karen_carney | A celebrity–pro partnership in one series |
| CelebrityProfile | s23_cel_karen_carney | A celebrity’s age, career and dance experience at series launch |
| ProfessionalProfile | s23_pro_carlos_gu | A pro’s cumulative track record prior to that series |
| ProDanceHistory | s23_pro_carlos_gu_rumba | A pro’s record in one dance before that series |
| JudgeDanceStats | jud_craig_revel_horwood_cha_cha_cha | A judge’s marking of one dance across all series |
| Performance | S23-W05-012 | One routine: dance, music, total, average mark |
| JudgeScore | S23-W05-012_jud_shirley_ballas | One judge’s mark on one routine, with lean vs panel |
| Dance | dan_tango | A dance discipline (ballroom, latin, specialty) |
| Week | s23_w05 | A specific round/theme within a series |
Every key is a readable string, so anyone reading raw data can tell what an instance is. Each relationship is bound to a mapping table that holds both keys, which is how Fabric IQ turns rows into graph edges.
Querying the ontology: from question to result#
A question to the ontology is a path through the graph. You describe the shape of the answer in the ontology’s own words, and the graph finds every match. Fabric offers three ways to ask:
| Way to ask | Who it suits | What you write |
|---|---|---|
| Plain English through a Fabric data agent (preview) | Anyone | “Which judges gave winners the most 10s?” |
| GQL in the graph’s code editor | Analysts | A MATCH pattern, like the examples below |
GQL through the REST API (executeQuery) | Apps and scripts | The same query, sent as JSON |
GQL is the ISO standard graph query language. Its core is one idea: draw the path in brackets and arrows. (a:Judge)-[:awards]->(s:JudgeScore) means “a judge who awards a score”. The four examples below go from simple to multi-step. Each answer was computed from the tables the ontology is bound to; once the graph is refreshed, the same queries run live in Fabric.
1. Who partnered Karen Carney, and how did they finish?#
The path is: celebrity → competes as → couple ← partners in ← professional.
MATCH (c:Celebrity
WHERE c.name = 'Karen Carney')
-[:competesAs]->(cp:Couple)
<-[:partnersIn]-(p:Professional)
RETURN p.name AS professional,
cp.series_id AS series,
cp.finish_label AS finish| professional | series | finish |
|---|---|---|
| Carlos Gu | 23 | Winner |
There are no IDs or join keys in the query. The ontology already knows how a celebrity connects to a professional.
2. Which core dances does Craig mark hardest?#
The path is: judge → awards → score → scores → performance → is a → dance. It then averages how far each mark sits from the rest of the panel.
MATCH (j:Judge
WHERE j.name = 'Craig Revel Horwood')
-[:awards]->(s:JudgeScore)
-[:scores]->(p:Performance)
-[:isDance]->(d:Dance)
WHERE d.dance_family IN
['ballroom', 'latin']
LET dance = d.name
RETURN dance,
avg(s.diff_from_panel) AS lean,
count(*) AS marks
GROUP BY dance
ORDER BY lean ASC
LIMIT 3| dance | lean vs panel | marks |
|---|---|---|
| Cha-cha-cha | −0.91 | 257 |
| Rumba | −0.86 | 164 |
| Samba | −0.76 | 195 |
Craig’s three harshest dances are all Latin. Only Arlene Phillips also marked the Cha-cha-cha below the panel (−0.31); the other seven regular judges sit above it.
3. What did Carlos Gu bring into series 23?#
The path is: professional → has season profile (series 23) → has dance history → history of dance → dance. The dance history holds only routines from before series 23, so nothing from the series being studied leaks in.
MATCH (pro:Professional
WHERE pro.name = 'Carlos Gu')
-[:hasSeasonProfile]->
(pp:ProfessionalProfile
WHERE pp.series_id = 23)
-[:hasDanceHistory]->
(h:ProDanceHistory)
-[:historyOfDance]->(d:Dance)
RETURN d.name AS dance,
h.prior_times_danced AS times,
h.prior_mean_avg_score AS avg_mark
ORDER BY avg_mark DESC
LIMIT 3| dance | times | avg mark |
|---|---|---|
| Rumba | 2 | 9.50 |
| Showdance | 1 | 9.25 |
| Charleston | 2 | 9.13 |
He went on to win series 23 with Karen Carney.
4. Which judges have given eventual winners the most 10s?#
This is the longest walk: series → won by → couple → performs → performance ← scores ← score ← awards ← judge.
MATCH (se:Series)
-[:wonBy]->(c:Couple)
-[:performs]->(p:Performance)
<-[:scores]-(s:JudgeScore)
<-[:awards]-(j:Judge)
FILTER s.score = 10
LET judge = j.name
RETURN judge,
count(*) AS tens_to_winners
GROUP BY judge
ORDER BY tens_to_winners DESC
LIMIT 5| judge | 10s to eventual winners |
|---|---|
| Bruno Tonioli | 81 |
| Shirley Ballas | 56 |
| Motsi Mabuse | 48 |
| Len Goodman | 46 |
| Darcey Bussell | 36 |
Bruno leads partly because he judged 17 series. In SQL this question needs five joins; in the ontology it is one path that reads like the question. Asked in plain English through a data agent, the ontology is designed to produce the same kind of query for you.
What’s next#
The ontology is the foundation for three demos:
- Strictly Predictor. Train on the leak-free profile entities (
CelebrityProfile,ProfessionalProfile,ProDanceHistory) to forecast the series 24 finish order as scores arrive. - Power BI. A Direct Lake semantic model over the same Lakehouse tables, with cross-filtering judge, dance and couple pages.
- Data agents. Point a Fabric data agent at the ontology so people can ask “Who has Craig given the most 10s to?” in plain English, grounded in the same vocabulary.
Sources#
- Strictly Come Dancing series pages — Wikipedia, series 1–24
- Strictly Come Dancing Wiki — fandom wiki, profiles
- Create an ontology with Fabric IQ — Microsoft Learn module
- GQL language guide for graph in Microsoft Fabric — query syntax used above
- GQL Query API reference — running the same queries over REST
