DAX<\/a>. This approach gives you transparency and allows for business-specific adjustments.<\/p>\n\n\n\nSimple Moving Average Forecast:<\/strong><\/p>\n\n\n\nMoving Average Forecast = <\/em><\/p>\n\n\n\nVAR CurrentDate = MAX(‘Date'[Date])<\/em><\/p>\n\n\n\nVAR PeriodLength = 3 \/\/ 3-period moving average<\/em><\/p>\n\n\n\nVAR HistoricalValues = <\/em><\/p>\n\n\n\n CALCULATETABLE(<\/em><\/p>\n\n\n\n VALUES(‘Sales'[Amount]),<\/em><\/p>\n\n\n\n DATESBETWEEN(<\/em><\/p>\n\n\n\n ‘Date'[Date],<\/em><\/p>\n\n\n\n CurrentDate – PeriodLength,<\/em><\/p>\n\n\n\n CurrentDate<\/em><\/p>\n\n\n\n )<\/em><\/p>\n\n\n\n )<\/em><\/p>\n\n\n\nRETURN<\/em><\/p>\n\n\n\n AVERAGEX(HistoricalValues, ‘Sales'[Amount])<\/em><\/p>\n\n\n\nExponential Smoothing with DAX:<\/strong><\/p>\n\n\n\nExponential Smoothing Forecast = <\/em><\/p>\n\n\n\nVAR Alpha = 0.3 \/\/ Smoothing parameter<\/em><\/p>\n\n\n\nVAR CurrentDate = MAX(‘Date'[Date])<\/em><\/p>\n\n\n\nVAR CurrentValue = SUM(‘Sales'[Amount])<\/em><\/p>\n\n\n\nVAR PreviousForecast = <\/em><\/p>\n\n\n\n CALCULATE(<\/em><\/p>\n\n\n\n [Exponential Smoothing Forecast],<\/em><\/p>\n\n\n\n ‘Date'[Date] = CurrentDate – 1<\/em><\/p>\n\n\n\n )<\/em><\/p>\n\n\n\nVAR InitialForecast = <\/em><\/p>\n\n\n\n CALCULATE(<\/em><\/p>\n\n\n\n AVERAGE(‘Sales'[Amount]),<\/em><\/p>\n\n\n\n ‘Date'[Date] <= CurrentDate – 30<\/em><\/p>\n\n\n\n )<\/em><\/p>\n\n\n\nRETURN<\/em><\/p>\n\n\n\n IF(<\/em><\/p>\n\n\n\n ISBLANK(PreviousForecast),<\/em><\/p>\n\n\n\n InitialForecast,<\/em><\/p>\n\n\n\n Alpha * CurrentValue + (1 – Alpha) * PreviousForecast<\/em><\/p>\n\n\n\n )<\/em><\/p>\n\n\n\nSeasonal Adjustment with DAX:<\/strong><\/p>\n\n\n\nSeasonal Forecast = <\/em><\/p>\n\n\n\nVAR CurrentMonth = MONTH(MAX(‘Date'[Date]))<\/em><\/p>\n\n\n\nVAR SeasonalIndex = <\/em><\/p>\n\n\n\n DIVIDE(<\/em><\/p>\n\n\n\n CALCULATE(<\/em><\/p>\n\n\n\n AVERAGE(‘Sales'[Amount]),<\/em><\/p>\n\n\n\n MONTH(‘Date'[Date]) = CurrentMonth,<\/em><\/p>\n\n\n\n ‘Date'[Date] < MAX(‘Date'[Date])<\/em><\/p>\n\n\n\n ),<\/em><\/p>\n\n\n\n CALCULATE(<\/em><\/p>\n\n\n\n AVERAGE(‘Sales'[Amount]),<\/em><\/p>\n\n\n\n ‘Date'[Date] < MAX(‘Date'[Date])<\/em><\/p>\n\n\n\n )<\/em><\/p>\n\n\n\n )<\/em><\/p>\n\n\n\nVAR TrendForecast = [Moving Average Forecast]<\/em><\/p>\n\n\n\nRETURN<\/em><\/p>\n\n\n\n TrendForecast * SeasonalIndex<\/em><\/p>\n\n\n\nPros of DAX forecasting:<\/strong><\/p>\n\n\n\n\n- Full transparency and control<\/li>\n\n\n\n
- Can incorporate business rules<\/li>\n\n\n\n
- Fast execution<\/li>\n\n\n\n
- Easy to modify and test<\/li>\n<\/ul>\n\n\n\n
Cons:<\/strong><\/p>\n\n\n\n\n- Limited to simple algorithms<\/li>\n\n\n\n
- Requires strong DAX skills<\/li>\n\n\n\n
- No built-in statistical validation<\/li>\n<\/ul>\n\n\n\n
Method 3: Python Integration for Advanced Forecasting<\/strong><\/h2>\n\n\n\nThis is where Power BI really shines for serious time series work. You can use Python’s extensive forecasting libraries directly within Power BI.<\/p>\n\n\n\n
Setting up Python forecasting in Power Query:<\/strong><\/p>\n\n\n\nimport pandas as pd<\/em><\/p>\n\n\n\nimport numpy as np<\/em><\/p>\n\n\n\nfrom statsmodels.tsa.holtwinters import ExponentialSmoothing<\/em><\/p>\n\n\n\nfrom statsmodels.tsa.arima.model import ARIMA<\/em><\/p>\n\n\n\nfrom sklearn.metrics import mean_absolute_error, mean_squared_error<\/em><\/p>\n\n\n\nimport warnings<\/em><\/p>\n\n\n\nwarnings.filterwarnings(‘ignore’)<\/em><\/p>\n\n\n\n# Power BI provides ‘dataset’ DataFrame<\/em><\/p>\n\n\n\ndataset[‘date’] = pd.to_datetime(dataset[‘date’])<\/em><\/p>\n\n\n\ndataset = dataset.sort_values(‘date’).reset_index(drop=True)<\/em><\/p>\n\n\n\n# Create time series<\/em><\/p>\n\n\n\nts = dataset.set_index(‘date’)[‘sales’]<\/em><\/p>\n\n\n\n# Ensure regular frequency<\/em><\/p>\n\n\n\nts = ts.asfreq(‘D’, method=’pad’) # Daily frequency, forward fill<\/em><\/p>\n\n\n\n# Split data for validation<\/em><\/p>\n\n\n\ntrain_size = int(len(ts) * 0.8)<\/em><\/p>\n\n\n\ntrain, test = ts[:train_size], ts[train_size:]<\/em><\/p>\n\n\n\n# Multiple forecasting methods<\/em><\/p>\n\n\n\nforecasts = {}<\/em><\/p>\n\n\n\n# 1. Exponential Smoothing<\/em><\/p>\n\n\n\nes_model = ExponentialSmoothing(<\/em><\/p>\n\n\n\n train, <\/em><\/p>\n\n\n\n trend=’add’, <\/em><\/p>\n\n\n\n seasonal=’add’, <\/em><\/p>\n\n\n\n seasonal_periods=7 # Weekly seasonality<\/em><\/p>\n\n\n\n).fit()<\/em><\/p>\n\n\n\nforecasts[‘exponential_smoothing’] = es_model.forecast(len(test))<\/em><\/p>\n\n\n\n# 2. ARIMA Model<\/em><\/p>\n\n\n\n# Auto-select parameters (this can be slow with large datasets)<\/em><\/p>\n\n\n\narima_model = ARIMA(train, order=(1, 1, 1)).fit()<\/em><\/p>\n\n\n\nforecasts[‘arima’] = arima_model.forecast(len(test))<\/em><\/p>\n\n\n\n# 3. Simple seasonal decomposition<\/em><\/p>\n\n\n\nfrom statsmodels.tsa.seasonal import seasonal_decompose<\/em><\/p>\n\n\n\ndecomposition = seasonal_decompose(train, model=’additive’, period=7)<\/em><\/p>\n\n\n\ntrend_forecast = np.full(len(test), decomposition.trend.dropna().iloc[-1])<\/em><\/p>\n\n\n\nseasonal_forecast = np.tile(decomposition.seasonal[:7], len(test)\/\/7 + 1)[:len(test)]<\/em><\/p>\n\n\n\nforecasts[‘seasonal_decomp’] = trend_forecast + seasonal_forecast<\/em><\/p>\n\n\n\n# Calculate accuracy metrics<\/em><\/p>\n\n\n\naccuracies = {}<\/em><\/p>\n\n\n\nfor method, forecast in forecasts.items():<\/em><\/p>\n\n\n\n mae = mean_absolute_error(test, forecast)<\/em><\/p>\n\n\n\n rmse = np.sqrt(mean_squared_error(test, forecast))<\/em><\/p>\n\n\n\n accuracies[method] = {‘MAE’: mae, ‘RMSE’: rmse}<\/em><\/p>\n\n\n\n# Choose best method (lowest RMSE)<\/em><\/p>\n\n\n\nbest_method = min(accuracies.items(), key=lambda x: x[1][‘RMSE’])[0]<\/em><\/p>\n\n\n\nbest_forecast = forecasts[best_method]<\/em><\/p>\n\n\n\n