Пометка интервалов с метками в matplotlib/gnuplot
У меня есть образец данных, который выглядит так:
a 10:15:22 10:15:30 OK
b 10:15:23 10:15:28 OK
c 10:16:00 10:17:10 FAILED
b 10:16:30 10:16:50 OK
Я хочу, чтобы эти данные были построены следующим образом:
captions ^
|
c | *------*
b | *---* *--*
a | *--*
|___________________
time >
С цветом строк в зависимости от состояния OK/FAILED
точки данных. Ярлыки (a/b/c/...
) могут повторяться или не повторяться.
Как я понял из документации для gnuplot и matplotlib, этот тип сюжета должен быть проще сделать в последнем, поскольку он не является стандартным сюжетом и требуется некоторая предварительная обработка.
Возникает вопрос:
- Есть ли стандартный способ делать такие графики в любом из инструментов?
- Если нет, как мне следует планировать эти данные (указатели на соответствующие инструменты/документацию/функции/примеры, которые делают что-то вроде вида, описанного здесь)?
Ответы
Ответ 1
Обновлено: теперь включает обработку образца данных и использование функций дат mpl.
import matplotlib.pyplot as plt
from matplotlib.dates import DateFormatter, MinuteLocator, SecondLocator
import numpy as np
from StringIO import StringIO
import datetime as dt
### The example data
a=StringIO("""a 10:15:22 10:15:30 OK
b 10:15:23 10:15:28 OK
c 10:16:00 10:17:10 FAILED
b 10:16:30 10:16:50 OK
""")
#Converts str into a datetime object.
conv = lambda s: dt.datetime.strptime(s, '%H:%M:%S')
#Use numpy to read the data in.
data = np.genfromtxt(a, converters={1: conv, 2: conv},
names=['caption', 'start', 'stop', 'state'], dtype=None)
cap, start, stop = data['caption'], data['start'], data['stop']
#Check the status, because we paint all lines with the same color
#together
is_ok = (data['state'] == 'OK')
not_ok = np.logical_not(is_ok)
#Get unique captions and there indices and the inverse mapping
captions, unique_idx, caption_inv = np.unique(cap, 1, 1)
#Build y values from the number of unique captions.
y = (caption_inv + 1) / float(len(captions) + 1)
#Plot function
def timelines(y, xstart, xstop, color='b'):
"""Plot timelines at y from xstart to xstop with given color."""
plt.hlines(y, xstart, xstop, color, lw=4)
plt.vlines(xstart, y+0.03, y-0.03, color, lw=2)
plt.vlines(xstop, y+0.03, y-0.03, color, lw=2)
#Plot ok tl black
timelines(y[is_ok], start[is_ok], stop[is_ok], 'k')
#Plot fail tl red
timelines(y[not_ok], start[not_ok], stop[not_ok], 'r')
#Setup the plot
ax = plt.gca()
ax.xaxis_date()
myFmt = DateFormatter('%H:%M:%S')
ax.xaxis.set_major_formatter(myFmt)
ax.xaxis.set_major_locator(SecondLocator(interval=20)) # used to be SecondLocator(0, interval=20)
#To adjust the xlimits a timedelta is needed.
delta = (stop.max() - start.min())/10
plt.yticks(y[unique_idx], captions)
plt.ylim(0,1)
plt.xlim(start.min()-delta, stop.max()+delta)
plt.xlabel('Time')
plt.show()
![Resulting image]()
Ответ 2
Гнуплот with vector
решением
Свернуто с: http://gnuplot.sourceforge.net/demo_5.2/gantt.html
main.gnuplot
#!/usr/bin/env gnuplot
$DATA << EOD
1 1 5
1 11 13
2 3 10
3 4 8
4 7 13
5 6 15
EOD
set terminal png size 512,512
set output "main.png"
set xrange [-1:]
set yrange [0:]
unset key
set border 3
set xtics nomirror
set ytics nomirror
set style arrow 1 nohead linewidth 3
plot $DATA using 2 : 1 : ($3-$2) : (0.0) with vector as 1, \
$DATA using 2 : 1 : 1 with labels right offset -2
GitHub вверх по течению.
Выход:
![enter image description here]()
Вы можете удалить метки, удалив вторую командную строку plot
, я добавил их, потому что они полезны во многих приложениях для более простой идентификации интервалов.
Пример Ганта, с которым я связан, показывает, как обрабатывать форматы даты вместо целых.
Протестировано в gnuplot 5.2 patchlevel 2, Ubuntu 18.04.
Ответ 3
версия gnuplot 5.2 с созданием уникального списка ключей
Основное отличие решения @CiroSantilli состоит в том, что список уникальных ключей создается автоматически из столбца 1, и к индексу можно получить доступ через определенную функцию Lookup()
. Ссылочная демонстрационная версия gnuplot уже использует список уникальных элементов, однако в случае OP есть дубликаты.
Создание такого списка уникальных элементов не существует в gnuplot сразу, поэтому вы должны реализовать его самостоятельно. Код требует gnuplot> = 5.2. Вероятно, трудно получить решение, которое работает в gnuplot 4.4 (время вопроса OP), потому что в то время не было реализовано несколько полезных функций: do for
-loops, summation
, блоки данных,... (версия для gnuplot 4.6 может быть возможно с некоторыми обходными путями).
Редактировать: более ранняя версия использовалась with vectors
и linewidth 20
для построения with vectors
, однако linewidth 20
также расширяется в направлении х, что здесь нежелательно. Поэтому with boxxyerror
теперь используется.
Код:
### Time chart
reset session
$Data <<EOD
# category start end status
"event 1" 10:15:22 10:15:30 OK
"event 2" 10:15:23 10:15:28 OK
pause 10:16:00 10:17:10 FAILED
"something else" 10:16:30 10:17:50 OK
unknown 10:17:30 10:18:50 OK
"event 3" 10:18:30 10:19:50 FAILED
pause 10:19:30 10:20:50 OK
"event 1" 10:17:30 10:19:20 FAILED
EOD
# create list of keys
List = ''
set table $Dummy
plot $Data u (List=List.'"'.strcol(1).'" ',NaN) w table
unset table
# create list of unique keys
UniqueList = ''
do for [i=1:words(List)] {
item = word(List,i)
found = 0
do for [j=1:words(UniqueList)] {
if (item eq word(UniqueList,j)) { found=1; break }
}
if (!found) { UniqueList = UniqueList.'"'.item.'" '}
}
print UniqueList
# define functions for lookup and color
Lookup(s) = (Index = NaN, sum [i=1:words(UniqueList)] \
(Index = s eq word(UniqueList,i) ? i : Index,0), Index)
Color(s) = s eq "OK" ? 0x00cc00 : 0xff0000
set xdata time
set timefmt "%H:%M:%S"
set format x "%M'".'%S"'
set yrange [0.5:words(UniqueList)+0.5]
plot $Data u (timecolumn(2)):(Idx=Lookup(strcol(1))): \
(timecolumn(3)):(timecolumn(2)):(Idx-0.3):(Idx+0.3): \
(Color(strcol(4))):ytic(strcol(1)) \
w boxxyerror fill solid 1.0 lc rgb var notitle
### end of code
Результат:
![enter image description here]()