Руководитель открывает дашборд, видит линию выручки за 12 месяцев. Хочет видеть, куда тренд идёт на 3-6 месяцев вперёд. Желание понятное — но в Power BI не так просто сделать красиво. Типичные неудачи:
- Две раздельные линии, которые не соединяются в точке «сегодня». Выглядит разорванно.
- Одна линия, но без визуального отличия факта и прогноза. Непонятно, где заканчиваются реальные данные.
- Встроенный Forecast в стандартном line-визуале Power BI — работает только для простых случаев, плохо настраивается, не поддерживает бизнес-логику.
Правильное решение: две меры (Actual и Forecast), одна точка перекрытия, conditional formatting на линии. Результат выглядит примерно так:
Линия проходит через точку «сегодня» без разрыва. Фактическая часть — сплошной синий. Прогноз — пунктирный фиолетовый. Доверительный интервал — полупрозрачная область вокруг прогноза. Сделаем это пошагово.
Модель данных: готовим источники
Минимум нужны две таблицы:
fact_sales— реальные данные: date, выручка. История, скажем, 24 месяца назад до «сегодня».fact_forecast— прогнозные значения: date, forecast_выручка, forecast_lower (нижняя граница интервала), forecast_upper (верхняя). От «сегодня» на 6-12 месяцев вперёд. Откуда берётся прогноз — отдельная тема (ниже обсудим варианты).
Обе таблицы связаны с общей dim_date. Важно: диапазон dim_date должен покрывать и факт, и прогноз. Если прогноз на июль 2027, а dim_date заканчивается в апреле 2026, прогнозные точки не отобразятся.
Базовые меры Actual и Forecast
Actual Revenue =
SUM(fact_sales[revenue])
Forecast Revenue =
SUM(fact_forecast[forecast_revenue])
Если положить обе в line-chart с осью dim_date[date], получите две линии: Actual заканчивается «сегодня», Forecast начинается «сегодня». Но визуал отрисует разрыв — в точке today одна линия заканчивается, другая стартует с нуля в предыдущей точке (NaN показывается как 0 или gap).
Трюк 1: общая точка «сегодня»
Чтобы линии соединились, в точке «сегодня» оба значения должны существовать. Делается это в мере Forecast: добавляем туда фактическое значение последнего дня.
Forecast Revenue (Seamless) =
VAR _today = TODAY()
VAR _current_date = SELECTEDVALUE(dim_date[date])
VAR _is_today_or_future = _current_date >= _today
VAR _forecast_value = SUM(fact_forecast[forecast_revenue])
VAR _actual_today =
CALCULATE(
SUM(fact_sales[revenue]),
dim_date[date] = _today
)
RETURN
SWITCH(
TRUE(),
_current_date = _today, _actual_today, // точка «сегодня» = actual
_is_today_or_future, _forecast_value, // будущее = forecast
BLANK() // прошлое = blank (не отрисовываем)
)
Симметрично в Actual — чтобы не было «хвоста» от фактической линии в будущее:
Actual Revenue (Seamless) =
VAR _today = TODAY()
VAR _current_date = SELECTEDVALUE(dim_date[date])
RETURN
IF(
_current_date <= _today,
SUM(fact_sales[revenue]),
BLANK()
)
Теперь в line-chart две линии проходят через общую точку «сегодня», Actual — слева, Forecast — справа. BLANK ячейки Power BI просто не рисует. Получается непрерывная кривая.
Трюк 2: разный стиль линий
По умолчанию обе линии визуал рисует одинаково. Чтобы forecast был пунктирным, в line-chart настраиваем:
- Выбираем серию «Forecast выручка (Seamless)»
- Format → Shapes → Stroke style: Dashed
- Stroke width: 2-3px
- Line type: Dashed (в зависимости от версии Power BI)
- Можно поменять цвет на более «гипотетический» (фиолетовый, серый)
Для Actual: Solid, более насыщенный цвет (обычно основной бренд-цвет).
Трюк 3: доверительный интервал
Прогноз без интервала — просто точка, которая может сильно ошибаться. Нормальный визуал показывает диапазон уверенности: «скорее всего будет от X до Y». Делается через тип графика «Line and stacked column chart» или через наложение shaded area.
Создаём две дополнительные меры:
Forecast Lower (Seamless) =
VAR _today = TODAY()
VAR _current_date = SELECTEDVALUE(dim_date[date])
RETURN
IF(
_current_date > _today,
SUM(fact_forecast[forecast_lower]),
BLANK()
)
Forecast Upper (Seamless) =
VAR _today = TODAY()
VAR _current_date = SELECTEDVALUE(dim_date[date])
RETURN
IF(
_current_date > _today,
SUM(fact_forecast[forecast_upper]),
BLANK()
)
// Вспомогательная: ширина интервала (для stacked area)
Forecast Band Width =
[Forecast Upper (Seamless)] - [Forecast Lower (Seamless)]
Вариант 1 (простой): в отдельном line-chart поверх основного рисуем область между Lower и Upper как полупрозрачный fill. Нужен custom visual «Advanced Scorecard Deneb» или аналогичный.
Вариант 2 (через нативный визуал): делаем Area Chart с двумя слоями — Lower (невидимый, только базис) и Band Width (полупрозрачный fill). Визуально получается «облако вокруг forecast».
Вариант 3 (Deneb / Vega-Lite): полная свобода. Задаёте JSON-спецификацию с AreaMark для интервала + LineMark для точек. Рекомендуется для production-дашбордов, где внешний вид критичен.
Откуда брать прогноз
Главный вопрос, который не связан с DAX: как посчитать forecast_выручка. Пять распространённых вариантов:
| Метод | Где считается | Когда применять |
|---|---|---|
| Простая экстраполяция (линейный тренд) | DAX (TRENDLINE) или ETL | Быстрый старт, стабильный тренд без сезонности |
| Скользящее среднее × YoY-коэффициент | ETL (SQL) или DAX | Есть сезонность, но нужна простая модель |
| Holt-Winters (экспоненциальное сглаживание) | Python/R в ETL | Сезонность + тренд, стандарт ритейла |
| ARIMA / SARIMA | Python (statsmodels) | Сложная временна́я динамика, большие ряды |
| Prophet / NeuralProphet | Python | Множественная сезонность, праздники, outliers |
| Бизнес-план (ручной прогноз) | Excel/CRM → fact_forecast | Когда есть S&OP-процесс и утверждённые цифры |
На практике чаще всего: ETL-скрипт на Python раз в сутки считает Prophet или Holt-Winters, складывает результат с доверительными интервалами в таблицу fact_forecast. Power BI читает оттуда через тот же DWH. Пересчёт автоматический, визуал обновляется при очередном refresh.
Простейший forecast прямо в DAX
Если ML-инфраструктуры нет, а прогноз нужен уже сегодня — можно собрать наивный forecast на DAX. Формула: «среднее за последние 3 месяца × YoY-коэффициент». Не production-качество, но даёт приемлемый результат в стабильном бизнесе.
Forecast Naive =
VAR _current = SELECTEDVALUE(dim_date[date])
VAR _today = TODAY()
VAR _is_future = _current > _today
VAR _last_3m_avg =
CALCULATE(
AVERAGEX(
VALUES(dim_date[year_month]),
[Actual Revenue]
),
DATESINPERIOD(dim_date[date], _today, -3, MONTH)
)
VAR _yoy_coef =
DIVIDE(
CALCULATE(
[Actual Revenue],
DATESINPERIOD(dim_date[date], _today, -12, MONTH)
),
CALCULATE(
[Actual Revenue],
DATESINPERIOD(dim_date[date], _today, -24, MONTH),
DATESINPERIOD(dim_date[date], EOMONTH(_today,-12), -12, MONTH)
)
)
RETURN
IF(_is_future, _last_3m_avg * _yoy_coef, BLANK())
Грубо, но работоспособно. Для серьёзных задач обязательно выносите прогноз в отдельный конвейер (Python/R), а в DAX только отображаете результат.
Фишки, которые делают визуал production-ready
Вертикальная линия «сегодня»
Добавляет читаемости. В Power BI через Analytics pane → Constant Line → установите значение как TODAY() (через DAX-меру).
Today Marker =
CALCULATE(MAX(dim_date[date]), fact_sales[revenue] > 0)
// возвращает последнюю дату факта
Подпись точки перехода
Label «Сегодня: X млн ₽» прямо на точке перехода. Делается через Data Labels только для точки today (conditional formatting): IF(SELECTEDVALUE(dim_date[date]) = TODAY(), Actual Revenue, BLANK()).
Tooltip с метаинформацией
Для точек прогноза в tooltip показывайте не только значение, но и интервал + тип («прогноз Prophet, обновлён 17 апреля 2026»). Это убирает иллюзию точности, которую прямая линия создаёт.
Accuracy back-check
Отдельный визуал «MAPE прогноза за последние 30 дней» — показывает, насколько модель точна. Считается как AVERAGEX(recent_days, ABS(actual - forecast) / actual). Если MAPE > 20%, модель врёт — нужно пересматривать.
Переключатель горизонта
срез «Прогноз на»: 1, 3, 6, 12 месяцев. В мере фильтруем Forecast по выбранному горизонту:
Forecast Horizon =
VAR _h = SELECTEDVALUE('Forecast Horizon'[Months], 6)
VAR _today = TODAY()
VAR _limit = EDATE(_today, _h)
VAR _current = SELECTEDVALUE(dim_date[date])
RETURN
IF(
_current > _today && _current <= _limit,
SUM(fact_forecast[forecast_revenue]),
BLANK()
)
Типичные ошибки
- Общая точка «сегодня» не добавлена. Две линии с разрывом в 1 точку. Визуально разорвано, руководитель сразу спрашивает «почему?».
- Разные dim_date для fact и forecast. Получаются две независимых оси, визуал не работает. Всегда одна dim_date на всю модель.
- Forecast выходит за dim_date. Прогноз на июль 2027, а календарь заканчивается апрелем 2026. Расширьте dim_date минимум на 2 года в будущее.
- Забыть BLANK в «сыне». Если в Actual не вернуть BLANK для будущих дат, а просто 0 — линия Actual уходит в ноль в будущем, выглядит как «мы обанкротились».
- Не тестировать на границе. «Сегодня» перескочит через 00:00 — TODAY() вернёт новое значение. Визуал может внезапно поменяться, если FACT обновляется только раз в сутки. Обсудите с бизнесом, что считается «сегодняшней датой» для отчёта.
- Показывать forecast без интервала. Создаёт иллюзию точности. Минимум — tooltip с интервалом.
- Одинаковые цвета для Actual и Forecast. Пользователь не понимает, где факт, где прогноз. Обязательно визуально отличать: пунктир + другой оттенок.
Вариации для разных задач
План-факт-прогноз на одном графике
Помимо факта и прогноза, показать ещё и план. Три меры: Plan, Actual, Forecast. Plan часто идёт тонкой серой линией на весь год, Actual — жирный синий до today, Forecast — пунктир после today. Полезно для CFO: одним взглядом видно «куда идём vs куда планировали».
Два forecast-сценария
Оптимистичный и пессимистичный (или base-case и best-case). Две пунктирные линии разного цвета, расходящиеся от today. Полезно при планировании: «в базовом сценарии закроем 500 млн, в оптимистичном — 600».
Forecast vs forecast (версионирование)
Текущий прогноз vs прогноз месячной давности. Показывает, как менялся взгляд на будущее. Если оба forecast сильно расходятся — значит, в бизнесе произошли изменения, которые стоит обсудить.
Шаги внедрения
- Решите, откуда брать forecast. От простого (наивная экстраполяция) до сложного (ML-модель). Лучше начать с простого и улучшать.
- Создайте таблицу fact_forecast в DWH. Схема: date, metric_name, forecast_value, forecast_lower, forecast_upper, forecast_run_date.
- Свяжите с общей dim_date. Расширьте календарь в будущее минимум на 1-2 года.
- Напишите пару Seamless-мер (Actual + Forecast) с логикой BLANK.
- Постройте line-chart с двумя сериями, настройте стиль: Actual sola, Forecast dashed.
- Добавьте доверительный интервал — через Deneb для production, через Area Chart для MVP.
- Протестируйте на граничных датах: первое число, конец месяца, смена года.
- Добавьте accuracy-виджет: MAPE последних 30 дней, чтобы бизнес видел степень доверия прогнозу.
Что делать дальше
- Нужен production forecast-дашборд — 30-минутный звонок. Типовой проект (ETL + ML-модель + визуализация) — 4-6 недель.
- Есть forecast, но модель ошибается — отдельный проект по улучшению точности через выбор правильного алгоритма и настройку гиперпараметров.
- Связанные материалы — DAX-справочник (15 формул), когорта удержание, RFM-сегментация.