What is Evaluation Context in Power BI?
Writing DAX formulas in Power BI can feel like trying to hit a moving target. What works in one situation breaks in another, and a measure that seems simple can produce wildly different numbers depending on where you put it. The secret to mastering this isn't memorizing functions, it's understanding a concept called evaluation context. This article will break down the two main parts of evaluation context - row context and filter context - showing you how they work separately and, more importantly, how they work together to bring your data to life.
What is Evaluation Context and Why Does It Matter?
Unlike a spreadsheet formula that refers to specific cells (like =A1+B1), a DAX formula doesn't have a fixed reference. Its result changes based on the "environment" or "context" where it's being calculated. Think of it like a GPS: the route it gives you depends entirely on your current location. In Power BI, evaluation context is that "current location" for your DAX formulas.
Every single DAX calculation happens within an evaluation context, which is made up of two key components:
- Row Context: This is about iterating through a table, one row at a time. It lets your formula know which specific row it's currently on.
- Filter Context: This is the set of active filters applied to your data model before a calculation begins. It filters your tables down to a smaller subset of data.
Understanding the interplay between these two contexts is the single most important skill for writing accurate and effective DAX. Once you grasp this, things like CALCULATE and iterator functions (SUMX, AVERAGEX) will finally click.
Getting to Know Row Context: The "Row-by-Row" Calculator
Row context allows a formula to "see" the values in each column of a single row. The classic place you encounter row context is in a calculated column.
Let's imagine we have a simple Sales table:
If you want to add a Line Total column, you’d create a new calculated column with this DAX formula:
Line Total = Sales[Quantity] * Sales[Unit Price]
Power BI evaluates this formula for each row individually. For the first row, it sees 2 and 1000 and calculates 2000. For the next row, it sees 5 and 50 and returns 250. This happens because the calculated column has an implicit row context. The formula is aware of its current row and can access any value on that same row.
Free PDF · the crash course
AI Agents for Marketing Crash Course
Learn how to deploy AI marketing agents across your go-to-market — the best tools, prompts, and workflows to turn your data into autonomous execution without writing code.
Creating Row Context with Iterator Functions
Calculated columns are helpful but storing large calculated columns can bloat your file size. The more powerful way to leverage row context is with measures using special DAX functions called iterators. Iterator functions (like SUMX, AVERAGEX, MAXX, FILTER) are your tools for creating a new, temporary row context within a measure.
Look at the signature for SUMX:
SUMX(<table>, <expression>)
Here’s how it works:
- It takes a table (
<table>) as its first argument. - It creates a row context and iterates through that table, one row at a time.
- For each row, it evaluates the formula you provide (
<expression>). - Finally, it aggregates the results of each row's calculation (in this case, it sums them up).
To recreate our Line Total logic as a measure, you'd write:
Total Sales = SUMX( Sales, Sales[Quantity] * Sales[Unit Price] )
This Total Sales measure scans the Sales table, calculates Quantity * Unit Price for every single row, and then adds all those individual line totals together to give you the grand total. No calculated column needed.
Understanding Filter Context: The Slicer
If row context is about going "row by row," then filter context is about reducing the number of rows a formula can see in the first place. Filter context is the active set of filters being applied to your data model at the time of calculation.
Where does this filtering come from? Everywhere!
- Report Slicers: If a user selects "2024" in a year slicer.
- Visuals Themselves: The rows, columns, or legend of a chart apply filters.
- Filters Pane: Any explicit filters applied to a visual, page, or the entire report.
Let's use our Total Sales measure inside a matrix visual like this:
The DAX for Total Sales doesn't change, but its result does for each cell. Here's why:
- Corporate row: Before the formula calculates, Power BI applies a filter context of
[Customer Segment] = "Corporate". TheTotal Salesmeasure is now evaluated only over the subset of sales data from corporate customers. - Consumer row: The filter context changes to
[Customer Segment] = "Consumer", and the measure recalculates. - Grand Total row: Here, there's no filter on
Customer Segment, so the measure calculates over the entire table (respecting any other external slicers).
In short, the filter context "pre-filters" the table that measures like SUM, AVERAGE, or iterators like SUMX run on.
When Row and Filter Context Collide
This is where things get interesting and where most developers get stuck. Row context and filter context can exist at the same time, but they don't directly influence each other without a little help. A row context does not automatically create a filter context.
This is a critical concept. When you're in the middle of an iteration (like in a calculated column or a SUMX), the row context knows its "current row." However, it doesn’t automatically filter other tables in your model based on that row. It's just a pointer.
To bridge this gap, we need the most important function in DAX: CALCULATE().
CALCULATE and Context Transition
At its heart, CALCULATE is a function that modifies the filter context. One of its most magical abilities is something called context transition. This means it can take the value from a row context and turn it into a brand-new filter context.
Let’s look at a common problem. Say you're in the Products table and want a calculated column showing the Total Sales for each product.
You might try this first (and it won't work as you expect):
// This gives the same grand total for every row!
Sales Per Product (Incorrect) = [Total Sales]If you put the [Total Sales] measure directly into a calculated column on the Products table, you’d see the grand total sales value repeated for every single product. Why? Because the calculated column has a row context for each product, but the [Total Sales] measure inside it ignores this. It just sees the entire, unfiltered Sales table every time and returns the total.
To fix this, we wrap it in CALCULATE:
// This works!
Sales Per Product = CALCULATE( [Total Sales] )
Here’s what CALCULATE is doing in the background for each row of the Products table:
- It looks at the current row context (e.g.,
ProductKey = 123). - It performs context transition: it takes the
ProductKey = 123pointer and turns it into a real filter in the filter context, just as if a user had sliced the report for that one product. - It then evaluates the
[Total Sales]measure within this new, modified filter context. - The result is the sales amount for only that specific product.
This process is the core of so much advanced DAX. When you see a measure wrapped in CALCULATE inside an iterator, it’s almost always for context transition.
Free PDF · the crash course
AI Agents for Marketing Crash Course
Learn how to deploy AI marketing agents across your go-to-market — the best tools, prompts, and workflows to turn your data into autonomous execution without writing code.
Putting It All Together: A Practical Example
Let's calculate the percentage of total company sales that each customer segment contributes. We need to divide the sales for the segment by the grand total sales.
Our formula would be:
% of Grand Total = DIVIDE( [Total Sales], CALCULATE( [Total Sales], ALL('Sales') ) )
Let's break down how this works in a visual sliced by customer segment:
- Numerator:
[Total Sales]This is simple. It respects the existing filter context from the visual. If it’s on the "Corporate" row, it will calculate sales for corporate customers. - Denominator:
CALCULATE( [Total Sales], ALL('Sales') )This is where the magic happens. We're usingCALCULATEto change the filter context.
- The
ALL('Sales')function is a filter modifier that tellsCALCULATEto remove any and all filters applied to theSalestable. - So, even if the visual is filtering for "Corporate,"
ALL('Sales')strips away that filter just for this part of the calculation. - As a result,
[Total Sales]is evaluated over the entire, unfiltered sales table, giving us the grand total every time.
By controlling the evaluation context, you can dynamically compare values within their own subset (numerator) against a different, broader context (denominator), enabling all kinds of powerful ratio analysis.
Final Thoughts
Mastering DAX depends entirely on understanding how row and filter contexts operate. Row context iterates through tables for "row-by-row" logic, while filter context pre-defines the data slice for a calculation. The true power is unlocked with CALCULATE, the function that elegantly bridges these two worlds through context transition and gives you total control over how your formulas behave.
Learning the intricacies of DAX is incredibly rewarding, but it's a huge time commitment when all you need are answers about your business. We created Graphed to remove this friction by letting you create complex analyses using plain English. Instead of engineering DAX formulas, you connect your data sources and simply ask questions like, "What were my top 10 products by profit margin last quarter?" Graphed generates interactive, real-time dashboards for you, handling all the complex context and calculations behind the scenes.
Related Articles
How to Create Facebook Ads API for Claude Code
Create a Meta Marketing API access token that never expires, using a system user, so Claude Code can pull Facebook ad reports and manage campaigns.
ClickHouse vs Postgres: When to Switch to Columnar
Comparing ClickHouse vs PostgreSQL means matching storage architecture to workload. Row storage wins for transactions. Columnar wins for scan-heavy analytics.
Google Ads for Orthodontists: Get More Booked Consults
Most orthodontic Google Ads campaigns waste money on low-intent searches. The problem is structure, not the platform.