Explore how common Power BI visual calculations like Running Sum and Moving Average work on a simple dataset. Input a series of values and see the conceptual output and DAX-like syntax.
Running Sum
Moving Average
Calculated Series:
Conceptual DAX Syntax:
Explanation:
function toggleWindowSize() {
var calcType = document.getElementById('calcType').value;
var windowSizeGroup = document.getElementById('windowSizeGroup');
if (calcType === 'movingAverage') {
windowSizeGroup.style.display = 'block';
} else {
windowSizeGroup.style.display = 'none';
}
}
function calculateVisuals() {
var salesValues = [];
for (var i = 1; i <= 5; i++) {
var value = parseFloat(document.getElementById('salesValue' + i).value);
if (!isNaN(value)) {
salesValues.push(value);
}
}
var calcType = document.getElementById('calcType').value;
var windowSize = parseInt(document.getElementById('windowSize').value);
var orderByField = document.getElementById('orderByField').value.trim();
var partitionByField = document.getElementById('partitionByField').value.trim();
var resultOutput = document.getElementById('resultOutput');
var daxSyntaxOutput = document.getElementById('daxSyntaxOutput');
var explanationOutput = document.getElementById('explanationOutput');
resultOutput.innerHTML = '';
daxSyntaxOutput.innerHTML = '';
explanationOutput.innerHTML = '';
if (salesValues.length === 0) {
resultOutput.innerHTML = 'Please enter at least one valid data point.';
return;
}
if (!orderByField) {
resultOutput.innerHTML = 'Please specify an "Order By Field" for the calculation context.';
return;
}
var calculatedSeries = [];
var daxSyntax = '';
var explanation = '';
var measureName = '[MyMeasure]'; // Placeholder for the measure being calculated
if (calcType === 'runningSum') {
var currentSum = 0;
for (var j = 0; j < salesValues.length; j++) {
currentSum += salesValues[j];
calculatedSeries.push(currentSum.toFixed(2));
}
daxSyntax = `[Running Sum ${measureName}] = \n RUNNINGSUM(\n ${measureName},\n ORDERBY(${orderByField})`;
if (partitionByField) {
daxSyntax += `,\n PARTITIONBY(${partitionByField})`;
}
daxSyntax += `\n )`;
explanation = `The Running Sum calculates a cumulative total. Each value in the series is the sum of the current data point and all preceding data points, ordered by the specified field. If a Partition By field is used, the sum resets for each new partition.`;
} else if (calcType === 'movingAverage') {
if (isNaN(windowSize) || windowSize salesValues.length) {
explanationOutput.innerHTML = 'Warning: Window Size is larger than the number of data points. The moving average will be calculated over fewer points for initial values.';
}
for (var k = 0; k < salesValues.length; k++) {
var start = Math.max(0, k – windowSize + 1);
var end = k + 1;
var windowValues = salesValues.slice(start, end);
var sum = windowValues.reduce(function(a, b) { return a + b; }, 0);
var average = sum / windowValues.length;
calculatedSeries.push(average.toFixed(2));
}
daxSyntax = `[Moving Average ${measureName}] = \n MOVINGAVERAGE(\n ${measureName},\n ORDERBY(${orderByField}),\n ${windowSize}`;
if (partitionByField) {
daxSyntax += `,\n PARTITIONBY(${partitionByField})`;
}
daxSyntax += `\n )`;
explanation = `The Moving Average calculates the average of a specified number of data points (the window size) up to and including the current point. It helps smooth out short-term fluctuations and highlight longer-term trends. If a Partition By field is used, the average window resets for each new partition.`;
}
resultOutput.innerHTML = 'Original Data: [' + salesValues.map(function(v) { return v.toFixed(2); }).join(', ') + ']';
resultOutput.innerHTML += 'Calculated Series: [' + calculatedSeries.join(', ') + ']';
daxSyntaxOutput.textContent = daxSyntax;
explanationOutput.innerHTML = explanation;
}
// Initialize the display based on default selection
toggleWindowSize();
.powerbi-visual-calc-calculator {
font-family: Arial, sans-serif;
max-width: 700px;
margin: 20px auto;
padding: 20px;
border: 1px solid #ccc;
border-radius: 8px;
background-color: #f9f9f9;
}
.powerbi-visual-calc-calculator h2 {
color: #2c3e50;
text-align: center;
margin-bottom: 20px;
}
.powerbi-visual-calc-calculator p {
line-height: 1.6;
margin-bottom: 15px;
}
.calc-input-group {
margin-bottom: 15px;
}
.calc-input-group label {
display: block;
margin-bottom: 5px;
font-weight: bold;
color: #34495e;
}
.calc-input-group input[type="number"],
.calc-input-group input[type="text"],
.calc-input-group select {
width: calc(100% – 22px); /* Account for padding and border */
padding: 10px;
border: 1px solid #ddd;
border-radius: 4px;
box-sizing: border-box;
font-size: 16px;
}
.calc-input-group input[type="number"]:focus,
.calc-input-group input[type="text"]:focus,
.calc-input-group select:focus {
border-color: #3498db;
outline: none;
}
.powerbi-visual-calc-calculator button {
display: block;
width: 100%;
padding: 12px 20px;
background-color: #3498db;
color: white;
border: none;
border-radius: 4px;
font-size: 18px;
cursor: pointer;
transition: background-color 0.3s ease;
margin-top: 20px;
}
.powerbi-visual-calc-calculator button:hover {
background-color: #2980b9;
}
.calc-result {
margin-top: 30px;
padding-top: 20px;
border-top: 1px solid #eee;
}
.calc-result h3 {
color: #2c3e50;
margin-bottom: 10px;
}
.calc-result p {
background-color: #eaf4fb;
padding: 10px;
border-left: 4px solid #3498db;
margin-bottom: 10px;
}
.calc-result pre {
background-color: #f4f4f4;
padding: 15px;
border: 1px solid #ddd;
border-radius: 4px;
overflow-x: auto;
white-space: pre-wrap; /* Ensures long lines wrap */
word-wrap: break-word; /* Ensures long words break */
}
Understanding Power BI Visual Calculations
Power BI Visual Calculations are a powerful new feature that allows you to define measures directly within the visual scope, making it easier to perform complex calculations like running sums, moving averages, percentages of parent, and more, without writing intricate DAX code that relies on context transitions or complex filtering. They operate on the data currently displayed in your visual, respecting its filters and groupings.
Why Use Visual Calculations?
Simplicity: They simplify DAX for common analytical patterns, reducing the need for complex `CALCULATE` and `ALL` variations.
Context Awareness: Visual calculations inherently understand the visual's context (rows, columns, and filters), making them intuitive to use.
Performance: Often, they can be more performant than traditional DAX measures for certain scenarios, as they operate on the already aggregated data within the visual.
Flexibility: They allow for dynamic calculations that adapt as users interact with the visual (e.g., drilling down, filtering).
Key Concepts in Visual Calculations
Visual calculations introduce several new functions and concepts that are crucial for their operation:
VISUAL_CALCULATE: The core function that wraps your expression, indicating it's a visual calculation.
ORDERBY: Specifies the order in which the calculation should be performed. This is critical for sequential calculations like running sums or moving averages. For example, `ORDERBY([Date])` ensures calculations proceed chronologically.
PARTITIONBY: Defines groups within your visual where the calculation should restart. For instance, `PARTITIONBY([Product Category])` would make a running sum restart for each new product category.
RUNNINGSUM: Calculates a cumulative sum of an expression over the specified order.
MOVINGAVERAGE: Computes the average of an expression over a defined window (number of preceding/following elements) based on the specified order.
RANK: Assigns a rank to each row within its partition based on the specified order.
PERCENTOFPARENT: Calculates the percentage of a value relative to its parent in the visual hierarchy.
WINDOW: Allows for more flexible windowing definitions than `MOVINGAVERAGE`, specifying start and end offsets relative to the current row.
OFFSET: Retrieves a value from a row that is a specified number of positions away from the current row within its partition and order.
How They Work (Conceptual)
Imagine your visual as a table of data. When you apply a visual calculation, Power BI essentially "looks" at this table. For a `RUNNINGSUM`, it goes row by row (according to your `ORDERBY` field) and adds up the values. If you have a `PARTITIONBY` field, it resets the sum whenever that field's value changes.
For a `MOVINGAVERAGE`, it also goes row by row, but for each row, it identifies a "window" of data points (e.g., the current row and the two preceding rows) and calculates their average. This window slides down the data as the calculation progresses.
Example Scenario: Sales Analysis
Consider a table showing daily sales. You might want to:
Running Sum of Sales: To see the cumulative sales total over time. This helps understand overall progress towards a target.
3-Day Moving Average of Sales: To smooth out daily fluctuations and identify underlying sales trends. A sudden dip on one day might be less concerning if the moving average remains stable.
Sales Rank by Product Category: To see which product is performing best within its category.
The calculator above demonstrates the core logic for Running Sum and Moving Average. By inputting a few data points, you can observe how these calculations transform the original series and how the `ORDERBY` and `PARTITIONBY` concepts influence the DAX syntax.
Limitations and Considerations
Visual calculations are tied to the visual. They cannot be reused across different visuals in the same way traditional measures can.
They are primarily for calculations that depend on the visual's layout and context. For complex, model-wide calculations, traditional DAX measures are still essential.
Understanding the interaction between `ORDERBY`, `PARTITIONBY`, and the visual's natural sorting is key to getting the desired results.
By leveraging visual calculations, Power BI users can create more dynamic, insightful, and easier-to-understand reports, bridging the gap between simple aggregations and complex DAX patterns.