跳至主要内容

精通 Power BI 中的 COUNTIF():基于 DAX 的实现方法

来自 Excel 的 Power BI 用户常会发现 Power BI 没有原生的 COUNTIF()。本文将展示在 Power BI 中实现 COUNTIF() 的多种方式及其实际用例。
· 11分钟 阅读

使用 AI 探索

COUNTIF() 函数在 Excel 用户中非常流行,因为它可以根据条件计数。然而,迁移到 Power BI 时,许多用户会发现 Power BI 并没有内置的 COUNTIF() 函数。

幸运的是,用户可以用多种 DAX(数据分析表达式)公式来复现 COUNTIF() 的功能。本文将指导您使用 CALCULATE()、COUNTROWS()、COUNTA() 和 FILTER() 等 DAX 函数,在 Power BI 中实现类似 COUNTIF() 的计算。

为什么 Power BI 中不存在 COUNTIF()

与基于单元格公式的 Excel 不同,Power BI 围绕基于表的计算和筛选上下文而构建。它针对表及其关系进行运算,并使用 DAX 完成计算。 

DAX 并不是引用单个单元格,而是与列打交道,并根据筛选上下文动态应用计算——该上下文由切片器、行/列筛选以及表关系决定。 

Excel COUNTIF() 使用基于单元格的方法,在指定区域内逐行应用公式。Power BI 的方法不同,它利用表之间的关系来细化计算。它不是扫描单个单元格,而是使用 DAX 基于报表上下文应用筛选。 

对用户的影响

作为从 Excel 转向 Power BI 的用户,您需要适应 Power BI 基于表的计算方式。作用于数据列的 DAX 方法比逐行迭代更高效。 

与 Excel 不同,Power BI 用户必须根据自身数据模型选择最合适的方式来复现 COUNTIF()。因此,您需要摒弃对单个单元格应用公式的习惯,转而以表和筛选上下文来思考。

复现 COUNTIF() 逻辑的核心 DAX 函数

在 Power BI 中实现 COUNTIF() 的方式取决于诸多因素,例如表之间的关系、数据中是否存在缺失值、数据规模等。一些 DAX 函数在速度和效率上优化得更好。  

关键函数概览

DAX 函数可用于创建计算列、度量值和表。以下这些 DAX 函数将帮助您在 Power BI 中复现 COUNTIF() 的功能。

  • CALCULATE():用于修改表达式的筛选上下文,允许动态应用新的筛选或修改现有筛选。例如,您可以用它来计算特定地区的总销售额。 

  • COUNTROWS():统计指定表的行数,且可与筛选函数结合计数特定行。例如计数金额大于 $1,000 的销售订单。

  • COUNTA():统计列中的非空值,适用于处理缺失数据。例如,您可以统计非空的客户 ID,确保只计入有效记录。 

  • FILTER():通常与 CALCULATE() 和 COUNTROWS() 联用,按条件返回一个表。例如,返回订单金额超过 $500 的销售表。

  • ALLEXCEPT():移除除指定列外的所有筛选;例如,尽管对数据应用了切片器或表筛选,仍可按地区返回总销售额。在处理层级汇总时这很重要。

  • RELATEDTABLE():基于现有关系返回一个表。例如,利用客户数据的关系统计每位客户的销售订单数量。

它们如何协同工作

由于 COUNTROWS() 统计表的行数,您可以用 FILTER() 从数据中返回满足条件的表。然后使用 CALCULATE() 修改筛选上下文,确保 COUNTROWS() 只统计满足条件的行。下面是一段 DAX 语法的示例,展示其工作方式。

Sales over 5 quantities = 
CALCULATE(
    COUNTROWS(Sales), 
    FILTER('Sales, 'Sales[Quantity] > 5)
)

CALCULATE() 函数在某种程度上就像把两个不同的表达式结合起来。与其使用 COUNTROWS() 统计所有数据,CALCULATE() 会将其限制在由 FILTER() 得到的筛选上下文中。 

Power BI 中 COUNTIF() 等价用法的基础示例

在展示如何在 Power BI 中实现 COUNTIF() 的示例之前,请先加载超市销售数据。 

示例 1:女性客户的订单数量

下面这段 DAX 语法用于计算女性客户的订单数量。

Orders by female customers = 
CALCULATE(
    COUNTROWS(Sales), 
    FILTER(Sales, Sales[Gender] = "Female")
)

显示女性客户订单总数的图片。

女性客户的订单数量。作者制图。

  • FILTER(Sales, Sales[Gender] = "Female") 将销售订单筛选为女性客户。

  • COUNTROWS(Sales) 在 CALCULATE() 的作用下,不再统计全部数据,而是统计筛选后的表。

示例 2:按城市计数订单

下面是按城市统计订单数量的 DAX 语法。

Total Orders by City = CALCULATE(COUNTA(Sales[City]), ALLEXCEPT(Sales, Sales[City]))

显示按城市统计的订单总数的图片。

按城市统计的订单数量。作者制图。

  • COUNTA(Sales[City]) 统计城市列中的非空值数量。

  • ALLEXCEPT(Sales, Sales[City]) 从 Sales 表中移除除 City 列外的所有筛选。

  • CALCULATE() 确保 COUNTA() 针对每个城市分别计算,效果类似于 Excel 的 COUNTIF()。

示例 3:使用 COUNTROWS 的替代方法

另一种做法是使用下面这段 DAX 语法,以 COUNTROWS() 代替 COUNTA()。

Total Orders by City = CALCULATE(COUNTROWS(Sales), ALLEXCEPT(Sales, Sales[City]))

COUNTROWS() 会统计 Sales 表中的行数,不受空值影响。 

如果 City 列没有空值,两种方法都会返回相同结果。但若 City 列存在缺失,COUNTA() 会将其排除,而 COUNTROWS() 会将其计入。

当每一行代表一条记录(如销售订单)时,应使用 COUNTROWS()。但如果数据存在无效条目,可以使用 COUNTA(),它只统计具有有效值的记录。 

使用度量值进行动态计算

有些场景需要创建会随表筛选或切片器动态变化的度量值。本节讨论如何在此类情形下实现 COUNTIF()。

场景:统计高价值订单

以下示例关注总数量大于 5 的订单。

High Value Orders = COUNTROWS(FILTER(Sales, Sales[Quantity] > 5))

显示高价值订单数量的图片。

高价值订单数量。作者制图。

  • FILTER(Sales, Sales[Quantity] > 5) 返回订购数量大于 5 的销售订单。

  • COUNTROWS() 统计筛选后表中数量大于 5 的订单行数。

与创建后会增加存储和模型体量的计算列不同,度量值仅在需要时计算,更为高效。创建一个计算列的 DAX 语法如下:

 High Value = IF(Sales[Quantity] > 5, 1, 0)

显示计算列 High Value 的图片。作者制图。

计算列 High Value。作者制图。

这会在每行存储静态的 1 或 0。要统计高价值订单,您还需要创建一个度量值,使用 SUM(Sales[High Value])。

想象一下为每一个筛选条件都创建计算列——这会增加存储占用并降低灵活性。 

下面这张表说明了为什么应优先考虑使用度量值而非计算列。

度量值(Measure)

计算列(Calculated Column)

查询时计算,对筛选有响应

静态,在数据加载/刷新时计算

更高效,仅在需要时计算

占用存储并增大模型体量

可根据筛选器、切片器和可视对象动态调整

除非刷新数据,否则数值保持不变。 

高级技巧与多条件

Excel 的 COUNTIFS() 支持多条件,在 Power BI 中同样可以复现。

多条件 / COUNTIFS() 逻辑

以下 DAX 语法用于统计订购数量大于 5 且通过信用卡支付的订单数。 

Quantity Above 5 and Credit Card = 
COUNTROWS(
    FILTER(
        Sales, 
        Sales[Quantity] > 5 && Sales[Payment] = "Credit card"
    )
)

显示订购数量超过 5 且通过信用卡支付的订单数量的图片。

订购数量超过 5 且信用卡支付的订单数量。作者制图。

上述公式对 Sales 表进行筛选:订单数量超过 5,且支付方式为信用卡。&& 表示 AND 运算符,确保只统计同时满足两项条件的订单。

如果您只关注满足任一条件的订单,则使用 OR 运算符,在 DAX 中用 || 表示。

Quantity Above 5 or Credit Card = 
COUNTROWS(
    FILTER(
        Sales, 
        Sales[Quantity] > 5 || Sales[Payment] = "Credit card"
    )
)

显示订购数量超过 5 或通过信用卡支付的订单数量的图片。

订购数量超过 5 或信用卡支付的订单数量。作者制图。

使用逻辑运算符和变量

用于复现 COUNTIF() 的 DAX 语法有时会较为复杂。使用变量可以让表达更易理解,并确保按期望顺序评估条件。它还能通过一次存储值来避免重复计算。 

下面这个示例展示了如何用变量统计女性客户的订单数量,这些订单要么通过信用卡支付,要么数量超过 5。

CreditCard Or Above 5 By Female = 
VAR FilteredSales = 
    FILTER(
        Sales,
        (Sales[Quantity] > 5 || Sales[Payment] = "Credit card") 
        && Sales[Gender] = "Female"
    )
RETURN 
    COUNTROWS(FilteredSales)

显示女性客户中,信用卡支付或订购数量超过 5 的订单数量的图片。

女性客户中,信用卡支付或数量超过 5 的订单数量。作者制图。

筛选被存储在一个变量中,并用于 COUNTROWS() 函数。下面是另一种写法:

CreditCard Or Above 5 By Female = 
VAR MinQuantity = 5
VAR FilteredSales = 
    FILTER(
        Sales,
        (Sales[Quantity] > MinQuantity || Sales[Payment] = "Credit card") 
        && Sales[Gender] = "Female"
    )
VAR Result = COUNTROWS(FilteredSales)
RETURN 
    Result

这一次,最小数量、筛选条件和结果都作为变量存储。类似数学中的 BODMAS 运算优先级规则(先算括号内),DAX 也会优先评估括号内的条件。 

在上述示例中,由于位于括号内,OR 运算符 || 会先于 AND 运算符 && 被评估。这意味着会先筛选出数量超过 5 或信用卡支付的订单,再筛选女性客户的订单。 

跨关联表的 COUNTIF()

当您处理多张表时,也可以结合 RELATEDTABLE(),使用 COUNTROWS() 或 COUNTA() 按另一张表中满足的条件来计数记录。 

RELATEDTABLE() 会考虑两张表之间的模型关系,可能是一对一或一对多。

假设还有一张名为 Sales2 的销售表,仍然使用同一份超市销售数据;Sales 与 Sales2 通过 Invoice ID 关联。 

显示 Sales 与 Sales2 表关系的图片。

Sales 与 Sales2 表之间的关系。作者制图。

假设 Sales 表没有 Customer type 列,而 Sales2 有,您可以使用下面的 DAX 语法来统计会员客户的订单数量。

Orders by members = 
COUNTROWS(FILTER(RELATEDTABLE(Sales2), Sales2[Customer Type] = "Member"))

显示会员客户订单总数的图片

会员客户的订单数量。作者制图。

  • RELATEDTABLE(Sales2) 会自动将 Sales2 筛选为只包含与 Sales 数据中相同 Invoice ID 的行。

  • 随后 FILTER() 进一步将其限定为 “Member” 订单,而 COUNTROWS() 统计筛选后的表。 

实用场景与整合建议

以下是实现 Power BI COUNTIF() 的一些实用场景与整合建议。

真实世界场景

如果您有一张学生测验成绩或订单的表,可以使用 COUNTROWS() 和 FILTER() 来统计重复记录的数量。借助分数列,您也可以统计多少学生及格或不及格。另一个重要的 COUNTIF() 等价函数是 SUMX(),可用于基于条件汇总学生分数。 

数据集成工具

与每次都从 Excel 或 Google 表格上传电子表格进行分析不同,您可以使用 Coupler.io 等工具来自动化数据提取。 

只要电子表格更新,Power BI 仪表板就会自动反映变化,无需您手动上传。 

这确保 Power BI 始终反映最新数据,无需手动上传文件,便于您专注于洞察与业务决策。您还可以使用这些外部工具将多个数据源连接到同一仪表板。 

可读性与性能方面的经验提示

当需要在聚合前进行逐行计算时,请使用计算列。当计算取决于用户选择的筛选或切片器,或您需要的是聚合值而非行级计算时,请使用度量值。 

理解筛选上下文是区分何时使用度量值与计算列的关键。与适用于表内单行的行上下文不同,筛选上下文适用于应用了切片器、筛选或分组的情形。计算列使用行上下文,可能会创建不必要的列,增加内存占用。 

在创建度量值时同样要考虑筛选上下文。例如,与其使用 SUM(Sales[Amount]),不如使用 CALCULATE(SUM(Sales[Amount])) 来控制筛选。如果数据上已经应用了筛选,使用前者可能得不到正确数值,而使用后者会忽略应用于数据的所有筛选。

此外,避免在单段 DAX 语法中使用过多的迭代器(如 FILTER()),对于大型数据集会因逐行迭代而降低速度。

结论

尽管 Power BI 缺少 Excel 的原生 COUNTIF(),但通过 CALCULATE()、COUNTROWS()、COUNTA() 与 FILTER() 等 DAX 函数,完全可以复现其功能。

选择哪种方法取决于数据特性。如果存在缺失值,可考虑使用 COUNTA();若数据完整,则可使用 COUNTROWS()。若要排除应用在数据上的外部筛选,请在 CALCULATE() 中使用 ALLEXCEPT()。

需牢记,您编写的 DAX 语法会影响仪表板可视对象的加载速度。请尽量编写优化的 DAX,并明确何时使用度量值或计算列。以下是一些 DataCamp 资源,可进一步探索高级 DAX 技巧。 

Adejumo Ridwan Suleiman

作者

Adejumo Ridwan Suleiman

资深数据科学讲师兼生物统计学家,精通 Python、R 和机器学习。

Power BI Countif() 常见问题

Power BI 是否有原生的 COUNTIF() 函数?

Power BI 没有原生的 COUNTIF() 函数,但可以通过多种方式在 Power BI 中实现其功能。

在 Power BI 中实现 COUNTIF() 可用哪些函数?

在 Power BI 中实现 COUNTIF() 的主要函数包括 CALCULATE()、COUNTROWS()、COUNTA()、FILTER()、ALLEXCEPT() 和 RELATEDTABLE()。可根据数据模型组合这些函数来实现 COUNTIF()。

COUNTROWS() 与 COUNTA() 有何区别?

COUNTROWS() 会统计数据中的行数,不论该行是否包含有效记录;而 COUNTA() 统计列中的有效(非空)记录数。

与计算列相比,使用度量值的优势是什么?

度量值更高效,且由于按需计算,不会占用太多存储;相反,计算列会增加数据维度,从而消耗存储空间。

在 Power BI 中实现 COUNTIF() 的实际应用有哪些?

Power BI 中 COUNTIF() 的应用广泛,从按条件计数记录到基于计数创建条件列等。

主题