Iterative/Recursive DAX Calculation

  • Iterative/Recursive DAX Calculation

    Posted by Austin Campbell on March 9, 2021 at 11:26 am
    • Austin Campbell

      Member

      March 9, 2021 at 11:26 AM

      Hello PBI Community,

      I’ve been working on a seemingly easy but in reality a complex visual for awhile now. I feel like I’m very close to solving the solution but I’m at the point where I could use some help.

      Basic Background:

      • I am a Power BI Pro user
      • I have a query that pulls current inventory levels
      • I have a query that pulls expected sales forecast
      • I have a query that pulls expected production forecast
      • I have a query that pulls product hierarchy
      • I have a date table that identifies production days from non-production days

      My request is to build an actual/forecast table that (see Excel file for a detailed example):

      1. If month is historical provide ACTUAL ENDING INVENTORY
        1. This is based off the Inventory query
      2. If month is current or future provide a calculation
        1. Inventory = [Prior Month Inventory]-[Sales]+[Production]

      Astute observers will recognize that my future calculation is an iterative or recursive calculation. DAX absolutely hates these types of circular dependencies and starts kicking up all sorts of errors. I have tried in vain dozens (if not hundreds by this point) different variations of DAX formulas to create the result but have always ended up in failure.

      I recognize that a key component to solving this dilemma is based on the EARLIER equation similar to:

      Sales Cumulative =
      CALCULATE(
      SUMX('SalesPlan'[SalesPlan]),
      Ā Ā Ā Ā FILTER(
      Ā Ā Ā Ā Ā Ā Ā Ā Ā ALL('SalesPlan'[Date]),
      Ā Ā Ā Ā Ā Ā Ā Ā Ā EARLIER('SalesPlan'[Date])>=[Date]
      Ā Ā Ā Ā Ā )
      )

      I have experimented with creating a “virtual” table via the VAR/RETURN similar to the following:

      Ending Inventory =
      //LOAD MEASURES

      VAR _actuals = SUM(Inventory[Inventory])

      VAR _initial =

      VAR _maxDate = CALCULATE(MAXX(Inventory,[DATE],REMOVEFILTERS(Inventory))

      Ā Ā Ā Ā Ā Ā Ā Ā Ā Ā Ā Ā Ā Ā Ā  RETURN

      Ā Ā Ā Ā Ā Ā Ā Ā Ā Ā Ā Ā Ā Ā Ā  CALCULATE(SUM(Inventory[Inventory],FILTER(Inventory,[Date]=_maxDate))

      VAR _sales = SUM(SalesPlan[SalesPlan])

      VAR _production = [AvgDailyProduction] * [ProdDaysInMonth]

      //LOAD DATE VARIABLES

      VAR _currentMonth = MONTH(TODAY())

      VAR _startDate = FIRSTDATE(ALLSELECTED(Dates[Date]))

      //TABLE CREATION
      VAR _table1 = GENERATESERIES(_currentMonth,12)

      VAR _table2 = ADDCOLUMNS(_table1, "Initial Inventory",_initial)

      VAR _table3 = ADDCOLUMNS(_table2, "Date",DATE(YEAR(_startDate),[Value]+1,1)-1)

      VAR _table4 = ADDCOLUMNS(_table3, "Production Cumulative", CALCULATE(_production, FILTER(_table3, EARLIER([Date])>=[Date])))

      VAR _table5 = ADDCOLUMNS(_table4, "Sales Cumulative", CALCULATE(_sales, FILTER(_table4, EARLIER([Date])>=[Date])))

      VAR _table6 = ADDCOLUMNS(_table5, "Shortfall Cumulative", [Sales Cumulative]-([Initial Inventory]+[Production Cumulative]))

      VAR _table7 = ADDCOLUMNS(_table6, "Shortfall Cumalative Max So Far", MAX(MAXX(FILTER(_table6, [Date] <= [Date]), [Shortfall Cumulative]),0))

      VAR _table8 = ADDCOLUMNS(_table7,"Closing Inventory",[Initial Inventory]+[Prouduction Cumulative]-[Sales Cumulative]+[Shortfall Cumalative Max So Far])

      VAR _table9 = CROSSJOIN(_table8,Date)

      RETURN

      IF(SELECTEDVALUE(Date[Month Number])<_currentMonth,_actual,Maxx(_table9,[Closing Inventory]))

      Ā FYI: this code resulted in providing actuals for historical months but generating 0.00 for future months

      Ā I have included a sample excel file and sample .pbix file that provides a very basic example of my true file setup (most of my queries are derived from Oracle, SQL, and Excel).

      Ā Also, as a point of record I’ve consulted the following webpages:

      ——————————
      Austin Campbell
      Manufacturing Data Analyst
      Mebane, NC
      919-304-7886
      ——————————

    • Marc Schroyen

      Member

      March 11, 2021 at 8:15 AM

      Hi Austin,

      You don’t need iterative or recursive calculation.

      You need to setup the right filter context.

      First, you need to get last inventory by product:

      Inventory Last Date = 
      VAR MaxDate =
          MAX ( 'Date'[Date] )
      VAR MaxInventoryDates =
          CALCULATETABLE (
              ADDCOLUMNS (
                  SUMMARIZE ( Inventory, 'Product'[Item #] ),
                  "@MaxInventoryDate", CALCULATE ( MAX ( Inventory[Date] ) )
              ),
              'Date'[Date] < MaxDate
          )
      VAR MAxInventoryDateWithDataLineage =
          TREATAS ( MaxInventoryDates, 'Product'[Item #], 'Date'[Date] )
      VAR Result =
          CALCULATE ( SUM ( Inventory[Inventory] ), MAxInventoryDateWithDataLineage )
      RETURN
          Result
      

      You can find the explanation here: https://www.daxpatterns.com/semi-additive-calculations/
      Once done, you have to calculate the forecast

      Ending Inventory = 
      VAR MaxInventoryDate = CALCULATE( MAX(Inventory[Date]), REMOVEFILTERS(Inventory))
      VAR MaxProductionDate = MAX(Production[Date] )
      VAR MaxSalesDate = MAX(SalesPlan[Date])
      VAR _actuals = SUM(Inventory[Inventory])
      VAR _sales = 
          CALCULATE(
          SUM( SalesPlan[SalesPlan] ),
          FILTER( ALL('Date'), 'Date'[Date] <= MAxSalesDate && 'Date'[Date] > MaxInventoryDate )
          )
      VAR _production = 
          CALCULATE(
              SUM(Production[Production]),
              FILTER(ALL('Date'),'Date'[Date] <= MaxProductionDate && 'Date'[Date] > MaxInventoryDate )
          )
      VAR Forecast = Inventory[Inventory Last Date] + _production - _sales
      RETURN
      SWITCH(
          TRUE(),
          MAX( 'Date'[Date]) <= MaxInventoryDate, _actuals,
          Forecast
      )

      Basically, you start everytime from the last inventory date and add production – sales up to the current month.

      Alternative for forecast:

      Ending Inventory = 
      VAR MaxInventoryDate = CALCULATE( MAX(Inventory[Date]), REMOVEFILTERS(Inventory))
      VAR MaxProductionDate = MAX(Production[Date] )
      VAR MaxSalesDate = MAX(SalesPlan[Date])
      VAR _actuals = SUM(Inventory[Inventory])
      VAR _sales = 
          CALCULATE(
          SUM( SalesPlan[SalesPlan] ),
          'Date'[Date] <= MAxSalesDate,
          'Date'[Date] > MaxInventoryDate 
          )
      /*VAR _production = 
          CALCULATE(
              SUM(Production[Production]),
              FILTER(ALL('Date'),'Date'[Date] <= MaxProductionDate && 'Date'[Date] > MaxInventoryDate )
          ) */
      VAR _production = 
          CALCULATE(
              SUM(Production[Production]),
              'Date'[Date] <= MaxProductionDate,
              'Date'[Date] > MaxInventoryDate 
          )
      VAR Forecast = Inventory[Inventory Last Date] + _production - _sales
      RETURN
      SWITCH(
          TRUE(),
          MAX( 'Date'[Date]) <= MaxInventoryDate, _actuals,
          Forecast
      )

      ——————————
      Marc Schroyen
      LiĆØge Belgium
      ——————————
      ——————————————-

    • Austin Campbell

      Member

      March 11, 2021 at 10:25 AM

      Ā Thank you so much. These calculations solved my issue. I cannot express my gratitude enough!?

      ——————————
      Austin Campbell
      Manufacturing Data Analyst
      Mebane NC
      9193047886
      ——————————
      ——————————————-

    Austin Campbell replied 4 years, 5 months ago 1 Member · 0 Replies
  • 0 Replies

Sorry, there were no replies found.

The discussion ‘Iterative/Recursive DAX Calculation’ is closed to new replies.

Start of Discussion
0 of 0 replies June 2018
Now

Welcome to our new site!

Here you will find a wealth of information created for peopleĀ  that are on a mission to redefine business models with cloud techinologies, AI, automation, low code / no code applications, data, security & more to compete in the Acceleration Economy!