Как создать сюжет с повторяющимся цветным рисунком? - PullRequest
0 голосов
/ 13 января 2020

Для своего отчета я создаю специальный цветной график в блокноте jupyter. Есть два параметра, x и y.

import numpy as np

x = np.arange(-1,1,0.1)
y = np.arange(1,11,1)

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

values = []
for i in range(len(y)) :
    z = y[i] * x**3
    # in my case the value z represents phases of oscillators
    # so I will transform the computed values to the intervall [0,2pi)
    values.append(z)
values = np.array(values) % 2*np.pi 

Я строю график y против x. Для каждого y = 1,2,3,4... будет горизонтальная линия общей длиной два. Например: координата (0.5,8) обозначает одну точку в строке 8 в позиции x = 0.5, а z(0.5,8) является ее связанным значением.

Теперь я хочу представить каждую точку на всех десяти линиях уникальным цветом, который определяется z(x,y). Поскольку z(x,y) принимает только значения в [0,2pi), мне нужна цветовая схема, которая начинается с нуля (например, z=0 соответствует синему). При увеличении z цвет непрерывно меняется, и в конце на 2pi он снова принимает тот же цвет (поэтому на z ~ 2pi он снова становится синим).

Кто-то знает, как это можно сделать в python

Ответы [ 2 ]

1 голос
/ 14 января 2020

Разноцветные линии несколько сложнее, чем просто точечные графики. В документах есть пример использования LineCollection с. Вот адаптированный код. Обратите внимание, что отрезки линий окрашены с использованием их начальной точки, поэтому убедитесь, что имеется достаточно значений x. Кроме того, пределы x и y больше не устанавливаются автоматически.

Код также добавляет цветную полосу, чтобы проиллюстрировать, как цвета отображаются в значения z. Интересный код из Джейка ВандерПласа показывает, как создавать тики для кратных π.

import numpy as np
from matplotlib import pyplot as plt
from matplotlib.collections import LineCollection

# code from Jake VanderPlas
def format_func(value, tick_number):
    # find number of multiples of pi/2
    N = int(np.round(2 * value / np.pi))
    if N == 0:
        return "0"
    elif N == 1:
        return r"$\pi/2$"
    elif N == 2:
        return r"$\pi$"
    elif N % 2 > 0:
        return r"${0}\pi/2$".format(N)
    else:
        return r"${0}\pi$".format(N // 2)

x = np.linspace(-1, 1, 500)
y_max = 10
# Create a continuous norm to map from data points to colors
norm = plt.Normalize(0, 2 * np.pi)

for y in range(1, y_max + 1):
    z = (y * x ** 3) % 2 * np.pi
    y_array = y * np.ones_like(x)
    points = np.array([x, y_array]).T.reshape(-1, 1, 2)
    segments = np.concatenate([points[:-1], points[1:]], axis=1)
    lc = LineCollection(segments, cmap='hsv', norm=norm)
    lc.set_array(z)  # Set the values used for colormapping
    lc.set_linewidth(2)
    line = plt.gca().add_collection(lc)
    # plt.scatter(x, y_array, c=z, s=10, norm=norm, cmap='hsv')

cbar = plt.colorbar(line)  # , ticks=[k*np.pi for k in np.arange(0, 2.001, 0.25)])
cbar.locator = plt.MultipleLocator(np.pi / 2)
cbar.minor_locator = plt.MultipleLocator(np.pi / 4)
cbar.formatter = plt.FuncFormatter(format_func)
cbar.ax.minorticks_on()
cbar.update_ticks()

plt.yticks(range(1, y_max + 1))  # one tick for every y
plt.xlim(x.min(), x.max())  # the LineCollection doesn't force the limits
plt.ylim(0.5, y_max + 0.5)
plt.show()

resulting image

1 голос
/ 14 января 2020

Тип структуры для x, y и z, который вам нужен, проще использовать meshgrid. Кроме того, чтобы иметь много значений x от -1 до 1, np.linspace(-1,1,N) делит диапазон на четные интервалы N.

Используя meshgrid, z можно вычислить в одной строке, используя numpy векторизация . Это выполняется намного быстрее.

Чтобы установить повторяющийся цвет, можно использовать cycli c colormap , например hsv. Там последний цвет совпадает с начальным цветом.

import numpy as np
from matplotlib import pyplot as plt

x, y = np.meshgrid(np.linspace(-1,1,100), np.arange(1,11,1))
z = (y * x**3) % 2*np.pi
plt.scatter(x, y, c=z, s=6, cmap='hsv')
plt.yticks(range(1,11))
plt.show()

resulting plot

В качестве альтернативы можно создать симметричную c цветовую карту с учетом цветов от и существующая карта и объединение их с одинаковыми цветами в обратном порядке.

import numpy as np
from matplotlib import pyplot as plt
import matplotlib.colors as mcolors

colors_orig = plt.cm.viridis_r(np.linspace(0, 1, 128))
# combine the colors with the reversed array and build a new colormap
colors = np.vstack((colors_orig, colors_orig[::-1]))
symcmap = mcolors.LinearSegmentedColormap.from_list('symcmap', colors)

x, y = np.meshgrid(np.linspace(-1,1,100), np.arange(1,11,1))
z = (y * x**3) % 2*np.pi
plt.scatter(x, y, c=z, s=6, cmap=symcmap)
plt.yticks(range(1,11))
plt.show()
...