Circular Sankey – UEFA Champions League

I created this UEFA Champions League viz using the tutorial found here created by Luke Stanke. Let me walk you through the steps on how I made my particular viz and how it differs from Luke’s original concept.

As you can see above, each year’s winner connects via a curved line to the league that they belong to. In order to get the lines to calculate this way I structured my data so that each line referred to a year, team, and their league as shown below. The Modified Country Rank is the most important field to notice here. This rank has been normalized by the number of competitions held.

Join the Data page above with the data model page shown below. This tab consists of two columns, one named ‘One’ and another named ‘t’. Join this to the data table on the field, One, via a left join.

This will give you the data repeated 100 times as you are duplicating the data for each number in the ‘t’ column. This will allow you to have a data point to plot for each point in your curve.

The t column has three values at the bottom (1,2 & 3) that don’t seem to fit the rest of the data model. These are used to differentiate the three main parts of the sankey (inside, outside, curves).

Here are the key fields that you need to make in order to get the circular sankey chart working.

x: ([distance] + 1) * COS([path (copy)] * 2 * pi())

y: ([distance] + 1) * SIN([path (copy)] * 2 * pi())

Distance: IF [T] <= 1
THEN 2.5 -((1/(1 + exp([t (logodds)] )))) * 2.5
ELSEIF [T] = 2
THEN .23
ELSEIF [T] = 3
THEN 2.3

t (logodds): IF [T] > 0 AND [T] < 1
THEN LOG([T]/(1-[T]))

x-lines: IF [T]=2 or [T]=3 then NULL ELSE [x] END

x-shape: IF [T]=2 or [T]=3 then [x] ELSE NULL END

Path: IF [T] <= 1 THEN
// Chord
((([Season #]-[Modified Country Rank])*[T])+[Modified Country Rank])/{COUNTD([Season])}
// Start (country)
[Country Rank Order]/{COUNTD([Winner Country])}
// End (team)
[Season #]/{COUNTD([Season])}

To get the tool-tips labeled the way we want them, use the T value to determine if you are showing country details or league details. The T=2 level is showing country details and the T=3

Country Tool-tip: IF [T]=2 THEN [Country Total] ELSE NULL END

Once you have created all of the calculated fields and they are working properly drag y to columns and x-lines to rows. Make sure to drag path to line in the marks card.

Once you have dragged your x-lines to the marks card you should start to see the general outline of the viz.

Create a dual axis and drag your x-shape field to rows and create a dual axis. Your marks cards should look like the marks cards to the left.

If you have not used shapes before, save your team and league logos to your My Tableau Repository file and assign them appropriately when dragging country and team to the Shape card.

Create a dashboard action that highlights each league when you click on its logo.

Thanks so much for reading this post. If you are having trouble recreating this chart type, make sure to send me a DM on Twitter or email me at

Huge shout out to Luke Stanke for his template on how to create this chart type and for his help on my viz.

Also a huge thank you to Ken Flerlage for helping me understand some of the geometry and other math included in this viz type.


  1. Hi Spencer!

    I am having a bit of trouble trying to overcame the normalized field for the rank as I seem to not be able to achieve it correctly! My issue is that I am trying to do something similar but for the most productive pair of players in the top 5 leagues. Almost there but can not get through that point.

    Is there any way you can help me out?

    Thank you & Thank you for the great blog!

Leave a Reply