2012-03-16 151 views
5

我刚从scipy堆栈开始。我正在使用CSV版本的虹膜数据集。我可以通过加载它只是罚款:Pylab:将标签映射到颜色

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))不起作用(来自文档,它期望float值或colormap)。我没有找到自动生成彩色地图的优雅方式。

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))) 

回答

4

不管它的价值,你通常会做更多的东西像这样在这种情况下:

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() 

enter image description here

这没有什么错什么@Yann建议,但scatter更适合于连续数据。

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

默认情况下,轴的颜色循环为:蓝色,绿色,红色,青色,品红色,黄色和黑色。

后7调用plot,它会循环回过这些颜色,所以如果你有更多的项目,你需要set it manually(或只是使用类似于插值的彩条指定每个呼叫的颜色plot @ Yann建议在上面)。

+0

谢谢。我看到了多重阴谋的选择,但我还没有意识到你在这里使用的优雅条件技巧(+1)。我不得不不同意'scatter'。据我的理解,这正是为这种情节,这些点是独立的,没有连接(你通过设置'linestyle =“none”''工作) – 2012-03-17 16:16:53

+0

'plot' vs'scatter'点是一个不幸的和常见的误解。当需要根据第3或第4个变量连续改变标记的大小和/或颜色时,可以使用'plot'绘制点,只使用'scatter'绘制事物。 'scatter'返回一个难以管理的集合。 'plot' _really is_旨在绘制断点,默认情况恰好是一条线。如果你想要一个更简洁的调用,'plt.plot(x,y,'o')'和'plt.plot(x,y,linestyle ='none',marker ='o')做同样的事情' 。 – 2012-03-17 16:21:01

+0

谢谢。我使用'np.unique(iris.field(4))'(因为我的CSV没有列标签行)。但除此之外,我现在基本上使用你的代码。我真的很喜欢这种情况。 – 2012-03-19 08:38:48

5

无论方式是优雅与否是有些主观。我个人发现你的方法比'matplotlib'更好。从matplotlib的color模块:

Colormapping通常包括两个步骤:一个数据阵列是映射到使用规格化的或 子类的实例的范围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() 

这产生:

enter image description here

我选择了“RdYlGn”色彩映射,使得很容易三种类型的点之间进行区分。我没有包含clip功能作为__call__的一部分,虽然可以进行一些修改。

传统上你可以测试使用norm关键字scatter方法正常化,但scatter测试c关键字,看看它存储的字符串,如果确实如此,那么它假定你逝去的颜色作为他们的字符串值,例如'红色','蓝色'等,因此致电plt.scatter(iris.field(0), iris.field(1), c=iris.field(4), cmap='RdYlGn', norm=norm)失败。相反,我只是使用TextNorm并在iris.field(4)上“运行”以返回范围从0到1的值数组。

请注意,对于不属于列表textvals的列表,返回值为-1。这是掩蔽会派上用场的地方。

+0

我正在研究一个例子...... – Yann 2012-03-16 16:55:16

+0

因为我在'R'(试图概述工具)中做了同样的事情,所以我想知道在''中是否有相当于'unclass'的' scipy'。 – 2012-03-16 17:27:25

+0

@ Anony-Mousse我不确定你在评论中提及什么。你将如何使用'unclass'以及你将如何使用它。 – Yann 2012-03-16 18:20:15