为什么我的线裁剪在matplotlib中?

时间:2015-05-12 08:09:01

标签: python matplotlib

我正在尝试绘制一系列线条。这些线的长度都相同,随机切换颜色为随机长度(蓝色到橙色)。我用蓝色绘制线条,然后在顶部覆盖橙色。你可以从我的照片中看到它是灰色的线条的剪裁部分。我无法弄清楚为什么会这样。另外我相信我的标签并没有像他们应该那样向左移动。非常感谢任何帮助。

Clipping bar graph

import numpy as np
import matplotlib.pyplot as plt
import matplotlib.lines as mlines
import random

plt.close('all')
fig, ax = plt.subplots(figsize=(15,11))


def label(xy, text):
    y = xy[1] - 2
    ax.text(xy[0], y, text, ha="left", family='sans-serif', size=14)


def draw_chromosome(start, stop, y, color):
    x = np.array([start, stop])
    y = np.array([y, y])
    line = mlines.Line2D(x , y, lw=10., color=color)
    ax.add_line(line)


x = 50
y = 100
chr = 1

for i in range(22):
    draw_chromosome(x, 120, y, "#1C2F4D")

    j = 0
    while j < 120:
        print j
        length = 1
        if random.randint(1, 100) > 90:
            length = random.randint(1, 120-j)
            draw_chromosome(j, j+length, y, "#FA9B00")
        j = j+length+1
    label([x, y], "Chromosome%i" % chr)

    y -= 3
    chr += 1


plt.axis('equal')
plt.axis('off')
plt.tight_layout()
plt.show()

2 个答案:

答案 0 :(得分:2)

您只能将蓝色背景从x = 50绘制到x = 120。

替换此行:

draw_chromosome(x, 120, y, "#1C2F4D")

用这个:

draw_chromosome(0, 120, y, "#1C2F4D")

一直画蓝线。

或者,如果您还想将标签移到左侧,则只需设置x=0,而不是将其设置为50。

答案 1 :(得分:1)

我建议使用LineCollection。下面是我根据http://matplotlib.org/examples/pylab_examples/multicolored_line.html的示例编写的一个小帮助函数(看起来很长,但有很多注释+文档字符串)

import numpy as np
import matplotlib.pyplot as plt
from matplotlib.collections import LineCollection
from matplotlib.colors import ListedColormap, BoundaryNorm
from matplotlib.ticker import NullLocator
from collections import OrderedDict


def binary_state_lines(ax, chrom_data, xmin=0, xmax=120,
                       delta_y=3, 
                       off_color = "#1C2F4D",
                       on_color = "#FA9B00"):
    """
    Draw a whole bunch of chromosomes

    Parameters
    ----------
    ax : Axes
        The axes to draw stuff to

    chrom_data : OrderedDict
        The chromosome data as a dict, key on the label with a list of pairs
        of where the data is 'on'.  Data is plotted top-down

    xmin, xmax : float, optional
        The minimum and maximum limits for the x values

    delta_y : float, optional
        The spacing between lines

    off_color, on_color : color, optional
        The colors to use for the the on/off state

    Returns
    -------
    collections : dict
        dictionary of the collections added keyed on the label

    """
    # base offset
    y_val = 0
    # make the color map and norm
    cmap = ListedColormap([off_color, on_color])
    norm = BoundaryNorm([0, 0.5, 1], cmap.N)
    # sort out where the text should be
    txt_x = (xmax + xmin) / 2
    # dictionary to hold the returned artists
    ret = dict()
    # loop over the input data draw each collection
    for label, data in chrom_data.items():
        # increment the y offset
        y_val += delta_y
        # turn the high windows on to alternating
        # high/low regions
        x = np.asarray(data).ravel()
        # assign the high/low state to each one
        state = np.mod(1 + np.arange(len(x)), 2)
        # deal with boundary conditions to be off
        # at start/end
        if x[0] > xmin:
            x = np.r_[xmin, x]
            state = np.r_[0, state]
        if x[-1] < xmax:
            x = np.r_[x, xmax]
            state = np.r_[state, 0]
        # make the matching y values
        y = np.ones(len(x)) * y_val
        # call helper function to create the collection
        coll = draw_segments(ax, x, y, state,
                                     cmap, norm)
        ret[label] = coll

    # set up the axes limits
    ax.set_xlim(xmin, xmax)
    ax.set_ylim(0, y_val + delta_y)
    # turn off x-ticks
    ax.xaxis.set_major_locator(NullLocator())
    # make the y-ticks be labeled as per the input
    ax.yaxis.set_ticks((1 + np.arange(len(chrom_data))) * delta_y)
    ax.yaxis.set_ticklabels(list(chrom_data.keys()))
    # invert so that the first data is at the top
    ax.invert_yaxis()
    # turn off the frame and patch
    ax.set_frame_on(False)
    # return the added artists
    return ret

def draw_segments(ax, x, y, state, cmap, norm, lw=10):
    """
    helper function to turn boundary edges into the input LineCollection
    expects.

    Parameters
    ----------
    ax : Axes
       The axes to draw to

    x, y, state : array
       The x edges, the y values and the state of each region

    cmap : matplotlib.colors.Colormap
       The color map to use

    norm : matplotlib.ticker.Norm
       The norm to use with the color map

    lw : float, optional
       The width of the lines
    """

    points = np.array([x, y]).T.reshape(-1, 1, 2)
    segments = np.concatenate([points[:-1], points[1:]], axis=1)
    lc = LineCollection(segments, cmap=cmap, norm=norm)
    lc.set_array(state)
    lc.set_linewidth(lw)

    ax.add_collection(lc)
    return lc

一个例子:

synthetic_data = OrderedDict()
for j in range(21):
    key = 'data {:02d}'.format(j)
    synthetic_data[key] = np.cumsum(np.random.randint(1, 10, 20)).reshape(-1, 2)

fig, ax = plt.subplots(tight_layout=True)
binary_state_lines(ax, synthetic_data, xmax=120)
plt.show()

example output

将绘图逻辑与其他所有内容分开将使您的代码更易于维护和更可重用。

我还冒昧地将标签从线条之间(它们可以模棱两可)移动到yaxis刻度标签上。