Simple Linear Regression in Power Query

Some Background

I’ve been doing some work setting up a custom work item tracking process in Azure DevOps to support our current way of delivering change. Azure Boards doesn’t really support the sort of customisations we require and so I’ve broken most of the usual Agile tools such as the burndown charts. As a result I have been building our custom reporting in Power BI and have run up against the frustrating limitation that PowerQuery/M doesn’t support forecasting functions. That’s right, those linear regression functions such as FORECAST.LINEAR that have existed in Excel since ever are not available in PowerQuery/M. Surprising and disappointing in equal measure. I guess as this is catered for in Power BI with visualisations and DAX Measures that the support for this isn’t strictly necessary, but my research of countless support forum posts asking for this feature with no real solution suggests it is wanted. At any rate, it is essential for what I am trying to do so I rolled up my sleeves.

The Source Data

In building up my data models for rendering my burn charts I have some data like below. For each week it shows the count of some particular item – in the following screenshot this is showing the number of completed stories. I add a numeric ‘AvgIndex’ helper column that indexes the data for use in projecting ‘Ideal Trend’ and the like.

I had some data like this

This sort of data immediately lends itself to modelling further predictions such as “How likely are we to hit our delivery dates?”, that sort of thing. This would usually require a simple linear regression function. To achieve this I implemented a custom PowerQuery function written in M that can be used across my data sets. Running my function on the above data set results in data like this:

I added the forecast

You can see the final highlighted model step calls the function named “ForecastLinear”, which in turns adds a column named after the regression Y column “WeeklyMaxCount” suffixed with “Forecast”. The function itself is as follows:

let
    Forecast = (sourceTable as table, datecol as text, xcol as text, ycol as text) =>
        let 
            range = Table.SelectRows(sourceTable, each Record.Field(_, datecol) <= Date.From(DateTimeZone.UtcNow())),
            rowcount = Table.RowCount(range),
            sumx = List.Sum(Table.Column(range, xcol)),
            sumx2 = List.Sum(List.Transform(Table.Column(range, xcol), each Number.Power(_, 2))),
            sumy = List.Sum(Table.Column(range, ycol)),
            sumxy = List.Sum(Table.TransformRows(range, each Record.Field(_, xcol) * Record.Field(_, ycol))),
            avgx = List.Average(Table.Column(range, xcol)),
            avgy = List.Average(Table.Column(range, ycol)),
            Slope = ((rowcount * sumxy - sumx * sumy) / (rowcount * sumx2 - Number.Power(sumx, 2))),
            Intercept = avgy - Slope * avgx,
            Result = Table.AddColumn(sourceTable, Text.Combine({ycol, "Forecast"}), each if (Record.Field(_, datecol) > Date.From(DateTimeZone.UtcNow())) then Intercept + Slope * Record.Field(_, xcol) else 0, type number )
        in 
            Result
in
    Forecast

The function expects your source table, your date column (in my case there is always a date column and it is used for determining the known value range versus the forecast range) and then of course the names of the x and y cols.

Once you add this as a ‘Blank Query’ via the Advanced Editor in PowerQuery you can easily call this function from your model query – also using the Advanced Editor or by simply adding a blank step.

You’re then free to populate your visuals as you see fit!

Credit

This solution would have taken me considerably longer without the work of those that came before, namely Daniil Maslyuk over at https://xxlbi.com/ with his excellent breakdown of simple linear regression in DAX: https://xxlbi.com/blog/simple-linear-regression-in-dax/

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s

This site uses Akismet to reduce spam. Learn how your comment data is processed.

%d bloggers like this: