客户产品购买的交集 (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] )
    )

结果是这样的