Skip to main content
cancel
Showing results for 
Search instead for 
Did you mean: 

Register now to learn Fabric in free live sessions led by the best Microsoft experts. From Apr 16 to May 9, in English and Spanish.

Reply
Anonymous
Not applicable

Number of Unique Customers with Exclusions and Inclusions

HI All, I have a Calendar table and Customer Invoice Table.   I am a very new recruit to Dax.

 

I am tasked with, for any date range, determining the number of Unique Customers and total Tons that were sold to Customers with the following conditions:

  • # of Customers and Tons that had 'Flat Rolled' but not 'Long Product' sales
  • # of Customers and Tons that had 'Long Product' but not 'Flat Rolled' sales
  • # of Customers and Tons that had 'Flat Rolled' and 'Long Product' sales

I dont have the dax experience to get to the correct numbers (I verified numbers for August 2020 in sql), and have tried combined calculated tables with filters, separate calculated tables by PRODUCT_TYPE with filters and isblank, but at this point i am just thrashing and i am gettting nonsense for results.

 

Any help would be immensely appreciated!  I need to be taken by the hands and guided through this.

Thanks very much in advance.  This will be a great learning experience.

 

The customer invoice table is related to the calendar by way of an Invoice Date (many invoices-to-1 calendar date) if that is helpful.

Each row of the Invoice has the following columns, with sample base detail data for August 2020 included below:

 

CUSTOMER_IDINVOICE_DATEPRODUCT_TYPETONS
1007948/10/2020Flat Rolled1.89375
1007948/10/2020Long Product0.37632
1007948/10/2020Long Product0.544919
1007948/28/2020Long Product0.199945
1008418/10/2020Flat Rolled0.441
1008538/19/2020Flat Rolled0.58647
1008538/5/2020Flat Rolled1.509
1010368/12/2020Flat Rolled0.25825
1010368/19/2020Flat Rolled0.120455
1010368/3/2020Flat Rolled1.0635
1010528/14/2020Flat Rolled0.4375
1010528/7/2020Flat Rolled0.302832
1010528/7/2020Flat Rolled0.27309
1010528/14/2020Flat Rolled0.2025
1010528/7/2020Flat Rolled0.192352
1010528/7/2020Flat Rolled1.72368
1010528/4/2020Flat Rolled0.172115
1010528/12/2020Long Product0.237584
1010528/4/2020Long Product0.84487
1011908/12/2020Flat Rolled0.4368
1011908/10/2020Long Product0.313515
1011908/18/2020Long Product0.367003
1011908/28/2020Long Product0.790013
1011908/28/2020Long Product0.409
1012848/26/2020Flat Rolled1.820025
1012848/14/2020Flat Rolled0.53975
1012848/14/2020Flat Rolled0.787125
1012848/3/2020Flat Rolled1.88799
1012848/14/2020Flat Rolled0.882
1012848/21/2020Flat Rolled1.164
1012848/7/2020Flat Rolled0.141765
1012848/18/2020Flat Rolled0.608
1012848/21/2020Flat Rolled1.46499
1012848/18/2020Flat Rolled0.30866
1012848/4/2020Flat Rolled0.763528
1012848/19/2020Flat Rolled1.46499
1012848/26/2020Flat Rolled0.75605
1012848/7/2020Flat Rolled0.540155
1012848/12/2020Flat Rolled1.72722
1012848/4/2020Flat Rolled0.312375
1012848/31/2020Flat Rolled0.6074
1012848/28/2020Flat Rolled1.46499
1014668/14/2020Flat Rolled0.485575
1014688/17/2020Flat Rolled1.1805
1014688/10/2020Flat Rolled1.5235
1014688/17/2020Flat Rolled1.87545
1014688/5/2020Flat Rolled0.96
1014688/19/2020Flat Rolled1.534075
1014688/25/2020Flat Rolled1.5252
1014688/3/2020Flat Rolled2.034
1016248/18/2020Flat Rolled0.3024
1016248/4/2020Flat Rolled0.14655
1016248/4/2020Flat Rolled0.060715
1016248/18/2020Flat Rolled0.361818
1016248/18/2020Long Product0.07908
1021118/31/2020Flat Rolled0.087587
1021118/5/2020Flat Rolled0.068273
1021688/12/2020Long Product0.030811
1 ACCEPTED SOLUTION
OwenAuger
Super User
Super User

Hi @Anonymous

 

Hi @amartin141

The category of measures you are wanting is Basket Analysis.

I have based my answer here on the Basket analysis page on DAX Patterns.
https://www.daxpatterns.com/basket-analysis/

I have attached a PBIX with sample measures using your data. I have not worried about the Date table in my PBIX as it is not essential to the Basket Analysis setup, but  


The steps I followed are:  

  1. Set up your data model with a Product table and a secondary Product table. On DAX Patterns they call these tables 'Product' and 'And Product'. In my example I called them 'Product A' and 'Product B'. These are both related to your main Sales table (I named this table Invoice) with an inactive relationship with the secondary table.

  2. Create measures as follows:

 

// BASE # CUSTOMERS MEASURE //
# Customers = 
SUMX ( SUMMARIZE ( Invoice, Invoice[CUSTOMER_ID] ), 1 )
// This is equivalent to DISTINCTCOUNT of CUSTOMER_ID
// but DAX Patterns explains why to consider using this instead.

// CUSTOMERS WITH BOTH PRODUCTS (INTERNAL) MEASURE //
# Customers with Both Products (Internal) =
VAR CustomersWithProductB =
    CALCULATETABLE (
        SUMMARIZE ( Invoice, Invoice[CUSTOMER_ID] ),
        REMOVEFILTERS ( 'Product A' ),
        REMOVEFILTERS ( Invoice[PRODUCT_TYPE] ),
        USERELATIONSHIP ( Invoice[PRODUCT_TYPE], 'Product B'[Product Type B] )
    )
VAR Result =
    CALCULATE ( [# Customers], KEEPFILTERS ( CustomersWithProductB ) )
RETURN
    Result


// CUSTOMERS WITH BOTH PRODUCTS FINAL MEASURE //
// Returns blank if same product selected on both A & B
# Customers with Both Products =
IF (
    ISEMPTY (
        INTERSECT (
            DISTINCT ( 'Product A'[Product Type A] ),
            DISTINCT ( 'Product B'[Product Type B] )
        )
    ),
    [# Customers with Both Products (Internal)]
)

// CUSTOMERS WITH PRODUCT A BUT NOT PRODUCT B MEASURE //
# Customers with Product A & Not Product B =
VAR CustomersWithProductB =
    CALCULATETABLE (
        SUMMARIZE ( Invoice, Invoice[CUSTOMER_ID] ),
        REMOVEFILTERS ( 'Product A' ),
        REMOVEFILTERS ( Invoice[PRODUCT_TYPE] ),
        USERELATIONSHIP ( Invoice[PRODUCT_TYPE], 'Product B'[Product Type B] )
    )
VAR CustomersWithoutProductB =
    EXCEPT ( ALL ( Invoice[CUSTOMER_ID] ), CustomersWithProductB )
VAR Result =
    CALCULATE ( [# Customers], KEEPFILTERS ( CustomersWithoutProductB ) )
RETURN
    Result

 

 


I also created similar measures for Tons. With Tons there is an additional measure I created to sum Tons for both products as well as just Product A. 

 

Then, for example, you can include slicers on 'Product A' and 'Product B' that let you filter as required. 

 

You may need to adjust the exact behaviour, but hopefully that helps as a starting point 🙂


Owen Auger
Did I answer your question? Mark my post as a solution!
Blog
Twitter
LinkedIn

View solution in original post

2 REPLIES 2
Anonymous
Not applicable

// With the table you have... call it T.
// You should also have a Calendar table
// that you'd connect via 1:* on [Date]
// to T[invoice_date]. Customer should
// also be a separate dimension.
// Product or 'Product Type', depending
// on what your model really contains,
// should also be a separate dimension.
// Please, read upon the star schema and
// its importance for modeling data for
// Power BI.

// # Cust that had Flat Rolled but not
// Long Product sales in the selected
// time frame. Time frames can only be
// selected from the calendar. Fact
// tables should always be hidden.
[# Cust (FR\LP)] = 
// First, get the id's of the customers
// that did have at least one entry
// of Flat Rolled in the fact table
// but no LP entry. Since there are
// only two possible entries, the formula
// below does the job.
var __custsFrNoLp =
    EXCEPT(
        // This bit gets all customers
        // that exist in the current
        // context.
        DISTINCT( T[customer_id] ),
        // This bit gets all customers
        // that have at least one entry
        // of LP in the current context.
        CALCULATETABLE(
            DISTINCT( T[customer_id] ),
            KEEPFILTERS( 
                'Product Type'[ProductTypeId]
                    = "Long Product"
            )
        )
    )
var __output =
    COUNTROWS( __custsFrNoLp )
return
    __output
    
// Here is the tons for the above customers...
// For the other sets of customers, you'll
// just copy this pattern with the obvious
// changes.
[Tons (FR\LP)] =
var __custsFrNoLp =
    EXCEPT(
        // This bit gets all customers
        // that exist in the current
        // context.
        DISTINCT( T[customer_id] ),
        // This bit gets all customers
        // that have at least one entry
        // of LP in the current context.
        CALCULATETABLE(
            DISTINCT( T[customer_id] ),
            KEEPFILTERS( 
                'Product Type'[ProductTypeId]
                    = "Long Product"
            )
        )
    )
var __output =
    CALCULATE(
        SUM( T[Tons] ),
        TREATAS(
            __custsFrNoLp,
            Customer[customer_id]
        ),
        ALL( Customer )
    )
return
    __output

    
// In the same vain, you can get the
// custs that had LP but no FR
[# Cust (LP\FR)] = 
var __custsLpNoFr =
    EXCEPT(
        DISTINCT( T[customer_id] ),
        CALCULATETABLE(
            DISTINCT( T[customer_id] ),
            KEEPFILTERS( 
                'Product Type'[ProductTypeId]
                    = "Flat Rolled"
            )
        )
    )
var __output =
    COUNTROWS( __custsLpNoFr )
return
    __output

// And here are the ones that did have
// both LP and FR.
[# Custs LP & FR] =
var __custsLpAndFr =
    INTERSECT(
        CALCULATETABLE(
            DISTINCT( T[customer_id] ),
            KEEPFILTERS( 
                'Product Type'[ProductTypeId]
                    = "Long Product"
            )            
        ),
        CALCULATETABLE(
            DISTINCT( T[customer_id] ),
            KEEPFILTERS( 
                'Product Type'[ProductTypeId]
                    = "Flat Rolled"
            )
        )
    )
var __output =
    COUNTROWS( __custsLpAndFr )
return
    __output

And here's the article that you HAVE to read about star schema and why it's of utmost importance in Power BI:

https://docs.microsoft.com/en-us/power-bi/guidance/star-schema

OwenAuger
Super User
Super User

Hi @Anonymous

 

Hi @amartin141

The category of measures you are wanting is Basket Analysis.

I have based my answer here on the Basket analysis page on DAX Patterns.
https://www.daxpatterns.com/basket-analysis/

I have attached a PBIX with sample measures using your data. I have not worried about the Date table in my PBIX as it is not essential to the Basket Analysis setup, but  


The steps I followed are:  

  1. Set up your data model with a Product table and a secondary Product table. On DAX Patterns they call these tables 'Product' and 'And Product'. In my example I called them 'Product A' and 'Product B'. These are both related to your main Sales table (I named this table Invoice) with an inactive relationship with the secondary table.

  2. Create measures as follows:

 

// BASE # CUSTOMERS MEASURE //
# Customers = 
SUMX ( SUMMARIZE ( Invoice, Invoice[CUSTOMER_ID] ), 1 )
// This is equivalent to DISTINCTCOUNT of CUSTOMER_ID
// but DAX Patterns explains why to consider using this instead.

// CUSTOMERS WITH BOTH PRODUCTS (INTERNAL) MEASURE //
# Customers with Both Products (Internal) =
VAR CustomersWithProductB =
    CALCULATETABLE (
        SUMMARIZE ( Invoice, Invoice[CUSTOMER_ID] ),
        REMOVEFILTERS ( 'Product A' ),
        REMOVEFILTERS ( Invoice[PRODUCT_TYPE] ),
        USERELATIONSHIP ( Invoice[PRODUCT_TYPE], 'Product B'[Product Type B] )
    )
VAR Result =
    CALCULATE ( [# Customers], KEEPFILTERS ( CustomersWithProductB ) )
RETURN
    Result


// CUSTOMERS WITH BOTH PRODUCTS FINAL MEASURE //
// Returns blank if same product selected on both A & B
# Customers with Both Products =
IF (
    ISEMPTY (
        INTERSECT (
            DISTINCT ( 'Product A'[Product Type A] ),
            DISTINCT ( 'Product B'[Product Type B] )
        )
    ),
    [# Customers with Both Products (Internal)]
)

// CUSTOMERS WITH PRODUCT A BUT NOT PRODUCT B MEASURE //
# Customers with Product A & Not Product B =
VAR CustomersWithProductB =
    CALCULATETABLE (
        SUMMARIZE ( Invoice, Invoice[CUSTOMER_ID] ),
        REMOVEFILTERS ( 'Product A' ),
        REMOVEFILTERS ( Invoice[PRODUCT_TYPE] ),
        USERELATIONSHIP ( Invoice[PRODUCT_TYPE], 'Product B'[Product Type B] )
    )
VAR CustomersWithoutProductB =
    EXCEPT ( ALL ( Invoice[CUSTOMER_ID] ), CustomersWithProductB )
VAR Result =
    CALCULATE ( [# Customers], KEEPFILTERS ( CustomersWithoutProductB ) )
RETURN
    Result

 

 


I also created similar measures for Tons. With Tons there is an additional measure I created to sum Tons for both products as well as just Product A. 

 

Then, for example, you can include slicers on 'Product A' and 'Product B' that let you filter as required. 

 

You may need to adjust the exact behaviour, but hopefully that helps as a starting point 🙂


Owen Auger
Did I answer your question? Mark my post as a solution!
Blog
Twitter
LinkedIn

Helpful resources

Announcements
Microsoft Fabric Learn Together

Microsoft Fabric Learn Together

Covering the world! 9:00-10:30 AM Sydney, 4:00-5:30 PM CET (Paris/Berlin), 7:00-8:30 PM Mexico City

PBI_APRIL_CAROUSEL1

Power BI Monthly Update - April 2024

Check out the April 2024 Power BI update to learn about new features.

April Fabric Community Update

Fabric Community Update - April 2024

Find out what's new and trending in the Fabric Community.

Top Solution Authors