Evaluation Contexts

Evaluation Contexts

So now that know how to use iterators, it's time for a little review. You can get away with not having a complete understanding of them and still use them pretty successfully for a while, but often iterators can still be confusing, even after the first explanation. To really get a good understanding , we are going to have to review filter context and row context and understand the difference.

Evaluation Contexts define what values an expression can see.

Evaluation contexts sounds fancy, like some calculus term, but what they really boil down is what rows can an expression see when it's evaluated and what context is it being computed?. The rest is just a matter of understanding what can manipulate and change these contexts.

Filter Context vs. Row Context

Filter Context

Filter Context is a combination of the visual filters and user filters.

We went over this when we dealt with the CALCULATE function. It's all the implied filters that are going on. And they make pretty intuitive sense when you think about it. We also learned that we can change that filter context with CALCULATE, that's what it does. CALCULATE's one job is to manipulate the filter context.

Something else is that the filter context propagates filters to other tables. So what does that means?. Well when we have a relationship between Product and Sales Order Detail table. And whenever we would filter on product color, whether we are doing it implicitly or explicitly, that filter would propagate down to the Sales Order Detail information. And so the Sales Order line items would get filtered as well, that's called cross filtering. When you have a filter context, it travels across those relationships. And we're going to see that's not the case when we deal with row context.

Row Context

Row Context limits to current row, and occurs when you're iteration through rows.

One case is it's used in calculated columns, and so whenever your're using an expression in a calculated column, it's able to refer to multiple columns because it's limited to just that row, so it knows exactly what you're talking about, there's nothing you need to aggregate, there's nothing fancy you need to do.

Row Context also occurs in iterators. These are the two main places you're going to see this, in a calculated column or an iterator, and so when the iterator is going row by row, for each row it creates a row context. It says you're limited to just this set of information. Additionally it doesn't propagate filters. So if we were to be using an iterator on the Product Table and maybe going through each product, that would not automatically filter anything on Sales Detail Table, and in fact we wouldn't even be able to access the Sales Detail Table normally. What we've to use is either the RELATED function or RELATEDTABLE function. So you're going to notice that the only time that you have to use RELATED or RELATEDTABLE is when you're in a row context, that's why we had to use it in calculated columns, but whenever we were dealing with measures, we didn't have to worry about any of that.

So those are the difference between filter context and row context. These are the two things, the only two things that affect what happens whenever you evaluate an expression. A filter context is just all the filters that have been applied or manipulated, and a row context is the row specified by a calculated column or an iterator behind the scenes.

Filter ContextRow Context
Combination of visual filters and user filtersLimits to the current row, when iterating
Can be changed with CALCULATEUsed in calculated columns
Propagates filters to other tablesused in iterators
Does not propagate filters
Requires RELATED/RELATEDTABLE to use other tables

Nesting Evaluation Contexts

Finishing up with the idea of evaluation context, let's go into something really advanced and talk about nesting. We can nest evaluation context. Specifically nesting row context.

= RANKX(                 // Creates a row context

    All( Products ),    // Over all products

    SUMX(              // Creates a new row context for each product

        RELATEDTABLE( InternetSales ),  // Pulls sales based on the inner row context
        'InternetSales[SalesAmount]'
    )
)

Now this code is from an example from Microsoft, and is based off one of their Adventure Works databases. So the first thing we have here is the RANKX function. You can tell by the name, the X at the end, and the parameters that is takes in, that it's an iterator, because it's taking in a table and an expression, and because it's an iterator it's going to create a row context and loop over what table we passed into it.

In this case we're going to pass in all of the products, so we're going to undo any filters and we're going to return a table. ALL returns a table and we're going to return the Products Table. The reason we're doing this is we want an absolute ranking. So we want to rank all of the products, no matter what's been filtered. So we have to rank based on something, are we going to rank based on alphabetical order of the description of the product? No that would be silly. Are we going to rank on quantity sold? Aw, that's a little bit closed, but what we care about is the total sales. So we're going to use SalesAmount. And to get that, we need to use another iterator because the information we care about is in a different table.

So we're going to use SUMX and so SUMX is creating a new row context because first we're going through each product and then for each product we're going to go through each row in the InternetSales Table. So we're going to pull in sales based on that inner row context. So this is where it gets interesting. Let's take a step back. We have a product table and we're going to loop through each product one by one, Product A, Product B, Product C. And let's say we're on Product A, well we're using the RELATEDTABLE function on the sales table, this returns all of the sales for the current product, product A, and then SUMX loops over each of the sales entries. If your head is spinning, don't worry. It's rare that you're going to have to nest iterators, but the important part is that you understand that you can and that evaluation context can get a little bit complicated.