【问题标题】:Break x-axis into several subsets in matplotlib在matplotlib中将x轴分成几个子集
【发布时间】:2015-08-01 15:15:40
【问题描述】:

采取以下matplotlib 图表/图,其中x 轴是时间。

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

假设我有一个标签列表,例如

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

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

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

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

通常如何添加此类信息?我将如何使用matplotlib 来实现它?

【问题讨论】:

标签: python numpy matplotlib


【解决方案1】:

我可能很想使用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)


从 cmets 看来,您希望在时间轴上表示值范围而不是单个时间点,并且您希望在同一组轴上绘制多个系列(因此您不希望注释的任何方面随时间序列的 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')

使用图例的好处是您不必担心在最后一个范围内塞入文本标签,因为范围很窄。

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

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)

这种方法需要更多的调整,以适应标签而不重叠彼此或轴脊。

【讨论】:

  • 我应该澄清这一点,但我不知道这个解决方案存在。我在该图上绘制了几个时间序列,因此我想要一个与序列无关的解决方案。
  • 您要标注特定时间点还是时间范围?
  • 我更新的答案中的任何一个解决方案都解决了您要查找的问题吗?
【解决方案2】:

要突出显示范围,您可以使用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范围。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2017-10-11
    • 2020-04-25
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-06-15
    相关资源
    最近更新 更多