Постройте цветовое колесо (полярное) на основе цветовой карты с использованием Python/Matplotlib

Я пытаюсь создать цветовое колесо в Python, предпочтительно используя Matplotlib. Выполняется следующее:

import numpy as np
import matplotlib as mpl
import matplotlib.pyplot as plt

xval = np.arange(0, 2*pi, 0.01)
yval = np.ones_like(xval)

colormap = plt.get_cmap('hsv')
norm = mpl.colors.Normalize(0.0, 2*np.pi)

ax = plt.subplot(1, 1, 1, polar=True)
ax.scatter(xval, yval, c=xval, s=300, cmap=colormap, norm=norm, linewidths=0)
ax.set_yticks([])

Собственная попытка создания цветового круга

Однако эта попытка имеет два серьезных недостатка.

Во-первых, при сохранении результирующей фигуры в качестве вектора (figure_1.svg) цветовое колесо состоит (как и ожидалось) из 621 различных фигур, соответствующих различным (x, y) значениям. Хотя результат выглядит как круг, на самом деле это не так. Я бы предпочел использовать фактический круг, определенный несколькими точками пути и кривыми Безье между ними, как, например, matplotlib.patches.Circle. Это мне кажется "правильным" способом сделать это, и результат будет выглядеть лучше (без полос, лучшего градиента, лучшего сглаживания).

Во-вторых (связанный), окончательные графические маркеры (последние несколько до 2*pi) перекрывают первые несколько. Это очень сложно увидеть в рендеринге пикселей, но если вы увеличите рендеринг на основе вектора, вы можете четко видеть, что последний диск перекрывает первые несколько.

Я попытался использовать разные маркеры (. или |), но ни одна из них не обошла вторую проблему.

Нижняя строка: могу ли я нарисовать круг в Python/Matplotlib, который определен в правильном пути кривой вектора/Безье и который имеет цвет границы, определенный в соответствии с цветовой схемой (или, если это не так, произвольный цветовой градиент)?

Ответы

Ответ 1

Один из способов, который я нашел, - создать цветную карту, а затем проецировать ее на полярную ось. Вот рабочий пример - он включает в себя неприятный взлом, хотя (четко прокомментированный). Я уверен, что есть способ либо настроить ограничения, либо (сложнее) написать свой собственный Transform, чтобы обойти его, но пока я еще не справился. Я думал, что ограничения на вызов Normalize будут делать это, но, по-видимому, нет.

import matplotlib.pyplot as plt
import numpy as np
from matplotlib import cm
import matplotlib as mpl

fig = plt.figure()

display_axes = fig.add_axes([0.1,0.1,0.8,0.8], projection='polar')
display_axes._direction = 2*np.pi ## This is a nasty hack - using the hidden field to 
                                  ## multiply the values such that 1 become 2*pi
                                  ## this field is supposed to take values 1 or -1 only!!

norm = mpl.colors.Normalize(0.0, 2*np.pi)

# Plot the colorbar onto the polar axis
# note - use orientation horizontal so that the gradient goes around
# the wheel rather than centre out
quant_steps = 2056
cb = mpl.colorbar.ColorbarBase(display_axes, cmap=cm.get_cmap('hsv',quant_steps),
                                   norm=norm,
                                   orientation='horizontal')

# aesthetics - get rid of border and axis labels                                   
cb.outline.set_visible(False)                                 
display_axes.set_axis_off()
plt.show() # Replace with plt.savefig if you want to save a file

Это создает

colorwheel прямо из matplotlib

Если вам нужно кольцо, а не колесо, используйте его до plt.show() или plt.savefig

display_axes.set_rlim([-1,1])

Это дает

color ring


В соответствии с @EelkeSpaak в комментариях - если вы сохраняете графику как SVG в соответствии с OP, вот подсказка для работы с полученной графикой: Маленькая элементы полученного SVG-изображения касаются и не перекрываются. Это приводит к слабым серым линиям в некоторых рендерах (Inkscape, Adobe Reader, возможно, не в печати). Простым решением этого является применение небольшого (например, 120%) масштабирования для каждого из отдельных элементов градиента, используя, например, Inkscape или Illustrator. Обратите внимание, что вам придется применять преобразование к каждому элементу отдельно (указанное программное обеспечение предоставляет функциональные возможности для этого автоматически), а не всему чертежу, в противном случае он не имеет эффекта.

Ответ 2

Мне просто нужно было сделать цветовое колесо и я решил обновить решение rsnape, чтобы оно было совместимо с matplotlib 2.1. Вместо того, чтобы поместить объект цветовой панели на ось, вы можете вместо этого построить полярную цветную сетку на полярной диаграмме.

import matplotlib.pyplot as plt
import numpy as np
from matplotlib import cm
import matplotlib as mpl

# If displaying in a Jupyter notebook:
# %matplotlib inline 

# Generate a figure with a polar projection
fg = plt.figure(figsize=(8,8))
ax = fg.add_axes([0.1,0.1,0.8,0.8], projection='polar')

# Define colormap normalization for 0 to 2*pi
norm = mpl.colors.Normalize(0, 2*np.pi) 

# Plot a color mesh on the polar plot
# with the color set by the angle

n = 200  #the number of secants for the mesh
t = np.linspace(0,2*np.pi,n)   #theta values
r = np.linspace(.6,1,2)        #radius values change 0.6 to 0 for full circle
rg, tg = np.meshgrid(r,t)      #create a r,theta meshgrid
c = tg                         #define color values as theta value
im = ax.pcolormesh(t, r, c.T,norm=norm)  #plot the colormesh on axis with colormap
ax.set_yticklabels([])                   #turn of radial tick labels (yticks)
ax.tick_params(pad=15,labelsize=24)      #cosmetic changes to tick labels
ax.spines['polar'].set_visible(False)    #turn off the axis spine.

Это дает это:

A color wheel for the viridis colormap. Made with matplotlib 2.1.