客户产品购买的交集 (powerBI)
Intersection of Customer product purchase (powerBI)
我需要帮助计算客户与他们购买的商品之间的交叉点。例如,如果有 5 种产品,客户可以购买任何一种产品或 5 种产品的任意组合。客户也可以在任何日期重新购买产品——这就是我的问题所在,因为最终用户希望能够查看任何选定日期范围的交叉点。
我已经想出了一个解决方案,其中包括参数的使用,但这并不理想,因为最终用户无权更改报告的任何参数。
我愿意接受任何不涉及参数的解决方案,理想情况下带日期的切片器是最好的解决方案
我在 table 上的字段是 customer_ID、date_ID 和产品
示例数据
customer_id date_id product
1 9/11/2018 A
1 10/11/2018 A
1 10/11/2018 B
1 11/11/2018 C
1 11/11/2018 A
2 9/11/2018 C
2 10/11/2018 D
2 11/11/2018 E
2 11/11/2018 A
3 10/11/2018 A
3 10/11/2018 B
3 11/11/2018 A
3 11/11/2018 B
3 11/11/2018 B
4 10/11/2018 A
4 11/11/2018 A
5 9/11/2018 A
5 10/11/2018 B
5 10/11/2018 E
5 10/11/2018 D
5 11/11/2018 C
5 11/11/2018 A
6 9/11/2018 A
6 10/11/2018 A
6 11/11/2018 A
不同切片器选择的可能输出
如有任何帮助,我们将不胜感激
这非常棘手,因为我想不出一种方法来将动态计算的 table 的值用作视觉对象中的字段。 (您可以创建计算的 tables,但它们对切片器没有响应。您还可以在度量内部创建动态计算的 tables,但度量不会 return tables,只有单个值。)
我能想到的唯一方法是为每个可能的产品组合创建一个 table。但是,如果您有 N 个产品,那么这个 table 有 2N 行,而且会很快爆炸。
这是一个计算出来的table,它将输出所有的组合:
Table2 =
VAR N = DISTINCTCOUNT(Table1[product])
VAR Products = SUMMARIZE(Table1,
Table1[product],
"Rank",
RANKX(ALL(Table1),
Table1[product],
MAX(Table1[product]),
ASC,
Dense
)
)
VAR Bits = SELECTCOLUMNS(GENERATESERIES(1, N), "Bit", [Value])
VAR BinaryString =
ADDCOLUMNS(
GENERATESERIES(1, 2^N),
"Binary",
CONCATENATEX(
Bits,
MOD( TRUNC( [Value] / POWER(2, [Bit]-1) ), 2)
,,[Bit]
,DESC
)
)
RETURN
ADDCOLUMNS(
BinaryString,
"Combination",
CONCATENATEX(Products, IF(MID([Binary],[Rank],1) = "1", [product], ""), "")
)
然后添加计算列以获得列分隔版本:
Delimited =
VAR Length = LEN(Table2[Combination])
RETURN
CONCATENATEX(
GENERATESERIES(1,Length),
MID(Table2[Combination], [Value], 1),
","
)
如果您将 Delimited
行部分放在矩阵视觉对象上,并将以下度量放在值部分:
customers =
VAR Summary = SUMMARIZE(Table1,
Table1[customer_id],
"ProductList",
CONCATENATEX(VALUES(Table1[product]), Table1[product], ","))
RETURN SUMX(Summary, IF([ProductList] = MAX(Table2[Delimited]), 1, 0))
并过滤掉任何 0 个客户值,您应该得到这样的结果:
所以是的......不是一个很好的解决方案,尤其是当 N 变大时,但也许总比没有好?
编辑:
为了处理更长的产品名称,让我们在 Combination
连接中使用分隔符:
CONCATENATEX(Products, IF(MID([Binary],[Rank],1) = "1", [product], ""), ",")
(注意最后的 ""
到 ","
的变化。)
然后重写 Delimited
计算列以删除多余的逗号。
Delimited =
VAR RemoveMultipleCommas =
SUBSTITUTE(
SUBSTITUTE(
SUBSTITUTE(
SUBSTITUTE(Table2[Combination], ",,", ","),
",,", ","),
",,", ","),
",,", ",")
VAR LeftComma = (LEFT(Table2[Combination]) = ",")
VAR RightComma = (RIGHT(Table2[Combination]) = ",")
RETURN
IF(RemoveMultipleCommas <> ",",
MID(RemoveMultipleCommas,
1 + LeftComma,
LEN(RemoveMultipleCommas) - RightComma - LeftComma
), "")
最后,让我们稍微修改一下 customers
度量,以便它可以小计。
customers =
VAR Summary = SUMMARIZE(Table1,
Table1[customer_id],
"ProductList",
CONCATENATEX(VALUES(Table1[product]), Table1[product], ","))
VAR CustomerCount = SUMX(Summary, IF([ProductList] = MAX(Table2[Delimited]), 1, 0))
VAR Total = IF(ISFILTERED(Table2[Delimited]), CustomerCount, COUNTROWS(Summary))
RETURN IF(Total = 0, BLANK(), Total)
Total
变量给出总客户数。请注意,我还将 return 的零设置为空白,这样您就不需要过滤掉零(它会自动隐藏这些行)。
You can also try this measure to calculate the result.
[Count Of Customers] :=
VAR var_products_selection_count = DISTINCTCOUNT ( Sales[product] )
VAR var_customers = VALUES ( Sales[customer_id] )
VAR var_customers_products_count =
ADDCOLUMNS(
var_customers,
"products_count",
VAR var_products_count =
COUNTROWS (
FILTER (
CALCULATETABLE ( VALUES ( Sales[product] ) ),
CONTAINS (
Sales,
Sales[product],
Sales[product]
)
)
)
RETURN var_products_count
)
RETURN
COUNTROWS (
FILTER (
var_customers_products_count,
[products_count] = var_products_selection_count
)
)
我想我找到了一个更好的 solution/workaround,它不需要预先计算所有可能的组合。关键是使用 rank/index 作为基列,然后以此为基础构建。
因为 customer_id
已经很好地从 1 开始索引,没有间隙,在这种情况下,我将使用它,但如果不是,那么您需要创建一个索引列来改用。请注意,在给定的过滤器上下文中,不同的产品组合不能多于客户的数量,因为每个客户只有一个组合。
对于每个 index/rank,我们想要找到与其关联的产品组合以及该组合的客户数量。
ProductCombo =
VAR PerCustomer =
SUMMARIZE (
ALLSELECTED ( Table1 ),
Table1[customer_id],
"ProductList",
CONCATENATEX ( VALUES ( Table1[product] ), Table1[product], "," )
)
VAR ProductSummary =
SUMMARIZE (
PerCustomer,
[ProductList],
"Customers",
DISTINCTCOUNT ( Table1[customer_id] )
)
VAR Ranked =
ADDCOLUMNS (
ProductSummary,
"Rank",
RANKX (
ProductSummary,
[Customers] + (1 - 1 / RANKX ( ProductSummary, [ProductList] ) )
)
)
VAR CurrID =
SELECTEDVALUE ( Table1[customer_id] )
RETURN
MAXX ( FILTER ( Ranked, [Rank] = CurrID ), [ProductList] )
这样做是首先创建一个摘要 table 来计算每个客户的产品列表。
然后你 table 总结不同的产品列表并计算具有每个特定组合的客户数量。
然后我在前面table的基础上添加一个排名列,先按客户数量排序,然后使用产品列表的字典顺序打破平局。
最后,我从这个 table 中提取产品列表,其中排名与当前行的 index/rank 匹配。
您可以对客户数量进行几乎相同的度量,但这是我使用的度量,它更简单一些,可以处理 0 个值和总数:
Customers =
VAR PerCustomer =
SUMMARIZE (
ALLSELECTED ( Table1 ),
Table1[customer_id],
"ProductList",
CONCATENATEX ( VALUES ( Table1[product] ), Table1[product], "," )
)
VAR ProductCombo = [ProductCombo]
VAR CustomerCount =
SUMX ( PerCustomer, IF ( [ProductList] = ProductCombo, 1, 0 ) )
RETURN
IF (
ISFILTERED ( Table1[customer_id] ),
IF ( CustomerCount = 0, BLANK (), CustomerCount ),
DISTINCTCOUNT ( Table1[customer_id] )
)
结果是这样的
我需要帮助计算客户与他们购买的商品之间的交叉点。例如,如果有 5 种产品,客户可以购买任何一种产品或 5 种产品的任意组合。客户也可以在任何日期重新购买产品——这就是我的问题所在,因为最终用户希望能够查看任何选定日期范围的交叉点。
我已经想出了一个解决方案,其中包括参数的使用,但这并不理想,因为最终用户无权更改报告的任何参数。
我愿意接受任何不涉及参数的解决方案,理想情况下带日期的切片器是最好的解决方案
我在 table 上的字段是 customer_ID、date_ID 和产品
示例数据
customer_id date_id product
1 9/11/2018 A
1 10/11/2018 A
1 10/11/2018 B
1 11/11/2018 C
1 11/11/2018 A
2 9/11/2018 C
2 10/11/2018 D
2 11/11/2018 E
2 11/11/2018 A
3 10/11/2018 A
3 10/11/2018 B
3 11/11/2018 A
3 11/11/2018 B
3 11/11/2018 B
4 10/11/2018 A
4 11/11/2018 A
5 9/11/2018 A
5 10/11/2018 B
5 10/11/2018 E
5 10/11/2018 D
5 11/11/2018 C
5 11/11/2018 A
6 9/11/2018 A
6 10/11/2018 A
6 11/11/2018 A
不同切片器选择的可能输出
如有任何帮助,我们将不胜感激
这非常棘手,因为我想不出一种方法来将动态计算的 table 的值用作视觉对象中的字段。 (您可以创建计算的 tables,但它们对切片器没有响应。您还可以在度量内部创建动态计算的 tables,但度量不会 return tables,只有单个值。)
我能想到的唯一方法是为每个可能的产品组合创建一个 table。但是,如果您有 N 个产品,那么这个 table 有 2N 行,而且会很快爆炸。
这是一个计算出来的table,它将输出所有的组合:
Table2 =
VAR N = DISTINCTCOUNT(Table1[product])
VAR Products = SUMMARIZE(Table1,
Table1[product],
"Rank",
RANKX(ALL(Table1),
Table1[product],
MAX(Table1[product]),
ASC,
Dense
)
)
VAR Bits = SELECTCOLUMNS(GENERATESERIES(1, N), "Bit", [Value])
VAR BinaryString =
ADDCOLUMNS(
GENERATESERIES(1, 2^N),
"Binary",
CONCATENATEX(
Bits,
MOD( TRUNC( [Value] / POWER(2, [Bit]-1) ), 2)
,,[Bit]
,DESC
)
)
RETURN
ADDCOLUMNS(
BinaryString,
"Combination",
CONCATENATEX(Products, IF(MID([Binary],[Rank],1) = "1", [product], ""), "")
)
然后添加计算列以获得列分隔版本:
Delimited =
VAR Length = LEN(Table2[Combination])
RETURN
CONCATENATEX(
GENERATESERIES(1,Length),
MID(Table2[Combination], [Value], 1),
","
)
如果您将 Delimited
行部分放在矩阵视觉对象上,并将以下度量放在值部分:
customers =
VAR Summary = SUMMARIZE(Table1,
Table1[customer_id],
"ProductList",
CONCATENATEX(VALUES(Table1[product]), Table1[product], ","))
RETURN SUMX(Summary, IF([ProductList] = MAX(Table2[Delimited]), 1, 0))
并过滤掉任何 0 个客户值,您应该得到这样的结果:
所以是的......不是一个很好的解决方案,尤其是当 N 变大时,但也许总比没有好?
编辑:
为了处理更长的产品名称,让我们在 Combination
连接中使用分隔符:
CONCATENATEX(Products, IF(MID([Binary],[Rank],1) = "1", [product], ""), ",")
(注意最后的 ""
到 ","
的变化。)
然后重写 Delimited
计算列以删除多余的逗号。
Delimited =
VAR RemoveMultipleCommas =
SUBSTITUTE(
SUBSTITUTE(
SUBSTITUTE(
SUBSTITUTE(Table2[Combination], ",,", ","),
",,", ","),
",,", ","),
",,", ",")
VAR LeftComma = (LEFT(Table2[Combination]) = ",")
VAR RightComma = (RIGHT(Table2[Combination]) = ",")
RETURN
IF(RemoveMultipleCommas <> ",",
MID(RemoveMultipleCommas,
1 + LeftComma,
LEN(RemoveMultipleCommas) - RightComma - LeftComma
), "")
最后,让我们稍微修改一下 customers
度量,以便它可以小计。
customers =
VAR Summary = SUMMARIZE(Table1,
Table1[customer_id],
"ProductList",
CONCATENATEX(VALUES(Table1[product]), Table1[product], ","))
VAR CustomerCount = SUMX(Summary, IF([ProductList] = MAX(Table2[Delimited]), 1, 0))
VAR Total = IF(ISFILTERED(Table2[Delimited]), CustomerCount, COUNTROWS(Summary))
RETURN IF(Total = 0, BLANK(), Total)
Total
变量给出总客户数。请注意,我还将 return 的零设置为空白,这样您就不需要过滤掉零(它会自动隐藏这些行)。
You can also try this measure to calculate the result.
[Count Of Customers] :=
VAR var_products_selection_count = DISTINCTCOUNT ( Sales[product] )
VAR var_customers = VALUES ( Sales[customer_id] )
VAR var_customers_products_count =
ADDCOLUMNS(
var_customers,
"products_count",
VAR var_products_count =
COUNTROWS (
FILTER (
CALCULATETABLE ( VALUES ( Sales[product] ) ),
CONTAINS (
Sales,
Sales[product],
Sales[product]
)
)
)
RETURN var_products_count
)
RETURN
COUNTROWS (
FILTER (
var_customers_products_count,
[products_count] = var_products_selection_count
)
)
我想我找到了一个更好的 solution/workaround,它不需要预先计算所有可能的组合。关键是使用 rank/index 作为基列,然后以此为基础构建。
因为 customer_id
已经很好地从 1 开始索引,没有间隙,在这种情况下,我将使用它,但如果不是,那么您需要创建一个索引列来改用。请注意,在给定的过滤器上下文中,不同的产品组合不能多于客户的数量,因为每个客户只有一个组合。
对于每个 index/rank,我们想要找到与其关联的产品组合以及该组合的客户数量。
ProductCombo =
VAR PerCustomer =
SUMMARIZE (
ALLSELECTED ( Table1 ),
Table1[customer_id],
"ProductList",
CONCATENATEX ( VALUES ( Table1[product] ), Table1[product], "," )
)
VAR ProductSummary =
SUMMARIZE (
PerCustomer,
[ProductList],
"Customers",
DISTINCTCOUNT ( Table1[customer_id] )
)
VAR Ranked =
ADDCOLUMNS (
ProductSummary,
"Rank",
RANKX (
ProductSummary,
[Customers] + (1 - 1 / RANKX ( ProductSummary, [ProductList] ) )
)
)
VAR CurrID =
SELECTEDVALUE ( Table1[customer_id] )
RETURN
MAXX ( FILTER ( Ranked, [Rank] = CurrID ), [ProductList] )
这样做是首先创建一个摘要 table 来计算每个客户的产品列表。
然后你 table 总结不同的产品列表并计算具有每个特定组合的客户数量。
然后我在前面table的基础上添加一个排名列,先按客户数量排序,然后使用产品列表的字典顺序打破平局。
最后,我从这个 table 中提取产品列表,其中排名与当前行的 index/rank 匹配。
您可以对客户数量进行几乎相同的度量,但这是我使用的度量,它更简单一些,可以处理 0 个值和总数:
Customers =
VAR PerCustomer =
SUMMARIZE (
ALLSELECTED ( Table1 ),
Table1[customer_id],
"ProductList",
CONCATENATEX ( VALUES ( Table1[product] ), Table1[product], "," )
)
VAR ProductCombo = [ProductCombo]
VAR CustomerCount =
SUMX ( PerCustomer, IF ( [ProductList] = ProductCombo, 1, 0 ) )
RETURN
IF (
ISFILTERED ( Table1[customer_id] ),
IF ( CustomerCount = 0, BLANK (), CustomerCount ),
DISTINCTCOUNT ( Table1[customer_id] )
)
结果是这样的