Naar hoofdinhoud
HENNYSPEELMAN.
Techniek

Formule 1-data in Power BI via een REST API

herwerkt op

Geschreven in 2022. De open API die ik toen gebruikte, Ergast, is intussen gestopt en dat domein is in andere handen. De techniek hieronder werkt nog altijd, en er bestaat een opvolger die dezelfde adresstructuur aanhoudt, zodat je enkel de basis-URL hoeft te wisselen. De voorbeelden staan al met die opvolger erin.

Formule 1 is een van mijn oudste passies. Elk racweekend voor de televisie, en elke keer opnieuw onder de indruk van de sfeer, de cultuur en vooral de snelheid. Die snelheid doet me denken aan data die van de ene plek naar de andere schiet.

Die twee passies samenbrengen wou ik al lang, en met open data en een rapportagetool kan dat gewoon.

Waarom een API en geen export

Het doel van je project bepaalt de weg ernaartoe. Ik koos voor een API omdat ik wou kunnen vernieuwen na elke race, zonder telkens opnieuw te exporteren en importeren. Had ik een infografiek over één rijder willen maken, dan was een eenmalige export prima geweest.

Dat blijft de kern: zoek het antwoord dat bij je vraag past, en toets elke mogelijkheid die je overweegt.

Eerst data vinden

Zoeken naar data op het web is elke keer een gok. Sommige verzamelingen koop je, andere zijn vrij te gebruiken. Ik zet er altijd de woorden open data bij in mijn zoekopdracht. Zo kom je bij bronnen die bedoeld zijn om opgehaald te worden, en die vaak ook onderhouden worden.

Voor Formule 1 leverde dat een open API op met rijders, races, landen en zelfs rondetijden, die na elk weekend werd bijgewerkt.

De verbinding leggen

Je begint bij Gegevens ophalen op het tabblad Start, met een web-adres:

https://api.jolpi.ca/ergast/f1/results/?limit=1000

Duizend records is het maximum per oproep. In de data zelf zag ik dat er toen 25.399 resultaten beschikbaar waren, dus daar zat meteen mijn eerste hindernis: door de bladzijden lopen.

Een API werkt vaak met bladzijden waar je doorheen wandelt. Hier is er een handiger weg, want er zit een parameter offset in het adres. Het antwoord geeft ook het totale aantal records terug, dus je weet precies hoeveel oproepen je nodig hebt. Deze code kan je in de geavanceerde editor plakken:

let
    Source = Xml.Tables(Web.Contents("https://api.jolpi.ca/ergast/f1/results/?limit=1000")),
    #"Removed Other Columns" = Table.SelectColumns(Source,{"Attribute:total"}),
    #"Changed Type" = Table.TransformColumnTypes(#"Removed Other Columns",{{"Attribute:total", Int64.Type}}),
    ValueMaxRecords = #"Changed Type"{0}[#"Attribute:total"],
    GeneratedOffsets = List.Generate(() => 0, each _ < ValueMaxRecords, each _ + 1000),
    #"Converted to Table" = Table.FromList(GeneratedOffsets, Splitter.SplitByNothing(), null, null, ExtraValues.Error),
    #"Renamed Columns" = Table.RenameColumns(#"Converted to Table",{{"Column1", "Offset"}}),
    #"Changed Type2" = Table.TransformColumnTypes(#"Renamed Columns",{{"Offset", type text}}),
    #"Added Custom" = Table.AddColumn(#"Changed Type2", "GetData", each Xml.Tables(Web.Contents("https://api.jolpi.ca/ergast/f1/results/?limit=1000&offset=" &[Offset] )))
in
    #"Added Custom"

Dit had ik nodig voor elke verzameling met meer dan duizend records, dus goot ik het in een functie die ik overal kon aanroepen. Zo staat de opbouw van die lijst op één plek en blijft het onderhoud beperkt. Daarna waren de andere verzamelingen zo binnengehaald: resultaten, kwalificaties, rijders, constructeurs, seizoenen, races en statussen.

Zodra je het lastige stuk in een functie zet, kost de tiende bron je evenveel als de tweede.

Het model

Opgehaalde data is zelden meteen bruikbaar, want er ligt nog geen enkel verband tussen de tabellen. Dan komen de vragen: heb ik een tijddimensie nodig, moet een dimensie meer dan één rol kunnen spelen, heb ik eigenlijk wel alles.

  • Behandel resultaten en kwalificaties als feiten.
  • Behandel de rest als dimensies.
  • Voeg surrogaatsleutels toe, zodat de verwijzingen leesbaar blijven.
  • Geef de rijderdimensie een tweede rol, zodat je twee rijders naast elkaar kan zetten.
Sterschema met twee feittabellen, Resultaten en Kwalificaties, in het midden. Daaromheen de dimensies Race, Seizoen, Constructeur, Status en Rijder. Rijder staat een tweede keer gestippeld, als tweede rol, om twee rijders met elkaar te vergelijken.ResultatenFEITKwalificatiesFEITRaceDIMENSIESeizoenDIMENSIERijderDIMENSIEConstructeurDIMENSIEStatusDIMENSIERijder (2)DIMENSIEDe gestippelde aansluiting is dezelfde rijdertabel, een tweede keer verbonden. Zo leg jetwee rijders naast elkaar zonder de data te dupliceren.
Twee feittabellen, de dimensies eromheen, en de rijdertabel een tweede keer aangesloten.

Het rapport

Visualiseren begint bij begrijpen. Ik keek dus eerst wat er te tonen viel en welke vorm daarbij paste. Wat ik wou weten, was de stand van een seizoen, en welke rijder het over de hele lijn beter deed op een aantal punten: snelste ronden, poleposities, overwinningen en punten. Daarbij wou ik vlot kunnen wisselen tussen punten en overwinningen, want dat zijn twee verschillende verhalen.

Voor de vormgeving hielp het dat elk team zijn eigen kleuren heeft, dus die legde ik via voorwaardelijke opmaak op de labels. Die tweede rol voor de rijderdimensie maakte de vergelijking tussen twee rijders mogelijk, met per rijder zijn code, naam, nationaliteit en leeftijd, plus zijn punten, poles, overwinningen en uitvalbeurten.

Wat je meeneemt

Ligt de data er en mag je ze gebruiken, dan is er weinig reden om te wachten. Onderweg kwam hier zowat alles voorbij wat een echt project ook tegenkomt: door bladzijden lopen, dimensioneel modelleren, een dimensie met twee rollen, en dan pas het beeld.

Dat het over Formule 1 ging, maakte het leuker. Het maakte het niet makkelijker, en dat is precies waarom zo'n project meer opsteekt dan een handleiding.

Power BIPower QueryREST API