Search code examples
powerbidaxincrementcircular-dependency

PowerBI DAX: How to create a calculated column, which increments in value when a certain condition is met


I am trying to create a calculated column [NewCol] in DAX. Everytime The previous value of [Col1] is different than that of the current row, I would want the value of NewCol to increase by 1. How can I achieve this without causing a circulair dependency?

Index Col1 NewCol
0 200 0
1 200 0
2 201 1
3 201 1
4 201 1
5 202 2
6 203 3
7 202 4
8 202 4

I know this will create a new column [Prv_Col1] which will fetch the pervious value of Col1.

Prv_Col1 = 
    VAR current_row = 'Table1'[Col1]
    VAR previous_row = CALCULATE(MAX('Table1'[Col1]), FILTER('Table1', 'Table1'[Index] = EARLIER('Table1'[Index]) - 1))
    RETURN
        previous_row

And this is the statement if would want to use to determine if the value of NewCol needs to increase by one.

IF('Table1'[Col1]='Table1'[Prv_Col1], False, True)

I would want to avoid creating the column [Prv_Col1], and have no clue on how to increment on itself when creating [NewCol], as reffering to itself causes circular dependancy as the column does not exist yet.


Solution

  • Try the following (added comments to help explain how it works):

    NewCol = 
      // an alternative to using EARLIER as it should be faster
      var thisIndex = [Index]
    
      // get all previous rows including this one
      var upToThisRow =
        FILTER(
          ALL('Table1'),
          'Table1'[Index] <= thisIndex 
        )
    
      // add a column to this virtual table with a 1 when value changes
      var addCheck = 
        ADDCOLUMNS(
          upToThisRow,
          "check", 
            var thisIndex = [Index]
            var previousValue = 
              CALCULATE(
                MIN('Table1'[Col1]),
                  upToThisRow, 
                  'Table1'[Index] = thisIndex - 1
                )
            return IF(NOT ISBLANK(previousValue ) && previousValue <> [Col1], 1)
        )
        
      // sum up
      return COALESCE(SUMX(addCheck, [check]), 0)
    

    In case it is needed, here's the same with an intermediary Calculate Column (I'm debating with myself if this would perform better than the one above):

    ValueChanged = // (intermediary)
      var thisIndex = [Index]
      var prevRowValue = 
        CALCULATE(
          MIN('Table1'[Col1]),
            REMOVEFILTERS(),
            'Table1'[Index] = thisIndex  - 1
        )
      return IF(NOT ISBLANK(prevRowValue) && prevRowValue <> [Col1], 1)
    
    
    NewCol = 
      var thisIndex = [Index]
      var result =
        CALCULATE(
          SUM('Table1'[ValueChanged]),
          REMOVEFILTERS(),
          'Table1'[Index] <= thisIndex 
        )
      return COALESCE(result, 0)
    

    Additional versions of NewCol to see if they perform better.

    NewCol v1 = // added ValueChanged to the filter
      var thisIndex = [Index]
      var result =
        CALCULATE(
          SUM('Table1'[ValueChanged]),
          REMOVEFILTERS(),
          'Table1'[Index] <= thisIndex && [ValueChanged] = 1
        )
      return COALESCE(result, 0)
    
    
    NewCol v2 =  // using COUNT instead of SUM
      var thisIndex = [Index]
      var result =
        CALCULATE(
          COUNT('Table1'[ValueChanged]),
          REMOVEFILTERS(),
          'Table1'[Index] <= thisIndex && [ValueChanged] = 1
        )
      return COALESCE(result, 0)
    
    
    NewCol v3 =  // using COUNTROWS instead of SUM
      var thisIndex = [Index]
      var result =
        CALCULATE(
          COUNTROWS('Table1'),
          REMOVEFILTERS(),
          'Table1'[Index] <= thisIndex && [ValueChanged] = 1
        )
      return COALESCE(result, 0)