Power BI: Sales Variance Dashboard
In this tutorial, I will show you how to create a sales variance dashboard in Power BI, which decomposes sales variance in quantity, price and mix variance. In particular, the variance dashboard highlights the main changes between budget and actual and allows you to drill-down on them: capitalize on positive variances and address negative variances.
Below is what the final dashboard looks like: a summary of the variances, then a focus on quantity, price and mix variance.
The components of sales variance: quantity variance, price variance, and mix variance
Sales variance
Sales variance is the total change in sales between budget and actual. In the example below, a sales increase of 1% is explained by a strong and positive price variance of 2%, a flat mix variance and a -1% negative quantity variance.
Next, I will explain each variance in detail. If you are looking for a more in-depth explanation of the variances, please check the resources at www.double-entry-bookkeeping.com. They have a great explanation of sales variance here and the difference between quantity and mix variance here.
Quantity variance
The first reason for a change in sales is a change in the number of units sold; this is known as 'quantity variance'. The calculation is simple, and it's equal to the percentage increase (or decrease) in the units.
In addition, this quantity variance excludes the impact of the change in mix, which is separately analyzed.
In the example below, 122 fewer units are sold in actual compared to budget. This corresponds to a quantity variance of -1%.
Price Variance
The second reason for a change in sales is a change in price: this is called the 'price variance'.
The price variance depends on 2 factors: how much the prices increase and on which product the prices increase. Prices increasing on a product with higher sales, for example, have a bigger impact than prices increasing on a product with lower sales.
In the example below, the price increase in Ford has an impact of 2% on the total company sales. At the same time, the price drops of Suzuki and Acura are compensated by the price increases of Cadillac and Audi.
Mix Variance
The final reason for a change in sales is the change in mix, called the 'mix variance'.
The mix impact of a product is positive when the product is growing faster than the average sales, and negative when the product is growing slower than the averages sales. In the example, total units are dropping at -1% so products growing at a rate faster than -1% have a positive mix variance.
The size of the variance (be it positive or negative) depends then on the product price: the higher the price, the bigger the variance.
In our example, GMC and Acura are growing faster than the average and create a positive effect for 5%. This positive impact is completely voided by the drop in Ford, Land Rover, and Honda. The total mix effect is therefore 0%.
The dashboard also shows the mix variance with the detail of:
– products added (not in budget): 5% such as GMC
– products removed from budget (not in actual): -5% such as Land Rover and Honda
– other products which are in both budget and actual
Creating the sales variance dashboard in Power BI
Load Budget and Actual numbers in Power BI
You can load numbers in Power BI from the 'Get data' icon in the Home tab.
In addition, you can download the sample data for Budget from here and for Actual from here to follow this tutorial.
Create a Power BI calculated table to calculate sales variance
After the data is loaded, we create a table that will do all the necessary variance calculations. You can go to 'Modeling', 'New Table', paste the below code and then click on Commit.
Code for creating the sales variance table
Here is the code for calculating the variances. The explanation is below.
Analysis Table =
// 1- create a table with all products
VAR productTable = DISTINCT ( UNION ( DISTINCT ('Budget'[Product] ), DISTINCT ('Actual'[Product] ) ) )
// 2- add units and sales
VAR addUnitsSales = ADDCOLUMNS (
productTable,
"units actual", CALCULATE ( SUM ('Actual'[Value] ),'Actual'[P&L] = "Units sold",'Actual'[Product] = EARLIER ( [Product] ) ),
"units budget", CALCULATE ( SUM ('Budget'[Value] ),'Budget'[P&L] = "Units sold",'Budget'[Product] = EARLIER ( [Product] ) ),
"sales actual", CALCULATE ( SUM ('Actual'[Value] ),'Actual'[P&L] = "Sales",'Actual'[Product] = EARLIER ( [Product] ) ),
"sales budget", CALCULATE ( SUM ('Budget'[Value] ),'Budget'[P&L] = "Sales",'Budget'[Product] = EARLIER ( [Product] ) )
)
// 3- add price and Mix Type (added product, removed product, other)
VAR addPriceAndMixType = ADDCOLUMNS (
addUnitsSales,
"price actual", [sales actual] / [units actual],
"price budget", [sales budget] / [units budget],
"mix type", IF ( AND ( [sales actual] <> 0, [sales budget] = 0 ), "added",
IF ( AND ( [sales actual] = 0, [sales budget] <> 0 ), "removed", "other" ) ),
"units actual if no change in mix", [units budget] / SUMX ( addUnitsSales, [units budget] ) * SUMX ( addUnitsSales, [units actual] ) )
// 4- add quantity, mix and price variance calculations
VAR addQuantityMixPriceVariance =
ADDCOLUMNS (
addPriceAndMixType,
"quantity variance", ( [units actual if no change in mix] – [units budget] ) * [price budget],
"mix variance", IF ( [mix type] = "added", [sales actual] – [sales budget],
( [units actual] – [units actual if no change in mix] ) * [price budget] ),
"price variance", IF ( [mix type] = "added", 0,
( [price actual] – [price budget] ) * [units actual] ) )
// 5- unpivot the table
VAR unpivotTable =
UNION (
SELECTCOLUMNS ( addQuantityMixPriceVariance, "product", [Product], "mix type", [mix type], "product and mix type", [Product] & " (" & [mix type] & ")", "field", "sales budget", "value", [sales budget]),
SELECTCOLUMNS ( addQuantityMixPriceVariance, "product", [Product], "mix type", [mix type], "product and mix type", [Product] & " (" & [mix type] & ")", "field", "sales actual", "value", [sales actual]),
SELECTCOLUMNS ( addQuantityMixPriceVariance, "product", [Product], "mix type", [mix type], "product and mix type", [Product] & " (" & [mix type] & ")", "field", "sales variation", "value", [sales actual] – [sales budget]),
SELECTCOLUMNS ( addQuantityMixPriceVariance, "product", [Product], "mix type", [mix type], "product and mix type", [Product] & " (" & [mix type] & ")", "field", "units budget", "value", [units budget]),
SELECTCOLUMNS ( addQuantityMixPriceVariance, "product", [Product], "mix type", [mix type], "product and mix type", [Product] & " (" & [mix type] & ")", "field", "units actual", "value", [units actual]),
SELECTCOLUMNS ( addQuantityMixPriceVariance, "product", [Product], "mix type", [mix type], "product and mix type", [Product] & " (" & [mix type] & ")", "field", "units variation", "value", [units actual] – [units budget]),
SELECTCOLUMNS ( addQuantityMixPriceVariance, "product", [Product], "mix type", [mix type], "product and mix type", [Product] & " (" & [mix type] & ")", "field", "quantity variance", "value", [quantity variance]),
SELECTCOLUMNS ( addQuantityMixPriceVariance, "product", [Product], "mix type", [mix type], "product and mix type", [Product] & " (" & [mix type] & ")", "field", "price variance", "value", [price variance]),
SELECTCOLUMNS ( addQuantityMixPriceVariance, "product", [Product], "mix type", [mix type], "product and mix type", [Product] & " (" & [mix type] & ")", "field", "mix variance", "value", [mix variance])
)
// 6- return the unpivot table
RETURN unpivotTable
Code explanation
The first part of the code creates a table with all products.
Part 2 of the code adds units and sales to the products table. Following that, part 3 adds product prices, 'mix type' and the 'units if no change in mix'.
The 'mix type' is:
– 'added' if the product is in actual but not in budget,
– 'removed' if the product is in budget but not in actual
– 'other' for products that are both in actual and budget
The 'units if no change in mix' corresponds to the units in actual if the product mix did not change. As an example, for a product that accounted for 20% of units sold in budget, actual units are recalculated to also account for 20% of total units.
Once all the above information is in the table, part 4 calculates the quantity, price and mix variances. If this calculation is unclear, please refer to an explanation of the price variance here, and quantity and mix variance here.
Finally, step 5 unpivots the tables. Unpivoting is needed for the data to be in the right format for the visualizations. If you want to learn about unpivoting in DAX, you can check the post here.
One last measure
Once the table is ready, you can add a 'percentage variance' measure to be used in the visualization, as below:
Percentage variance =
SUM ( 'Analysis Table'[value] )
/ CALCULATE ( SUM ( 'Analysis Table'[value] ),
'Analysis Table'[field] = "sales budget", ALL ( 'Analysis Table' )
)
That's it!
Enjoyed the article? Subscribe to the newsletter and download the Power BI of the article for free.
2 Comments
Thank you very much for the excellent article on PVM. Would you have different DAX measures for PVM analysis starting with simplified tables like a "Sales Table" and "Product Table" and using prior year for the variance calculation, say using the Microsoft databases like AdventureWorks or Contosso
Hi Chris,
thank you for your kind comment!
Yes, I would very likely approach the problem differently, and create the measures in a different way.
My goal with the articles is to create relatively easy walk-through examples, which you can follow, and then apply them to your data.
Because your data can be in so many different forms, it is unlikely that my example will be the ideal solution for it. The solution of the articles should be a working solution which you can use to start, and then it can be optimized based on your data model.
Does this answer your question?
And regarding this solution specifically, I am not a huge fan of unpivoting in DAX.
Unpivoting in DAX requires a long code, which is not very reader-friendly. In real life, I would likely unpivot in Power Query and not in DAX.
I made the choice to keep this example as much as possible in DAX, so that the reader can focus on DAX and does not need to also understand M code (even if M code can most of the times be automatically generated by Power Query).
I hope that my explanation makes sense!
Feel free to add any comments to it,
Luca