简体   繁体   English

用 matplotlib/pyplot 混合线条颜色绘制曲线

[英]Plot curve with blending line colors with matplotlib/pyplot

I want to start the curve with one color and progressively blend into another color until the end.我想从一种颜色开始曲线并逐渐融入另一种颜色直到结束。 The following function in my MCVE works, but surely, there has to be a better way I haven't found out about, yet?!我的 MCVE 中的以下功能有效,但肯定有更好的方法我还没有发现?!

import numpy as np
import matplotlib.pyplot as plt

def colorlist(color1, color2, num):
    """Generate list of num colors blending from color1 to color2"""
    result = [np.array(color1), np.array(color2)]
    while len(result) < num:
        temp = [result[0]]
        for i in range(len(result)-1):
            temp.append(np.sqrt((result[i]**2+result[i+1]**2)/2))
            temp.append(result[i+1])
        result = temp
    indices = np.linspace(0, len(result)-1, num).round().astype(int)
    return [result[i] for i in indices]

x = np.linspace(0, 2*np.pi, 100)
y = np.sin(x)
colors = colorlist((1, 0, 0), (0, 0, 1), len(x))

for i in range(len(x)-1):
    xi = x[i:i+1+1]
    yi = y[i:i+1+1]
    ci = colors[i]
    plt.plot(xi, yi, color=ci, linestyle='solid', linewidth='10')

plt.show()

1

Not sure what "better way" refers to.不确定“更好的方法”是指什么。 A solution with less code, which would draw faster is the use of a LineCollection together with a colormap.使用更少代码、绘制速度更快的解决方案是使用 LineCollection 和颜色图。

A colormap can be defined by two colors and any colors in between are automatically interpolated.颜色图可以由两种颜色定义,中间的任何颜色都会自动插值。

cmap = matplotlib.colors.LinearSegmentedColormap.from_list("", [(1, 0, 0), (0, 0, 1)])

A LineCollection can be used to plot a lot of lines at once. LineCollection 可用于一次绘制多条线。 Being a ScalarMappable it can use a colormap to colorize each line differently according to some array - in this case one may just use the x values for that purpose.作为 ScalarMappable,它可以使用颜色图根据某个数组对每条线进行不同的着色 - 在这种情况下,可以只使用 x 值来实现该目的。

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

x = np.linspace(0, 2*np.pi, 100)
y = np.sin(x)

cmap = LinearSegmentedColormap.from_list("", [(1, 0, 0), (0, 0, 1)])

points = np.array([x, y]).T.reshape(-1,1,2)
segments = np.concatenate([points[:-1],points[1:]], axis=1)

lc = LineCollection(segments, cmap=cmap, linewidth=10)
lc.set_array(x)
plt.gca().add_collection(lc)
plt.gca().autoscale()
plt.show()

在此处输入图像描述

The drawback of this solution as can be see in the picture is that the individual lines are not well connected.从图中可以看出,这种解决方案的缺点是各条线路没有很好地连接。

So to circumvent this, one may plot those points overlapping, using因此,为了规避这一点,可以绘制这些点重叠,使用

segments = np.concatenate([points[:-2],points[1:-1], points[2:]], axis=1)

在此处输入图像描述


In the above the color is linearly interpolated between the two given colors. 在上面,颜色是在两个给定颜色之间线性插值的。 The plot therefore looks different than the one from the question using some custom interpolation. 因此,该图看起来与使用一些自定义插值的问题不同。

在此处输入图像描述

To obtain the same colors as in the question, you may use the same function to create the colors used in the colormap for the LineCollection.要获得与问题中相同的颜色,您可以使用相同的函数来创建 LineCollection 的颜色图中使用的颜色。 If the aim is to simplify this function you may directly calculate the values as the square root of the color difference in the channels.如果目的是简化此函数,您可以直接将值计算为通道中色差的平方根。

 import numpy as np import matplotlib.pyplot as plt from matplotlib.collections import LineCollection from matplotlib.colors import LinearSegmentedColormap x = np.linspace(0, 2*np.pi, 100) y = np.sin(x) def colorlist2(c1, c2, num): l = np.linspace(0,1,num) a = np.abs(np.array(c1)-np.array(c2)) m = np.min([c1,c2], axis=0) s = np.sign(np.array(c2)-np.array(c1)).astype(int) s[s==0] =1 r = np.sqrt(np.c_[(l*a[0]+m[0])[::s[0]],(l*a[1]+m[1])[::s[1]],(l*a[2]+m[2])[::s[2]]]) return r cmap = LinearSegmentedColormap.from_list("", colorlist2((1, 0, 0), (0, 0, 1),100)) points = np.array([x, y]).T.reshape(-1,1,2) segments = np.concatenate([points[:-2],points[1:-1], points[2:]], axis=1) lc = LineCollection(segments, cmap=cmap, linewidth=10) lc.set_array(x) plt.gca().add_collection(lc) plt.gca().autoscale() plt.show()

在此处输入图像描述

In response to a comment above: If you want to change the color depending on the y value, you can use the following code:回应上面的评论:如果你想根据y值改变颜色,你可以使用下面的代码:

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

x = np.linspace(0, 2 * np.pi, 100)
y = np.sin(x)
ynorm = (y - y.min()) / (y.max() - y.min())


def colorlist2(c1, c2, num):
    l = np.linspace(0, 1, num)
    a = np.abs(np.array(c1) - np.array(c2))
    m = np.min([c1, c2], axis=0)
    s = np.sign(np.array(c2) - np.array(c1)).astype(int)
    s[s == 0] = 1
    r = np.sqrt(np.c_[(l * a[0] + m[0])[::s[0]],
                      (l * a[1] + m[1])[::s[1]], (l * a[2] + m[2])[::s[2]]])
    return r


cmap = LinearSegmentedColormap.from_list(
    "", colorlist2((1, 0, 0), (0, 0, 1), 100))
colors = [cmap(k) for k in ynorm[:-1]]

points = np.array([x, y]).T.reshape(-1, 1, 2)
segments = np.concatenate([points[:-2], points[1:-1], points[2:]], axis=1)

lc = LineCollection(segments, colors=colors, linewidth=10)
lc.set_array(x)
plt.gca().add_collection(lc)
plt.gca().autoscale()
plt.show()

This will output this graph:这将输出此图:

Graph with color depending on y value图表的颜色取决于 y 值

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM