左右对齐matplotlib散点图标记

时间:2014-11-01 06:16:54

标签: python matplotlib scatter-plot

我正在使用matplotlib散点图功能在垂直线上创建手柄的外观,以描绘图形的某些部分。但是,为了使它们看起来正确,我需要能够将散点图标记对齐到左边(对于左边的线/描绘边)和/或右边(对于右边的线/描绘边)。

以下是一个例子:

#create the figure
fig = plt.figure(facecolor = '#f3f3f3', figsize = (11.5, 6))
ax = plt. ax = plt.subplot2grid((1, 1), (0,0))

#make some random data
index = pandas.DatetimeIndex(start = '01/01/2000', freq  = 'b', periods = 100)
rand_levels = pandas.DataFrame( numpy.random.randn(100, 4)/252., index = index, columns = ['a', 'b', 'c', 'd'])
rand_levels = 100*numpy.exp(rand_levels.cumsum(axis = 0))
ax.stackplot(rand_levels.index, rand_levels.transpose())

#create the place holder for the vertical lines
d1, d2 = index[25], index[50]

#draw the lines
ymin, ymax = ax.get_ylim()
ax.vlines([index[25], index[50]], ymin = ymin, ymax = ymax, color = '#353535', lw = 2)

#draw the markers
ax.scatter(d1, ymax, clip_on = False, color = '#353535', marker = '>', s = 200, zorder = 3)
ax.scatter(d2, ymax, clip_on = False, color = '#353535', marker = '<', s = 200, zorder = 3)

#reset the limits
ax.set_ylim(ymin, ymax)
ax.set_xlim(rand_levels.index[0], rand_levels.index[-1])
plt.show()

上面的代码为我提供了几乎我正在寻找的图表,如下所示:

focus_timeline

但是,我希望最左边的标记(“&gt;”)“向左对齐”(即稍微向右移动),以便该行继续到标记的背面同样,我想要最右边的标记(“&lt;”)“向右对齐”(即略微向左移动)。像这样:

desired_fig

有关如何以灵活方式完成此任务的任何指导或建议?

注意:实际上,我的DataFrame索引pandas.Datetime不是整数,正如我为此简单示例所提供的那样。

4 个答案:

答案 0 :(得分:5)

我喜欢这个问题,对第一个答案不满意。特别是,为了对齐标记,创建特定于图形的对象(mark_align_*)似乎是不必要的麻烦。我最终发现的是通过 verts 指定标记的功能(2元素浮点数列表或Nx2数组,它指定相对于目标绘图点的标记顶点{{1 }})。为了这个目的利用这个功能,我编写了这个函数,

(0, 0)

使用此功能,可以将所需的标记绘制为

from matplotlib import markers
from matplotlib.path import Path

def align_marker(marker, halign='center', valign='middle',):
    """
    create markers with specified alignment.

    Parameters
    ----------

    marker : a valid marker specification.
      See mpl.markers

    halign : string, float {'left', 'center', 'right'}
      Specifies the horizontal alignment of the marker. *float* values
      specify the alignment in units of the markersize/2 (0 is 'center',
      -1 is 'right', 1 is 'left').

    valign : string, float {'top', 'middle', 'bottom'}
      Specifies the vertical alignment of the marker. *float* values
      specify the alignment in units of the markersize/2 (0 is 'middle',
      -1 is 'top', 1 is 'bottom').

    Returns
    -------

    marker_array : numpy.ndarray
      A Nx2 array that specifies the marker path relative to the
      plot target point at (0, 0).

    Notes
    -----
    The mark_array can be passed directly to ax.plot and ax.scatter, e.g.::

        ax.plot(1, 1, marker=align_marker('>', 'left'))

    """

    if isinstance(halign, (str, unicode)):
        halign = {'right': -1.,
                  'middle': 0.,
                  'center': 0.,
                  'left': 1.,
                  }[halign]

    if isinstance(valign, (str, unicode)):
        valign = {'top': -1.,
                  'middle': 0.,
                  'center': 0.,
                  'bottom': 1.,
                  }[valign]

    # Define the base marker
    bm = markers.MarkerStyle(marker)

    # Get the marker path and apply the marker transform to get the
    # actual marker vertices (they should all be in a unit-square
    # centered at (0, 0))
    m_arr = bm.get_path().transformed(bm.get_transform()).vertices

    # Shift the marker vertices for the specified alignment.
    m_arr[:, 0] += halign / 2
    m_arr[:, 1] += valign / 2

    return Path(m_arr, bm.get_path().codes)

或使用ax.plot(d1, 1, marker=align_marker('>', halign='left'), ms=20, clip_on=False, color='k', transform=ax.get_xaxis_transform()) ax.plot(d2, 1, marker=align_marker('<', halign='right'), ms=20, clip_on=False, color='k', transform=ax.get_xaxis_transform())

ax.scatter

在这两个示例中,我都指定了ax.scatter(d1, 1, 200, marker=align_marker('>', halign='left'), clip_on=False, color='k', transform=ax.get_xaxis_transform()) ax.scatter(d2, 1, 200, marker=align_marker('<', halign='right'), clip_on=False, color='k', transform=ax.get_xaxis_transform()) ,以便标记的垂直位置在轴坐标中(transform=ax.get_xaxis_transform()是轴的顶部),这与标记无关对准

与我之前的解决方案相比,此解决方案的明显优势在于它不需要了解 markersize 绘图功能1 vs. { {1}})或(用于转换)。相反,只需指定一个标记及其对齐方式!

干杯!

答案 1 :(得分:2)

一种解决方案是使用mpl.transforms,将transform输入参数用于ax.scatterax.plot。具体来说,我首先要添加,

from matplotlib import transforms as tf

在这种方法中,我使用tf.offset_copy创建偏移了一半大小的标记。但是标记的大小是多少?事实证明,ax.scatterax.plot以不同方式指定标记大小。有关详细信息,请参阅this question

  1. s=的{​​{1}}输入参数指定了点^ 2中的标记大小(即,这是标记适合的方形区域)。

  2. ax.scatter markersize输入参数指定标记的宽度和高度(即标记适合的方形的宽度和高度)。

  3. 使用ax.plot

    因此,如果您想用ax.scatter绘制标记,可以这样做,

    ax.scatter

    这里我使用了ms_scatter = 200 # define markersize mark_align_left_scatter = tf.offset_copy(ax.get_xaxis_transform(), fig, ms_scatter ** 0.5 / 2, units='points') mark_align_right_scatter = tf.offset_copy(ax.get_xaxis_transform(), fig, -ms_scatter ** 0.5 / 2, units='points') ,它是一个变换,它将点放在沿x轴的数据坐标中,但是在y轴上的(0到1)坐标上-轴。这样,我可以使用ax.get_xaxis_transform将点放在图的顶部,而不是使用ymax。此外,如果我平移或缩放图形,标记仍将位于顶部!在我定义了新的转换后,我在调用1时将它们分配到transform属性,

    ax.scatter

    使用ax.scatter(d1, 1, s=ms_scatter, marker='>', transform=mark_align_left_scatter, clip_on=False, color='k') ax.scatter(d2, 1, s=ms_scatter, marker='<', transform=mark_align_right_scatter, clip_on=False, color='k')

    因为它有点简单,我可能会使用ax.plot。在那种情况下我会这样做,

    ax.plot

    最终评论

    您可能想要创建一个包装器,以便更轻松地创建ms = 20 mark_align_left = tf.offset_copy(ax.get_xaxis_transform(), fig, ms / 2, units='points') mark_align_right = tf.offset_copy(ax.get_xaxis_transform(), fig, -ms / 2, units='points') ax.plot(d1, 1, marker='>', ms=ms, transform=mark_align_left, clip_on=False, color='k') ax.plot(d2, 1, marker='<', ms=ms, transform=mark_align_right, clip_on=False, color='k') 转换,但如果您愿意,我会留下您的实现。

    无论您使用mark_align_*还是ax.scatter,输出图都会显示为

    Final figure

答案 2 :(得分:0)

不是最优雅的解决方案,但如果我正确理解您的问题,请分别从d1d2中减去并添加一个:

ax.scatter(d1-1, ymax, clip_on = False, color = '#353535', marker = '>', s = 200, zorder = 3)
ax.scatter(d2+1, ymax, clip_on = False, color = '#353535', marker = '<', s = 200, zorder = 3)

答案 3 :(得分:0)

我找到了解决这个问题的简单方法。 Matplotlib具有不同比对的内置标记: lines_bars_and_markers example code: marker_reference.py enter image description here

只需将>标记更改为9,将<标记更改为8

#draw the markers
ax.scatter(d1, ymax, clip_on=False, color='#353535', marker=9, s=200, zorder=3)
ax.scatter(d2, ymax, clip_on=False, color='#353535', marker=8, s=200, zorder=3)