Forum Discussion

kangx322's avatar
kangx322
Frequent Visitor
4 years ago
Solved

Calculation with previous month's value

I have excel calculation that I would like to replicate in Power Query. 

 

Is this possible in Power Query?

 

 

 

  • kangx322  try this

    let
        Source = Table.FromRows(Json.Document(Binary.Decompress(Binary.FromText("i45WMjIwMtQ1ACElHSUjC6VYHSQxI6CYOaqQMUiZGaqYCVDMxABVzBQoZogiZGgGEgJqjQUA", BinaryEncoding.Base64), Compression.Deflate)), let _t = ((type nullable text) meta [Serialized.Text = true]) in type table [Date = _t, Value = _t]),
        #"Changed Type" = Table.TransformColumnTypes(Source,{{"Date", type date}, {"Value", Int64.Type}}),
        Value = #"Changed Type"[Value],
        Loop = List.Generate(
                            ()=>[i=0,j=Value{i},k=j],
                            each [i]<List.Count(Value),
                            each [i=[i]+1, j=Value{i}, k=[k]*3+j*0.5],
                            each[k]
        ),
        Custom1 = Table.FromColumns(Table.ToColumns(#"Changed Type")&{Loop},List.Combine({Table.ColumnNames(#"Changed Type"),{"cumulativeValue"}}))
    in
        Custom1

     

     

10 Replies

  • smpa01's avatar
    smpa01
    Community Champion

    kangx322  try this

    let
        Source = Table.FromRows(Json.Document(Binary.Decompress(Binary.FromText("i45WMjIwMtQ1ACElHSUjC6VYHSQxI6CYOaqQMUiZGaqYCVDMxABVzBQoZogiZGgGEgJqjQUA", BinaryEncoding.Base64), Compression.Deflate)), let _t = ((type nullable text) meta [Serialized.Text = true]) in type table [Date = _t, Value = _t]),
        #"Changed Type" = Table.TransformColumnTypes(Source,{{"Date", type date}, {"Value", Int64.Type}}),
        Value = #"Changed Type"[Value],
        Loop = List.Generate(
                            ()=>[i=0,j=Value{i},k=j],
                            each [i]<List.Count(Value),
                            each [i=[i]+1, j=Value{i}, k=[k]*3+j*0.5],
                            each[k]
        ),
        Custom1 = Table.FromColumns(Table.ToColumns(#"Changed Type")&{Loop},List.Combine({Table.ColumnNames(#"Changed Type"),{"cumulativeValue"}}))
    in
        Custom1

     

     

    • kangx322's avatar
      kangx322
      Frequent Visitor

      Thank you. 

      Could you explain the code little bit? 
      What if I change my original column name to "Current"? which part of the code would change? 

      Also, would this be only way? I am little worried about the performance as my real data is little big

  • smpa01's avatar
    smpa01
    Community Champion

    kangx322 if you change the column name Value to Current then you need to change the only one line in code, i.e. following line

     

    Value =#"Changed Type"[Current]

     

    I am not sure if there is any other way to achieve this, unless you are querying from a RDBMS and run a for loop on the server side

     

  • kangx322's avatar
    kangx322
    Frequent Visitor

    smpa01  Do you have any recommendation for improving performance? When I run this with my real data, it is taking about 3 hours to run. 

    • ronrsnfld's avatar
      ronrsnfld
      Super User

      Buffer the list.

       

      Change the third line of code to:

          Value = List.Buffer(#"Changed Type"[Value]),
    • smpa01's avatar
      smpa01
      Community Champion

      kangx322 

      there are few things you can try out.

      A. If you are querying a database, run the loop on the server side.

       

      B. Known PQ performance tuning

      https://www.thebiccountant.com/speedperformance-aspects/

       

      C. You can try List. Buffer as ronrsnfld mentioned

       

      D. For calualtion if you prefre List.Generate, you can reduce one step like this

          Loop = List.Generate(
                              ()=>[i=0,k=Value{i}],
                              each [i]<List.Count(Value),
                              each [i=[i]+1, k=[k]*3+Value{i}*0.5],
                              each[k]
          )

       

      E You can obtain same result by using an Accumulator too

        Accumulator = List.Skip(
          List.Accumulate(
            Value, 
            {0}, 
            (state, current) =>
              if current = Value{0} then
                state & {List.Last(state) + current}
              else
                state & {List.Last(state) * 3 + current * 0.5}
          )
        )

      The combinded code is here and I am not sure wich one would give you better performance, you need to test it out.

       

      The combined code is here.

      let
        Source = Table.FromRows(
          Json.Document(
            Binary.Decompress(
              Binary.FromText(
                "i45WMjIwMtQ1ACElHSUjC6VYHSQxI6CYOaqQMUiZGaqYCVDMxABVzBQoZogiZGgGEgJqjQUA", 
                BinaryEncoding.Base64
              ), 
              Compression.Deflate
            )
          ), 
          let
            _t = ((type nullable text) meta [Serialized.Text = true])
          in
            type table [Date = _t, Value = _t]
        ), 
        #"Changed Type" = Table.TransformColumnTypes(Source, {{"Date", type date}, {"Value", Int64.Type}}), 
        Value = #"Changed Type"[Value], 
        Loop = List.Generate(
          () => [i = 0, k = Value{i}], 
          each [i] < List.Count(Value), 
          each [i = [i] + 1, k = [k] * 3 + Value{i} * 0.5], 
          each [k]
        ), 
        Accumulator = List.Skip(
          List.Accumulate(
            Value, 
            {0}, 
            (state, current) =>
              if current = Value{0} then
                state & {List.Last(state) + current}
              else
                state & {List.Last(state) * 3 + current * 0.5}
          )
        ), 
        Custom1 = Table.FromColumns(
          Table.ToColumns(#"Changed Type") & {Accumulator}, 
          List.Combine({Table.ColumnNames(#"Changed Type"), {"cumulativeValue"}})
        )
      in
        Custom1

      Would have been a lot easier performance wise, had it been achievable in DAX and I don't know if I can do recursion of this sort in DAXAlexisOlson  

       

       

      • AlexisOlson's avatar
        AlexisOlson
        Super User

        It's possible in DAX but complexity is O(n^2) rather than the O(n) in Power Query since you have to calculate each row from the beginning instead of the last row.

         

        Cumulative = 
        VAR Subtable = FILTER ( Query1, Query1[Date] <= EARLIER ( Query1[Date] ) )
        VAR AddIndex = ADDCOLUMNS ( Subtable, "Index", RANKX ( Subtable, Query1[Date],, DESC ) )
        VAR MaxIndex = MAXX ( AddIndex, [Index] )
        RETURN
            SUMX (
                AddIndex,
                POWER ( 3, [Index] - 1 ) * [Value] * IF ( [Index] = MaxIndex, 1, 0.5 )
            )