Power BI Course
CALCULATE in Depth
CALCULATE is the engine of advanced DAX. Every time you need a measure that ignores a filter, adds a filter, swaps out a filter, or compares a value against a different period or a different subset of data — CALCULATE is doing the work. This lesson covers every filter modifier, how multiple filter arguments interact, the common patterns professionals use every day, and the edge cases that produce wrong results silently.
The Working Dataset
| OrderID | Revenue | Region | Status | CustomerTier | OrderDate |
|---|---|---|---|---|---|
| 1001 | 1,200 | North | Delivered | Premium | 2024-01-05 |
| 1002 | 850 | South | Processing | Standard | 2024-01-18 |
| 1003 | 430 | North | Late | Premium | 2024-02-03 |
| 1004 | 95 | South | Late | Standard | 2024-02-14 |
| 1005 | 1,540 | West | Shipped | Premium | 2024-02-20 |
CALCULATE Anatomy
CALCULATE takes an expression and zero or more filter arguments. It first modifies the filter context using those arguments, then evaluates the expression in the modified context. The modifications apply only to the expression inside CALCULATE — they do not affect anything outside it.
SUM(Orders[Revenue]), -- expression
Orders[Region] = "North", -- filter 1 (replaces Region filter)
Orders[CustomerTier] = "Premium" -- filter 2 (adds Tier filter)
)
How Filter Arguments Interact
Each filter argument in CALCULATE follows a specific rule depending on whether the column it targets already has a filter in the current context. Understanding these rules prevents the most common CALCULATE mistakes.
| Situation | What CALCULATE does | Example |
|---|---|---|
| Column has no existing filter | The filter argument is added to the context — it intersects with all other active filters. | No Tier filter active → CALCULATE adds Tier=Premium. Now only Premium rows are visible. |
| Column already has a filter (e.g. from a slicer) | The filter argument replaces the existing filter on that column — it does not add to it. The slicer's filter is overridden for the expression. | Slicer = North, then CALCULATE adds Region="West" → the Region="North" filter is replaced by Region="West". Expression sees only West rows. |
| Multiple filters on different columns | All filter arguments are applied simultaneously. They intersect — a row must satisfy every filter condition to be included. | CALCULATE(SUM, Region="North", Tier="Premium") → only North AND Premium rows. Result: 1,200 + 430 = 1,630 but only if Premium → 1,200 (1003 is Premium but low rev → 1,630 total). |
| Filter modifier functions (ALL, ALLSELECTED…) | These do not add filters — they remove existing ones from the context before the expression evaluates. Covered in the next section. | CALCULATE(SUM, ALL(Orders[Region])) → removes Region filter entirely, expression sees all regions. |
Filter Modifier Functions
Filter modifiers are functions used exclusively as arguments inside CALCULATE. They control which existing filters are kept, removed, or overridden. Mastering these four functions gives you complete control over what the expression inside CALCULATE is allowed to see.
// ALL(table_or_column)
// Removes ALL filters from a table or a specific column
Grand Total =
CALCULATE(SUM(Orders[Revenue]), ALL(Orders))
// Ignores every filter — slicer, visual, page — returns 4,115 always
All Regions Revenue =
CALCULATE(SUM(Orders[Revenue]), ALL(Orders[Region]))
// Removes Region filter only — other filters (Tier, Status) still apply
| Region | Total Revenue | Grand Total (ALL Orders) | All Regions (ALL Region) |
|---|---|---|---|
| North | 1,630 | 4,115 | 3,170 |
| West | 1,540 | 4,115 | 3,170 |
| Total | 3,170 | 4,115 | 3,170 |
// ALLSELECTED(table_or_column)
// Removes visual-level row/column filters but KEEPS slicer filters
// The "% of total within slicer" workhorse
Revenue % of Total =
DIVIDE(
SUM(Orders[Revenue]),
CALCULATE(SUM(Orders[Revenue]), ALLSELECTED(Orders))
)
| Region | Revenue | % using ALL (wrong) | % using ALLSELECTED (correct) |
|---|---|---|---|
| North | 1,630 | 39.6% | 63.3% |
| South | 945 | 23.0% | 36.7% |
| Total | 2,575 | 62.6% ❌ | 100.0% ✓ |
// ALLEXCEPT(table, column_to_keep1, column_to_keep2, ...)
// Removes all filters EXCEPT from the specified columns
// Useful when you want to aggregate across one dimension
// while keeping all other filters intact
Revenue Ignoring Status =
CALCULATE(
SUM(Orders[Revenue]),
ALLEXCEPT(Orders, Orders[Region], Orders[CustomerTier])
-- Removes filters from ALL columns EXCEPT Region and Tier
-- Status filter is removed; Region and Tier filters stay
)
| Region | Revenue (Status=Late) | Revenue Ignoring Status |
|---|---|---|
| North | 430 | 1,630 |
| South | 95 | 945 |
| Total | 525 | 2,575 |
// KEEPFILTERS(filter_expression)
// Adds a filter WITHOUT replacing an existing one on the same column
// Result = intersection of the existing filter AND the new filter
North Revenue (KEEPFILTERS) =
CALCULATE(
SUM(Orders[Revenue]),
KEEPFILTERS(Orders[Region] = "North")
)
// Normal CALCULATE with Region="North":
// Replaces any existing Region filter → always returns North revenue
// KEEPFILTERS with Region="North":
// Intersects with existing Region filter
// When visual row = South: South ∩ North = empty → BLANK
// When visual row = North: North ∩ North = North → 1,630
// When no row filter: no existing filter ∩ North = North → 1,630
| Region (row) | Total Revenue | CALCULATE Region="North" (replaces) | KEEPFILTERS Region="North" (intersects) |
|---|---|---|---|
| North | 1,630 | 1,630 | 1,630 |
| South | 945 | 1,630 | BLANK |
| West | 1,540 | 1,630 | BLANK |
| Total | 4,115 | 1,630 | 1,630 |
// REMOVEFILTERS(table_or_column) — explicit alias for ALL()
// Clearer intent in some scenarios — "I am removing filters" vs "ALL"
Revenue No Region Filter =
CALCULATE(
SUM(Orders[Revenue]),
REMOVEFILTERS(Orders[Region])
)
// Identical to: CALCULATE(SUM(Orders[Revenue]), ALL(Orders[Region]))
| Region | Total Revenue | Revenue No Region Filter |
|---|---|---|
| North | 1,630 | 4,115 |
| South | 945 | 4,115 |
| West | 1,540 | 4,115 |
| Total | 4,115 | 4,115 |
The FILTER() Function Inside CALCULATE
When a filter condition cannot be expressed as a simple column=value, use FILTER() to create a table of matching rows and pass that table as the filter argument. FILTER() is an iterator — it evaluates the condition row by row. This is more powerful but slower than a direct column filter.
// When to use FILTER() instead of column = value
// Simple condition — use direct column filter (faster)
North Revenue = CALCULATE(SUM(Orders[Revenue]), Orders[Region] = "North")
// Condition involving a measure — MUST use FILTER()
// (measures cannot be used directly as CALCULATE filter arguments)
High Value Revenue =
CALCULATE(
SUM(Orders[Revenue]),
FILTER(
Orders,
Orders[Revenue] > AVERAGE(Orders[Revenue])
)
)
// FILTER iterates every row of Orders, keeps rows where
// Revenue > average Revenue. Returns those rows as a table.
// CALCULATE then evaluates SUM only over those filtered rows.
| OrderID | Revenue | Above Avg? | Included in High Value Revenue |
|---|---|---|---|
| 1001 | 1,200 | ✓ Yes | included |
| 1002 | 850 | ✓ Yes | included |
| 1003 | 430 | ✗ No | excluded |
| 1004 | 95 | ✗ No | excluded |
| 1005 | 1,540 | ✓ Yes | included |
| High Value Revenue (total) | 3,590 | ||
Common CALCULATE Patterns
// Pattern 1 — Conditional total (fixed segment regardless of context)
Premium Revenue =
CALCULATE(SUM(Orders[Revenue]), Orders[CustomerTier] = "Premium")
// Returns Premium revenue in every cell — ignores Tier slicer
| Region | Total Revenue | Premium Revenue |
|---|---|---|
| North | 1,630 | 1,630 |
| South | 945 | BLANK |
| West | 1,540 | 1,540 |
| Total | 4,115 | 3,170 |
// Pattern 2 — Prior period comparison using VAR for clarity
Revenue vs Prior Month =
VAR CurrentRev = SUM(Orders[Revenue])
VAR PriorRev =
CALCULATE(
SUM(Orders[Revenue]),
DATEADD(Calendar[Date], -1, MONTH)
)
RETURN
CurrentRev - PriorRev
| Month | Revenue | Prior Month Revenue | vs Prior Month |
|---|---|---|---|
| Jan 2024 | 2,050 | BLANK | BLANK |
| Feb 2024 | 2,065 | 2,050 | +15 |
// Pattern 3 — Multiple filter arguments (AND logic)
North Premium Revenue =
CALCULATE(
SUM(Orders[Revenue]),
Orders[Region] = "North",
Orders[CustomerTier] = "Premium"
)
| OrderID | Region | Tier | Revenue | Included? |
|---|---|---|---|---|
| 1001 | North | Premium | 1,200 | ✓ |
| 1002 | South | Standard | 850 | ✗ |
| 1003 | North | Premium | 430 | ✓ |
| 1004 | South | Standard | 95 | ✗ |
| 1005 | West | Premium | 1,540 | ✗ (not North) |
| North Premium Revenue | 1,630 | (1001+1003) | ||
// Pattern 4 — OR logic using FILTER (two values on same column)
Late or Processing Revenue =
CALCULATE(
SUM(Orders[Revenue]),
FILTER(
ALL(Orders[Status]),
Orders[Status] = "Late" || Orders[Status] = "Processing"
)
)
// Note: multiple CALCULATE arguments = AND
// For OR on the same column, use FILTER() with || operator
// Alternative using IN operator:
Late or Processing Revenue =
CALCULATE(
SUM(Orders[Revenue]),
Orders[Status] IN {"Late", "Processing"}
)
| OrderID | Status | Revenue | Included? |
|---|---|---|---|
| 1001 | Delivered | 1,200 | ✗ |
| 1002 | Processing | 850 | ✓ |
| 1003 | Late | 430 | ✓ |
| 1004 | Late | 95 | ✓ |
| 1005 | Shipped | 1,540 | ✗ |
| Late or Processing Revenue | 1,375 (850+430+95) | ||
The CALCULATE Modifier Reference
| Modifier | Effect on filter context | When to use |
|---|---|---|
| ALL(table) | Removes all filters from the entire table including slicers | Grand totals that must ignore all user selections |
| ALL(column) | Removes filters from one column only; other columns unaffected | Show all regions in a region-filtered context; RANKX denominators |
| ALLSELECTED(table) | Removes row/column visual filters; keeps slicer and page filters | % of total that resets within the user's slicer selection |
| ALLEXCEPT(tbl, col…) | Removes all filters except from the specified columns | Subtotal that ignores one dimension but keeps all others |
| KEEPFILTERS(expr) | Intersects new filter with existing filter instead of replacing it | Show value only for matching rows; return BLANK elsewhere |
| REMOVEFILTERS(col) | Explicit alias for ALL() — removes filters from table or column | Same as ALL(); use when intent ("removing filters") is clearer than "all" |
Teacher's Note: The most important thing to know about CALCULATE is that it replaces filters on the same column by default — and this surprises almost every beginner the first time they see it. You write CALCULATE(SUM([Revenue]), Region="North") inside a slicer that has already selected North and South, expecting to see North revenue. You do see North revenue — but not because of the slicer. The Region="North" argument replaced the slicer's Region filter. If the user changes the slicer to South only, the measure still returns North revenue. Always ask: "Do I want to add to the existing context, or replace it?" If you want to add, use KEEPFILTERS. If you want to override, the default behaviour is what you want.
Practice
Practice 1 of 3
To build a Revenue % of Total measure where the denominator respects the user's slicer but ignores the visual's row grouping — so percentages always sum to 100% within whatever is selected — you use CALCULATE with ___(Orders) as the filter modifier.
Practice 2 of 3
When CALCULATE receives two filter arguments targeting different columns — for example Region="North" and Tier="Premium" — the resulting filter context applies both conditions as a logical ___ — only rows satisfying both are included.
Practice 3 of 3
When you need to apply an OR condition on the same column inside CALCULATE — for example including Status = "Late" OR Status = "Processing" — you either use the ___ operator with a list like Orders[Status] IN {"Late","Processing"} or wrap the condition in FILTER().
Lesson Quiz
Quiz 1 of 3
A report has a Region slicer. You write North Revenue = CALCULATE(SUM([Revenue]), Orders[Region]="North"). When a user selects South in the slicer, what does North Revenue return?
Quiz 2 of 3
You need a measure that shows each region's revenue as a % of the total across all five orders — but when the user applies a Tier slicer to show Premium only, the percentages should recalculate to sum to 100% within Premium. Which formula is correct?
Quiz 3 of 3
You write Revenue = CALCULATE(SUM([Revenue]), Orders[Status]="Late", Orders[Status]="Processing") expecting to get revenue for Late OR Processing orders. What actually happens and how do you fix it?
Next up — Lesson 37 covers Time Intelligence, including every DATEYTD, SAMEPERIODLASTYEAR, DATEADD, and running total pattern, and exactly how the Calendar table relationship enables these functions to work.