density: оценка плотности распределения

Оценка плотности распределения (Density Estimation) используется для анализа формы распределения числовых данных. В отличие от гистограммы, где значения группируются по интервалам, оценка плотности строит непрерывную сглаженную кривую, отражающую вероятность появления значений в различных областях диапазона.

В Vega-Lite для этого применяется трансформация density, основанная на методе KDE (Kernel Density Estimation — ядерная оценка плотности).


Назначение density

Трансформация density позволяет:

  • визуализировать распределение данных;
  • анализировать концентрацию значений;
  • выявлять пики и аномалии;
  • сравнивать распределения между группами;
  • строить сглаженные альтернативы гистограммам.

Чаще всего density используется:

  • в статистике;
  • в аналитике данных;
  • в ML/DS-проектах;
  • при исследовательском анализе данных (EDA).

Принцип работы KDE

Алгоритм KDE строит непрерывную функцию плотности вероятности на основе исходных наблюдений.

Каждое значение добавляет вклад в итоговую кривую через ядро сглаживания.

Основные особенности:

  • отсутствуют жёсткие интервалы;
  • форма распределения получается более плавной;
  • легче анализировать общую структуру данных.

Базовая структура density

Минимальная конфигурация:

{
  "transform": [
    {
      "density": "value"
    }
  ]
}

Где:

  • density — поле с числовыми значениями;
  • результатом трансформации становится набор точек плотности.

Простейший пример

<!DOCTYPE html>
<html>
<head>
  <script src="https://cdn.jsdelivr.net/npm/vega@5"></script>
  <script src="https://cdn.jsdelivr.net/npm/vega-lite@5"></script>
  <script src="https://cdn.jsdelivr.net/npm/vega-embed@6"></script>
</head>
<body>
<div id="view"></div>

<script>
const spec = {
  "$schema": "https://vega.github.io/schema/vega-lite/v5.json",

  "data": {
    "values": [
      {"score": 10},
      {"score": 12},
      {"score": 15},
      {"score": 16},
      {"score": 18},
      {"score": 20},
      {"score": 21},
      {"score": 25},
      {"score": 27},
      {"score": 30}
    ]
  },

  "transform": [
    {
      "density": "score"
    }
  ],

  "mark": "line",

  "encoding": {
    "x": {
      "field": "value",
      "type": "quantitative"
    },

    "y": {
      "field": "density",
      "type": "quantitative"
    }
  }
};

vegaEmbed('#view', spec);
</script>
</body>
</html>

Как работает результат density

После выполнения трансформации Vega-Lite создаёт новые данные:

[
  { "value": 10, "density": 0.01 },
  { "value": 11, "density": 0.03 },
  { "value": 12, "density": 0.05 }
]

Где:

  • value — координата по оси X;
  • density — оценка плотности.

Эти значения затем используются для построения линии.


Поля результата

По умолчанию создаются:

Поле Назначение
value значение по оси X
density вычисленная плотность

Названия можно изменить через параметр as.


Настройка as

{
  "density": "score",
  "as": ["x", "kde"]
}

Теперь результат:

{
  "x": 15,
  "kde": 0.08
}

Использование:

"encoding": {
  "x": {"field": "x"},
  "y": {"field": "kde"}
}

Настройка extent

Параметр extent определяет диапазон вычисления плотности.


Пример

{
  "density": "score",
  "extent": [0, 100]
}

Без extent Vega-Lite автоматически определяет диапазон на основе данных.


Когда использовать extent

extent полезен:

  • при сравнении нескольких распределений;
  • при фиксированных диапазонах;
  • при синхронизации осей;
  • при анимациях;
  • при потоковых данных.

Параметр steps

steps определяет количество вычисляемых точек.


Пример

{
  "density": "score",
  "steps": 200
}

Влияние steps

Малое значение:

"steps": 20
  • грубая линия;
  • меньше вычислений.

Большое значение:

"steps": 500
  • плавная линия;
  • больше вычислений;
  • выше нагрузка.

Параметр bandwidth

bandwidth задаёт степень сглаживания.

Это один из важнейших параметров KDE.


Малый bandwidth

{
  "density": "score",
  "bandwidth": 1
}

Особенности:

  • много деталей;
  • чувствительность к шуму;
  • возможны ложные пики.

Большой bandwidth

{
  "density": "score",
  "bandwidth": 20
}

Особенности:

  • сильное сглаживание;
  • потеря локальных особенностей;
  • более общая форма распределения.

Сравнение bandwidth

Низкое сглаживание

{
  "density": "score",
  "bandwidth": 2
}

Высокое сглаживание

{
  "density": "score",
  "bandwidth": 15
}

Автоматический bandwidth

Если параметр не указан:

{
  "density": "score"
}

Vega-Lite автоматически вычисляет подходящее значение.

В большинстве случаев этого достаточно.


Параметр counts

По умолчанию плотность нормализуется.

Площадь под кривой равна 1.


Стандартное поведение

{
  "density": "score"
}

Использование counts

{
  "density": "score",
  "counts": true
}

Теперь:

  • значения отражают количество наблюдений;
  • кривая масштабируется относительно объёма данных.

Групповая оценка плотности

Одна из самых мощных возможностей — вычисление KDE отдельно для групп.

Используется параметр groupby.


Пример

<!DOCTYPE html>
<html>
<head>
  <script src="https://cdn.jsdelivr.net/npm/vega@5"></script>
  <script src="https://cdn.jsdelivr.net/npm/vega-lite@5"></script>
  <script src="https://cdn.jsdelivr.net/npm/vega-embed@6"></script>
</head>
<body>
<div id="view"></div>

<script>
const spec = {
  "$schema": "https://vega.github.io/schema/vega-lite/v5.json",

  "data": {
    "values": [
      {"group": "A", "score": 10},
      {"group": "A", "score": 15},
      {"group": "A", "score": 20},

      {"group": "B", "score": 30},
      {"group": "B", "score": 35},
      {"group": "B", "score": 40}
    ]
  },

  "transform": [
    {
      "density": "score",
      "groupby": ["group"]
    }
  ],

  "mark": "line",

  "encoding": {
    "x": {
      "field": "value",
      "type": "quantitative"
    },

    "y": {
      "field": "density",
      "type": "quantitative"
    },

    "color": {
      "field": "group",
      "type": "nominal"
    }
  }
};

vegaEmbed('#view', spec);
</script>
</body>
</html>

Как работает groupby

Для каждой группы:

  1. выбираются собственные данные;
  2. вычисляется отдельная KDE;
  3. строится независимая кривая.

Совмещение density и area

Очень популярный вариант — использование area.


Пример area-графика

{
  "mark": "area"
}

Полный пример:

{
  "transform": [
    {
      "density": "score"
    }
  ],

  "mark": {
    "type": "area",
    "opacity": 0.5
  }
}

Преимущества area

  • лучше воспринимается форма распределения;
  • визуально понятнее;
  • хорошо подходит для дашбордов.

Наложение density и histogram

Часто KDE комбинируется с гистограммой.


Концепция layered chart

{
  "layer": [
    {
      "mark": "bar"
    },

    {
      "mark": "line"
    }
  ]
}

Пример комбинированного графика

{
  "$schema": "https://vega.github.io/schema/vega-lite/v5.json",

  "data": {
    "url": "data/movies.json"
  },

  "layer": [

    {
      "mark": {
        "type": "bar",
        "opacity": 0.4
      },

      "encoding": {
        "x": {
          "bin": true,
          "field": "IMDB_Rating",
          "type": "quantitative"
        },

        "y": {
          "aggregate": "count"
        }
      }
    },

    {
      "transform": [
        {
          "density": "IMDB_Rating",
          "counts": true
        }
      ],

      "mark": {
        "type": "line",
        "color": "red"
      },

      "encoding": {
        "x": {
          "field": "value",
          "type": "quantitative"
        },

        "y": {
          "field": "density",
          "type": "quantitative"
        }
      }
    }
  ]
}

Density и интерактивность

KDE хорошо сочетается с параметрами (params) и фильтрами.


Интерактивная фильтрация

{
  "params": [
    {
      "name": "minScore",
      "value": 10,
      "bind": {
        "input": "range",
        "min": 0,
        "max": 100
      }
    }
  ]
}

Использование filter

{
  "transform": [
    {
      "filter": "datum.score >= minScore"
    },

    {
      "density": "score"
    }
  ]
}

Density и faceting

KDE удобно комбинировать с facet.


Пример

{
  "facet": {
    "field": "category",
    "type": "nominal"
  },

  "spec": {
    "transform": [
      {
        "density": "score"
      }
    ],

    "mark": "line"
  }
}

Преимущества facet

  • сравнение распределений;
  • независимые панели;
  • высокая читаемость;
  • анализ категорий.

Density и cumulative

Параметр cumulative строит накопленное распределение.


Пример

{
  "density": "score",
  "cumulative": true
}

Что показывает cumulative

Значение отражает вероятность того, что случайная величина:

X <= x

Это аналог функции распределения CDF.


Сравнение density и histogram

Характеристика Histogram Density
Основа интервалы непрерывная функция
Сглаживание нет есть
Детализация средняя высокая
Шум ниже выше
Визуальная плавность низкая высокая

Density vs boxplot

Density Boxplot
показывает форму распределения показывает статистические сводки
отображает пики показывает квартили
полезна для анализа структуры полезен для быстрого сравнения

Производительность

KDE требует дополнительных вычислений.

На производительность влияют:

  • steps;
  • количество групп;
  • размер данных;
  • bandwidth;
  • число слоёв.

Оптимизация

Уменьшение steps

{
  "steps": 50
}

Ограничение extent

{
  "extent": [0, 100]
}

Предварительная агрегация

Иногда KDE лучше вычислять на сервере.

Особенно:

  • для больших данных;
  • для real-time систем;
  • для BI-платформ.

Стилизация density-графиков


Настройка линии

{
  "mark": {
    "type": "line",
    "strokeWidth": 3,
    "color": "steelblue"
  }
}

Плавность линии

{
  "mark": {
    "type": "line",
    "interpolate": "monotone"
  }
}

Типы interpolate

Тип Описание
linear прямые сегменты
monotone плавная линия
basis spline-интерполяция
step ступенчатая линия

Density для временных рядов

Хотя KDE обычно применяется к количественным данным, она может использоваться и для временных значений.


Пример

{
  "density": "timestamp"
}

Однако чаще временные данные:

  • агрегируют;
  • группируют;
  • преобразуют в числовой формат.

Density в Vega

В чистом Vega трансформация выглядит так:

{
  "type": "density",
  "field": "score"
}

Vega-Lite автоматически преобразует декларативное описание в соответствующую Vega-структуру.


Полный пример профессионального density-графика

<!DOCTYPE html>
<html>
<head>
  <script src="https://cdn.jsdelivr.net/npm/vega@5"></script>
  <script src="https://cdn.jsdelivr.net/npm/vega-lite@5"></script>
  <script src="https://cdn.jsdelivr.net/npm/vega-embed@6"></script>
</head>
<body>
<div id="view"></div>

<script>
const spec = {
  "$schema": "https://vega.github.io/schema/vega-lite/v5.json",

  "width": 700,
  "height": 400,

  "data": {
    "url": "data/movies.json"
  },

  "transform": [
    {
      "filter": "datum.IMDB_Rating != null"
    },

    {
      "density": "IMDB_Rating",
      "bandwidth": 0.3,
      "steps": 300,
      "extent": [0, 10]
    }
  ],

  "mark": {
    "type": "area",
    "interpolate": "monotone",
    "opacity": 0.6,
    "line": true
  },

  "encoding": {

    "x": {
      "field": "value",
      "type": "quantitative",
      "title": "IMDB Rating"
    },

    "y": {
      "field": "density",
      "type": "quantitative",
      "title": "Density"
    },

    "tooltip": [
      {"field": "value"},
      {"field": "density"}
    ]
  }
};

vegaEmbed('#view', spec);
</script>
</body>
</html>

Типичные ошибки


Использование категориальных данных

Неправильно:

{
  "density": "country"
}

density требует числовое поле.


Слишком маленький bandwidth

{
  "bandwidth": 0.01
}

Результат:

  • шум;
  • ложные пики;
  • нестабильная форма.

Чрезмерное сглаживание

{
  "bandwidth": 100
}

Проблемы:

  • потеря структуры;
  • исчезновение мод;
  • неинформативная кривая.

Слишком большой steps

{
  "steps": 5000
}

Последствия:

  • лишняя нагрузка;
  • медленный рендеринг;
  • избыточная детализация.

Практические сценарии использования


Анализ оценок пользователей

{
  "density": "rating"
}

Распределение зарплат

{
  "density": "salary"
}

Анализ времени отклика API

{
  "density": "responseTime"
}

ML и Data Science

KDE активно используется для:

  • анализа признаков;
  • поиска аномалий;
  • анализа распределений;
  • feature engineering;
  • exploratory analysis.

Рекомендации по использованию


Когда density особенно полезна

  • большие выборки;
  • анализ формы распределения;
  • сравнение групп;
  • поиск мод;
  • исследовательская аналитика.

Когда лучше использовать histogram

  • нужны точные интервалы;
  • важны абсолютные количества;
  • требуется простота восприятия;
  • анализируются небольшие выборки.

Комбинация density с другими transform


density + filter

{
  "transform": [
    {
      "filter": "datum.score > 50"
    },

    {
      "density": "score"
    }
  ]
}

density + calculate

{
  "transform": [
    {
      "calculate": "datum.price * datum.count",
      "as": "total"
    },

    {
      "density": "total"
    }
  ]
}

density + window

{
  "transform": [
    {
      "window": [
        {
          "op": "rank",
          "as": "rank"
        }
      ]
    },

    {
      "density": "rank"
    }
  ]
}

Внутренний механизм Vega-Lite

При использовании density Vega-Lite:

  1. собирает значения поля;
  2. строит KDE;
  3. генерирует последовательность точек;
  4. создаёт новую таблицу данных;
  5. передаёт результат в mark.

Исходные данные при этом не изменяются.


Архитектурные особенности

density относится к transform-операциям.

Следовательно:

  • выполняется до визуализации;
  • может комбинироваться с другими transform;
  • участвует в pipeline обработки данных;
  • не зависит от типа mark.

Поддерживаемые mark

Чаще всего используются:

Mark Назначение
line классическая KDE
area заполненная плотность
trail переменная толщина
point отображение sampled points

Density и статистическая интерпретация

Важно понимать:

  • KDE — оценка, а не точное распределение;
  • форма зависит от bandwidth;
  • результаты чувствительны к объёму выборки;
  • малые выборки могут давать искажения.

Поэтому density следует интерпретировать как инструмент визуального анализа, а не как строгую математическую модель.