Matplotlib中的平行坐标图

2024-09-30 06:10:55 发布

您现在位置:Python中文网/ 问答频道 /正文

使用传统的绘图类型可以相对直观地查看二维和三维数据。即使是四维数据,我们也经常能找到显示数据的方法。不过,4维以上的维度越来越难以显示。幸运的是,parallel coordinates plots提供了一种查看高维结果的机制。

Example Parallel Coordinates Plot from Wikipedia

有几个绘图包提供平行坐标绘图,例如MatlabRVTK type 1VTK type 2,但我不知道如何使用Matplotlib创建一个。

  1. Matplotlib中是否有内置的平行坐标图?我当然看不到。
  2. 如果没有内置类型,是否可以使用Matplotlib的标准功能生成平行坐标图?

编辑

基于下面的Zhenya提供的答案,我开发了以下支持任意轴数的泛化。按照我在上面原始问题中发布的示例的打印样式,每个轴都有自己的比例。我通过规范化每个轴点的数据并使轴的范围为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()

编辑2:

下面是一个在绘制Fisher's Iris data时从上述代码中得到的结果的示例。它不像维基百科上的参考图片那么好,但是如果你只有Matplotlib并且你需要多维图的话,它是可以接受的。

Example result of parallel coordinates plot from this answer


Tags: the数据infordatamatplotlibsetsrange
3条回答

pandas有一个平行坐标包装器:

import pandas
import matplotlib.pyplot as plt
from pandas.tools.plotting import parallel_coordinates

data = pandas.read_csv(r'C:\Python27\Lib\site-packages\pandas\tests\data\iris.csv', sep=',')
parallel_coordinates(data, 'Name')
plt.show()

screenshot

源代码,他们是如何做到的:plotting.py#L494

当使用熊猫(如θ所建议的)时,没有办法独立地缩放轴。

The reason you can't find the different vertical axes is because there aren't any. Our parallel coordinates is "faking" the other two axes by just drawing a vertical line and some labels.

https://github.com/pydata/pandas/issues/7083#issuecomment-74253671

我相信有更好的方法,但这里有一个快速和肮脏的(一个真正肮脏的):

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

#vectors to plot: 4D for this example
y1=[1,2.3,8.0,2.5]
y2=[1.5,1.7,2.2,2.9]

x=[1,2,3,8] # spines

fig,(ax,ax2,ax3) = plt.subplots(1, 3, sharey=False)

# plot the same on all the subplots
ax.plot(x,y1,'r-', x,y2,'b-')
ax2.plot(x,y1,'r-', x,y2,'b-')
ax3.plot(x,y1,'r-', x,y2,'b-')

# now zoom in each of the subplots 
ax.set_xlim([ x[0],x[1]])
ax2.set_xlim([ x[1],x[2]])
ax3.set_xlim([ x[2],x[3]])

# set the x axis ticks 
for axx,xx in zip([ax,ax2,ax3],x[:-1]):
  axx.xaxis.set_major_locator(ticker.FixedLocator([xx]))
ax3.xaxis.set_major_locator(ticker.FixedLocator([x[-2],x[-1]]))  # the last one

# EDIT: add the labels to the rightmost spine
for tick in ax3.yaxis.get_major_ticks():
  tick.label2On=True

# stack the subplots together
plt.subplots_adjust(wspace=0)

plt.show()

这基本上是基于Joe Kingon的一篇(更好的)文章,Python/Matplotlib - Is there a way to make a discontinuous axis?。你可能还想看看同一问题的另一个答案。

在这个例子中,我甚至没有尝试缩放垂直比例,因为这取决于您到底要实现什么。

编辑:这是结果enter image description here

相关问题 更多 >

    热门问题