Является ли способ элегантным или нет, это несколько субъективно. Я лично нахожу ваши подходы лучше, чем «matplotlib». Из color модуля matplotlib:
Colormapping обычно состоит из двух этапов: массив данных является первым
отображается на диапазон 0-1 с использованием экземпляра Normalize или
подкласс; затем это число в диапазоне 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__
, хотя это возможно с некоторыми изменениями.
Традиционно вы можете проверить нормализацию метода scatter
, используя ключевое слово norm
, но scatter
проверяет ключевое слово c
, чтобы увидеть, хранит ли оно строки, и если это так, то предполагается, что вы передаете в цветах как их строковые значения, например «Красный», «Синий» и т. Д. Так что набрать plt.scatter(iris.field(0), iris.field(1), c=iris.field(4), cmap='RdYlGn', norm=norm)
не удается. Вместо этого я просто использую TextNorm
и «оперирую» на iris.field(4)
, чтобы получить массив значений в диапазоне от 0 до 1.
Обратите внимание, что для строки, не указанной в списке, возвращается значение -1 textvals
. Вот где маскировка пригодится.