HOME/Articles/

週単位のガントチャート作成コード

Article Outline

週単位のガントチャート作成コード

ざっくりスケジュール作成のための、週単位のガントチャートを作る。

参考:

https://plotly.com/python/gantt/

https://megatenpa.com/python/plotly/px/gantt-charts/

https://note.nkmk.me/python-calendar-datetime-nth-dow/

import pandas as pd
import matplotlib as mpl
import seaborn as sns
import datetime as dt
import time
import calendar
from calendar import Calendar
import plotly.express as px
sns.set()

# ガントチャート用データフレーム作成
def make_dict(year_1, month_1, nweek1, span_week=1, start_offset=0, Task='task'):
    cl = Calendar(firstweekday=6)
    nweek_list = cl.monthdays2calendar(year_1, month_1)
    for nw, days_ in enumerate(nweek_list):
        for n_, d_ in enumerate(days_):
            if nw+1 == nweek1 and n_+1 == 1 and d_[0] > 0:
                start_day1 = dt.date(year_1, month_1, d_[0])#.strftime('%Y-%m-%d')
            elif nw+1 == nweek1 and n_+1 == 1 and d_[0] == 0:
                last_day = cl.monthdays2calendar(year_1, month_1-1)[-1][0][0]
                start_day1 = dt.date(year_1, month_1-1, last_day)#.strftime('%Y-%m-%d')
    end_day1 = start_day1 + dt.timedelta(weeks=span_week)
    start_day1 = start_day1 + dt.timedelta(weeks=start_offset)
    start_day1 = start_day1.strftime('%Y-%m-%d')
    end_day1 = end_day1.strftime('%Y-%m-%d')
    dict_ = dict(Task=Task, Start=start_day1, Finish=end_day1,)  # ジョブの名称 # 開始日 # 終了日
    return dict_

# 年と月、第何週から始まるか(nweek1)、期間は何週か(span_week)を引数とする
# start_offsetはnweek1をずらすためにある(そのままだと第n週の途中から設定できないため)
dict0 = make_dict(2023, 10, nweek1=2, span_week=1, start_offset=-0.5, Task='タスク1')
dict1 = make_dict(2023, 10, nweek1=3, span_week=2, start_offset=0, Task='タスク2')
dict2 = make_dict(2023, 10, nweek1=4, span_week=3, start_offset=0, Task='タスク3')
dict3 = make_dict(2023, 11, nweek1=3, span_week=2, start_offset=0, Task='タスク4')
dict4 = make_dict(2023, 11, nweek1=4, span_week=1, start_offset=0, Task='タスク5')
dict5 = make_dict(2023, 11, nweek1=5, span_week=3, start_offset=0, Task='タスク6')
dict6 = make_dict(2023, 12, nweek1=4, span_week=4, start_offset=0, Task='タスク7')
dict7 = make_dict(2023, 12, nweek1=3, span_week=1, start_offset=0, Task='タスク7')
dict7_2 = make_dict(2024, 1, nweek1=2, span_week=1, start_offset=0, Task='タスク8')
dict8 = make_dict(2024, 1, nweek1=3, span_week=2, start_offset=0, Task='タスク9')
dict9 = make_dict(2024, 1, nweek1=4, span_week=1.2, start_offset=0, Task='タスク10')
df = pd.DataFrame([dict0, dict1, dict2, dict3, dict4, dict5, dict6, dict7, dict7_2, dict8, dict9])
display(df)

df

# ある日時の第n週を取得
def get_nth_week(year, month, day, firstweekday=0):
    first_dow = calendar.monthrange(year, month)[0]
    offset = (first_dow - firstweekday) % 7
    return (day + offset - 1) // 7 + 1

# Plotlyのpxでガントチャートを作成
def gantt_chart(df, x_start='Start', x_end='Finish', y='Task'
                , save=False, fname='tmp.svg'
                , width=None, height=None, font_size=None, hoverlabel_font_size=None):
    dfadd=df.copy()
    dfadd['color'] = 'color'
    fig = px.timeline(
        dfadd,  # 使用するデータフレーム
        x_start=x_start, x_end=x_end,  # 横軸の開始・終了の列名
        y=y,  # 縦軸の列名
        color='color',
        color_discrete_map={'color': '#12B3C7'},
        width=width, height=height,
    )
    fig.update_yaxes(autorange="reversed")
    fig.update_xaxes(tickformat="%Y-%m-%d")
    # グラフ全体とホバーのフォントサイズ変更
    min_s = pd.to_datetime(df[x_start].min())
    max_e = pd.to_datetime(df[x_end].max())
    ticks_vals = pd.date_range(min_s + dt.timedelta(weeks=-1), max_e + dt.timedelta(weeks=1), freq='1W')
    ticktext = [(str(i.year)+'-'+str(i.month)+'  '+str(get_nth_week(i.year, i.month, i.day, firstweekday=6))+'W') for i in ticks_vals]
    fig.update_layout(font_size=font_size, hoverlabel_font_size=hoverlabel_font_size
                      , plot_bgcolor='white'
                      , xaxis_title=None, yaxis_title=None
                      , showlegend=False
                      , xaxis = dict(tickvals = ticks_vals.astype(str), ticktext = ticktext, tickangle = -20, side='top', gridcolor='lightgrey')
                      , yaxis=dict(side='left')
                     )
    if save:
        fig.write_image(fname)
    fig.show()

gantt_chart(df, x_start='Start', x_end='Finish', y='Task', save=True, fname='tmp.svg', font_size=14, width=1800, height=900)

df