美文网首页
4.6 在筛选上下文中使用DISTINCT和SUMMARIZE

4.6 在筛选上下文中使用DISTINCT和SUMMARIZE

作者: jweishan | 来源:发表于2020-02-29 12:01 被阅读0次

    在筛选上下文中使用DISTINCT和SUMMARIZE

    既然您对评估上下文有深入的了解,我们就可以使用这些知识逐步解决实际问题。同时,我们提供了一些细节的分析,希望能对行上下文和筛选上下文的基本概念有更多的了解。此外,在此示例中,我们还进一步阐述了SUMMARIZE函数,在第3章“使用基本表函数”中作了简要介绍。

    在进入更多细节之前,请注意,基于培训的目的,此示例在得出正确的解决方案之前显示了一些不准确的计算。因为我们想教导编写DAX代码的过程,而不是给出解决方案。在制定度量值的过程中,您可能会犯几个初始错误。在此指导示例中,我们描述了正确的推理方法,可帮助您自己解决类似的错误。

    要求是计算Contoso客户的平均年龄。即使这看起来是合理的要求,也并不完整。我们是在谈论他们的当前年龄还是在购买时的年龄?如果客户购买了3次,那么平均应算作一次事件还是三次事件?如果他们在不同年龄段购买了三次,该怎么办?我们需要更加精确。这是更完整的要求:“计算产生销售时客户的平均年龄,如果每个客户在同一年龄进行多次购买,则仅计算一次。”

    该解决方案可以分为两个步骤:

    • 计算购买发生时的客户年龄
    • 平均化

    每次销售时客户的年龄都会改变。因此,年龄需要存储在Sales表中。对于Sales中的每一行,可以计算产生销售时的客户年龄。计算列非常适合此需求:

    Sales[Customer Age] =
    DATEDIFF (                            -- Compute the difference between
        RELATED ( Customer[Birth Date] ), -- the customer's birth date
        Sales[Order Date],                -- and the date of the sale
        YEAR                              -- in years
    )
    

    由于Customer Age是计算列,因此将在迭代Sales的行上下文中对其进行评估。该公式需要访问与客户关系一侧的Customer[Birth Date](客户生日),这是Customer(客户)表中的一列。在这种情况下,需要RELATED才能让DAX访问目标表。在示例数据库Contoso中,许多客户的生日为空。如果第一个参数为空,则DATEDIFF返回值为空。

    因为要求提供平均值,所以第一个且不准确的解决方案可能是对该列求平均值的度量值:

    Avg Customer Age Wrong := AVERAGE ( Sales[Customer Age] )

    结果不正确,因为如果客户在某个年龄进行多次购买,则 Sales [Customer Age] 将包含多个具有相同年龄的行。要求仅计算每个客户一次,此公式未遵循这样的要求。图4-25并排显示了最后一个度量值的结果和预期结果。

    图4-25 一个简单的平均值计算得出的客户年龄的错误结果

    这是问题所在:每个客户的年龄只能计算一次。一个可能的解决方案(仍然不准确)将是通过以下措施对客户年龄进行平均,然后取平均值:

    Avg Customer Age Wrong Distinct :=
    AVERAGEX (                             -- Iterate on the distinct values of
        DISTINCT ( Sales[Customer Age] ),  -- Sales[Customer Age] and compute the
        Sales[Customer Age]                -- average of the customer's age
    )
    

    此解决方案不是正确的解决方案。实际上,DISTINCT 返回客户年龄的不同值。此公式仅计算一次两个具有相同年龄的客户。要求是对每个客户计数一次,而此公式对每个年龄计数一次。实际上,图4-26显示了具有“平均客户年龄”新公式的报告。您会看到此解决方案仍然不准确。

    图4-26 不同客户年龄的平均值仍然是错误的

    在最后一个公式中,可能尝试用CustomerKey作为DISTINCT的参数替换Customer Age,如以下代码所示:

    Avg Customer Age Invalid Syntax :=
    AVERAGEX (                             -- Iterate on the distinct values of
        DISTINCT ( Sales[CustomerKey] ),   -- Sales[CustomerKey] and compute the
        Sales[Customer Age]                -- average of the customer's age
    )
    

    此代码包含错误,DAX不会接受。您可以在不阅读下一段中提供的解决方案的情况下找出原因吗?

    AVERAGEX生成对表进行迭代的行上下文。作为AVERAGEX的第一个参数提供的表是DISTINCT(Sales [CustomerKey])。DISTINCT返回仅包含一列的表以及客户键的所有唯一值。因此,由AVERAGEX生成的行上下文仅包含一列,即Sales [CustomerKey]。DAX无法在仅包含Sales [CustomerKey]的行上下文中评估Sales [客户年龄]

    所需要的是行上下文,其粒度为Sales [CustomerKey],但还包含Sales [Customer Age]。第3章介绍的SUMMARIZE可以生成两列的现有唯一组合。现在,我们终于可以显示实现所有要求的此代码的版本:

    Correct Average :=
    AVERAGEX (                     -- Iterate on
        SUMMARIZE (                -- all the existing combinations
            Sales,                 -- that exist in Sales
            Sales[CustomerKey],    -- of the customer key and
            Sales[Customer Age]    -- the customer age
        ),                         --
        Sales[Customer Age]        -- and average the customer's age
    )
    

    像往常一样,可以使用变量将计算分为多个步骤。请注意,访问Customer Age列仍需要在AVERAGEX函数的第二个参数中引用Sales表名称。变量可以包含表,但不能用作表引用。

    Correct Average :=
    VAR CustomersAge =
        SUMMARIZE (                -- Existing combinations
            Sales,                 -- that exist in Sales
            Sales[CustomerKey],    -- of the customer key and
            Sales[Customer Age]    -- the customer age
        )
    RETURN
    AVERAGEX (                     -- Iterate on list of
        CustomersAge,              -- Customers/age in Sales
        Sales[Customer Age]        -- and average the customer's age
    )
    

    SUMMARIZE生成当前筛选上下文中可用的客户和年龄的所有组合。因此,具有相同年龄的多个客户将复制该年龄,每个客户一次。AVERAGEX忽略表中存在CustomerKey的情况;它仅使用客户年龄。CustomerKey*只需用来计算每个年龄段的正确出现次数。

    值得强调的是,完整的度量值在报表生成的筛选上下文中执行。因此,SUMMARIZE只会评估和返回购买商品的客户。报告的每个单元格都有不同的筛选上下文,度量值仅考虑购买了至少一种对应报告中显示颜色产品的客户。

    结论

    现在该回顾一下您在本章中学到的有关评估上下文的最相关主题。

    • 有两个评估上下文:筛选上下文和行上下文。这两个评估上下文不是同一概念的变体:筛选上下文过滤模型;行上下文迭代一个表。
    • 要了解公式的行为,您总是需要考虑两个评估上下文,因为它们同时运行。
    • DAX为计算列自动创建行上下文。也可以使用迭代函数以编程方式创建行上下文。每个迭代函数都定义一个行上下文。
    • 您可以嵌套行上下文,如果它们位于同一表上,则最里面的行上下文将隐藏同一表上的先前行上下文。当访问所需的行上下文时,变量可用于存储检索到的值。在DAX的早期版本中,变量不可用,而EARLIER函数用于访问前一行上下文。从今天起,不鼓励使用EARLIER。
    • 当遍历表表达式返回的表时,行上下文仅包含表表达式返回的列。
    • 当您在行、列、切片器和筛选上使用字段时,Power BI等客户端工具会创建筛选上下文。也可以使用CALCULATE以编程方式创建筛选上下文,我们将在下一章中介绍它。
    • 行上下文不会自动通过关系传播。需要使用RELATEDRELATEDTABLE强制传播。您需要在一对多关系正确一侧的行上下文中使用这些函数:在“多”侧使用RELATED,在“一”侧使用RELATEDTABLE。
    • 筛选上下文过滤模型,并根据其交叉筛选方向使用关系。它总是从一侧传播到另一侧。此外,如果您使用双向交叉筛选方向,则传播也会从多侧向一侧发生。

    至此,您已经学习了DAX语言最复杂的概念主题。这些要点支配您公式的所有评估流程,它们是DAX语言的支柱。每当遇到无法计算所需内容的表达式时,很有可能是因为您没有完全理解这些规则。

    正如我们在导言中所说,乍一看,所有这些主题看起来都很简单。实际上也简单。使它们变得复杂的原因是,在DAX表达式中,您可能在公式的不同部分具有多个评估上下文。掌握评估上下文是您要通过经验获得的技能,我们将在下一章中通过显示许多示例来帮助您。编写了自己的一些DAX公式后,您将直观地知道使用了哪些上下文以及它们需要哪些函数,最终您将掌握DAX语言。

    相关文章

      网友评论

          本文标题:4.6 在筛选上下文中使用DISTINCT和SUMMARIZE

          本文链接:https://www.haomeiwen.com/subject/umuthhtx.html