将x轴分成matplotlib中的几个子集

时间:2015-08-01 15:15:41

标签: python numpy matplotlib

采用以下matplotlib图形/图形,其中x轴为时间。

import numpy as np
import matplotlib.pyplot as plt
time = np.linspace(1500, 2000)
plt.plot(time, np.exp(time*0.01))

plot: time series

说我有一个标签列表,例如

myLabels = {1500:'Awful times', 1800:'Somewhat better times', 1930:'Bad again', 1990:'We are alright'}

标签应该间隔而不是点; Awful times[1500, 1800]的标签。

我想以某种方式将这些标签中的信息添加到图中。 我的实际数字包含几个图表/时间序列,因此解决方案需要“系列独立”。我不知道什么看起来很好,以及如何做到这一点。我有一些想法

  • x轴下的文字,对应于开始和结束的附加刻度
  • x轴或图表中的文字,图中的虚线

添加此类信息通常会采取什么措施?我将如何使用matplotlib实现该功能?

2 个答案:

答案 0 :(得分:5)

我可能想用plt.annotate绘制带标签的箭头:

import numpy as np
import matplotlib.pyplot as plt

time = np.linspace(1500, 2000)
yvals = np.exp(time * 0.01)
myLabels = {1500:'Awful times', 1800:'Somewhat better times',
            1930:'Bad again', 1990:'We are alright'}

fig, ax = plt.subplots(1,  1)
ax.plot(time, yvals)

for x, label in myLabels.iteritems():

    ax.annotate(label, xy=(x, np.exp(x * 0.01)), xytext=(-40, 40),
                xycoords='data', textcoords='offset points',
                ha='center', va='bottom', fontsize='large',
                arrowprops=dict(arrowstyle='->', lw=2))

ax.set_xlim(1300, 2100)
ax.set_ylim(0, yvals.max() * 1.2)

enter image description here

从评论看,你似乎想要在时间轴而不是单个时间点上表示值的范围,并且你想在同一组轴上绘制多个系列(所以你不想要任何方面的注释随时间序列的y值变化。)

你有很多方法可以做到这一点,我仍然不太确定你在寻找什么。一个相当简单的选择是使用plt.axvspan绘制彩色阴影区域(类似于chepyle的答案,除非不改变高度)并使用图例来显示标签:

edges, labels = zip(*sorted(myLabels.iteritems()))
edges = edges + (2000,)
colors = ['r', 'b', 'g', 'c']

for ii, ll in enumerate(labels):
    ax.axvspan(edges[ii], edges[ii + 1], facecolor=colors[ii],
               label=labels[ii], alpha=0.3)

ax.legend(loc='upper left')

enter image description here

使用图例的优势在于,您不必担心在最后一个范围的文本标签中填写,这个范围很窄。

你也可以使用垂直线并挤压上面的标签(可选择使用双头箭头来表示范围):

from matplotlib.transforms import blended_transform_factory

# x-position specified in data coordinates, y-position specified in [0, 1]
# relative axis coordinates
tform = blended_transform_factory(ax.transData, ax.transAxes)

edges, labels = zip(*sorted(myLabels.iteritems()))
edges = np.r_[edges, 2000]
centers = (edges[:-1] + edges[1:]) / 2.

# mark edges with dashed lines
for ee in edges:
    ax.axvline(ee, ymax=0.75, ls='--', c='k')

# plot labels
for cc, ll in zip(centers, labels):
    ax.annotate(ll, xy=(cc, 0.75), xytext=(0, 10),
                xycoords=tform, textcoords='offset points',
                ha='left', va='bottom', rotation=60)

# plot double-ended arrows
for start, stop in zip(edges[:-1], edges[1:]):
    ax.annotate('', xy=(start, 0.75), xytext=(stop, 0.75),
                xycoords=tform, textcoords=tform,
                arrowprops=dict(arrowstyle='<->', lw=2, shrinkA=0, shrinkB=0))

# turn off spines and ticks on the top and right, so that they don't overlap
# with the labels
for sp in ('top', 'right'):
    ax.spines[sp].set_visible(False)
ax.tick_params(top=False, right=False)

# rescale the y-axis so that the labels and arrows are positioned nicely relative
# to the line
ax.set_ylim(0, yvals.max() * 1.4)

enter image description here

这种方法需要进行更多调整才能使标签适合而不会相互重叠或轴刺。

答案 1 :(得分:1)

要突出显示范围,您可以使用axhspanaxvspan在范围周围绘制框,并附上文本注释:

import numpy as np
import matplotlib.pyplot as plt
time = np.linspace(1500, 2000)
y=lambda t: np.exp(t*0.01)
plt.plot(time, y(time))
#use a list of tuples instead of a dictionary - it must be sorted!
myLabels = [(1500,'Awful times'), (1800,'Somewhat better times'), (1930,'Bad again'), (1990,'We are alright')]
colorLabels=['black', 'blue', 'red', 'green'] # set list of colors
for ix ,((xloc,txt),colr) in enumerate(zip(myLabels,colorLabels)):
    # look ahead
    if ix+1==len(myLabels):
        xloc2=np.max(time)
    else:
        xloc2=myLabels[ix+1][0]

    # draw a polygon between the lower and upper xrange, using our y function:
    plt.axhspan(np.min(y(time)), y(xloc2),
                xmin=(xloc-np.min(time))/(np.max(time)-np.min(time)),
                xmax=(xloc2-np.min(time))/(np.max(time)-np.min(time)),
                facecolor=colr, alpha=0.5)
    # add a text arrow pointing to the center of the region of interest
    plt.annotate(txt,xy=(np.mean([xloc,xloc2]),np.mean([y(xloc),y(xloc2)])),
                 xytext=(xloc*0.75+0.25*np.min(time),
                         y(xloc)*0.75+0.25*np.max(y(time))),
                 xycoords='data',
                 textcoords='data',
                 arrowprops=dict(arrowstyle="->"))

plt.show()

我使用该函数来设置矩形的顶部,但是如果有多个y,则可以预先计算它们并取最大值或仅使用axvspan默认y使用完整的y范围。

enter image description here