In Part 1 of this series, I introduced one workaround to the issue of getting Grand Totals to show a different value from the Tableau defaults, by using two worksheets – one for the detail rows and one for the Grand Total row – on a dashboard. That method has a few limitations, the biggest being that it can’t handle Subtotals. Here, you’ll learn two additional techniques for customizing Grand Totals and Subtotals in a single worksheet, and their limitations: using MIN() and MAX() to test for the Grand Total row, and using a table calculation with a duplicated dimension.
Thanks to Joe Mako for his support in creating this, he provided the seeds for starting my own explorations into Grand Totals, and also feedback on this post. If you want to boost your Tableau knowledge, sign up for the forums, go to Joe’s user page, and start reading posts he’s has contributed to, there are over 3,500 of them!
For this example, the goal is to get the Grand Total row to show a sum of the average sales per product category, instead of the overall average of the data set, which is the Superstore Sales sample data:
How Grand Totals Work
Tableau does not specifically compute Grand Totals and Subtotals as an aggregation of what is displayed; instead, Tableau is performing the same calculation as the measure, but at a higher level of aggregation. For Grand Totals for a column, Tableau takes out all the dimensions on the Rows Shelf and then performs the calculation. For Grand Totals for a row, Tableau takes out all the dimensions on the Columns Shelf and then performs the calculation. In essence, Tableau is combining two different views of the data set to create the final worksheet, as in this example using AVG(Sales):
Tableau first computes the AVG(Sales) for each Category, then for the Grand Total row it takes Category out and computes AVG(Sales) across the entire data set. You can see what Tableau displays in the Grand Total row for each of the aggregations in the online help. On a more technical level, Tableau attempts to compute the Grand Total from the in-memory data cache. If there’s not enough information available, Tableau revises the query to the data source to include additional fields so Tableau can compute the Grand Total. For example, for AVG(Sales), Tableau will add COUNT(Sales) and SUM(Sales) to the query. You can see this the Tableau log files located in the …\My Tableau Repository\Logs directory.
If we want to gain control over the value for the Grand Total, we have to do the following:
- Identify how and when Tableau is computing the grand total row
- Create the measure that returns the desired value for the Grand Total rows and another value for the data rows. This post offers two different calculations, one using MIN() and MAX() and the second using a table calculation with a duplicated dimension.
Identifying the Grand Total Row
The key to working with the Grand Total row is to consider the dimension(s) that is/are available to the Grand Total computation, as compared to what is available to the computations for the detail rows. In the graphic above, we can see that for the detail rows that there is a row for each Category is being used, whereas in the Grand Total row, there is no Category because it’s been removed from the overall level of detail in the query. Therefore, all members of the Category dimension will be available within query that makes up the Grand Total row.
If that’s confusing, here’s an example to help you out: Here are two views, one with Category and ATTR(Category) on the Rows Shelf, the second with just ATTR(Category) on the Rows Shelf. ATTR() is an aggregation that returns a value for the specified field if there is one and only one value within the partition, otherwise it returns Null (which is displayed as an asterisk “*”). On the top view, you can see that ATTR(Category) returns the correct Category value when Category is on the Rows Shelf because there is only one Category being returned, but ATTR(Category) displays * when Category is no longer a dimension in the view, because all the values of Category are being returned.
These two views duplicate what is happening inside of Tableau when it’s computing a Grand Total row.
Customizing Grand Totals with MIN() and MAX()
We can take advantage of the fact that the dimension(s) used in generating the Detail rows are not used in the query that generates the Grand Total row. In the Grand Total query, all the values of Category – Furniture, Office Supplies, and Technology – are being returned, and Furniture != Technology, so we can use the following formula to identify the Grand Total row:
MIN([Category]) != MAX([Category])
Here’s that test in a calculated field, showing one common request, for the Grand Total to show the sum of all sales while the detail rows show the average of sales:
IF MIN([Category]) != MAX([Category]) THEN //Grand Total result SUM([Sales]) ELSE //detail row result AVG([Sales]) END
You can also filter data within the Grand Total Row, such as if we wanted the Grand Total row to only show results for certain values of Category like Technology:
IF MIN([Category]) != MAX([Category]) THEN AVG(IF ([Category] = "Technology" THEN [Sales] END) ELSE AVG([Sales]) END
Or maybe you just don’t want the Grand Total row to show anything for certain columns in a crosstab, so the evaluation could return Null for the Grand Total row and the desired aggregate for the detail rows:
IF MIN([Category]) != MAX([Category]) THEN Null ELSE AVG([Sales]) END
This last calc could be simplified even further, but for ease of maintenance I like to be explicit about what the calculation is returning.
Using MIN() and MAX() with Text Tables with Measure Names and Measure Values
You can use a whole set of these calculations in a text table using Measure Names and Measure Values in the same view:
Custom Subtotals using MIN() and MAX()
In order to use this with Subtotals, the dimension used for testing the MIN() and MAX() needs to be the one with the right-most pill on the Rows Shelf (for column Grand Totals) or Columns Shelf (for Row Grand totals). For example, in the following view the original test on Category fails in the Subtotals but works in the Grand Total, while the test on Container works for both the Subtotal and Grand Total rows:
Limitations of the MIN() and MAX() Technique
This technique has the following limitations:
- Given that testing for MIN() and MAX() explicitly identifies a dimension, if a user dynamically expands or collapses a hierarchy then this calculation can fail. There are a couple of workarounds: One is to use parameters and calculated fields to control the dimensions in the view and the calculations – see this post at Clearly and Simply for the basics, a second is to use parameters to swap out different views that would be at different levels in the hierarchy – see this post on the Tableau forums for instructions, and a third would be to use a calculated field with nested tests on the different dimensions.
- When using subtotals, if the bottom level of a subtotal dimension has only one member, then the test will fail, since MIN() and MAX() will return the same result. The solution to this is to use Custom SQL, which will be covered in Part 3 of this series.
- This test can only return an aggregate result. The goal for this example is to return the sum of the average of Sales per Category, in other words an aggregate of an aggregate, and in Tableau that means we’ll need to use a table calculation and a different method of computing the Grand Total.
Customizing Grand Totals with Table Calculations
The desired Grand Total result of $6716 is an aggregation (sum) of an aggregation (avg) that is performed in Tableau, so that requires a table calculation:
The AVG([Sales]) requires Category to be in the view so the average can be generated, as in the following view:
The default Compute Using of Table (Down) creates the $6716 result for each detail row for Category, but the Grand Total row is showing $1776. The same occurs when the Compute Using is set to Category. This is a big clue, but there’s a problem: In order to get the Grand Total calc to work, Category needs to be in the overall level of detail at the time Tableau is computing the Grand Total; however, Tableau is removing Category from the view to compute the Grand Total. It’s a conundrum.
Cracking the Category Catch-22
The solution to the problem of Category not being available in the overall level of detail is incredibly simple, while taking one more step to clean up. Duplicate the dimension, and put the duplicate on the Level of Detail Shelf, as in the following view that uses the WINDOW_SUM(AVG([Sales])), with the Compute Using set to Category (copy).
This looks really good, though it’s not quite there:
- What’s happening in the detail rows (the ones for each Category) is that the calculation is being performed for each value of Category (copy), i.e. the Compute Using or addressing, with a new calculation happening for each new value of Category, i.e. the partitioning. Since Category and Category (copy) share the same values, the calculation is performed once for each value and the WINDOW_SUM(AVG([Sales])) just returns the AVG([Sales]).
- For the Grand Total row, Category is removed from the overall level of detail. So the calculation is performed for each value of Category (copy), i.e. the AVG([Sales]) is calculated for each value of Category (copy) and then all the results are summed together. Only this process happens 3 times, once for each value of Category (copy), and we end up with overlapping text in the Grand Total row that looks bold, but really isn’t, since Grand Total rows aren’t bold, as we can see in the initial view:
To fix this, we turn to Richard Leeke’s work on improving table calc performance and reducing the # of rows returned, to create the following formula:
IF FIRST()==0 THEN WINDOW_SUM(AVG([Sales]),0,IIF(FIRST()==0,LAST(),0)) END
Here’s what’s happening under the hood:
Using Table Calcs with Text Tables with Measure Names and Measure Values
When using this technique, if you drag a regular aggregate measure such as SUM([Sales]) into the view, Tableau displays overlapping text for the total because of the copied dimension being in the level of detail:
To fix this, each and every measure needs to be wrapped in a calculated field that has the Compute Using set to the dimension (copy), as in:
IF FIRST()==0 THEN WINDOW_SUM(SUM([Sales]),0,IIF(FIRST()==0,LAST(),0)) END
Custom Subtotals Using Table Calcs
In order to get the subtotals to work at the custom level, you need to add a copy of each dimension to the Level of Detail, then for the calculation set an Advanced… Compute Using that includes all of the copies:
Limitations of the Table Calculation Technique
Using table calculations to generate the custom grand total has the following limitations:
- Just like the MIN() and MAX() technique, the calculation will fail when using subtotals and there is a single value for a dimension in the subtotal. The solution to this is to use Custom SQL, which will be covered in Part 3 of this series.
- Because this technique uses a special Compute Using to generate the totals instead of testing for the Grand Total row, it is not possible to have the custom sub-aggregations like the MIN() and MAX() technique. One workaround is to use Custom SQL, another it to use a more complex table calculation that I might cover later, since there are only rare use cases.
Hopefully, you learned more about ATTR() and aggregation in Tableau, how to remove overlapping text, and, of course, how to generate your own custom values for Grand Totals and Subtotals using two different techniques: using MIN() and MAX(), and a table calculation with a duplicated dimension.
Part 3 of this series describes how to use Custom SQL to have full control over your Grand Totals and how to display text in the Grand Total row.