【问题标题】:Pylab: map labels to colorsPylab:将标签映射到颜色
【发布时间】:2012-04-02 04:55:18
【问题描述】:

我只是从scipy 堆栈开始。我正在使用 CSV 版本的 iris 数据集。我可以很好地加载它:

iris=numpy.recfromcsv("iris.csv")

并绘制它:

pylab.scatter(iris.field(0), iris.field(1))
pylab.show()

现在我还想绘制存储在iris.field(4) 中的类:

chararray(['setosa', ...], dtype='|S10')

将这些字符串映射到绘图颜色的优雅方法是什么? scatter(iris.field(0), iris.field(1), c=iris.field(4)) 不起作用(从文档中它期望浮点值或颜色图)。我还没有找到一种自动生成颜色图的优雅方法。

cols = {"versicolor": "blue", "virginica": "green", "setosa": "red"}
scatter(iris.field(0), iris.field(1), c=map(lambda x:cols[x], iris.field(4)))

大致符合我的要求,但我不太喜欢手动颜色规范。

编辑:最后一行稍微更优雅的版本:

scatter(iris.field(0), iris.field(1), c=map(cols.get, iris.field(4)))

【问题讨论】:

    标签: python scipy matplotlib scatter-plot


    【解决方案1】:

    不管它值多少钱,在这种情况下你通常会做更多这样的事情:

    import numpy as np
    import matplotlib.pyplot as plt
    
    iris = np.recfromcsv('iris.csv')
    names = set(iris['class'])
    
    x,y = iris['sepal_length'],  iris['sepal_width']
    
    for name in names:
        cond = iris['class'] == name
        plt.plot(x[cond], y[cond], linestyle='none', marker='o', label=name)
    
    plt.legend(numpoints=1)
    plt.show()
    

    @Yann 的建议没有错,但scatter 更适合连续数据。

    依靠坐标轴颜色循环并多次调用 plot 更容易(您还可以获得单独的艺术家而不是集合,这对于诸如此类的离散数据是一件好事)。

    默认情况下,坐标区的颜色循环为:蓝色、绿色、红色、青色、洋红色、黄色、黑色。

    在 7 次调用 plot 后,它将循环返回这些颜色,因此如果您有更多项目,则需要 set it manually(或者只需在每次调用 plot 时使用插值指定颜色类似于上面@Yann 建议的颜色条)。

    【讨论】:

    • 谢谢。我看到了多重绘图选项,但我还不知道您在此处使用的优雅条件技巧 (+1)。我不得不不同意scatter。据我了解,它完全适用于这种图,其中点是独立的且不相连(您可以通过设置 linestyle="none" 来解决)
    • plotscatter 点是一个不幸且常见的误解。使用plot 绘制点,并且仅当您需要根据第三或第四变量连续改变标记的大小和/或颜色时才使用scatter 绘制事物。 scatter 返回一个更难管理的集合。 plot 真的是 用于绘制断开的点,默认值恰好是一条线。如果您想要更简洁的通话,plt.plot(x, y, 'o') 将执行与 plt.plot(x, y, linestyle='none', marker='o') 相同的操作。
    • 谢谢。我使用np.unique(iris.field(4))(因为我的 CSV 没有列标签行)。但除此之外,我现在基本上使用的是您的代码。我真的很喜欢条件技巧。
    • 如何使用 sklearn.datasets.load_iris() 重现这个?我不确定 numpy.recfromcsv() 返回什么。
    【解决方案2】:

    一种方式是否优雅有点主观。我个人发现您的方法比“matplotlib”方式更好。来自 matplotlib 的 color 模块:

    颜色映射通常涉及两个步骤:首先是一个数据数组 使用 Normalize 或 a 的实例映射到范围 0-1 子类;然后使用 0-1 范围内的这个数字映射到颜色 Colormap 子类的一个实例。

    关于您的问题,我从中得到的是,您需要一个 Normalize 的子类,它接受字符串并将它们映射到 0-1。

    这里有一个例子,它继承自Normalize,创建一个子类TextNorm,用于将字符串转换为0到1之间的值。这种规范化用于获取对应的颜色。

    import matplotlib.pyplot as plt
    from matplotlib.colors import Normalize
    import numpy as np
    from numpy import ma
    
    class TextNorm(Normalize):
        '''Map a list of text values to the float range 0-1'''
    
        def __init__(self, textvals, clip=False):
            self.clip = clip
            # if you want, clean text here, for duplicate, sorting, etc
            ltextvals = set(textvals)
            self.N = len(ltextvals)
            self.textmap = dict(
                [(text, float(i)/(self.N-1)) for i, text in enumerate(ltextvals)])
            self.vmin = 0
            self.vmax = 1
    
        def __call__(self, x, clip=None):
            #Normally this would have a lot more to do with masking
            ret = ma.asarray([self.textmap.get(xkey, -1) for xkey in x])
            return ret
    
        def inverse(self, value):
            return ValueError("TextNorm is not invertible")
    
    iris = np.recfromcsv("iris.csv")
    norm = TextNorm(iris.field(4))
    
    plt.scatter(iris.field(0), iris.field(1), c=norm(iris.field(4)), cmap='RdYlGn')
    plt.savefig('textvals.png')
    plt.show()
    

    这会产生:

    我选择了“RdYlGn”颜色图,以便于区分三种类型的点。我没有将clip 功能作为__call__ 的一部分包含在内,尽管可以进行一些修改。

    传统上,您可以使用norm 关键字测试scatter 方法的规范化,但scatter 测试c 关键字以查看它是否存储字符串,如果是,则假定您正在通过颜色作为它们的字符串值,例如“红色”、“蓝色”等。因此调用 plt.scatter(iris.field(0), iris.field(1), c=iris.field(4), cmap='RdYlGn', norm=norm) 失败。相反,我只使用TextNormiris.field(4) 上的“操作”来返回一个从0 到1 的值数组。

    请注意,对于不在列表 textvals 中的字符串,返回值 -1。这就是掩蔽会派上用场的地方。

    【讨论】:

    • 由于我刚刚在R 中做了同样的事情(试图对这些工具进行概述),我想知道scipy 中是否存在与unclass 等效的内容。
    • @Anony-Mousse 我不确定您在评论中要问什么。你会如何使用unclass,你会在什么地方使用它。
    • 好吧,unclass 基本上枚举了不同的标签。因此,每一行都是 iris 数据集的1..3 之一(R 索引从 1 开始)。然后您可以使用它来索引不同颜色的列表。有点像cls = list(numpy.unique(iris.field(4)))cols=["red", "green", "blue"]scatter(..., c=map(lamda x:cols[cls.index(x)], iris.field(4)))
    • 从另一个答案可以看出,可以通过按每个标签过滤数据集并用plot而不是scatter分别绘制它们来实现类似的效果。
    猜你喜欢
    • 1970-01-01
    • 2018-06-21
    • 2012-03-19
    • 1970-01-01
    • 1970-01-01
    • 2013-10-13
    • 1970-01-01
    • 2010-10-04
    • 1970-01-01
    相关资源
    最近更新 更多