It was Sean’s first challenge of 2023, and he asked us to recreate this chart . Sean referred to it in the requirements as a floating bar chart, others may refer to it as a diverging bar chart or likert chart.
Modelling the data
Fake survey data was provided which was structured with each question in a column
The first thing we need to do is to pivot the data, so we have 1 row per question, per respondent. On the data source tab in Tableau Desktop, multi-select the question columns (Ctrl-click), then click the context menu for one of the selected columns and select Pivot
Due to the width of the screen and the number of columns, you may have to do this in multiple steps. Just multi-select the next set of columns, and select Add Data to Pivot from the context menu instead.
Once complete you should have a data set with 3 columns
Rename Pivot Field Names to Question and Pivot Field Values to Response.
Building the calculations
All the questions have 5 options to respond to ranging from Strongly Disagree -> Disagree -> Neutral -> Agree – > Strongly Agree. Sean hinted we would need to group the questions based on their sentiment. Some questions were worded in a positive way, which meant ‘agree’ responses could be taken as a ‘good thing’. While other questions were worded in a negative way, meaning agree’ responses were actually a ‘bad thing’.
So the first thing to do was to group the questions. I used the group functionality (right click on Question -> Create -> Group).
All the questions where I deemed an ‘agree’ answer was a favourable response to the product I grouped under Positive.
All the question where I deemed an ‘agree’ answer was a non-favourable response to the product I grouped under Negative.
I then wanted to bucket the responses into 3 groups – Disagree, Neutral and Agree – based on the combination of the actual Response, and the Sentiment grouping.
IF [Sentiment] = ‘Positive’ THEN
IF CONTAINS([Response],’Disagree’) THEN ‘Disagree’
ELSEIF CONTAINS([Response], ‘Agree’) THEN ‘Agree’
ELSEIF [Sentiment] = ‘Negative’ THEN
IF CONTAINS([Response],’Disagree’) THEN ‘Agree’
ELSEIF CONTAINS([Response], ‘Agree’) THEN ‘Disagree’
With this I am essentially swapping disagree responses against questions of a negative sentiment into the agree/positive grouping and vice versa.
Finally I need another couple of calculations just to sense check were we are
Question – Display
TRIM( SPLIT( [Question], “-“, -1 ) )
This removes the text that precedes the ‘-‘ from the Question string. I actually created this field using the Split functionality. Right click on Question -> Transform -> Custom Split. Use the separator – and split off the Last 1 columns
This will generate the field int he dimensions pane which I then renamed.
If we put all these out into a table, we can see how the data is shaping up
The next step is to work out the NPS values. When I’ve dealt with NPS in the past, I’ve referred to terms such as Promoters and Detractors, with an NPS score being computed as (Number of Promoters – Number of Detractors)/Total No of Respondents.
In this case a Promoter is anyone who is in the agree bucket.
IF [Response Group]=’Agree’ THEN 1 ELSE 0 END
while a Detractor is anyone who is in the disagree bucket
IF [Response Group]=’Disagree’ THEN 1 ELSE 0 END
The total number of respondents is
So I can now calculate
Format this to percentage with 0 dp.
Let’s put this into another table so we can validate the data.
Ok, so this gives us the data required to plot the circle marks.
But we need to work out what’s need to plot the bars. And for this we need to know the respondents who are neither Promoters or Detractors.
IF [Response Group] = ‘Neutral’ THEN 1 ELSE 0 END
We are going to plot 2 bars for each row. 1 bar that represents the proportion of respondents who are in the positive side and 1 bar to represent the proportion of respondents who are in the negative side. The positive side mainly consist of the promoters, while the negative is the detractors. However, we need to consider the neutrals too, and we do this by halving the number and making half positive and half negative. Let’s see what I mean
# Respondents – Positive
(SUM([Promoters]) + (SUM([Neutrals])/2))/SUM([Total Respondents])
format this to percentage with 0 dp.
# Respondents – Negative
-1 * ((SUM([Detractors]) + (SUM([Neutrals])/2))/SUM([Total Respondents]))
format this to percentage with 0 dp.
In this case the result is multipled by -1 as we want the bar to be plotted on the negative side of the axis.
Let’s pop these into the table too.
Ok. So now we have the core building blocks required to start building the viz.
Building the Likert Chart
Add Question – Display to Rows and # Respondents – Positive to Columns.
Click and drag # Respondents – Negative onto the canvas and drop the pill on the bottom axis when you see the 2 green columns icon appear.
This will automatically add the pill onto the same axis, and the view will update to use Measure Names and Measure Values
Remove Measure Names from the Rows and the bars will all appear on the same row. Sort the Question – Display field by NPS descending.
Add Response Group to the Colour shelf and adjust colours accordingly (using the Nuriel Stone palette). I also set the transparency to about 70%. You may need to reorder the values of Response Group – just do this manually in the colour legend if need be.
Increase the width of each row a bit, expand the Question – Display column, and remove the tooltips from displaying. Format the Question – Display column so it is aligned left and the font is a bit bolder/darker. Remove the Question – Display column heading (right click label > hide field labels for rows).
Format the axis to display as percentages to 0 dp, then edit the axis and
- remove the title
- fix the axis from -1.05 to 1.05
- fix the tick marks to display every 0.5
Adjust the row banding to show, and remove any column/row dividers. Remove all gridlines. Adjust the column axis rulers and tick marks to be a solid dark line. Set the zero line for the columns to be a wider solid white line – it should just about be visible through the viz.
Now we need to label the agree and the disagree sections only. For this we need additional fields
Label – Positive
If MIN([Response Group]) = ‘Agree’ THEN [# Respondents – Positive] END
Label – Negative
If MIN([Response Group]) = ‘Disagree’ THEN [# Respondents – Negative] END
Add both these fields to the Label shelf and arrange side by side. Manually adjust the colour of the font to a dark grey.
This is the core design of a likert chart. It could be bult using dual axis too, but that wasn’t an option in this instance, as we need to add the additional NPS circles to the chart.
Adding the NPS circles
Add NPS to Columns. On the NPS marks card, remove all the fields, and change the mark type to circle. Add NPS to the Colour shelf, and adjust the colours so they range from the same yellow/green to blue/teal colours already used. The easiest way to do this is to click on the coloured square at each of the edit colour dialog and use the pick screen colour option to select the colours already used.
Set the colour to be 100% opacity and add a white border around the circles (via the Colour shelf)
Set the chart to be dual axis and synchronise the axis. Change the mark types back to bar and circle accordingly if they changed.
Now we can see some of the labels on the bar chart are positioned where the circles are/very close to them. Manually move those affected – click on the section of the bar to highlight it, then click on the label and when the cursor changes to a crosshatch, drag the label to where you want. Note – this isn’t something I would typically do if the data was to change behind this viz without any manual involvement.
Hide the top axis (uncheck show header) and remove all row/column dividers.
Now we just need to label the circles. I need additional fields for this. Firstly, the NPS field is on the wrong format – it’s actually stored as a decimal, formatted to a percentage. Secondly, I ended up with two label fields, due to the colouring. Sometimes when you add a label to a mark, the colour of the text may display black or white depending on the contrast with the underlying colour. Tableau automatically does this, and it isn’t something you can really control. As soon as you start manually adjusting any colours, it can be hard to get back to the right state. I fiddled around trying to get things to work properly for a while using the auto colouring (which involved rebuilding by starting with the NPS dot plot and then adding the bars), but it still wasn’t a ‘cast-iron’ solution. So I ended up creating
IF [NPS] * 100 <=70 THEN [NPS] *100 END
IF [NPS]*100 >70 THEN [NPS]*100 END
I just chose an arbitrary ‘cut off’ for when the colour might change.
Add both these fields to the Label shelf of the NPS marks card, and arrange them side by side. Edit to font so the Label-NPS-White field is coloured with white font, and the other black. Set the alignment of the text to be middle centre. Adjust the size of the circles if required. Verify tooltips don’t exist on any marks.
Stopping the chart from being clicked
In the solution, if you try to click on the bar chart, you get no interaction – nothing highlights, like it does if you click on the axis or the questions. On a dashboard, this is managed by adding a floating blank object and carefully placing it over the top of the section you don’t want to be clickable.
And that should be it! My published viz is here.
I have to admit I did have a few false starts when building this out, and its highly probable this could have been created without all the fields I ended up with. It’s sometimes just the path your brain follows, and there’s nothing wrong with that!