Как настроить диаграмму Ганта с помощью matplotlib и отобразить вертикальную линию, показывающую текущее время на графике? - PullRequest
0 голосов
/ 17 февраля 2020

Вот мой Python код, который в основном строит диаграмму Ганта:

import pandas as pd
import random
from datetime import datetime
import matplotlib.dates as mdates
import matplotlib.pyplot as plt
%matplotlib inline
import math
plt.style.use('ggplot')

df = pd.read_csv('zpp00141_new.csv')

def timestr_to_num(timestr):
    return mdates.date2num(datetime.strptime('0' + timestr if timestr[1] == ':' else timestr, '%I:%M:%S %p'))

df.rename(columns={"Earl. start / time": "start", "Latest finish / time": "finish"}, inplace = True)

df['Operation/Activity'] = df['Operation/Activity'].astype(str)

fig, ax = plt.subplots(figsize=(10, 5))
operations = pd.unique(df['Operation/Activity'])
#df.assign(start=df['Earl. start / time'])
colors = plt.cm.tab10.colors  # get a list of 10 colors
colors *= math.ceil(len(operations) / (len(colors)))  # repeat the list as many times as needed
for operation, color in zip(operations, colors):
    for row in df[df['Operation/Activity'] == operation].itertuples():
        left = timestr_to_num(row.start)
        right = timestr_to_num(row.finish)
        ax.barh(operation, left=left, width=right - left, height=3, color=color)
ax.set_xlim(timestr_to_num('07:00:00 AM'), timestr_to_num('4:30:00 PM'))
ax.xaxis.set_major_formatter(mdates.DateFormatter('%H:%M'))  # display ticks as hours and minutes
ax.xaxis.set_major_locator(mdates.HourLocator(interval=1))  # set a tick every hour
ax.set_xlabel('Time')
ax.set_ylabel('Operation')
plt.tight_layout()
plt.show()

Вы можете увидеть результат на приложенном рисунке: Диаграмма Ганта

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

plt.axvline(pd.Timestamp.now(),color='r')

Я был бы очень признателен за любую помощь в этом вопросе. Вот изображение желаемого результата, я хочу, чтобы мой график был похож: Желаемый график

Кроме того, я хотел бы добавить / добавить еще одну категорию к моей оси Y "Операция короткая text "вместе с" Operation / Activity "#, чтобы он не только показывал номер операции, но и отражал описание операции рядом с ней. Чтобы узнать, как выглядят мои данные, см. Ниже (первая строка - заголовок):

Operation short text,Operation/Activity,Earl. start / time,Latest finish / time
Mount right racks,0250,7:00:00 AM,9:22:00 AM
Mount right side motion unit carriage,0251,9:22:00 AM,10:30:00 AM
Mount left side motion unit carriage,0252,10:30:00 AM,11:17:00 AM
Install motion unit complete,0253,11:17:00 AM,1:01:00 PM
Move machine to next step + EPA,0254,1:01:00 PM,3:30:00 PM
Mount Left Racks,0200,7:00:00 AM,9:12:00 AM
Mount cable motor & Lubricate guide carr,0201,9:12:00 AM,9:44:00 AM
Mount suction components,0202,9:44:00 AM,11:04:00 AM
Mount extraction,0203,11:04:00 AM,12:34:00 PM
Mount temporary diamond plates,0204,12:34:00 PM,1:04:00 PM
Mount piping inside,0205,1:04:00 PM,1:44:00 PM
Move Machine to next step + EPA,0206,1:44:00 PM,3:30:00 PM

Ответы [ 2 ]

0 голосов
/ 19 февраля 2020

Кажется, что проще всего отсортировать информационный кадр по операции, а затем построить горизонтальные столбцы, используя индекс информационного кадра в качестве координаты y. Затем, изменяя пределы оси Y (устанавливая ее с высокого на низкое), получаются операции с наименьшим номером сверху. (В коде теперь предполагается, что каждый столбец будет находиться на новой строке, в то время как старый код предполагал, что для одной операции будет больше столбцов).

Поскольку теперь кажется, что операции принадлежат друг другу, цветовая карта с последовательными цветами выбирается, и цвета запускаются снова каждый раз, когда операция начинается раньше, чем предыдущая. Не стесняйтесь использовать любую схему, которая соответствует вашим целям.

Поскольку datetime.strptime смотрит только на время, он получает дату по умолчанию (1 января 1900 года). Таким образом, ваш подход к использованию одного и того же преобразования для времени «сейчас» очень точен.

Обратите внимание, что анализатор типов pd.read_csv дает формат с плавающей точкой для столбца операций. Вы можете предотвратить это, предоставив ему явную информацию о преобразовании. Например, pd.read_csv(..., converters={1: str}) чтобы иметь второй столбец в виде строки.

import matplotlib.pyplot as plt
import matplotlib.dates as mdates
from datetime import datetime
import pandas as pd
import math
# % matplotlib inline

def timestr_to_num(timestr):
    return mdates.date2num(datetime.strptime('0' + timestr if timestr[1] == ':' else timestr, '%I:%M:%S %p'))

plt.style.use('ggplot')
# df = pd.read_csv('zpp00141_new.csv')
columns = ['Operation short text', 'Operation/Activity', 'Earl. start / time', 'Latest finish / time']
rows = [['Mount right racks', '0250', '7:00:00 AM', '9:22:00 AM'],
        ['Mount right side motion unit carriage', '0251', '9:22:00 AM', '10:30:00 AM'],
        ['Mount left side motion unit carriage', '0252', '10:30:00 AM', '11:17:00 AM'],
        ['Install motion unit complete', '0253', '11:17:00 AM', '1:01:00 PM'],
        ['Move machine to next step + EPA', '0254', '1:01:00 PM', '3:30:00 PM'],
        ['Mount Left Racks', '0200', '7:00:00 AM', '9:12:00 AM'],
        ['Mount cable motor & Lubricate guide carr', '0201', '9:12:00 AM', '9:44:00 AM'],
        ['Mount suction components', '0202', '9:44:00 AM', '11:04:00 AM'],
        ['Mount extraction', '0203', '11:04:00 AM', '12:34:00 PM'],
        ['Mount temporary diamond plates', '0204', '12:34:00 PM', '1:04:00 PM'],
        ['Mount piping inside', '0205', '1:04:00 PM', '1:44:00 PM'],
        ['Move Machine to next step + EPA', '0206', '1:44:00 PM', '3:30:00 PM']]
df = pd.DataFrame(data=rows, columns=columns)
df.rename(columns={"Earl. start / time": "start", "Latest finish / time": "finish"}, inplace=True)

df['Operation/Activity'] = df['Operation/Activity'].astype(int)
df.sort_values('Operation/Activity', ascending=True, inplace=True, ignore_index=True)

fig, ax = plt.subplots(figsize=(10, 5))

#colors = plt.cm.tab10.colors  # get a list of 10 colors
cmap = plt.cm.get_cmap('plasma_r')
colors = [cmap(i/9) for i in range(10)]   # get a list of 10 colors

previous_start = math.inf  # 'previous_start' helps to indicate we're starting again from the left
color_start = 0
for row in df.itertuples():
    left = timestr_to_num(row.start)
    right = timestr_to_num(row.finish)
    if left <= previous_start:
        color_start = row.Index
    ax.barh(row.Index, left=left, width=right - left, height=1, color=colors[(row.Index - color_start) % len(colors)])
    previous_start = left
ax.set_xlim(timestr_to_num('7:00:00 AM'), timestr_to_num('4:30:00 PM'))
ax.xaxis.set_major_formatter(mdates.DateFormatter('%H:%M'))  # display ticks as hours and minutes
ax.xaxis.set_major_locator(mdates.HourLocator(interval=1))  # set a tick every hour
ax.set_xlabel('Time')
ax.set_ylabel('Operation')
ax.set_ylim(len(df), -1)  # set the limits and reverse the order
ax.set_yticks(range(len(df)))
# ax.set_yticklabels(list(df['Operation/Activity']))
ax.set_yticklabels(list(df['Operation short text']))

now = datetime.now().strftime('%I:%M:%S %p')
ax.axvline(x=timestr_to_num(now),color='r')

plt.tight_layout()
plt.show()

example plot

0 голосов
/ 18 февраля 2020

Мне удалось построить вертикальную линию, используя этот код:

now = datetime.now()
now = now.strftime('%I:%M:%S %p')
plt.axvline(x=timestr_to_num(now),color='r')

Я в основном преобразовал время "сейчас" в определенный формат и снова преобразовал его, используя timestr_to_num, который я определил в начале.

Однако мне все еще нужна помощь с добавлением вторичных значений к моим значениям по оси Y (краткий текст операции)

...