我正在使用matplotlib
scatterplot函数在垂直线上创建句柄的外观以描绘图形的某些部分。但是,为了使它们看起来正确,我需要能够将散点图标记对齐到左边(对于左边的线/描绘边)和/或右(对于右边的线/描边器)。
这是一个例子:
#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()
上面的代码几乎给了我正在寻找的图形,如下所示:
但是,我希望最左边的标记(“>”)“向左对齐”(即稍微向右移动),以便该行继续到标记的后面同样地,我想要最右边的标记( “<”)“向右对齐”(即略微向左移动)。像这样:
有关如何以灵活方式完成此任务的任何指导或建议?
注意:在实践中,我的DataFrame
索引是pandas.Datetime
而不是整数,因为我已经提供了这个简单的例子。
我喜欢这个问题,对我的第一个答案不满意。特别是,为了对齐标记,创建图形特定对象(mark_align_*
)似乎是不必要的麻烦。我最终发现的是通过verts指定标记的功能(2元素浮点数列表,或Nx2数组,指定相对于(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()
,使得标记的垂直位置在轴坐标(1
是轴的顶部),这与标记对齐无关。
与我之前的解决方案相比,这个解决方案的明显优势在于它不需要了解标记大小,绘图函数(ax.plot
与ax.scatter
)或轴(用于转换)的知识。相反,只需指定一个标记及其对齐!
干杯!
一种解决方案是使用mpl.transforms和transform
输入参数到ax.scatter
或ax.plot
。具体来说,我会先添加,
from matplotlib import transforms as tf
在这种方法中,我使用tf.offset_copy
来创建偏移一半大小的标记。但是标记的大小是多少?事实证明,ax.scatter
和ax.plot
以不同方式指定标记大小。有关更多信息,请参阅this question。
s=
的ax.scatter
输入参数指定点^ 2中的标记大小(即,这是标记适合的方形区域)。markersize
的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')
这里我使用了ax.get_xaxis_transform
,它是一个变换,它将点放在沿x轴的数据坐标中,但是在y轴的轴(0到1)坐标上。这样,我可以用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
还是ax.plot
,你的输出图都会看起来像,
不是最优雅的解决方案,但如果我正确理解你的问题,分别从/向d1
和d2
减去和添加一个应该这样做:
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)
我找到了解决这个问题的简单方法。 Matplotlib具有不同比对的内置标记:lines_bars_and_markers example code: marker_reference.py
只需将>
标记更改为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)