Matplotlib绘制垂直线直至曲线

时间:2019-12-21 03:40:15

标签: python matplotlib

我当前正在使用Rectangle来尝试以每个矩形单色填充曲线下的区域。但是,矩形的宽度大于1像素。我想绘制1像素宽的线,以免它们重叠。目前,曲线下方的垂直矩形水平重叠1或2个像素。

def rect(x,y,w,h,c):
    ax = plt.gca()
    polygon = plt.Rectangle((x,y),w,h,color=c, antialiased=False)
    ax.add_patch(polygon)

def mask_fill(X,Y, fa, cmap='Set1'):
    plt.plot(X,Y,lw=0)  
    plt.xlim([X[0], X[-1]])
    plt.ylim([0, MAX])
    dx = X[1]-X[0]
    for n, (x,y, f) in enumerate(zip(X,Y, fa)):
        color = cmap(f)
        rect(x,0,dx,y,color)

如果我使用下面的代码画线,则重叠减少了,但仍然有重叠

def vlines(x_pos, y1, y2, c):
    plt.vlines(x_pos, ymin=y1, ymax=y2, color=c)

def draw_lines(X, Y, trend_len, cmap='Blues_r']):
    plt.plot(X, Y, lw=0)  
    plt.xlim([X[0], X[-1]])
    plt.ylim([0, MAX])
    dx = X[1] - X[0]
    ydeltas = y_trend(Y, trend_len)
    for n, (x, y, yd) in enumerate(zip(X, Y, ydeltas)):
        color = cmap(y / MAX)
        vlines(x, y1=0, y2=y, c=color)

将参数值的前3个迭代打印到vlines中,我们可以看到x_pos递增了1-但是红线与下面的图像清晰地重叠了第一条蓝线(NB优先(LHS)蓝线是1像素宽) ):

x_pos: 0, y1: 0, y2: 143.51, c: (0.7816378316032295,     0.8622683583237216, 0.9389773164167627, 1.0)
x_pos: 1, y1: 0, y2: 112.79092811646952, c: (0.9872049211841599, 0.5313341022683583, 0.405843906189927, 1.0)
x_pos: 2, y1: 0, y2: 123.53185623293905, c: (0.9882352941176471, 0.6059669357939254, 0.4853671664744329, 1.0)

样本数据:

47.8668447889, 1
78.5668447889, 1
65.9768447889, 1
139.658525932, 2
123.749454049, 2
116.660382165, 3
127.771310282, 3
114.792238398, 3

上面的第一列对应于系列的y值(x值只是值的数量,从0开始计数) 第二列对应于该类。

我正在生成两个图像:

一个具有每个类别的唯一值(0-6),每个具有不同的颜色(7个唯一的颜色),并且颜色填充到y值将被用作下面数据图像的遮罩。

第二张图片(显示的示例)针对不同的类别值(例如0 = Blues_r,1 = Reds_r等)使用不同的颜色图,并且颜色的强度由y的值给出。

用于计算颜色的代码很好,但是我只是无法获得matplotlib来绘制竖线像素宽度的垂直线。

enter image description here

1 个答案:

答案 0 :(得分:1)

由于您的目标不是创建交互式图形,并且您正在尝试操纵像素列,因此可以使用numpy而不是matplotlib来生成结果。

这是一个函数,它将接受ycategory数组,并使用指定的y创建宽度为height长的图像。颜色缩放与解决方案类似,y除以最大值。

from matplotlib import pyplot as plt
import numpy as np

def draw_lines(y, category, filename, cmap='Set1', max=None, height=None):
    y = np.asanyarray(y).ravel()
    category = np.asanyarray(category).ravel()
    assert y.size == category.size
    if max is None:
        max = y.max()
    if height is None:
        height = int(np.ceil(max))
    if isinstance(cmap, str):
        cmap = plt.get_cmap(cmap)

    colors = cmap(category)
    colors[:, 3] = y / max
    colors = (255 * colors).astype(np.uint8)
    output = np.repeat(colors[None, ...], height, axis=0)

    heights = np.round(height * (y / max))
    mask = np.arange(height)[:, None] >= heights
    mask = np.broadcast_to(mask[::-1, :, None], output.shape)
    output[mask] = 0

    plt.imsave(filename, output)
    return output

第一部分只是设置输入值。第二部分获取颜色值。调用具有n值数组的颜色图将返回(n, 4)范围内的[0, 1.0]颜色数组。 colors[:, 3] = y / max设置与高度成比例的Alpha通道。然后将颜色垂直涂抹到所需的height。根据{{​​3}}提出的方法,最后一部分创建一个掩码,将每列的顶部设置为零。

此版本使用透明度关闭颜色并修剪形状。如果您愿意缩放颜色而不是调整透明度,则可以在白色背景上执行相同的操作:

def draw_lines_b(y, category, filename, cmap='Set1', max=None, height=None):
    y = np.asanyarray(y).ravel()
    category = np.asanyarray(category).ravel()
    assert y.size == category.size
    if max is None:
        max = y.max()
    if height is None:
        height = int(np.ceil(max))
    if isinstance(cmap, str):
        cmap = plt.get_cmap(cmap)

    colors = cmap(category)
    colors[..., :3] *= (y / max)[..., None]
    colors = (255 * colors).astype(np.uint8)
    output = np.repeat(colors[None, ...], height, axis=0)

    heights = np.round(height * (y / max))
    mask = np.arange(height)[:, None] >= heights
    mask = np.broadcast_to(mask[::-1, :, None], output.shape)
    output[mask] = 255

    plt.imsave(filename, output)
    return output

在两种情况下,正如您可以想象的那样,matplotlib并非绝对必要。您可以定义自己的颜色列表,并使用更合适的库(例如PIL)来保存图像。