2015-08-01 4 views
0

Возьмите следующий график matplotlib, где ось x - это время.Разделите ось x на несколько подмножеств в matplotlib

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].

Я хочу как-то добавить информацию с этих этикеток к рисунку. Моя фактическая цифра содержит несколько графиков/временных рядов, поэтому решение должно быть «независимым от серии». Я понятия не имею, что хорошо выглядит, и как это сделать. У меня есть некоторые идеи

  • Текст под оси х, дополнительные прожилки, соответствующие начало и конец
  • Текст под оси х или в графике, пунктир по всей фигуре

Что обычно делается добавить такую ​​информацию? И как бы реализовать это, используя matplotlib?

+0

возможно дубликат [Matplotlib: Добавить строки как пользовательские рентгеновские тики, но и сохранить существующую (числовой) отметьте метки? Альтернативы matplotlib.pyplot.annotate?] (Http://stackoverflow.com/questions/10615960/matplotlib-add-strings-as-custom-x-ticks-but-also-keep-existing-numeric-tick) – Evert

ответ

4

Я мог бы возникнуть соблазн использовать 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

Этот метод требует намного больше тонкой настройки в порядке для установки этикеток без наложения друг на друга или на оси.

+0

I должен был прояснить это, но я не знал, что это решение существует. Я рисую несколько временных рядов на этой фигуре, поэтому я хотел бы иметь решение, которое не зависит от серийности. – FooBar

+0

Вы хотите аннотировать конкретные моменты во времени или диапазоны раз? –

+0

Диапазон раз. – FooBar

1

Для выделения диапазонов, вы можете использовать axhspan или axvspan рисовать коробки вокруг диапазонов, а также аннотировать текст:

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

Смежные вопросы