Can you visualise and measure distance travelled?

Kyle took inspiration from the 2026 FIFA World Cup for this week’s challenge, looking at how many miles each team need to travel between their base camp and the stadiums where their group matches are being held. He sourced some data which he provided for download.

Modelling the data

The data provided contained 3 sheets. Kyle gave some hints on how he’d used the data, but nothing specific, so I just related the sheets in the following way, to see whether this would be enough.

I related Schedule to Base Camp, but BEFORE I applied the relationship join condition, I pivoted the Schedule data (as Kyle had hinted this may be required). I did this by

  • Adding Base Camp to canvas
  • Then adding Schedule to canvas
  • Then clicking on the Team A and Team B columns in Schedule, and selecting Pivot
  • The renamed Pivot Names field heading to Team A | B
  • and renamed Pivot Values field heading to Team1 (I couldn’t call it Team as this field already exists in Base Camp)
  • I then created a relationship between Base Camp.Team and Schedule.Team1

The add Venues to the canvas a relate to Schedule on the Venue field.

Creating the Map with all locations

For the maps we need to work with spatial data, and need to define the location of each base camp and each venue. The data sets had longitude and latitude fields for both types of location, but for some reason, both the longitude fields were resolving as string fields. So I had to change these to geographic fields by the following steps

  • right click Base Camp > Longitude, and change data type > number (decimal)
  • then right click field again , geographic role > latitude
  • repeat same 2 steps for the Venues > Laitude (Venues) field.

Once done create fields

Base Location

MAKEPOINT([Latitude],[Longitude])

and

Venue Location

MAKEPOINT([Latitude (Venues)],[Longitude (Venues)])

Then create

Line

MAKELINE([Base Location],[Venue Location])

and then

Distance – Base to Venue

DISTANCE([Base Location], [Venue Location],’mi’)

On a new sheet, add Venue Location to Detail, and Venue to Detail. Change the mark type to circle. Adjust the map background layers so only the Base, County/Region Names and State/Province border options are selected (Map menu > background layers)

Create a parameter to store the name of the selected team we want to focus the data for

pTeam

string parameter defaulted to ‘Austria’

Create a field

Is Selected Team?

Team = [pTeam]

Add this to Colour and Size and adjust accordingly. Make sure the ‘true’ is listed first so that the marks for the selected team are ‘on top’ . This will require the Size to ‘be reversed’

Remove all text from the Tooltip

Drag Base Location onto the display and drop it when ‘Add a Marks Layer’ option appears, which will create a 2nd marks card

Drag this card to be beneath the Venues one. Change the mark type to circle. Add Team to Detail. Add Is Selected Team to Colour and Size. Add Training Site and City to Tooltip and update accordingly.

Then click on the context menu of the Venues marks card, and disable selection

Nowt drag Line on to the canvas to make another marks layer. Again move this marks card to the bottom of the list, so it’s beneath the Base Camp marks card. Add Team to Detail and Is Selected Team to Colour. Create a copy of Is Selected Team (right click field and duplicate to create Is Selected Team (copy)) and add this to Size and adjust. I found I needed a copy so I could have different sizes between the circles and the lines Add Training Site and Distance to Tooltip and update the Tooltip accordingly. Remove row & column dividers.

Creating the ‘Team specific’ map

The easiest way I found to do this initially, was just to duplicate the sheet with the above map, and then add Is Selected Team to the Filter shelf, and set to True. This gives us the display we need, but tooltips need changing.

Re-enable the Venues marks card. We’ll need to display information about the match on the venues tooltip, wihich includes details for the teams playing.

As we initially pivoted the data, this information is now across 2 rows, so we need to create fields to capture both the teams on each row. I used FIXED LoDs for this:

Team A

{FIXED [Match]:MIN(IF [Team A | B]=’Team A’ THEN [Team_1] END)}

Team B

{FIXED [Match]:MIN(IF [Team A | B]=’Team B’ THEN [Team_1] END)}

Add these to Tooltip, along with Training Site, Date, Time (Local) and Distance. Format the Date field to “Month, Day Year” format, and custom format the Time (Local) field to h:nn AMPM. The adjust tooltip to suit.

Now do similar to the Line marks card – add Match to the Detail shelf as a blue disaggregated discrete pill, and then add Team A, Team B, Venue, Date and Time(Local) to the Tooltip and update accordingly.

Create the Bar Chart

On a new sheet, add Is Selected Team and Team to Rows and Distance to Columns. Sort descending. Order so Is Selected Team : True is listed first.

Make the rows a little wider. Add Is Selected Team to Colour. Add Team to Label. Adjust Label so the team name is aligned left and coloured in white bold text. Adjust the Tooltip. Add a Reference line to the Distance axis to show per cell the value of the sum of the distance. Don’t show line or tooltip.

Format the reference line, so the numbers are aligned in bold font, middle right

Hide the Is Selected Team, Team and Distance headers & axis (right click pill > uncheck show header). Then remove all row/column dividers and gridline, axis rulers etc.

Finally create 2 fields

True

TRUE

False

FALSE

and add these to the Detail shelf (we’ll need them later to ensure the bar doesn’t highlight when we select values).

Creating the match cards

On a new sheet, add Is Selected Team to Filter and set to true.

We need to identify which match is match 1st, 2nd and 3rd. I did this using the dates, but in hindsight could have used the Match field which just contains an unique number per match… anyway…

Match 1 Date

{FIXED [Team_1] : MIN(Date)}

Match 3 Date

{FIXED [Team_1] : MAX(Date)}

Match No for Team

IF [Date] = [Match 1 Date] THEN ‘Match 1’
ELSEIF [Date] = [Match 3 Date] THEN ‘Match 3’
ELSE ‘Match 2’
END

Add Match No for Team to Filter and set to Match 1.

Chang the mark type to shape and set to use a transparent shape (see here for more details on this). Then add Match No for Team, Match, Venue, City (Venues), Distance, Date and Time (Local) to Label. Format the date and time field as you did above.

Create a field

Opponent

IIF([Team_1]=[Team A],[Team B],[Team A])

and add this to Label too. Then adjust the label as required, and align middle left. Don’t show tooltips.

Duplicate this sheet and change the filter to select Match 2 for match 2, and then repeat for Match 3 so you have 3 separate sheets for the matches.

Creating the ‘team’ sheet

On a new sheet, add Is Selected Team to Filter and set to True. Set the mark type to shape and use a transparent shape. Add Team, Distance, Training Site and City to Label and adjust and format accordingly, aligning left middle. Don’t show tooltips.

Building the dashboard and adding the interactivity

I set the background of the dashboard to dark grey and then used layout containers to organise the content, using padding and rounded corners to style as required. The layout of my dashboard is pictured below. It will take some time to get this layout just right, and you might find that in Desktop some text doesn’t display but will when published on Public.

Add a parameter dashboard action to change the team when the bar chart is clicked on

Set Team

on select of the bar chart sheet, set the pTeam parameter with the value from the Team field.

To prevent the selected bar from being ‘highlighted’ when clicked, add a dashboard filter action

Deselect Bar

On select of the bar chart on the dashboard, target the bar chart sheet directly, setting fields true = false.

And that should be it.! My published viz is here

Happy vizzin’!

Donna

Can you create a double-sided multi-row Stem-and-Leaf Plot?

After a couple of weeks off due to holiday, it was my turn to set the challenge. When browsing around for inspiration, I came across this stem & leaf Power BI WOW challenge by Meagan Longoria, which in turn was inspired by a Tableau WOW challenge set by Yusuke in 2024.

So I thought it would be fun to go ‘full circle’ and see if I could recreate Meagan’s challenge in Tableau, which builds on Yusuke’s challenge, as this requires the ‘leaves’ to be spread across multiple rows.

Defining the calculations

The data set provided contains a rows uniquely identified by a Row ID, and each row defines a Species of Iris and the Petal length, which is a decimal number in cm.

To build the stem and leaf chart, we need to first identify the Stem and then the Leaf. If the length is 4.6cm for example, then the stem is 4 and the leaf is 6.

Stem

INT([Petal length (cm)])

Format this to a number with 0 dp and move to the ‘dimensions’ section of the data pane (above the line).

Leaf

INT(ROUND(([Petal length (cm)] – [Stem])*10,0))

Again, format this to a number with 0 dp and move to the ‘dimensions’ section of the data pane (above the line).

Note – Originally my function was INT(([Petal length (cm)] – [Stem])*10), but I found in some occasions this wasn’t given me the right values eg 4.1 was reporting a Leaf of 0, due to the precision of the original number stored. Using the ROUND function to convert the number to have 0 dp resolved this.

Put all these fields into a table like below, so we can start to sense check the other calculations we’ll need.

The final chart will plot the ‘leaves’ as points on an X and Y axis. The central ‘spine’ of the chart is where X=0, and the leaves are the plotted with the position based on the Stem value and then which row and column the leaf is in. Records associated to the Iris-versicolor Species will be plotted on the left side (negatives) while the Iris-virginica Species will be plotted to the right (positives).

The leaves need to organised into rows of 10 per Stem, per Species, sorted by the Leaf value (smallest first). To manage this, we first need to understand how many leaves are associated with each Stem and Species, and give them a ‘counter’ (ie index) per Stem/Species cohort.

Leaf Index per Stem

/*
For each stem per species, index the leaves from 1 to however many there are in the cohort.
*/

INDEX()

Add this into the table, and adjust the table calculation so it is computing by Leaf and Row ID only and add a Custom Sort by Leaf ascending

We now want to identify which ‘row’ (per stem) the leaf will sit on based on it’s index number. If there’s more than 10 leaves per stem, then we need to plot on multiple rows, where the row count starts at 1. Dividing Leaf Index per Stem by 10 will help us do this, but as all leaves indexed from 1-10 need to be on the 1st row, we need to subtract 1 from the index before we divide. We can then convert to a whole number with the INT function, but as we want rows to start at 1, we then need to increment.

Leaf Row Number

INT(([Leaf Index per Stem]-1)/10) + 1

Eg

Leaf Index = 4 -> subtract 1 = 3 -> divide by 10 = 0.3 -> apply INT function = 0 -> add 1 = row 1

Leaf Index = 10 -> subtract 1 = 9 -> divide by 10 = 0.9 -> apply INT function = 0 -> add 1 = row 1

Leaf Index = 11 -> subtract 1 = 10 -> divide by 10 = 1.0 -> apply INT function = 1 -> add 1 = row 2

Add this into the table and apply/verify the table calculation settings are as above.

We then want to identify which column each leaf should be in, which should be a number between 1 and 10 for Iris-virginica and -1 to -10 for Iris-versicolor. We can use the modulo (%10) function for this, based on the Leaf Index per Stem value, to find the remainder if the index is divided by 10. Similarly to before, as all leaves indexed from 1-10 need to be in the equivalent numbered column, we first need to subtract 1 from the index before we find the remainder, but then we need to add 1 to the final result, to get the desired result.

Leaf Column Number

(([Leaf Index per Stem] -1)%10)+1

Eg

Leaf Index = 4 -> subtract 1 = 3 -> %10 =3 -> add 1 = column 4

Leaf Index = 10 -> subtract 1 = 9 -> %10 = 9 -> add 1 = column 10

Leaf Index = 11 -> subtract 1 = 10 -> %10 = 0 -> add 1 = column 1


Add this into the table and apply/verify the table calculation settings are as above.

Now we know the Stem and the leaf row and column position, we can define the actual X and Y points for each leaf.

X Axis

FLOAT(IIF(MIN([Species])=’Iris-versicolor’, -1 * [Leaf Column Number], [Leaf Column Number]))

This is essentially just taking the Leaf Column Number and making it negative for the Iris-versicolor Species. We’ve wrapped it in a FLOAT to make the number decimal, as we need the axis to be able to handle decimal values later on.

For the Y Axis, we’re going to plot the leaves on rows at intervals of 0.2 related to the stem position

Y Axis

MIN([Stem]) + (0.2 * [Leaf Row Number])

Add these into the table and apply/verify the table calculation settings are as above.

Building the viz

Now we have the core data we need, we can start to build the viz

On a new sheet, add Species, Row ID, Stem and Leaf to Detail. Then add X Axis to Columns and Y Axis to Rows. Adjust the tableau calculation settings as before (remembering to apply the custom sort too!)

Change the mark type to circle. Add Species to Colour and adjust as required, adding a coloured border. Set the sheet to Entire View. Increase the Size a bit, then move Leaf from Detail to Text. Align middle centre and bold and allow labels to overlap. Reverse the Y Axis.

Fix the Y-Axis from 2.5 to 7 and set the tick marks to occur at intervals of 1.

Format Petal length (cm) to be a number with 1 dp, then add to Tooltip and adjust accordingly.

Set the background of the worksheet to pale blue. Remove column gridlines. Set the row gridlines to be a pale blue. Remove zero lines, axis rules & tick marks.

To plot the Stem value, double click into the Columns and manually type MIN(0.0) to create a second axis. Remove all the fields except Stem from the marks card of this axis. Change the mark type to shape and use a transparent shape. Move Stem onto Text and align middle centre and increase font size and make it bold. Clear all the text from the Tooltip associated to this mark.

Make the chart dual axis and synchronise the axis. Then add a Reference Band to the X-Axis which plots at -0.5 to 0.5, formatted with a blue line and a pale yellow fill.

Finally, hide all the axes (uncheck show header) and remove row & column dividers.

Add to a dashboard, using containers to organise the content. Use a horizontal container above the main viz to add text fields to label the parts of the chart. Ensure the chart specific title, the label headings and the chart itself are in a vertical container which can then have formatting applied (border/ curved edges etc). My dashboard item hierarchy is shown below

My published viz is here.

Happy vizzin’!

Donna

Fun with Gantt Charts

Sean used some Super Bowl data for this week’s #WOW2026 challenge , using Gantt charts to display ‘arrowed bars’.

Creating the calculations

It took me a little bit of time to understand what I needed from the data to create the required visualisation, so let’s tackle that first.

We need to know the Season and the number of the Super Bowl in roman numerals. The Super Bowl field has this info as it displays <Super Bowl Number as integer> (<year>) ie 4 (1970) – the 4th Super Bowl for Season 1970.

To quickly generate the data I needed, I used the Split function on the Super Bowl. field (Right click > Transform > Split). This automagically generates fields Super Bowl – Split 1 containing the numeric number and Super Bowl – Split 2 containing the year.

I simply renamed Super Bowl – Split 2 to Season, but for completeness, this is what the calculated field looks like

Season

TRIM( SPLIT( SPLIT( [Super Bowl], “(“, 2 ), “)”, 1 ) )

I then renamed Super Bowl – Split 1 to SB Number (int) and changed the data type from string to whole number. Again the field looks like

SB Number (int)

INT(TRIM( SPLIT( [Super Bowl], “(“, 1 ) ))

But I want the number in the format SB + roman numerals. A quick search on the web, and I found the required logic needed to make the conversion, so I created

SB #

“SB ” +
// Thousands Place
CASE INT([SB Number (int)] / 1000)
WHEN 1 THEN “M” WHEN 2 THEN “MM” WHEN 3 THEN “MMM” ELSE “”
END +

// Hundreds Place
CASE INT(([SB Number (int)] % 1000) / 100)
WHEN 1 THEN “C” WHEN 2 THEN “CC” WHEN 3 THEN “CCC” WHEN 4 THEN “CD”
WHEN 5 THEN “D” WHEN 6 THEN “DC” WHEN 7 THEN “DCC” WHEN 8 THEN “DCCC”
WHEN 9 THEN “CM” ELSE “”
END +

// Tens Place
CASE INT(([SB Number (int)] % 100) / 10)
WHEN 1 THEN “X” WHEN 2 THEN “XX” WHEN 3 THEN “XXX” WHEN 4 THEN “XL”
WHEN 5 THEN “L” WHEN 6 THEN “LX” WHEN 7 THEN “LXX” WHEN 8 THEN “LXXX”
WHEN 9 THEN “XC” ELSE “”
END +

// Ones Place
CASE INT([SB Number (int)] % 10)
WHEN 1 THEN “I” WHEN 2 THEN “II” WHEN 3 THEN “III” WHEN 4 THEN “IV”
WHEN 5 THEN “V” WHEN 6 THEN “VI” WHEN 7 THEN “VII” WHEN 8 THEN “VIII”
WHEN 9 THEN “IX” ELSE “”
END

Let’s add the fields we need into the table; add Season and SB# into Rows and sort Season to be listed descending. Then add O/U Line to Rows as a discrete dimension (blue dis-aggregated pill).

Note – I added the results of the 2026 Super Bowl into the provided data set.

We need to compare the O/U Line number with the combined score of the match. The score can be found in the Final field which has info of the winning team and score eg for PHI 40-22, the combined score is 40+22 = 62. I again used the Split functionality to extract the data needed.

The automatically created field Final – Split 2 contains the 2nd half of the score. I renamed this and changed the data type to a whole number

Result Part 2

INT(TRIM( SPLIT( [Final], “-“, 2 ) ))

I then ‘split’ Final – Split 1 again. The automatically created field Final – Split 1 – Split 2 contains the 1st half of the score. I again renamed and changed data type to int.

Result Part 1

INT( SPLIT( [Final – Split 1], ” “, 2 ) )

I then created

Combined Score

[Result Part 1] + [Result Part 2]

and

O/U Difference

[Combined Score]-[O/U Line]

Pop these onto the table along with the Result field and we have all the fields we need to build the viz

Building the viz

ON a new sheet, again add add Season and SB# into Rows and sort Season to be listed descending. Then add O/U Line to Rows as a discrete dimension (blue dis-aggregated pill). Add Combined Score to Columns and change the mark type to shape. Add Result to Shape and set the filled arrows shapes accordingly. And add Result to Colour and adjust that too.

Add O/U Line to Columns which will create a 2nd marks card. Click on this marks card, and change the mark type to Gantt Bar. Remove the Result pill that is now listed on the Detail shelf (keep the one on Colour). Add O/U Difference to the Size shelf, and then click on the Size shelf, and reduce the width a little.

Make the chart dual axis and synchronise the axis. Remove the Measure Values pill from the colour shelf of the All marks card.

Now add O/U Difference to Columns and change the mark type to bar. Remove the Result pill from this card. Adjust the colour and reduce the Size.

In the solution, these bars have rounded ends, so add another instance of O/U Difference to Columns. Change the mark type to Circle, and set the Colour to match the bars. Make the char dual axis and synchronise axes. Adjust the size of the circle and/or bar marks, so the circle makes the bar look like it has a rounded end.

And that’s the crux of the challenge. Formatting changes include

  • Set background colour of worksheet
  • Remove all gridlines, zero lines, axis rules/tick marks
  • Remove all row & column dividers
  • Add row banding
  • Add Season to Filter and exclude 1967
  • Adjust axis titles
  • Adjust formatting style of the Season, SB #, O/U Line values
  • Add title and description

And then add to a dashboard, and you’re all set. My published viz is here.

Happy vizzin’!

Donna

Can you visualise customer spend insights?

Lorna set this week’s challenge inspired by a challenge first set in 2018 that was a ‘combo’ challenge with Prep : use Prep to generate the data set, then visualise. In this instance, we’re doing all the data calculations in Desktop itself (and adding on a few extra too).

Building out the calculations

We need to find the first purchase date per customer, so we use a FIXED LOD for this (we’ll be using a lot of these 🙂 )

First Purchase Date

{FIXED [Customer ID]: MIN([Order Date])}

We then want, for each customer, then next purchase date, which is the earliest order date, where the date is after the first purchase date

Second Purchase Date

{FIXED [Customer ID]: MIN(IF [Order Date]>[First Purchase Date] THEN [Order Date] END)}

With both these fields we can then get, for each customer, their

First Purchase Sales

{FIXED [Customer ID]: SUM(IF [Order Date] = [First Purchase Date] THEN [Sales] END)}

and their

Second Purchase Sales

{FIXED [Customer ID]: SUM(IF [Order Date] = [Second Purchase Date] THEN [Sales] END)}

and we can get the difference between these

Difference Between Purchases

ABS(SUM([First Purchase Sales]) – SUM([Second Purchase Sales]))

and an indicator about which purchase is higher

Purchase Diff

IF SUM([First Purchase Sales]) >= SUM([Second Purchase Sales]) THEN ‘First Purchase Higher than Second Purchase’
ELSE ‘Second Purchase Higher than First Purchase’
END

Let’s put all this into a table

To determine the type of outlier, we need more fields. We need to get a value for the average of all the First Purchase Sales

Avg First Purchase Order Value

{AVG([First Purchase Sales])}

note this is a short notation for {FIXED:AVG([First Purchase Sales])} and is an instruction to average across the whole data set, as we want a single value that is the same for all the rows of data. We also need

Avg Second Purchase Order Value

{AVG([Second Purchase Sales])}

Pop these into the table too

and we need to know what the standard deviation is for each value, which again are essentially ‘a constant’ across the whole data set

First Purchase Std

{FIXED :STDEV([First Purchase Sales])}

and

Second Purchase Std

{FIXED :STDEV([Second Purchase Sales])}

And now we can determine the outlier type for each customer

Outlier Type

IF SUM([First Purchase Sales])>=10000 OR SUM([Second Purchase Sales]) >= 10000 THEN ‘High Purchase Outlier’
ELSEIF SUM([First Purchase Sales])> (SUM([Avg First Purchase Order Value])+ (3*MIN([First Purchase Std]))) AND SUM([Second Purchase Sales]) > (SUM([Avg Second Purchase Order Value]) + (3*MIN([Second Purchase Std]))) THEN ‘Outlier’
ELSEIF SUM([First Purchase Sales])> (SUM([Avg First Purchase Order Value])+ (3*MIN([First Purchase Std]))) THEN ‘First Purchase 3STD Outlier’ ELSEIF SUM([Second Purchase Sales]) > (SUM([Avg Second Purchase Order Value]) + (3*MIN([Second Purchase Std]))) THEN ‘Second Purchase 3STD Outlier’
ELSE ‘Within Range’
END

Add the Outlier Type into the table and also add to Filter and show the filter, and do some checks on the different types

We’ve got all the data, now we can build.

Building the chart

ON a new sheet, add First Purchase Sales to Columns and Second Purchase Sales tp Rows. Add Customer ID to Detail. Add Purchase Diff to Shape and adjust accordingly. Hide the null indicator

Add Difference Between Purchases to Size and adjust mark size range to suit

Add Outlier Type to Colour, and then add another instance of Purchase Diff to Detail, then click on the ‘detail’ icon to the left of the Purchase Diff pill on the marks card, and change it to Colour, so 2 pills are on the Colour shelf. Adjust colours to suit.

Add Outlier Type to Filter and show the filter and uncheck the High Purchase Outlier option. Then add Customer Name to Tooltip and adjust to suit.

To make the diagonal ‘reference line’, add another instance of First Purchase Sales to Rows. This creates another marks card. Remove all fields from this card, except Customer ID and change the mark type to Line. Remove all text from the Tooltip for this marks card.

Make the chart dual axis and synchronise the axis.

Format the chart by

  • hide the second axis (uncheck show header)
  • set the row and column dividers to not display on the header sections (but to display on the pane sections)
  • Set background of worksheet to grey, but set the pane background to white

And that should be the build

Create dashboard, and use a horizontal layout container. In the right hand side, add the viz. In the left side, add a vertical container and use text objects to display the information, and add the filter control into this section too. Use a blank object to make a vertical divider, and add a border around the ‘parent container’.

And that should be it. My published viz is here.

Happy vizzin’!

Donna

Let’s Visualise TC25! (#TC26 Live Edition)

It’s Tableau Conference week, so this week’s challenge, set by Yoshi, was presented as part of the live session at #TC26. Yoshi set 3 levels of the challenge, to represent session data from TC25. I managed to get through the Basic and Bonus challenges

Building the Basic KPI sheet

After connecting to the data set, create a new field to determine if the session is AI related or not

AI-related

CONTAINS([Title], “AI”) OR CONTAINS([Title],”Agent”) OR CONTAINS(IFNULL([Topic],”),”Agentic Analytics”) OR CONTAINS(IFNULL([Topic],”),”Artificial Intelligence”)

Then create

% Sessions AI Related

SUM(IIF([AI-related],1,0)) / COUNT([sessions.csv])

and format to % with 0 dp.

Add this to the Text field on a new sheet, then adjust the text to include the rest of the words, adjusting the font style and colour as required.

Building the basic calendar sheet

Format Start Time and End Time to custom date format of hh:nn, and format Date as mmm dd

On a new sheet add Date as a discrete exact date (blue pill) to Columns and Start Time as a continuous exact date (green pill) to Rows, and edit the Start Time axis so it is reversed.

Add ID to Detail and change the mark type to circle. Create a new field

Index

INDEX()

and add this to Columns

Add AI-related to Colour and adjust accordingly. The edit the Index table calculation so it is computing by Id and AI-related only and sorted by the min value of AI-related descending, so the AI related sessions are listed first

Format the Start Time axis, so the Scale is formatted as h AM/PM and change the font style to larger, blue and bold

Format row and column dividers in the pane only to be thicker blue lines, with no dividers on the headers

Add thin blue gridlines to the Rows and remove column gridlines and zero lines

Edit the Start Time axis again, and set the Tick Marks to start at 08:00 and display every 2 hours

Remove the axis title. Hide the Index axis (right click, uncheck show header). Format the Date header fields (large, blue, bold) and remove the Date label (right click – hide field labels for columns).

Add Title, Description, Start Time and End Time to Tooltip and adjust accordingly.

Add both sheets onto a dashboard, and add an additional Text object to contain the additional explanation.

Building the Bonus KPI sheet

Create a new field

Session Duration

DATEDIFF(‘minute’, [Start Time], [End Time])

Then create another field

% Duration AI related

SUM(IIF([AI-related],[Session Duration],0)) / SUM([Session Duration])

and format to % with 0dp.

As before, add this to the Text field on a new sheet and adjust text to contain the additional wording and formatting.

Building the Bonus Calendar Sheet

Start by duplicating the basic calendar sheet. Add Session Duration to Size.

The sessions need to be sorted by the longest durations first, while still be grouped with AI -related sessions listed before non AI session. Create a new field

Sort

[Session Duration] + (INT([AI-related])*100)

I’m basically creating a numeric value to sort by, based on the duration. adding 100 if the session is AI related, ensuring the Sort value for even the shortest AI session (20 mins) will be larger than the longest non AI session (90 mins).

Adjust the Sort property on the Index field table calculation, to now sort by the minimum of Sort descending

Adjust the tooltip to include the Session Duration, and you should be done.

Duplicate your basic dashboard, then use the ‘object swap’ feature to change the 2 vizzes

And that should be it.

My published versions are here

Happy vizzin’!

Donna

Can you build a radar chart with viz extensions?

For this week’s challenge, Kyle took the challenge I posted the previous week about building a radar chart with map layers, and used a viz extension to build the chart instead – a stroke of genius!

If you want to use this method within your organisation for use on Tableau Cloud or Tableau Server, you’ll need to factor in licensing costs for the extension, and you also might need to agree access with your security team. The map layer solution I blogged about here, works as part of the product itself.

After connecting to the dataset (I used the Engagement Survey Data – Filled sheet), click Add Extension from the marks card dropdown

search for radar in the resulting Add an Extension dialog, and then select the Radar option by LaDataViz. and then select Open on the resulting screen

The presented sheet will give some prompts to get started, but due to the requirements of this challenge, we need to build some calculations first.

The user need to be able to select the business function (Area) to display, so right click Area and then Create Parameter to create

pSelectArea

string parameter defaulted to Whole Company. As the parameter is created from a field, the list of available options is pre-populated. Delete the Benchmark entry from this list.

We only want the selected Area and the ‘Benchmark’ option to display on the chart, so create

Area to Display

[Area]=’Benchmark’ OR [Area] = [pSelectArea]

Add this the Filter shelf and set to True

The chart displays the ‘Benchmark’ as an area bounded by a dashed line. This is achieved by setting the Benchmark value as a target, so we need to define that value in its own calculation

Target

IF [Area]=’Benchmark’ THEN [Value] END

set this to % with 0 dp.

We also want to isolate the values associated with the selected area too

Selected Value

IF [Area] = [pSelectArea] THEN [Value] END

set this to % with 0 dp too.

Add Themes to the Spokes shelf, Selected Value to the Values shelf and Target to the Target shelf

We’ve got all the info we need, now just to format the display, so select Format Extension and apply the following properties

Radar tab

  • Line Style = Linear
  • Fill opacity = 10
  • Range: uncheck auto, set min =0, max = 1
  • Marker = None
  • Expand the Target section, and set Fill Opacity = 2

Grid tab

  • #Lines = 10
  • Spacing = 0
  • Style = Straight

Labels tab

Spokes section : Orientation = Horizontal

Labels section : check the Labels checkbox, Font style = bold

Number format : Style = Percentage

Expand the Axis section, check the Axis Labels checkbox,

Line: uncheck the Show Line checkbox

Number Format : style = percentage

Finally, adjust the Tooltip, and then that should be it – just need to add to dashboard and show the pSelectArea parameter

My published viz is here.

Happy vizzin’!

Donna

Mekko charts & Offset Labels

This week’s WOW challenge for community month, saw Sean partner with Tableau Visionary and ‘Hall of Famer’, Linsdsay Betzendahl to deliver this double challenge – creating a Mekko chart with staggered labels AND a stacked bar chart with staggered labels.

There was quite a lot going on in this challenge, and I had utilised all the concepts in the past, but did still need a refresher to remind how to get things done. So I did draw on the links provided in the challenge, as well as blogs I’ve written for other WOW challenges, specifically

Modelling the data

The link in the challenge downloaded a CSV file to connect to. Once connected, the instruction was to pivot the data, so all the ‘types of death’ columns were transposed from rows into columns. In the data source pane, multi-select all the ‘Total Dead xxxx’ columns then right-click and select pivot

I then renamed the Pivot Field Names column as Disaster Type Raw and Pivot Values as Decadal Deaths.

In the Data pane, I then created a group against the Disaster Type Raw field, called Disaster Type. Every value was grouped, even if it only contained a single entry, just so I could display the name of the disaster in the way that matched the solution

Building the calcs for the Mekko chart

A Mekko chart is based on % – in this instance, the height of the chart is split based on the % of deaths by type per decade, while the width is based on the % of total deaths per decade. For this we’ll need some table calculations, so building out a tabular view to check the values is always something I find very valuable.

Change the Year field to be discrete, then add to Columns. Add Disaster Type to Rows and Entity to Filter and restrict to World. Add Decadal Deaths to Text.

To get the % of deaths by type per year, create

% Decadal Deaths

SUM([Decadal Deaths]) / TOTAL(SUM([Decadal Deaths]))

Add this into the table and then adjust the table calculation so it is computing by Disaster Type only, which essentially means based on the layout of this table view, that each column should be adding up to 100%

This is giving us the value we need to plot on the Y-Axis of the chart (ie the height)

To determine the value to plot on the X-Axis, we need several calculations. First we need to know the total deaths per decade

Total Deaths per Decade

{FIXED [Year],[Entity]:SUM([Decadal Deaths])}

Add this into the table. The value should be the same for every row for the same year.

Then we need to know what this value is as a % of all the deaths across all the decades

% Total Deaths per Decade

SUM([Total Deaths per Decade]) / TOTAL(SUM([Total Deaths per Decade]))

Add this into the table, and adjust the table calculation so that it is computing by Year, which means the values per row should add up to 100%

To build a Marimekko chart, which is a variable width chart, we need to use the cumulative value of this %, so we plot points from the start up to 100%. So we need

Running Sum % Total Deaths per Decade

RUNNING_SUM([% Total Deaths Per Decade])

Add this into the table, verify the table calc is computing explicitly by Year as described above for both the nested calculations, and we should see the values increasing up to 100%

Now typically this would be enough for the chart and is what is mentioned in the referenced blog posts for building these charts. However, due to the staggered labelling requirement, we need to go a step further, as the value we plot on the X-Axis needs to be used for both the Mekko chart and the ‘pointers’ associated to the staggered labels. As these labels are centred, we need to plot our point midway

Mekko – Plot Point X Axis

[Running Sum % Total Deaths Per Decade] – ([% Total Deaths Per Decade]/2)

ie take the running sum value, but subtract half of the % value for the decade.

As before add to the table, setting all nested table calculations to be computing by Year.

Now we have all this, we can start to build out the chart

Building the Mekko chart

On a new sheet, add Entity to Filter and restrict to World. Add Year to Detail and Disaster Type to Colour, and adjust colours to suit.

Then add Mekko – Plot Point X-Axis to Columns. Adjust the table calc, so both next calculations are computing by Year only. Then add % Decadal Deaths to Rows and adjust the table calc to be computing by Disaster Type only. Change the mark type to bar and adjust the sort on the Disaster Type field to sort by field Decadal Deaths ascending

Then add % Total Deaths per Decade to Size. Adjust the table calc so it is computing by Year only. Then click on Size and change the option from Manual to Fixed and align Centre, so the width of the bar is stretched equally, either side of the central plot point. Add a white border via the Colour shelf.

Add Decadal Deaths and Total Deaths per Decade to Tooltip and update as required.

Add Disaster Type to Label, then adjust the Label settings so the label is only displayed based on the maximum value of the Year field, and match the mark colour.

At this point, while the labels are displayed, they aren’t in the required location. To resolve this, start by extending the X axis to be fixed to end at 1.2 (right click axis > Edit)

This extends the axis, giving some ‘space’ in the chart for the labels. Trying to adjust the alignment of the labels doesn’t work though, so move them manually. Click on the label so the cursor changes to a cross, and then drag the label to the side to reposition it.

Do this for all the labels displayed in the solution, doing your best to align them vertically. Once done, then in the label settings, uncheck the ‘allow labels to overlap other marks’ option, so the other values disappear.

Now we have the Mekko chart, we now need to handle the staggered labelling.

Mekko – Label Position YAxis

IF INDEX()%2 = 1 THEN -0.1 ELSE -0.3 END

We’ll index the year from 1 to 13, and every odd number index will have the label positioned at -0.1 and even at -0.3

Add this to Rows which will create a 2nd marks card. Remove all the fields except Year and Total Deaths per Decade from the card.

Adjust the table calc to explicitly compute by Year, and then change the Size from Fixed to Manual and reduce the slider to as small as possible. You should now have your label ‘pointers’

Move the Year and Total Deaths per Decade pills onto the Label shelf, then set the Label settings, so All labels are shown, they can overlap other marks, and adjust the alignment, so they’re not skewed (you may need to find you have to click the sideways position first before you can get the alignment working)

Adjust the formatting of the label, then edit the Mekko – Label Position Y Axis axis so that it is fixed to start at -0.5, and the title and tick marks are removed, so the axis looks to disappear.

Finally tidy up by removing all gridlines, row and column dividers and hiding the Mekko – Plot Point X Axis axis.

Add to a dashboard, and include the requested citation to the data source. I added this within a floating text box with a show/hide button.

Building the calcs for the Bar chart

The bar chart employs similar principals as the Mekko chart, we need to calculate a position to plot the point on the X-axis, but the bar has a fixed height and not split. Let’s start with getting the calcs we need again.

Build a table with Entity on Filter (filtered to World), Year on Rows and Disaster Type on Columns. Add Decadel Deaths onto Text and then add % Decadel Deaths into the table, and adjust the table calc so it is computing by Disaster Type. Due to the layout of this table, the values in each row now add up to 100%.

The % Decadal Deaths defines the ‘width’ of each segment in the bar chart, so one again we need a cumulative value to help determine the plot point.

Running Sum % Decadal Deaths

RUNNING_SUM([% Decadal Deaths])

Add to the table and ensure all the table calcs for the nested calcs are all computing by Disaster Type.

And as before, as we need a ‘central’ point to plot, create

Bar – Plot Point X Axis

[Running Sum % Decadal Deaths] – ([% Decadal Deaths]/2)

Add to the table and ensure all the table calcs for the nested calcs are all computing by Disaster Type.

There is an additional requirement for this chart, to restrict what is displayed based on the Year selected. Create a parameter based off of the Year field

pYear

integer parameter, defaulted to 2020 (formatted without thousand separators), that lists the values

Then create a field

Show Last 3 Decades

[Year]>= [pYear]-20 AND [Year]<=[pYear]

and add to the Filter shelf and set to True.

For the label pointer position, we also need a similar field as before, but only want labels to show when the % is over 4%, so create

Bar – Label Position Y Axis

IF [% Decadal Deaths] > 0.04 THEN
IF INDEX()%2 = 1 THEN -0.1 ELSE -0.3 END
ELSE NULL
END

format to show 2dp, then add to the table, and ensure all table calcs are computing by Disaster Type.

Values will only display if the % Decadal Deaths value is >4%, and the value alternates based on the index position.

Building the bar chart

The steps are very similar to the Mekko chart, except this time, we need the Year dimension on Rows and we can ‘hard code’ a ‘y-axis’ by double clicking into Rows and typing MIN(0.5). This axis is then fixed from 0 to 1 to provide some spacing. Bar – Plot Point X Axis is on Columns and % Decadal Deaths is on Size with the size once again Fixed and centred. This time to Disaster Type field is sorted by Decadal Deaths descending.

Add Bar – Label Position Y Axis to Rows to create the 2nd marks card, and again apply similar steps as above to end up with labelled pointers. I set the axis of this measure to be fixed from -1

The you just need to tidy up the formatting by removing the gridlines, axis lines, row/column dividers and hiding the various axis. Then add to a dashboard and show the parameter.

My published vizzes are here:

Happy vizzin’!

Donna

Can you calculate the correct metric?

For this week’s challenge we’re using the Superstore data set to simple averages with weighted averages

Connect to the data and on a new sheet add Order Date to Filter and select the last 3 months in your data set (I had the 2025.3 instance of Superstore, so the data was filtered to Oct, Nov & Dec 2025.

Simple Average

In the data we have, a single Order ID can have multiple order lines (ie Products), and the quantity of each product can differ. Different discounts can be applied to each product. In the example below, we are at the lowest ‘grain’ of the data, ie each row in the table is equivalent to a single row in the raw data set. As a result the SUMs and the AVG values are identical. This order has 5 order lines. (Note, the Discount field has been formatted to a % with 1 dp).

If this data is summarised just at the Order ID level, the SUM and AVG differ, as you would expect.

The AVG([Discount]) is automatically being computed by Tableau as SUM(Discount) / number of rows associated to the Order ID, which is 280/5 = 56%.

This is the ‘simple’ average and is taking no account of the Quantity associated to each order line.

Weighted Average

We need to get the average based on the fact that in this example, there are actually 26 ‘items’ ordered across the 5 order lines, so we need the following

Weighted Avg

SUM(([Discount]) * ([Quantity])) / SUM([Quantity])

At the raw line level, the Discount is being multiplied by the Quantity (so a weighting is being applied per line as the quantities and discounts can vary). This is then being aggregated (ie summed up) and then the average is determined by dividing by the total number of items ordered. So based on the example we have (2*0.2) + (3*0.8) + (5*0.8) + (9*0.8) + (7*0.2) = 15.4 which is then divided by 26 = 0.592 or 59.2%

Building the Viz

Add Discount to Columns and change the aggregation to Average. Then add Quantity to Rows and change that aggregation to Average too. Add Order ID to Detail. Change the mark type to circle.

Create a new field

Difference from correct metric

ABS([Weighted Avg] – AVG([Discount]))

Add to Tooltip and then create

Is difference?

ROUND([Difference from correct metric],3)<>0

note – I am rounding here as I was getting some anomalies to Erica’s solution due to very very small differences

Add to Colour and adjust to suit. Reduce the opacity and size as desired. Add Discount to Label (aggregated to Average). Set the Label to display only when highlighted.

Add Difference from correct metric to Filter

Remove row/column dividers and adjust Tooltip as required.

Name the viz Simple Avg or similar and then duplicate the sheet. Replace the AVG(Discount) pills on the Label and Columns shelf with the Weighted Avg pill. Adjust tooltip and rename axis title. Name this sheet Weighted Avg or similar.

Creating the Viz in Tooltip

On a new sheet, add Order ID, Category to Rows and then Quantity and Discount into the table aggregated at the Average level. Remove row banding and column dividers and adjust row dividers. Name the sheet Order Details or similar.

On the Simple Avg sheet, edit the Tooltip and insert the Order Details sheet, adjusting the width if required.

Repeat on the Weight Avg sheet.

Creating the Dashboard

Using layout containers, create the desired arrangement. My hierarchy is below

I used blank objects with a grey background to create the horizontal and vertical ‘lines’, using outer padding to help. Here’s an example of the top horizontal line. The height of the object is 134 pixels, but I’ve added 130 px top padding, making the ‘line’ 4px wide.

Add a highlight dashboard action that applies when a mark is selected

And that should then be done! My published viz is here.

Happy vizzin’!

Donna

Let’s Viz Tsukurima Show!

This week’s #WOW2026 challenge was inspired by the annual viz challenge held at the Japanese TUG. It took me a bit longer than 10 minutes to build though!

I’ve built this using 3 sheets – a sheet for the section of the calendar above the displayed bar chart, the bar chart and then the section of the calendar below the bar chart.

Building the Calendar Sheets

On a new sheet, add Order Date at the month-year level to Filter and select December 2025. Then add Order Date at the Weekday level to Columns and Order Date at the Week (number) level to Rows. Additionally add Order Date at the month-year level to Columns as a discrete (blue) pill positioned first.

Create a parameter

pSelectedDate

date parameter defaulted to 10 Dec 2025

and show this parameter on the view.

Create a new field

Is Selected Date

[pSelectedDate] = [Order Date]

Change the mark type to circle then add Is Selected Date to colour. Adjust colours to suit and add a dark border to the circle. Set the sheet to entire view. Add Order Date at the Day level to Label and align middle centre.

We want the calendar to show ‘no selected date’, so will utilise 01 Jan 1900 as a ‘null’ date. We also want the calendar to only show the dates related to the week of the day selected and any previous in the month. So create

Show Top

[pSelectedDate] = #1900-01-01# OR WEEK([Order Date])<= WEEK([pSelectedDate])

and add to the Filter shelf and set to True

We also need all the days of the week to display, even if there aren’t sales on those dates. Eg, set pSelectedDate to 03/12/2025 and we lose the ‘Sunday’ column.

To resolve this, select the Show Empty Columns from the Analysis > Table Layout menu

Adjust Tooltip to suit.

Hide the Order Date column heading (right click > hide field labels for columns). Adjust the format (font size) of the other labels. Hide the WEEK(Order Date) row heading (right click, uncheck Show Header). Hide row & column dividers.

Create fields

True

TRUE

False

FALSE

and

Date for Param

IF [Order Date] = [pSelectedDate] THEN #1900-01-01# ELSE [Order Date] END

Add all these to the Detail shelf, setting the Date for Param field to be a discrete (blue) exact date.. They’ll be needed for the interactivity later.

Name this sheet Top or similar, then duplicate the sheet and change the Show Top filter to be False. Then hide the header labels (uncheck Show Header). Remove the True & False fields form the Detail shelf. Name this sheet Bottom or similar.

Building the bar chart

On a new sheet add Is Selected Date to the Filter shelf and set to True. Add Sales to Columns and Region to Colour and add Region to Label. Expand the width to make the bar wider.

Add Order Date to Tooltip and adjust Tooltip to suit. Set the background colour of the worksheet to dark grey, then format axis to have white font. Remove gridlines.

Building the dashboard

Use a vertical container, add the Top sheet and then then Bottom sheet underneath. Remove all padding from these sheets, and remove the title

Then add a horizontal container between the 2 calendar sheets. Add a text object and adjust text as required and reference the pSelectedDate parameter. Then add an image object, and select the image you hopefully downloaded from the link in the challenge page. Then add the bar chart, removing the title and setting to fit entire view.

Remove all padding for each object in this container and set the background colour of the text and image objects to the same dark grey as the bar chart.

Change the colour of the font in the text box, and I set some inner padding on the text object and the bar chart (10 pts all round) just for some extra breathing room. If need be, set the background colour of the bar chart object too.

Change the date in the pSelectedDate parameter and watch the bar chart move

Adding the interactivity

Add a dashboard parameter action

Select Date

on select of the Top or Btm sheets, set the pSelectedDate parameter by passing the value from the Date For Param field.

Also add a dashboard filter action

Unhighlight Top

On select of the Top sheet on the dashboard, target the Top sheet directly, explicitly setting the True field to False. Show all values when selection cleared.

Finally, we need to make the horizontal container disappear when no date is selected (ie the pSelectedDate parameter is set to 01 Jan 1900).

Create a new field in the data set

Is Not Null Date

[pSelectedDate] <> #1900-01-01#

then back on the dashboard, select the whole horizontal container, and from the Layout pane, check the option to control visibility using value, and select the Is Not Null Date field

When the date is 01 Jan 1900, this whole section will now disappear. You should be able to click dates now, and get the functionality as expected.

Once done, you can tidy up the dashboard by removing the container with all the filters and legends, adding a title, and adding some extra padding and background colours as required.

And that should be “Tabutta!”

My published viz is here.

Happy vizzin!

Donna

Can you create a Gauge Chart with Tableau Extensions?

For this week’s challenge, Yoshi got us creating gauge charts using Tableau’s radial viz extension.

Modelling the data

Yoshi provided a version of the Superstore dataset along with a Budget csv. After downloading, I related the 2 files within Desktop using the following relationships pictured below

Building the gauge chart

On a sheet, select the Add an extension option from the mark type dropdown and then select the Radial (by Tableau) option, and click Open on the resulting screen

Add Measure Names to Ring and Measure Values to Angle and then add Measure Names to filter and retain the Sales and Budget options only.

Click Format Extension and then set the options as follows

  • Total angle (degrees) : 180
  • Starting angle (degrees) : 270
  • Ring padding : 30
  • Segment padding : 5
  • Segment labels : Ring and Angle Values
  • Font : size 9
  • Centre size (%) : 50
  • Show centre label: on
  • Automatic font size: on
  • Edit colours and select appropriate colours

Adjust the order of the Sales and Budget pills in the Measure values pane if required

Add Region, Segment, Category and Order Date at the month-year level to the Filter shelf. Set Category to Furniture and Order Date to December 2025.

Format the Measure Values pill on the Angle shelf so that it displays the numbers as $ with 0 dp.

Create a new field

Acheivement %

(SUM([Sales])/SUM([Budget])) / 2

format this to % with 1 dp and add to the Centre shelf. Note, the division by 2 is necessary due to their being 2 measures (and therefore 2 marks) displayed and the number duplicating itself.

Create another field

Tooltip – Achievement %

(SUM([Sales])/SUM([Budget]))

Format this to % with 1 dp and add to the Tooltip shelf along with Sales, Budget and Category. Update the Tooltip to suit. Edit the title to reference the Category field, and then name this sheet Gauge Furniture or similar.

Then duplicate the sheet. Change the Category filter to Office Supplies and name the sheet Gauge – Office. Repeat to create a version for Technology. So you should end up with 3 sheets, one for each Category. Apply the Region, Segment and Order Date filters to be shared across all 3 workbooks.

Building the bar chart

Create a field called

Sales Rank

RANK(SUM([Sales]))

and change it to be discrete (right click > convert to discrete)

Add Category to Columns and Sales Rank to Rows and Sub-Category to Detail. Set the table calculation associated to Sales Rank to be computing by Sub-Category only.

Double click into Columns and manually type MIN(1) to create a ‘fake axis’. Change the mark type to bar and edit the axis to be fixed from 0 to 1. Widen each row slightly. Then move Sub-Category to Label

Then via the Colour shelf, reduce the opacity to 0% and remove the border

Now add Sales to Columns. and on the Sales marks card, move Sub-Category back to Detail, and rest the opacity to 100%

Colour the bars as required. Add Budget to the Detail shelf, then add a reference line to the Sales axis, which shows the Budget per cell as a line with a fill below of light grey.

From one the gauge sheets, set the Region, Segment and Order Date filters to also apply to this sheet.

Add Achievement % to Tooltip and adjust the Tooltip on the Sales marks card only. Remove all the text from the Tooltip on the MIN(1) marks card.

Then format the sheet by

  • editing Sales axis and removing the axis title
  • editing the MIN(1)axis and removing the title and tickmarks
  • hide the sales Rank header (right click pill > uncheck show header
  • hide the Category header
  • remove row & column dividers

Then add the Gauge sheets into a horizontal container on a dashboard, setting the container to ‘distribute contents evenly’. Add the bar chart underneath, but I ‘floated’ it into position as each gauge chart object takes up more vertical space than necessary (the size of the object doesn’t adapt to the fact you’re only showing half a circle).

My published viz is here.

Happy vizzin’!

Donna