График параллельных координат в Matplotlib

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

Example Parallel Coordinates Plot from Wikipedia

Несколько пакетов черчения предоставляют графики параллельных координат, такие как Matlab, R, VTK тип 1 и VTK тип 2, но я не вижу, как создать такой график с помощью Matplotlib.

  1. Есть ли в Matplotlib встроенный график параллельных координат? Я не вижу ни одного в галерее.
  2. Если встроенного типа нет, можно ли построить график параллельных координат, используя стандартные возможности Matplotlib?

Edit:

Основываясь на ответе Жени ниже, я разработал следующее обобщение, которое поддерживает произвольное количество осей. В соответствии со стилем графика примера, который я разместил в исходном вопросе выше, каждая ось имеет свой собственный масштаб. Для этого я нормализовал данные в каждой точке оси и сделал так, чтобы оси имели диапазон от 0 до 1. Затем я возвращаюсь назад и накладываю метки на каждую отметку, которые дают правильное значение на этом перехвате.

Функция работает, принимая итерабельность наборов данных. Каждый набор данных рассматривается как набор точек, каждая из которых лежит на другой оси. Пример в __main__ берет случайные числа для каждой оси в двух наборах по 30 линий. Линии случайны в пределах диапазонов, которые вызывают кластеризацию линий; это поведение я хотел проверить.

Это решение не так хорошо, как встроенное решение, поскольку у вас странное поведение мыши, и я подделываю диапазоны данных через метки, но пока Matplotlib не добавит встроенное решение, это приемлемо.

#!/usr/bin/python
import matplotlib.pyplot as plt
import matplotlib.ticker as ticker

def parallel_coordinates(data_sets, style=None):

    dims = len(data_sets[0])
    x    = range(dims)
    fig, axes = plt.subplots(1, dims-1, sharey=False)

    if style is None:
        style = ['r-']*len(data_sets)

    # Calculate the limits on the data
    min_max_range = list()
    for m in zip(*data_sets):
        mn = min(m)
        mx = max(m)
        if mn == mx:
            mn -= 0.5
            mx = mn + 1.
        r  = float(mx - mn)
        min_max_range.append((mn, mx, r))

    # Normalize the data sets
    norm_data_sets = list()
    for ds in data_sets:
        nds = [(value - min_max_range[dimension][0]) / 
                min_max_range[dimension][2] 
                for dimension,value in enumerate(ds)]
        norm_data_sets.append(nds)
    data_sets = norm_data_sets

    # Plot the datasets on all the subplots
    for i, ax in enumerate(axes):
        for dsi, d in enumerate(data_sets):
            ax.plot(x, d, style[dsi])
        ax.set_xlim([x[i], x[i+1]])

    # Set the x axis ticks 
    for dimension, (axx,xx) in enumerate(zip(axes, x[:-1])):
        axx.xaxis.set_major_locator(ticker.FixedLocator([xx]))
        ticks = len(axx.get_yticklabels())
        labels = list()
        step = min_max_range[dimension][2] / (ticks - 1)
        mn   = min_max_range[dimension][0]
        for i in xrange(ticks):
            v = mn + i*step
            labels.append('%4.2f' % v)
        axx.set_yticklabels(labels)


    # Move the final axis' ticks to the right-hand side
    axx = plt.twinx(axes[-1])
    dimension += 1
    axx.xaxis.set_major_locator(ticker.FixedLocator([x[-2], x[-1]]))
    ticks = len(axx.get_yticklabels())
    step = min_max_range[dimension][2] / (ticks - 1)
    mn   = min_max_range[dimension][0]
    labels = ['%4.2f' % (mn + i*step) for i in xrange(ticks)]
    axx.set_yticklabels(labels)

    # Stack the subplots 
    plt.subplots_adjust(wspace=0)

    return plt


if __name__ == '__main__':
    import random
    base  = [0,   0,  5,   5,  0]
    scale = [1.5, 2., 1.0, 2., 2.]
    data = [[base[x] + random.uniform(0., 1.)*scale[x]
            for x in xrange(5)] for y in xrange(30)]
    colors = ['r'] * 30

    base  = [3,   6,  0,   1,  3]
    scale = [1.5, 2., 2.5, 2., 2.]
    data.extend([[base[x] + random.uniform(0., 1.)*scale[x]
                 for x in xrange(5)] for y in xrange(30)])
    colors.extend(['b'] * 30)

    parallel_coordinates(data, style=colors).show()

Edit 2:

Вот пример того, что получается из приведенного выше кода при построении графика данных Fisher's Iris. Это не так красиво, как эталонное изображение из Википедии, но вполне пригодно, если у вас есть только Matplotlib и вам нужны многомерные графики.

Example result of parallel coordinates plot from this answer

47
задан Community 23 May 2017 в 12:02
поделиться