Руководство по визуальному анализу данных с использованием параллельных координат

Автор: Дмитрий Иванов [Команда P9X]

~8 минут чтения

Несколько лет назад, работая консультантом, я выводил относительно сложный алгоритм машинного обучения и столкнулся с задачей сделать внутреннюю работу этого алгоритма прозрачной для моих заинтересованных сторон. Именно тогда я впервые начал использовать параллельные координаты — потому что визуализировать отношения между двумя, тремя, может быть, четырьмя или пятью переменными легко. Но как только вы начинаете работать с векторами более высокой размерности (скажем, с тринадцатью, например), человеческий разум часто не в состоянии уловить эту сложность.

Введите параллельные координаты: инструмент настолько простой, но в то же время настолько эффективный, что я часто задаюсь вопросом, почему он так мало используется в повседневной исследовательской работе (мои команды — исключение). Поэтому в этой статье я поделюсь с вами преимуществами параллельных координат на основе набора данных Wine, подчеркнув, как этот метод может помочь выявить корреляции, закономерности или кластеры в данных без потери семантики признаков (например, в PCA).

Что такое параллельные координаты

Параллельные координаты — это распространённый метод визуализации многомерных наборов данных. И да, это технически правильное определение, хотя оно не полностью отражает эффективность и элегантность метода. В отличие от стандартного графика, где у вас есть две ортогональные оси (и, следовательно, два измерения, которые вы можете построить), в параллельных координатах у вас столько вертикальных осей, сколько измерений в вашем наборе данных. Это означает, что наблюдение может быть представлено в виде линии, которая пересекает все оси в соответствующем значении.

Хотите узнать модное слово, чтобы произвести впечатление на следующем хакатоне? «Полилиния» — это правильный термин для неё. И закономерности появляются в виде пучков полилиний со сходным поведением. Или, более конкретно: кластеры появляются в виде пучков, а корреляции — в виде траекторий с последовательными наклонами на соседних осях.

Интересно, почему бы просто не сделать PCA (анализ главных компонент)? В параллельных координатах мы сохраняем все исходные признаки, а это значит, что мы не сжимаем информацию и не проецируем её в пространство меньшей размерности. Так что это значительно упрощает интерпретацию как для вас, так и для ваших заинтересованных сторон!

Но (да, помимо всего этого волнения, должно быть ещё одно «но…») вам следует позаботиться о том, чтобы не попасть в ловушку чрезмерного наложения графиков. Если вы не подготовите данные тщательно, ваши параллельные координаты легко станут нечитаемыми — я покажу вам в пошаговом руководстве, что отбор признаков, масштабирование и корректировка прозрачности могут оказать большую помощь.

Кстати, здесь я должен упомянуть профессора Альфреда Инсельберга. Я имел честь поужинать с ним в 2018 году в Берлине. Он — тот, кто подсадил меня на параллельные координаты. И он — крёстный отец параллельных координат, доказавший их ценность во множестве случаев использования в 1980-х годах.

Доказательство моей точки зрения на примере набора данных Wine

Для этой демонстрации я выбрал набор данных Wine. Почему? Во-первых, мне нравится вино. Во-вторых, я попросил ChatGPT предоставить общедоступный набор данных, который по структуре похож на один из наборов данных моей компании, над которым я сейчас работаю (и я не хотел брать на себя все хлопоты по публикации/анонимизации/… корпоративных данных). В-третьих, этот набор данных хорошо изучен во многих приложениях машинного обучения и аналитики.

Он содержит данные анализа 178 вин, выращенных тремя сортами винограда в одном регионе Италии. Каждое наблюдение имеет тринадцать непрерывных атрибутов (например, алкоголь, концентрация флавоноидов, содержание пролина, интенсивность цвета,…). А целевая переменная — это класс винограда.

Для того чтобы вы могли следовать за мной, позвольте мне показать вам, как загрузить набор данных в Python.

import pandas as pd

# Load Wine dataset from UCI

uci_url = "https://archive.ics.uci.edu/ml/machine-learning-databases/wine/wine.data"

# Define column names based on the wine.names file

col_names = [
    "Class", "Alcohol", "Malic_Acid", "Ash", "Alcalinity_of_Ash", "Magnesium",
    "Total_Phenols", "Flavanoids", "Nonflavanoid_Phenols", "Proanthocyanins",
    "Color_Intensity", "Hue", "OD280/OD315", "Proline"
]

# Load the dataset

df = pd.read_csv(uci_url, header=None, names=col_names)

df.head()

Первый шаг: встроенные в Pandas

Давайте воспользуемся встроенной функцией построения графиков pandas:

from pandas.plotting import parallel_coordinates

import matplotlib.pyplot as plt

plt.figure(figsize=(12,6))

parallel_coordinates(df, 'Class', colormap='viridis')

plt.title("Parallel Coordinates Plot of Wine Dataset (Unscaled)")

plt.xticks(rotation=45)

plt.show()

Неувеличенный график параллельных координат

Хорошо, теперь давайте выведем наивный график в качестве основы.

Первый шаг: встроенные в Pandas

Давайте воспользуемся встроенной функцией построения графиков pandas:

from pandas.plotting import parallel_coordinates

import matplotlib.pyplot as plt

plt.figure(figsize=(12,6))

parallel_coordinates(df, 'Class', colormap='viridis')

plt.title("Parallel Coordinates Plot of Wine Dataset (Unscaled)")

plt.xticks(rotation=45)

plt.show()

Неувеличенный график параллельных координат

Выглядит хорошо, правда?

Нет, это не так. Вы, конечно, можете различить классы на графике, но из-за различий в масштабировании сложно сравнивать по осям. Сравните порядки величин пролина и оттенка, например: пролин имеет сильное оптическое доминирование только из-за масштабирования. Неувеличенный график выглядит почти бессмысленным или, по крайней мере, очень сложным для интерпретации.

Даже так, едва заметные пучки по классам, кажется, появляются, так что давайте примем это как обещание того, что будет дальше…

Всё дело в масштабе

Многие из вас (все?) знакомы с масштабированием min-max из конвейеров предварительной обработки ML. Поэтому давайте не будем использовать его. Я проведу некоторую стандартизацию данных, то есть здесь мы будем использовать Z-масштабирование (у каждого признака будет среднее значение, равное нулю, и дисперсия, равная единице), чтобы придать всем осям одинаковый вес.

from sklearn.preprocessing import StandardScaler

# Separate features and target

features = df.drop("Class", axis=1)

scaler = StandardScaler()

scaled = scaler.fit_transform(features)

# Reconstruct a DataFrame with scaled features

scaled_df = pd.DataFrame(scaled, columns=features.columns)

scaled_df["Class"] = df["Class"]

plt.figure(figsize=(12,6))

parallel_coordinates(scaled_df, 'Class', colormap='plasma', alpha=0.5)

plt.title("Parallel Coordinates Plot of Wine Dataset (Scaled)")

plt.xticks(rotation=45)

plt.show()

Стандартизированные оси выявляют структуру

Помните картинку выше? Разница поразительна, не так ли? Теперь мы можем различить закономерности. Попробуйте выделить кластеры линий, связанных с каждым классом вина, чтобы узнать, какие признаки наиболее различимы.

Выбор признаков

Вы что-нибудь обнаружили? Правильно! У меня создалось впечатление, что алкоголь, флавоноиды, интенсивность цвета и пролин демонстрируют почти классические шаблоны. Давайте отфильтруем их и посмотрим, поможет ли отбор признаков сделать наши наблюдения ещё более яркими.

selected = ["Alcohol", "Flavanoids", "Color_Intensity", "Proline", "Class"]

plt.figure(figsize=(10,6))

parallel_coordinates(scaled_df[selected], 'Class', colormap='coolwarm', alpha=0.6)

plt.title("Parallel Coordinates Plot of Selected Features")

plt.xticks(rotation=45)

plt.show()

Вид с четырьмя осями (алкоголь, флавоноиды, интенсивность цвета, пролин)

Приятно видеть, как вина класса 1 всегда имеют высокие показатели по флавоноидам и пролину, тогда как вина класса 3 ниже по этим показателям, но выше по интенсивности цвета! И не думайте, что это бесполезное упражнение… 13 измерений всё ещё можно обработать и проверить, но я сталкивался со случаями, когда было более 100 измерений, что делает необходимым уменьшение размерности.

Добавление взаимодействия

Я признаю: приведённые выше примеры довольно механистичны. При написании статьи я также разместил оттенок рядом с алкоголем, что привело к коллапсу моих красиво показанных классов; поэтому я перенёс интенсивность цвета рядом с флавоноидами, и это помогло. Но моя цель здесь состояла не в том, чтобы дать вам идеальный фрагмент кода для копирования; скорее, я хотел показать вам использование параллельных координат на основе нескольких простых примеров.

В реальной жизни я бы настроил более исследовательский интерфейс. Параллельные координаты Plotly, например, имеют функцию «чистки»: там вы можете выбрать подраздел оси, и все полилинии, попадающие в этот поднабор, будут выделены.

Вы также можете переставлять оси простым перетаскиванием, что часто помогает выявить корреляции, которые были скрыты в порядке по умолчанию. Совет: попробуйте соседние оси, которые, как вы подозреваете, могут изменяться совместно.

И даже лучше: масштабирование не обязательно для проверки данных с помощью plotly: оси автоматически масштабируются до минимальных и максимальных значений каждого измерения.

Вот код для воспроизведения в вашем Colab:

import plotly.express as px

# Keep class as a separate column; Plotly's parcoords expects numeric colour for 'color'
df["Class"] = df["Class"].astype(int)

fig_all = px.parallel_coordinates(
    df,
    color="Class",                  # numeric colour mapping (1..3)
    dimensions=features.columns,    
    labels={c: c.replace("_", " ") for c in scaled_df.columns},
)

fig_all.update_layout(
    title="Interactive Parallel Coordinates — All 13 Features"
)

# The file below can be opened in any browser or embedded via <iframe>.
fig_all.write_html("parallel_coordinates_all_features.html",
    include_plotlyjs="cdn", full_html=True)

print("Saved:")
print(" - parallel_coordinates_all_features.html")

# show figures inline
fig_all.show()

Заключение

Параллельные координаты — это не столько о жёстких цифрах, сколько о закономерностях, которые возникают из этих чисел. В наборе данных Wine вы могли наблюдать несколько таких закономерностей — без вычисления корреляций, проведения PCA или построения матриц рассеяния. Флавоноиды сильно помогают отличить класс 1 от остальных. Интенсивность цвета и оттенок разделяют классы 2 и 3. Пролин дополнительно подтверждает это.

И это именно та сила, которая есть у параллельных координат по сравнению с t-SNE, PCA и т. д. Эти методы проецируют данные в компоненты, которые отлично различают классы… Но удачи вам, если вы попытаетесь объяснить химику, что означает для него «первый компонент».

Не поймите меня неправильно: параллельные координаты — это не швейцарский армейский нож для исследовательского анализа данных. Вам нужны заинтересованные стороны с очень хорошим пониманием данных, чтобы иметь возможность использовать параллельные координаты для общения с ними (иначе продолжайте использовать ящиковые диаграммы и гистограммы!). Но для вас (и для меня) как для специалиста по данным параллельные координаты — это тот микроскоп, о котором вы всегда мечтали.

Часто задаваемые вопросы

В1. Для чего используются параллельные координаты в науке о данных?

О. Параллельные координаты в основном используются для исследовательского анализа многомерных наборов данных. Они позволяют вам выявлять кластеры, корреляции и выбросы, сохраняя при этом исходные переменные интерпретируемыми.

В2. Почему перед построением параллельных координат необходимо масштабирование?

О. Без масштабирования признаки с большими числовыми диапазонами доминируют на графике. Стандартизация каждого признака до среднего значения, равного нулю, и дисперсии, равной единице, обеспечивает равный вклад каждой оси в визуальный паттерн.

В3. Как параллельные координаты сравниваются с PCA или t-SNE?

О. PCA и t-SNE уменьшают размерность, но оси теряют своё первоначальное значение. Параллельные координаты сохраняют семантическую связь с переменными за счёт некоторого беспорядка и потенциального чрезмерного наложения графиков.