Don't miss your chance to take exam DP-600 or DP-700 on us!
Request nowLearn from the best! Meet the four finalists headed to the FINALS of the Power BI Dataviz World Championships! Register now
Dear Power Query forum,
I am stuck with a logically simple and in an excel spreadsheet fairly easy to realize problem; but in PQ it exceeds my current capabilities. Also search in the internet, including this forum and youtube, did not provide a tangible result.
I have a list of year & month combinations which I want to enrich with a decreasing value, based on the previous row value multiplied by a rate of decrease. In the spreadsheet the column [DeclineExcel] shows my aspired outcome with excel formulas. But in the particular case I want to use this, I cannot do it in excel. And also as thinking about this in PQ intrigued me, I want to learn how to achieve that in PQ.
So I added an index column and tried to reference with the previous step and the current index -1. But it failed and I also sense that this approach is too simple to work.
https://c.gmx.net/@324888734501700174/fqDf8Y0Tnd_LIMscLcrNkQ
Does the forum have any idea or an example which PQ statements can solve such task?
Thank you!
Best regards, Andreas
Solved! Go to Solution.
This just goes through ValueStart and Rate and applies the same formula as in your Excel.
Sample
| Year | Month | ValueStart | Rate |
| 2025 | 4 | 14 | 0.015 |
| 2025 | 5 | null | 0.015 |
| 2025 | 6 | null | 0.015 |
| 2025 | 7 | null | 0.015 |
| 2025 | 8 | null | 0.015 |
| 2025 | 9 | null | 0.015 |
| 2025 | 10 | null | 0.015 |
| 2025 | 11 | null | 0.015 |
| 2025 | 12 | null | 0.015 |
| 2026 | 1 | null | 0.015 |
| 2026 | 2 | null | 0.015 |
| 2026 | 3 | null | 0.015 |
| 2026 | 4 | null | 0.015 |
| 2026 | 5 | null | 0.015 |
| 2026 | 6 | null | 0.015 |
| 2026 | 7 | null | 0.015 |
| 2026 | 8 | null | 0.015 |
| 2025 | 2 | 28 | 0.023 |
| 2025 | 3 | null | 0.023 |
| 2025 | 4 | null | 0.023 |
| 2025 | 5 | null | 0.023 |
| 2025 | 6 | null | 0.023 |
| 2025 | 7 | null | 0.023 |
| 2025 | 8 | null | 0.023 |
| 2025 | 9 | null | 0.023 |
| 2025 | 10 | null | 0.023 |
| 2025 | 11 | null | 0.023 |
| 2025 | 12 | null | 0.023 |
| 2026 | 1 | null | 0.023 |
| 2026 | 2 | null | 0.023 |
| 2026 | 3 | null | 0.023 |
| 2026 | 4 | null | 0.023 |
M (advanced editor)
let
Source = Sample,
ValueStarts = List.Buffer( Source[ValueStart] ),
Rates = List.Buffer( Source[Rate] ),
GenDecline = List.Generate(
()=>[i=0,v=ValueStarts{i}], each [i] < Table.RowCount(Source),
each [
i = [i] + 1,
v = if ValueStarts{i} is null then [v]*(1-Rates{i}) else ValueStarts{i}
],
each [v]
),
CombineColumns = Table.FromColumns(
Table.ToColumns(Source) & {GenDecline},
// dynamically adds new column name and type to Source
// with given sample, equivalent of:
// type table [Year=Int64.Type,Month=Int64.Type,ValueStart=number,Rate=number,Decline=number]
type table Type.ForRecord(
Type.RecordFields( Type.TableRow( Value.Type( Source ) ) )
& [Decline=[Type=type number,Optional=false]],
false
)
)
in
CombineColumns
Output
This just goes through ValueStart and Rate and applies the same formula as in your Excel.
Sample
| Year | Month | ValueStart | Rate |
| 2025 | 4 | 14 | 0.015 |
| 2025 | 5 | null | 0.015 |
| 2025 | 6 | null | 0.015 |
| 2025 | 7 | null | 0.015 |
| 2025 | 8 | null | 0.015 |
| 2025 | 9 | null | 0.015 |
| 2025 | 10 | null | 0.015 |
| 2025 | 11 | null | 0.015 |
| 2025 | 12 | null | 0.015 |
| 2026 | 1 | null | 0.015 |
| 2026 | 2 | null | 0.015 |
| 2026 | 3 | null | 0.015 |
| 2026 | 4 | null | 0.015 |
| 2026 | 5 | null | 0.015 |
| 2026 | 6 | null | 0.015 |
| 2026 | 7 | null | 0.015 |
| 2026 | 8 | null | 0.015 |
| 2025 | 2 | 28 | 0.023 |
| 2025 | 3 | null | 0.023 |
| 2025 | 4 | null | 0.023 |
| 2025 | 5 | null | 0.023 |
| 2025 | 6 | null | 0.023 |
| 2025 | 7 | null | 0.023 |
| 2025 | 8 | null | 0.023 |
| 2025 | 9 | null | 0.023 |
| 2025 | 10 | null | 0.023 |
| 2025 | 11 | null | 0.023 |
| 2025 | 12 | null | 0.023 |
| 2026 | 1 | null | 0.023 |
| 2026 | 2 | null | 0.023 |
| 2026 | 3 | null | 0.023 |
| 2026 | 4 | null | 0.023 |
M (advanced editor)
let
Source = Sample,
ValueStarts = List.Buffer( Source[ValueStart] ),
Rates = List.Buffer( Source[Rate] ),
GenDecline = List.Generate(
()=>[i=0,v=ValueStarts{i}], each [i] < Table.RowCount(Source),
each [
i = [i] + 1,
v = if ValueStarts{i} is null then [v]*(1-Rates{i}) else ValueStarts{i}
],
each [v]
),
CombineColumns = Table.FromColumns(
Table.ToColumns(Source) & {GenDecline},
// dynamically adds new column name and type to Source
// with given sample, equivalent of:
// type table [Year=Int64.Type,Month=Int64.Type,ValueStart=number,Rate=number,Decline=number]
type table Type.ForRecord(
Type.RecordFields( Type.TableRow( Value.Type( Source ) ) )
& [Decline=[Type=type number,Optional=false]],
false
)
)
in
CombineColumns
Output
Dear MarkLaf,
thank you so much for providing your solution! List.Generate I did not have on my radar. Your code works as expected and directed me towards several aspects I must now further explore with the knowledge materials out there.
Best regards, Andreas
There are probably more efficient ways to do this. But this should work for you. It relies on grouping the table into sections and generating an output list for each section. A nested zero based index is then used to extract the desired output from the index position of the list.
let
Source = Table.FromRows(Json.Document(Binary.Decompress(Binary.FromText("fdFBDoAgDETRu7AmhlaoeBbj/a+hjYmK6XcBizeQNNNtS1q0pZzqecSvMhVpac934idyA1/AO/gKLoUCoUCDwMeMPrjT+xm8gkcVGVRkUJH9VORzar8Snd/JM+noFbyBG/gC3sFX8NcyP4FQoEEwLnN0eh9VNC7TfT8A", BinaryEncoding.Base64), Compression.Deflate)), let _t = ((type nullable text) meta [Serialized.Text = true]) in type table [Year = _t, Month = _t, ValueStart = _t, Rate = _t]),
#"Changed Type" =
Table.TransformColumnTypes(
Source,
{
{"Year", Int64.Type}, {"Month", Int64.Type}, {"ValueStart", Int64.Type}, {"Rate", type number}
}
),
//this uses the ValueStart column to group the the table into sections that can be indexed and can have the list generate fuction applied.
//it would be more stable if a more reliable grouping column was used.
#"Filled Down" =
Table.FillDown(
#"Changed Type",
{"ValueStart"}
),
//group the table by ValueStart and Rate, getting a nested table row count and all rows in the table
#"Grouped Rows" =
Table.Group(
#"Filled Down",
{"ValueStart", "Rate"},
{
{"Count", each Table.RowCount(_), Int64.Type},
{"AllRows", each _, type table [Year=nullable number, Month=nullable number, ValueStart=nullable number, Rate=nullable number]}
}
),
//use the List.Generate function to create a list of outputs
Custom2 =
Table.AddColumn(
#"Grouped Rows",
"list",
each
List.Generate(
()=> [
x=[ValueStart], //initial x value is the ValueStart value for the row
y=[Rate], //y is always the Rate for the row
z=[Count] //z is the number of rows in the nested table, i.e., the number of output rows we need
],
each [z] > 0, //iterates as long as z is > 0
each [
x= [x] - ([x]*[y]), //for every iteration, x is set to the previous value of x minus the previous value of x times the rate
y=[y], //y never changes
z = [z]-1 //z is decreased by one for every iteration
],
each [x] //return the value of x to the list
),
type list
),
//add an index column to the nested AllRows table. IMPORTANT that this starts at 0 for list indexing reasons.
Custom1 =
Table.TransformColumns(
Custom2,
{
{"AllRows", each Table.AddIndexColumn(_, "Index", 0, 1, Int64.Type), type table [Year=nullable number, Month=nullable number, Index=nullable number]}
}
),
//expand the needed columns from the nested table to new rows
#"Expanded AllRows" =
Table.ExpandTableColumn(
Custom1,
"AllRows",
{"Year", "Month", "Index"},
{"Year", "Month", "Index"}
),
//add a column that displays the value from the list at the current row's index position.
Custom3 =
Table.AddColumn(
#"Expanded AllRows",
"Output",
each [list]{[Index]},
type number
),
//sort the needed columns
Custom4 =
Table.SelectColumns(
Custom3,
{"Year", "Month", "ValueStart", "Rate", "Output"}
)
in
Custom4
Proud to be a Super User! | |
Dear jgeddes,
thank you for taking the time and come up with a solution. I have implmented the code in my sample file and it works! For efficiency I think I will go for now with the other solution in this thread, but as in your code some other commands are featured I will also keep it to learn more about these commands.
Best regards, Andreas
Share feedback directly with Fabric product managers, participate in targeted research studies and influence the Fabric roadmap.
Check out the February 2026 Power BI update to learn about new features.
| User | Count |
|---|---|
| 10 | |
| 9 | |
| 9 | |
| 8 | |
| 6 |