Forum Discussion

Georgetimes's avatar
Georgetimes
Frequent Visitor
1 year ago

calculate difference between two separate filtered measure

Hi,

 

I'm having some issues solving this. I've tried multiple way, none with success.

 

It's hard to provide you with a data sample, but my tables are as per below:

Main Data - columns that matter are: BookingID, Date, Country, Customer


I have 1 date filter to select a period (i.e 30/06/2024 - 12/07/2024)
I have table 1, with filter1 where I'm selecting customer A, B for example and inside the table I can see by country how many bookings they had as per below:

 

 

Same thing for table2, with filter2, however I'm selecting now different customers, customer C,D and I'm seeing for those customers, between the same date period, the number of bookings they had by country:

 

 


What I want:
 - I need to see the difference between results from table1 and table 2. If country X doesn't appear in table1, but appears in table2, then show as -100 (table1 minus table2), or if it appears in table 1 but not table2, then 100


Everything that I've tried shows as blank because of those filter by customer (filter1 and filter2) and I can't find a way of displaying just the countries that are relevent for that date period and customer
A small sample created by me would be this:

BookingIDDateCountryCustomer
130/06/2024United KingdomA
230/06/2024United KingdomA
330/06/2024United KingdomB
401/07/2024United KingdomA
501/07/2024BelgiumA
601/07/2024United KingdomB
701/07/2024United KingdomA
801/07/2024BelgiumA
902/08/2024BelgiumB
1001/01/2023FranceB

 

7 Replies

  • Anonymous's avatar
    Anonymous
    Not applicable

    Hi Georgetimes ,
    You are trying to filter to see the difference between the different customer regions in table 1 and table 2, right, and since I don't understand the structure of your two tables, I'll try to use your example data to try to recreate your problem as best as I can.

    BookingsTable1 = 
    CALCULATE(
        COUNTROWS('Table'),
        FILTER('Table','Table'[Customer] IN {"A", "B"})
    )
    
    BookingsTable2 = 
    CALCULATE(
        COUNTROWS('Table'),
        FILTER('Table','Table'[Customer]IN{"C","D"})
    )
    
    Difference = 
    VAR Table1Bookings = [BookingsTable1]
    VAR Table2Bookings = [BookingsTable2]
    RETURN
        IF(
            ISBLANK(Table1Bookings) && NOT(ISBLANK(Table2Bookings)),
            -100,
            IF(
                NOT(ISBLANK(Table1Bookings)) && ISBLANK(Table2Bookings),
                100,
                Table1Bookings - Table2Bookings
            )
        )
    

    You can see that I set up two MEASURES to make a judgment, I chose two different customers to compare and also I tried to divide the customers of AB and CD into two tables, but after dividing them into two tables, I could not get the correct country field in the matrix, so I tried to write all the COUNTRY fields in one table and then pass a filter to make a judgment. It can fulfill your requirement.

    I hope my thoughts have been helpful, and if you have further questions, feel free to contact me and I'll get back to you the first time I hear from you!

    Hope it helps!

     

    Best regards,
    Community Support Team_ Tom Shen

     

    If this post helps then please consider Accept it as the solution to help the other members find it more quickly.

     

     

    • Georgetimes's avatar
      Georgetimes
      Frequent Visitor

      Hi,

       

      Thank you very much for taking your time to help me with this, I really appreciate it.

       

      This helps me with a part of my issue, however I'm afraid it's not exactly what I was looking for.

       

       - the first two tables should show the count of bookingId.  This will be an easy fix, however wanted to mention it in case it will affect the next steps :

                                  - in table1 Belgium should show 2, UK 2, France 1 and Italy 1

                                  - in table 2 Italy 1, Belgium 1

       

      - I'm not sure if this is correct, but BookingsTable1 and BookingsTable2, both measures might need a different approach as I'm trying to have this fully dynamic. I'm not sure if this will work if I'm selecting for Table1, only customerB, or customer C and D. The "user" will select what Customer he wants to see in table1 and table2 (therefore, the filters). I've tried to play with your PBI example and Table3(the difference table) didn't show the right countries.

      I've selected only customer B (table1) and customer D (table2) and it was supposed to display Belgium, France, UK and Italy. Italy was not showing. ( it should show all countries, just once, no matter if it's in both tables or only in one). It looks likeSee screenshot below:

       

      - this leads me to the next thing. The difference table. This should show the booking difference between table1 and table2. As per the above screenshot, it should show:

                       - Belgium 0 (table 1 has 1 booking for Belgium minus table2 which has 1 booking as well)

                       - France 100 (France is not in table2, therefore 100)

                       - UK 100 (Again, UK not in table2, therefore 100)

                       - Italy -100 (this should show as minus 100 as Italy is only in table2, so the calculation would be 0 minus 100)

       

       

      Hope this makes more sense and thank you very much once again for your help

       

      • Georgetimes's avatar
        Georgetimes
        Frequent Visitor

        Anonymous - any idea about how can I do the above?

         

        Any help is highly appreciated. Thank you!

  • Georgetimes's avatar
    Georgetimes
    Frequent Visitor

    Got it! Here's how we can approach writing a VBA script to create the new Excel file with all the formatting you need. I'll break it down into steps based on your description, and I'll provide the VBA code for each.

    ### Steps Breakdown:
    1. **Customer Reference** – Copy as is.
    2. **Full Name** – Combine Forename, Middle Name, and Surname into one column with proper capitalization.
    3. **Date of Birth** – Combine the day, month, and year columns into one column in the `DD/MM/YYYY` format.
    4. **Address** – Combine the address columns (building number, street, postcode, city, and country) into one column, ensuring capitalization and no extra spaces.
    5. **Email and Mobile** – Add after the address.
    6. **Codes and Meanings** – Use an IF function to display the appropriate meaning based on the code.
    7. **Output** – Create a new sheet with these combined columns in the same order.

    ---

    ### VBA Code:

    ```vba
    Sub CreateNewFile()
    Dim wsSource As Worksheet
    Dim wsNew As Worksheet
    Dim lastRow As Long
    Dim i As Long
    Dim customerRef As String, fullName As String, dob As String, address As String, email As String, mobile As String
    Dim code As String, meaning As String
    Dim forename As String, middleName As String, surname As String
    Dim day As String, month As String, year As String
    Dim building As String, street As String, postcode As String, city As String, country As String

    ' Reference to the original sheet (adjust if your sheet name is different)
    Set wsSource = ThisWorkbook.Sheets("Sheet1")

    ' Create new sheet
    Set wsNew = ThisWorkbook.Sheets.Add
    wsNew.Name = "FormattedData"

    ' Get the last row in the source data (assuming data starts from row 2, row 1 being headers)
    lastRow = wsSource.Cells(wsSource.Rows.Count, "A").End(xlUp).Row

    ' Loop through each row of data
    For i = 2 To lastRow
    ' 1. Customer Reference
    customerRef = wsSource.Cells(i, 1).Value

    ' 2. Full Name (combine Forename, Middle Name, Surname)
    forename = Application.WorksheetFunction.Proper(wsSource.Cells(i, 2).Value)
    middleName = Application.WorksheetFunction.Proper(wsSource.Cells(i, 3).Value)
    surname = Application.WorksheetFunction.Proper(wsSource.Cells(i, 4).Value)
    fullName = forename & " " & middleName & " " & surname

    ' 3. Date of Birth (combine Day, Month, Year)
    day = Format(wsSource.Cells(i, 5).Value, "00")
    month = Format(wsSource.Cells(i, 6).Value, "00")
    year = wsSource.Cells(i, 7).Value
    dob = day & "/" & month & "/" & year

    ' 4. Address (combine Building, Street, Postcode, City, Country)
    building = wsSource.Cells(i, 8).Value
    street = wsSource.Cells(i, 9).Value
    postcode = wsSource.Cells(i, 10).Value
    city = wsSource.Cells(i, 11).Value
    country = wsSource.Cells(i, 12).Value
    address = Application.WorksheetFunction.Proper(Trim(building & " " & street & " " & postcode & " " & city & " " & country))

    ' 5. Email and Mobile (get email and mobile)
    email = wsSource.Cells(i, 13).Value
    mobile = wsSource.Cells(i, 14).Value

    ' 6. Codes and Meanings (example logic)
    code = wsSource.Cells(i, 15).Value
    meaning = GetCodeMeaning(code)

    ' 7. Add the results to the new sheet (order as requested)
    wsNew.Cells(i, 1).Value = customerRef
    wsNew.Cells(i, 2).Value = fullName
    wsNew.Cells(i, 3).Value = dob
    wsNew.Cells(i, 4).Value = address
    wsNew.Cells(i, 5).Value = email
    wsNew.Cells(i, 6).Value = mobile
    wsNew.Cells(i, 7).Value = meaning
    Next i
    End Sub

    ' Function to get the meaning of a code (can be expanded for more codes)
    Function GetCodeMeaning(code As String) As String
    Select Case code
    Case "A1"
    GetCodeMeaning = "Meaning 1"
    Case "B2"
    GetCodeMeaning = "Meaning 2"
    Case "C3"
    GetCodeMeaning = "Meaning 3"
    Case Else
    GetCodeMeaning = "Unknown Code"
    End Select
    End Function
    ```

    ### Explanation:

    1. **Customer Reference** – Simply copies the customer reference as it is.
    2. **Full Name** – The `PROPER` function is used to ensure the first letter of each name part is capitalized. We combine the forename, middle name, and surname.
    3. **Date of Birth** – The day, month, and year columns are combined using `Format` to ensure leading zeros for the day and month. We concatenate them in `DD/MM/YYYY` format.
    4. **Address** – The building, street, postcode, city, and country are combined into one string, and `PROPER` ensures the address is properly capitalized. `Trim` ensures no extra spaces.
    5. **Email and Mobile** – These are placed after the address.
    6. **Codes and Meanings** – The `GetCodeMeaning` function checks the code and returns the corresponding meaning.
    7. **New Sheet** – The script creates a new sheet called `"FormattedData"`, and it fills the columns with the formatted data in the order you requested.

    ---

    ### How to Use the Code:

    1. Press `Alt + F11` to open the VBA editor in Excel.
    2. In the editor, click `Insert > Module` to create a new module.
    3. Paste the code into this module.
    4. Press `F5` or run the macro from Excel to execute it.

    This will create a new sheet in the original workbook with the formatted data as specified. If you need to adjust any of the column indexes or logic, feel free to tweak the code. Let me know if you'd like any further modifications or explanations!

  • Georgetimes's avatar
    Georgetimes
    Frequent Visitor

    Sub ExtractAndProperForename()
    Dim wsSource As Worksheet
    Dim wsTarget As Worksheet
    Dim lastRow As Long
    Dim header As Range
    Dim columnHeaders As Variant
    Dim columnsDict As Object
    Dim i As Long
    Dim forenameColumn As Long
    Dim forenameValue As String

    ' Create a dictionary to hold column headers and their respective column numbers
    Set columnsDict = CreateObject("Scripting.Dictionary")

    ' Define the headers you are looking for
    columnHeaders = Array("Forename", "Surname", "Country", "DOB", "Email", "Phone") ' Add more headers if needed

    ' Reference to the source sheet (adjust if your sheet name is different)
    Set wsSource = ThisWorkbook.Sheets("Sheet1")

    ' Create or reference a target sheet (e.g., "FormattedData")
    On Error Resume Next ' In case the sheet doesn't exist
    Set wsTarget = ThisWorkbook.Sheets("FormattedData")
    On Error GoTo 0 ' Reset error handling

    If wsTarget Is Nothing Then
    ' If "FormattedData" sheet doesn't exist, create it
    Set wsTarget = ThisWorkbook.Sheets.Add
    wsTarget.Name = "FormattedData"
    End If

    ' Get the last row of data in the sheet
    lastRow = wsSource.Cells(wsSource.Rows.Count, "A").End(xlUp).Row

    ' Loop through the first row (header row) to find column numbers for each required header
    For Each header In wsSource.Rows(1).Cells
    headerName = header.Value
    ' If the header matches any in the columnHeaders array, add the column number to the dictionary
    If Not IsError(Application.Match(headerName, columnHeaders, 0)) Then
    columnNumber = header.Column
    columnsDict.Add headerName, columnNumber
    End If
    Next header

    ' If the dictionary has columns, process each row and extract the data
    If columnsDict.Count > 0 Then
    ' Loop through each row and extract the corresponding data
    For i = 2 To lastRow
    ' Extract Forename value using the dynamically found column
    If columnsDict.Exists("Forename") Then
    forenameColumn = columnsDict("Forename")
    forenameValue = Application.WorksheetFunction.Proper(wsSource.Cells(i, forenameColumn).Value)

    ' You can now move the Forename to the target sheet
    wsTarget.Cells(i, 1).Value = forenameValue ' Move Forename to column A
    End If
    Next i
    Else
    MsgBox "No required columns found!", vbExclamation
    End If
    End Sub

    • FlowerPower's avatar
      FlowerPower
      New Member

      Sub TransformCustomerData()

          Dim wsSrc As Worksheet, wsDest As Worksheet

          Dim lastRow As Long, lastCol As Long

          Dim colMap As Object: Set colMap = CreateObject("Scripting.Dictionary")

          Dim i As Long, destRow As Long

         

          'Set source worksheet

          Set wssrc=ThisWorkbook.Sheets("Sheet1")

          lastRow = wsSrc.Cells(wsSrc.Rows.Count, 1).End(xlUp).Row

          lastCol = wsSrc.Cells(1, wsSrc.Columns.Count).End(xlToLeft).Column

         

          'Map Headers

          For i = 1 To lastCol

              If Trim(wsSrc.Cells(1, i).Value) <> "" Then

                 colMap(Trim(wsSrc.Cells(1, i).Value)) = i

              End If

          Next i

         

          'Create destination sheet

          On Error Resume Next: Application.DisplayAlerts = False

          ThisWorkbook.Sheets("Transformed Data").Delete

          Application.DisplayAlerts = True: On Error GoTo 0

         

          Set wsDest = ThisWorkbook.Sheets.Add(After:=wsSrc)

          If wsDest Is Nothing Then

             MsgBox "Failed to create destination sheet. Aborting."

             Exit Sub

            End If

          wsDest.Name = "Transformed Data"

         

          'Output headers

          Dim outHeaders As Variant

          outHeaders = Array("Customer Reference", "Full Name", "Date of birth", "Building number", "Premise", "Street", "City", "Postcode", "Country", "Email", "Landline", "Mobile", _

                             "AuthenticationID", "Date", "Band Text", "Synthetic ID Score", "Email Fraud Type", "Email Fraud Risk Level", "Email First Seen", "Email Searches", _

                             "Email Domain Seen First Time", "SIM Swap", "Call Forwarding")

          For i = 0 To UBound(outHeaders)

              wsDest.Cells(1, i + 1).Value = outHeaders(i)

          Next i

         

          'Setup code maps

          Dim fraudMap As Object: Set fraudMap = CreateObject("Scripting.Dictionary")

          fraudMap("4089520") = "Card Not Present Fraud"

          fraudMap("4089521") = "Chargeback"

          fraudMap("4089522") = "First Party Fraud"

          fraudMap("4089523") = "First Payment Default"

          fraudMap("4089524") = "Identity Theft"

          fraudMap("4089525") = "Suspected Fraud"

          fraudMap("4089526") = "Synthetic ID"

          fraudMap("4089527") = "Suspected Synthetic ID"

          fraudMap("4089528") = "Fraud Type Not Available"

        

          Dim riskMap As Object: Set riskMap = CreateObject("Scripting.Dictionary")

          riskMap("4083510") = "Very Low"

          riskMap("4083511") = "Low"

          riskMap("4083512") = "Moderate"

          riskMap("4083513") = "Review"

          riskMap("4083514") = "High"

          riskMap("4083515") = "Very High"

          riskMap("4083600") = "No Risk Flags Hit"

         

          Dim firstSeenMap As Object: Set firstSeenMap = CreateObject("Scripting.Dictionary")

          firstSeenMap("4083400") = "Today"

          firstSeenMap("4083401") = "1-7 Days Ago"

          firstSeenMap("4083402") = "1-2 Weeks Ago"

          firstSeenMap("4083403") = "2-4 Weeks Ago"

          firstSeenMap("4083404") = "1-3 Months Ago"

          firstSeenMap("4083405") = "3-6 Months Ago"

          firstSeenMap("4083406") = "6-12 Months Ago"

          firstSeenMap("4083407") = "1-3 Years Ago"

          firstSeenMap("4083408") = "3-5 Years Ago"

          firstSeenMap("4083409") = "5+ Years Ago"

         

          Dim searchMap As Object: Set searchMap = CreateObject("Scripting.Dictionary")

          searchMap("4083410") = "1 time in last 7 days"

          searchMap("4083411") = "2-4 times in last 7 days"

          searchMap("4083412") = "5-9 times in last 7 days"

          searchMap("4083413") = "10+ times in last 7 days"

         

          Dim domainSeenMap As Object: Set domainSeenMap = CreateObject("Scripting.Dictionary")

          domainSeenMap("4083200") = "less than 6 months ago"

          domainSeenMap("4083201") = "6-12 months ago"

          domainSeenMap("4083202") = "1-3 years ago"

          domainSeenMap("4083203") = "3-5 years ago"

          domainSeenMap("4083204") = "5+ years ago"

         

          Dim simSwapMap As Object: Set simSwapMap = CreateObject("Scripting.Dictionary")

          simSwapMap("5176504") = "SIMSwap 24hrs"

          simSwapMap("5176505") = "SIMSwap 48hrs"

          simSwapMap("5176506") = "SIMSwap 7 days"

          simSwapMap("5176507") = "SIMSwap 30 days"

          simSwapMap("5176508") = "SIMSwap 60 days"

          simSwapMap("5176509") = "SIMSwap 90 days"

          simSwapMap("5176510") = "SIMSwap 180 days"

          simSwapMap("5176511") = "SIMSwap 365 days"

          simSwapMap("5176512") = "SIMSwap over 365 days"

         

          'Loop rows

          destRow = 2

          For i = 2 To lastRow

              With wsDest

                   .Cells(destRow, 1).Value = wsSrc.Cells(i, colMap("Customer Reference"))

                  

                    Dim fname As String, mname As String, sname As String

                    fname = ProperName(wsSrc.Cells(i, colMap("Forename")).Value)

                    mname = ProperName(wsSrc.Cells(i, colMap("Middle Name")).Value)

                    sname = ProperName(wsSrc.Cells(i, colMap("Surname")).Value)

                    .Cells(destRow, 2).Value = BuildFullName(fname, mname, sname)

       

         

                    Dim dd, mm, yyyy

                    dd = wsSrc.Cells(i, colMap("Day of Birth")).Value

                    mm = wsSrc.Cells(i, colMap("Month of Birth")).Value

                    yyyy = wsSrc.Cells(i, colMap("Year of Birth")).Value

                    If IsNumeric(dd) And IsNumeric(mm) And IsNumeric(yyyy) Then

                    If dd >= 1 And dd <= 31 And mm >= 1 And mm <= 12 And yyyy >= 1900 Then

                       .Cells(destRow, 3).Value = DateSerial(yyyy, mm, dd)

                       .Cells(destRow, 3).NumberFormat = "dd/mm/yyyy"

                    End If

                  End If

         

                    Dim line1, line2

                    line1 = Trim(wsSrc.Cells(i, colMap("Current Address Building")).Value)

                    line2 = Trim(wsSrc.Cells(i, colMap("Current Address Premise")).Value)

                    .Cells(destRow, 4).Value = ProperName(IIf(line1 <> "", line1, line2))

                    .Cells(destRow, 5).Value = ProperName(wsSrc.Cells(i, colMap("Current Address Premise")).Value)

                    .Cells(destRow, 6).Value = ProperName(wsSrc.Cells(i, colMap("Current Address Street")).Value)

                    .Cells(destRow, 7).Value = ProperName(wsSrc.Cells(i, colMap("Current Address City")).Value)

                    .Cells(destRow, 8).Value = wsSrc.Cells(i, colMap("Current Address Zip/Postcode")).Value

          

                    .Cells(destRow, 9).Value = wsSrc.Cells(i, colMap("Current Address Country")).Value

                    .Cells(destRow, 10).Value = wsSrc.Cells(i, colMap("Email")).Value

                    .Cells(destRow, 11).Value = wsSrc.Cells(i, colMap("Land Telephone Number")).Value

                    .Cells(destRow, 12).Value = wsSrc.Cells(i, colMap("Mobile Telephone Number")).Value

                    .Cells(destRow, 13).Value = wsSrc.Cells(i, colMap("Authentication ID")).Value

                    .Cells(destRow, 14).Value = wsSrc.Cells(i, colMap("Timestamp")).Value

                    .Cells(destRow, 15).Value = wsSrc.Cells(i, colMap("Band Text")).Value

                    .Cells(destRow, 16).Value = wsSrc.Cells(i, colMap("Synthetic ID Score")).Value

         

                    Dim j As Long, checkCode As String

                    Dim fraudVal As String: fraudVal = "False"

                    Dim riskVal As String: riskVal = "False"

                    Dim firstSeenVal As String: firstSeenVal = "False"

                    Dim searchVal As String: searchVal = "False"

                    Dim domainSeenVal As String: domainSeenVal = "False"

                    Dim simSwapVal As String: simSwapVal = "False"

                    Dim callForwardingVal As String: callForwardingVal = "False"

         

                    For j = wsSrc.Range("IZ1").Column To wsSrc.Range("ABC1").Column

                        checkCode = Trim(wsSrc.Cells(i, j).Text)

                        If fraudVal = "False" And fraudMap.exists(checkCode) Then fraudVal = fraudMap(checkCode)

                        If riskVal = "False" And riskMap.exists(checkCode) Then riskVal = riskMap(checkCode)

                        If firstSeenVal = "False" And firstSeenMap.exists(checkCode) Then firstSeenVal = firstSeenMap(checkCode)

                        If searchVal = "False" And searchMap.exists(checkCode) Then searchVal = searchMap(checkCode)

                        If domainSeenVal = "False" And domainSeenMap.exists(checkCode) Then domainSeenVal = domainSeenMap(checkCode)

                        If simSwapVal = "False" And simSwapMap.exists(checkCode) Then simSwapVal = simSwapMap(checkCode)

                        If callForwardingVal = "False" And checkCode = "5176600" Then callForwardingVal = "True"

                   Next j

         

                   .Cells(destRow, 17).Value = fraudVal

                   .Cells(destRow, 18).Value = riskVal

                   .Cells(destRow, 19).Value = firstSeenVal

                   .Cells(destRow, 20).Value = searchVal

                   .Cells(destRow, 21).Value = domainSeenVal

                   .Cells(destRow, 22).Value = simSwapVal

                   .Cells(destRow, 23).Value = callForwardingVal

               End With

               destRow = destRow + 1

           Next i

       

           MsgBox "Data transformation complete!"

       End Sub

       

       

      Function ProperName(ByVal str As String) As String

         Dim x As Variant, i As Integer

         str = Application.WorksheetFunction.Trim(str)

         x = Split(LCase(str))

         For i = 0 To UBound(x)

             If Len(x(i)) > 0 Then

                 x(i) = UCase(Left(x(i), 1)) & Mid(x(i), 2)

             End If

          Next i

          ProperName = Join(x, " ")

      End Function

       

      Function BuildFullName(fname As String, mname As String, sname As String) As String

           Dim parts As Collection

           Set parts = New Collection

           If Len(Trim(fname)) > 0 Then parts.Add Trim(fname)

           If Len(Trim(mname)) > 0 Then parts.Add Trim(mname)

           If Len(Trim(sname)) > 0 Then parts.Add Trim(sname)

          

           Dim fullName As String, part As Variant

           For Each part In parts

               fullName = fullName & part & " "

           Next part

          

           BuildFullName = Application.WorksheetFunction.Trim(fullName)

      End Function