#plot #matplotlib #gnuplot #intervals
#построение графика #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()
Комментарии:
1. Спасибо. Я успешно нарисовал график, используя ваше решение в качестве основы. Примет ваш ответ, если никто не предложит лучшего решения.
2. Я обновил свой ответ, я всегда хотел изучить функциональность даты matplotlibs.
3. Для разных конечных символов вы заменяете строки символами разброса. plt.scatter(xstart, y, s = 100, c = color, marker= ‘x’, lw = 2, edgecolor =цвет)
4. Этот пример не работает с matplotlib 1.2 (python 2.7, Fedora 19) — кажется, что код застрял в бесконечном цикле.
5. У меня работает с matplotlib 1.4.0 Python 2.7 в Mac OS 10.10.
Ответ №2:
ответ для @tillsten больше не работает для Python3, я внес некоторые изменения, надеюсь, это поможет.
import matplotlib.pyplot as plt
from matplotlib.dates import DateFormatter, MinuteLocator, SecondLocator
import numpy as np
import pandas as pd
import datetime as dt
import io
### The example data
a=io.StringIO("""
caption start stop state
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""")
data = pd.read_table(a, delimiter=" ")
data["start"] = pd.to_datetime(data["start"])
data["stop"] = pd.to_datetime(data["stop"])
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()
Ответ №3:
версия gnuplot 5.2 с созданием уникального списка ключей
Основное отличие от решения @CiroSantilli заключается в том, что список уникальных ключей создается автоматически из столбца 1, а доступ к индексу можно получить с помощью определенной функции Lookup()
. Демонстрационная версия gnuplot, на которую ссылается ссылка, уже использует список уникальных элементов, однако в случае с OP есть дубликаты.
Создание такого списка уникальных элементов не существует в gnuplot сразу, поэтому вы должны реализовать его самостоятельно. Для кода требуется gnuplot >=5.2. Вероятно, трудно получить решение, которое работает под gnuplot 4.4 (на момент вопроса OP), потому что в то время не было реализовано несколько полезных функций: do for
циклы, summation
, блоки данных, … (версия для gnuplot 4.6 может быть возможна с некоторыми обходными путями).
Редактировать: однако более ранняя версия, использовавшая with vectors
и linewidth 20
для построения графиков, linewidth 20
также распространяется в направлении x, что здесь нежелательно. Следовательно, with boxxyerror
теперь используется.
Да, это можно сделать короче и понятнее.
Сценарий:
### Time chart with gnuplot (requires gnuplot>=5.0)
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 unique items
uniqueList = ''
item(col) = ' "'.strcol(col).'"'
isInList(list,col) = strstrt(uniqueList,item(col)) # returns a number >0 if found
addToList(list,col) = list.item(col)
stats $Data u (!isInList(uniqueList,1) ? uniqueList = addToList(uniqueList,1) : 0) nooutput
timeCenter(col1,col2) = (timecolumn(col1,myTimeFmt) timecolumn(col2,myTimeFmt))*0.5
timeDeltaT(col1,col2) = (timecolumn(col1,myTimeFmt)-timecolumn(col2,myTimeFmt))*0.5
Lookup(col) = int(sum [i=1:words(uniqueList)] (strcol(col) eq word(uniqueList,i)) ? i : 0)
myColor(col) = strcol(col) eq "OK" ? 0x00cc00 : 0xff0000
myBoxWidth = 0.6
myTimeFmt = "%H:%M:%S"
set format x "%M:%S" timedate
set yrange [0.5:words(uniqueList) 0.5]
set grid x,y
plot $Data u (timeCenter(2,3)):(Lookup(1)):(timeDeltaT(2,3)):(0.5*myBoxWidth):
(myColor(4)):ytic(1) w boxxyerror fill solid 1.0 lc rgb var notitle
### end of script
Результат:
Ответ №4:
решение gnuplot 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
Вывод:
Вы можете удалить метки, удалив вторую plot
командную строку, я добавил их, потому что они полезны во многих приложениях для более легкой идентификации интервалов.
Пример Ганта, на который я ссылался, показывает, как обрабатывать форматы даты вместо целых чисел.
Протестировано в gnuplot 5.2 patchlevel 2, Ubuntu 18.04.