Shift Optimzation using SCOP (Solver for Constraint Programming)
YouTubeVideo("OmfDYolmT2g")

plot_scop_for_shift[source]

plot_scop_for_shift(file_name:str='scop_out.txt')

plot_scop_for_shift関数の使用法

fig = plot_scop_for_shift("scop_out.txt")
plotly.offline.plot(fig);

モデルの定式化

データ生成

日データ day_df (ファイル名はday.csv)

列:

  • id : 0から始まる整数
  • day : 日付
  • day_of_week : 曜日;holidaysパッケージを用いて日本の祝日の場合には Holidayを入れる。
  • day_type: 人数の必要量データは、この列の要素ごとに定義される。ここでは、平日 (weekday)、日曜 (sunday)と祝日(holiday)の3種類を準備する。

日データ生成関数 generate_day

引数:

  • start_date: 開始日を表す文字列
  • end_date: 終了日を表す文字列

返値:

day_df: 日データフレーム

generate_day[source]

generate_day(start_date, end_date)

日データ day_df 生成

generate_day関数の使用例

day_df = generate_day('2020-5-1', '2020-5-5')
#day_df.to_csv(folder+"day.csv")    
day_df.head()
id day day_of_week day_type
0 0 2020-05-01 Fri weekday
1 1 2020-05-02 Sat weekday
2 2 2020-05-03 Holiday holiday
3 3 2020-05-04 Holiday holiday
4 4 2020-05-05 Holiday holiday
jp_holidays = holidays.Japan()
#dt.date(2015, 1, 1) in jp_holidays 
#print(jp_holidays)
day_df = pd.DataFrame(pd.date_range('2020-5-1', '2020-5-15', freq='D'),columns=["day"])
day_df["day_of_week"] = [('Holiday') if  t in jp_holidays else (t.strftime('%a')) for t in day_df["day"] ]
n_day = len(day_df)

row_ = []
for row in day_df.itertuples():
    if row.day_of_week =="Holiday":
        row_.append("holiday")
    elif row.day_of_week =="Sun":
        row_.append("sunday")
    else:
        row_.append("weekday")
day_df["day_type"] = row_
day_df["id"] = [t for t in range(len(day_df))] 
day_df = day_df.reindex(columns=["id", "day", "day_of_week", "day_type"])
day_df
id day day_of_week day_type
0 0 2020-05-01 Fri weekday
1 1 2020-05-02 Sat weekday
2 2 2020-05-03 Holiday holiday
3 3 2020-05-04 Holiday holiday
4 4 2020-05-05 Holiday holiday
5 5 2020-05-06 Holiday holiday
6 6 2020-05-07 Thu weekday
7 7 2020-05-08 Fri weekday
8 8 2020-05-09 Sat weekday
9 9 2020-05-10 Sun sunday
10 10 2020-05-11 Mon weekday
11 11 2020-05-12 Tue weekday
12 12 2020-05-13 Wed weekday
13 13 2020-05-14 Thu weekday
14 14 2020-05-15 Fri weekday

期間データ period_df (ファイル名 period.csv)

1日のスケジュールは、基本時間単位の区間(これを期と呼ぶ)に対して決められる。 以下では、1時間を1期として生成するが、30分や15分でも構わない。 従来のシフトスケジューリングでは、6時間などを1期として扱うことが多かったが、本システムではより細かい時間単位を用いて最適化を行う。 これは、昨今の時間給で働く従業員が増えたことを考慮したものである。

列:

  • id : 0から始まる整数
  • description: 時間区分の説明;期の開始時刻を入れる。例えば、最初の期は9:00から10:00までを表す。

期間データ生成関数 generate_period

引数:

  • start_time: 開始期の開始時刻
  • end_time: 終了期の終了時刻
  • freq: 時間の刻みを表す文字列; 1時間(既定値)の場合は"1h",30分の場合は"30min"と入力する.

返値: period_df: 期間データフレーム

generate_period[source]

generate_period(start_time, end_time, freq='1h')

期間データ生成関数 generate_period

generate_period関数の使用例

0時から4時まで1時間刻みで期データを生成する. 実際には0から3期までの4期分が計画期間になるが,終了時刻を表す4時が最後に追加されている.これはガントチャートを描画するときに用いるだけである.

start_time = pd.to_datetime("9:00")
end_time = pd.to_datetime("21:00")
period_df = generate_period(start_time, end_time, freq="1h")
period_df
id description
0 0 09:00:00
1 1 10:00:00
2 2 11:00:00
3 3 12:00:00
4 4 13:00:00
5 5 14:00:00
6 6 15:00:00
7 7 16:00:00
8 8 17:00:00
9 9 18:00:00
10 10 19:00:00
11 11 20:00:00
12 12 21:00:00
n_period = 12 
id_ = [t for t in range(n_period+1)] #最後のシフトの終了時刻まで入力するために1を加える。(ガントチャートの描画で使う.)
description_ = [f"{t}:00" for t in range(9,9+n_period+1)]
period_df = pd.DataFrame({"id":id_, "description":description_})
#period_df.to_csv(folder + "period.csv")
period_df
id description
0 0 9:00
1 1 10:00
2 2 11:00
3 3 12:00
4 4 13:00
5 5 14:00
6 6 15:00
7 7 16:00
8 8 17:00
9 9 18:00
10 10 19:00
11 11 20:00
12 12 21:00

休憩データ break_df (ファイル名は break.csv)

時間ごとのシフトスケジュールを組む際には、休憩を考慮することも重要になる。 ここでは、就業規則を反映した休憩データを準備するものとする。 これは、1日の稼働時間に対して、何期分の休憩を入れるかを定義したものである。 また、シフトの開始から(もしくは終了前の)何期の間は休憩を入れることができないといった制約も加える。

列:

  • period : 1日のシフトの稼働時間。最初の行に入っている数字が、最低稼働時間になる。
  • break_time : 休憩を行う期の数
random.seed(1)
T = 13
break_prob = 0.3
period_, break_ = [], []
min_work_time = 3
for t in range(min_work_time, T):
    period_.append(t)
    if t == min_work_time:
        break_ = [0]
    else:
        if random.random() <= break_prob:
            break_.append(break_[-1] + 1)
        else:
            break_.append(break_[-1])
break_df = pd.DataFrame({"period":period_, "break_time":break_})
#break_df.to_csv(folder + "break.csv")
break_df
period break_time
0 3 0
1 4 1
2 5 1
3 6 1
4 7 2
5 8 2
6 9 2
7 10 2
8 11 2
9 12 3

ジョブ(業務)データ job_df (ファイル名は job_csv)

列:

  • id: : 0から始まる整数
  • description : ジョブ(業務、仕事)の名称;最初の行(idは0)には必ず休憩を表す"break"を入れておく。
#description_ = ["break", レジ打ち", "バックヤード", "接客", "調理"]
description_ = ["break", "レジ打ち", "接客"]
n_job = len(description_)
id_ = [t for t in range(n_job)]
job_df = pd.DataFrame({"id":id_, "description":description_})
#job_df.to_csv(folder + "job.csv")
job_df
id description
0 0 break
1 1 レジ打ち
2 2 接客

スタッフ(従業員)データ staff_df (ファイル名は staff.csv)

列:

  • name : スタッフの名前
  • wage_per_period : 1期あたりの賃金
  • max_period : 1日あたりの最大稼働時間
  • max_day : 計画期間内に出勤できる最大日数
  • job_set : スタッフに割り当てることが可能なジョブ(業務)の集合;ジョブidをリスト形式の文字列で入力する。
  • day_off : 出勤できない日のidをリスト形式の文字列で入力する。
  • start: 出勤可能な最早期id
  • end: 退勤する最遅期id
fake = Faker(['en_US', 'ja_JP','zh_CN','ko_KR'])
Faker.seed(1)

n_day = len(day_df)
n_job = len(job_df)
n_staff = 30
name_ = []
job_list = list(job_df["id"][1:]) #最初のジョブは休憩なので除く
for i in range(n_staff):
    name_.append( fake.name() )

staff_df = pd.DataFrame( {"name": name_, 
                          "wage_per_period": np.random.randint(low=850,high=1300,size=n_staff),
                          "max_period": np.random.randint(5,break_df.period.max()+1, n_staff),
                          "max_day": np.random.randint(1,3, n_staff),
                          "job_set": [ str(random.sample(job_list,random.randint(1,n_job-1) )) for s in range(n_staff) ],
                          "day_off": [ str(random.sample( list(range(n_day)), 1 )) for s in range(n_staff) ],
                          #"day_off": [ "[]" for s in range(n_staff) ],
                          "start": np.random.randint(low=0, high=len(period_df)//2 -1, size= n_staff),
                           "end": np.random.randint(low=len(period_df)//2 + 1, high=len(period_df)-1, size= n_staff)
                         } )

#staff_df.to_csv(folder+"staff.csv")
staff_df
name wage_per_period max_period max_day job_set day_off start end
0 毛秀荣 1183 5 1 [1] [2] 2 7
1 박영길 998 6 2 [2] [3] 0 9
2 안미숙 1155 5 1 [1] [3] 0 11
3 村山 拓真 1137 8 2 [2, 1] [2] 1 9
4 김영희 1111 7 1 [1] [3] 4 7
5 김현정 1223 6 2 [1] [0] 4 10
6 青山 明美 971 8 2 [2] [1] 2 7
7 심영미 1279 9 1 [2, 1] [1] 4 10
8 周洋 1209 8 2 [1] [4] 0 8
9 徐娟 1057 9 1 [2] [0] 1 9
10 喜嶋 修平 1251 7 1 [2] [3] 0 8
11 陈磊 1020 11 1 [2] [0] 1 7
12 罗玉英 1264 9 1 [1, 2] [3] 3 10
13 渡辺 陽一 1193 11 1 [1, 2] [1] 1 8
14 Gary Griffith 1205 7 1 [2, 1] [3] 3 10
15 谷红霞 931 8 1 [1, 2] [4] 3 10
16 董建军 958 8 2 [2] [2] 1 7
17 山田 結衣 892 9 1 [2] [1] 1 11
18 宋勇 1074 11 1 [2, 1] [0] 4 10
19 方楠 1238 12 2 [2] [0] 1 7
20 高畅 1286 7 1 [1] [0] 2 10
21 黄芳 1293 9 1 [1] [3] 4 8
22 황옥순 1041 11 1 [1, 2] [3] 1 8
23 朱洋 1011 12 2 [1, 2] [1] 2 7
24 한현준 1027 5 1 [1, 2] [1] 4 7
25 Jennifer Jackson 972 6 2 [1] [4] 3 8
26 渡辺 桃子 1130 11 1 [2] [4] 2 7
27 山岸 くみ子 1034 7 2 [1, 2] [1] 1 7
28 解英 1153 10 2 [1] [4] 1 10
29 桐山 直人 1120 5 1 [2] [3] 3 10

必要人数データ requirement_df (ファイル名は requirement.csv)

列:

  • day_type : day_dfの day_type列で入力した日の種類;この種類別に必要人数を定義する。
  • job : ジョブid 
  • period : 期id
  • requirement : 必要人数
n_period = len(period_df)-1
day_type = ["weekday", "sunday", "holiday"]
type_, job_, period_, lb_ = [],[],[],[] 
for d in day_type:
    for j in range(1,n_job): #ジョブ番号0は休憩なので除く
        req_ = np.ones(n_period, int)
        lb = 0
        ub = n_period
        for iter_ in range(4):
            lb = lb + random.randint(1, 3)
            ub = ub - random.randint(1, 3)
            if lb < ub:
                for t in range(lb,ub):
                    req_[t]+=1
            
        for t in range(n_period):
            type_.append(d)
            job_.append(j)
            period_.append(t)
            lb_.append(req_[t])

requirement_df = pd.DataFrame({"day_type":type_, "job":job_, "period":period_,"requirement":lb_ })
#requirement_df.to_csv(folder+"requirement.csv")
requirement_df.head()
day_type job period requirement
0 weekday 1 0 1
1 weekday 1 1 2
2 weekday 1 2 2
3 weekday 1 3 2
4 weekday 1 4 3

アナリティクス

スタッフの情報と必要人数データから,大まかな実行可能性を調べる.

スタッフが確率的にジョブや時間に割り当てられたと仮定して,必要量との比率を求め,可視化する.

n_day = len(day_df)
n_job = len(job_df)
n_period = len(period_df)-1
work_hours = np.zeros( (n_job,n_day,n_period) )

for row in staff_df.itertuples():
    job_set = ast.literal_eval(row.job_set) 
    day_off = set( ast.literal_eval(row.day_off) )
    max_period = row.max_period  #最大稼働時間/1日の稼働時間 だけ加算する
    max_day = row.max_day #最大稼働日数/計画期間 だけ加算する.
    ratio = max_period/n_period * max_day/n_day
    #print(ratio)
    for d in range(n_day):
        if d not in day_off:
            for j in job_set:
                for t in range(row.start, row.end+1):
                    work_hours[j,d,t]+= ratio
                   
work_hours[1]
array([[ 1.6       ,  3.03333333,  4.68333333,  6.76666667,  7.96666667,
         7.96666667,  7.96666667,  7.96666667,  7.41666667,  5.61666667,
         5.01666667,  1.88333333],
       [ 1.95      ,  3.7       ,  5.2       ,  7.75      ,  9.68333333,
         9.68333333,  9.68333333,  9.68333333,  7.51666667,  6.2       ,
         5.46666667,  1.88333333],
       [ 1.6       ,  4.18333333,  6.28333333,  8.83333333, 10.3       ,
        10.3       , 10.3       , 10.3       ,  8.13333333,  5.98333333,
         4.65      ,  1.88333333],
       [ 1.03333333,  3.61666667,  5.16666667,  7.16666667,  9.1       ,
         9.1       ,  9.1       ,  9.1       ,  7.48333333,  5.8       ,
         4.46666667,  1.33333333],
       [ 1.61666667,  3.6       ,  5.2       ,  6.21666667,  7.41666667,
         7.41666667,  7.41666667,  7.41666667,  5.25      ,  3.6       ,
         2.26666667,  0.55      ]])
requirement ={}
for row in requirement_df.itertuples():
    requirement[row.day_type, row.period, row.job] = row.requirement
req = np.zeros( (n_job, n_day, n_period) )
for d, row in enumerate(day_df.itertuples()):
    for j in range(1,n_job):
        for t in range(n_period):
            req[j,d,t] += requirement[row.day_type,t,j]
req[1]
array([[1., 2., 2., 2., 3., 3., 3., 2., 2., 2., 1., 1.],
       [1., 2., 2., 2., 3., 3., 3., 2., 2., 2., 1., 1.],
       [1., 2., 2., 2., 3., 3., 4., 3., 2., 1., 1., 1.],
       [1., 2., 2., 2., 3., 3., 4., 3., 2., 1., 1., 1.],
       [1., 2., 2., 2., 3., 3., 4., 3., 2., 1., 1., 1.]])
import seaborn as sns
sns.heatmap(work_hours[1]/req[1], annot=True, fmt="1.2f");
fig = px.imshow(work_hours[1]/req[1], color_continuous_scale=px.colors.sequential.Viridis)
fig.update_xaxes(side="top")
plotly.offline.plot(fig);

スタッフの希望と必要人数の関係を調べる関数 estimate_requirement

estimate_requirement[source]

estimate_requirement(day_df, period_df, job_df, staff_df, requirement_df, days=None)

fig = estimate_requirement(day_df, period_df, job_df, staff_df, requirement_df, days=[1,2,3])
#plotly.offline.plot(fig);
Image("../figure/estimate_requirement.png")

制約逸脱計算関数 evaluate_violation

引数:

  • violated (最適化で得られた逸脱量を表す辞書)

返値:

  • cost_df: 逸脱ペナルティを入れたデータフレーム

evaluate_violation[source]

evaluate_violation(violated, x, period_df, day_df, staff_df)

制約逸脱計算関数

evaluate_violation関数の使用例

shift_schedulingの中で使用する.

SCOP Model

制約最適化ソルバー SCOP を用いたモデルを記述する。

引数:

  • period_df : 期間データフレーム
  • break_df : 休憩データフレーム
  • day_df : 日データフレーム
  • job_df : ジョブデータフレーム
  • staff_df : スタッフデータフレーム
  • requirement_df : 必要人数データフレーム
  • theta : 開始直後(もしくは終了直前)に休憩を禁止する期間数(既定値は1)
  • lb_penalty : 必要人数を下回った場合のペナルティ(既定値は10000)
  • ub_penalty : 必要人数を上回った場合のペナルティ(既定値は0)
  • job_change_penalty : ジョブを切り替えたときのペナルティ(既定値は10)
  • break_penalty : 開始直後・終了直前の休憩を逸脱したときのペナルティ(既定値は10000)
  • max_day_penalty : 最大稼働日数を超過したときのペナルティ(既定値は5000)
  • OutputFlag : 出力フラグ;ソルバーの出力を出す場合にはTrue (既定値はFalse)
  • TimeLimit : 計算時間上限(既定値は10秒)
  • random_seed : ソルバーで用いる擬似乱数の種(既定値は1)
  • cloud: 複数人が同時実行する可能性があるときTrue(既定値はFalse); Trueのとき,ソルバー呼び出し時に生成されるファイルにタイムスタンプを追加し,計算終了後にファイルを消去する.

返値:

  • x : 変数 $x$ を入れた辞書
  • y : 変数 $y$ を入れた辞書
  • sol : 解を表す辞書
  • violated : 逸脱した制約を表す辞書
  • day_off : 希望休日を入れた辞書
  • requirement : 必要人数を入れた辞書
  • status : 最適化の状態を表す数字;以下の意味を持つ。
status 意味
0 最適化成功
1 求解中にユーザが Ctrl-C を入力したことによって強制終了した.
2 入力データファイルの読み込みに失敗した.
3 初期解ファイルの読み込みに失敗した.
4 ログファイルの書き込みに失敗した.
5 入力データの書式にエラーがある.
6 メモリの確保に失敗した.
7 実行ファイル scop.exe のよび出しに失敗した.
10 モデルの入力は完了しているが,まだ最適化されていない.
負の値 その他のエラー

shift_scheduling[source]

shift_scheduling(period_df, break_df, day_df, job_df, staff_df, requirement_df, theta=1, lb_penalty=10000, ub_penalty=0, job_change_penalty=10, break_penalty=10000, max_day_penalty=5000, OutputFlag=False, TimeLimit=10, random_seed=1, cloud=False)

シフト最適化

shift_scheduling関数の使用例

period_df = pd.read_csv(folder+"period.csv", index_col=0)
break_df = pd.read_csv(folder+"break.csv", index_col=0)
day_df = pd.read_csv(folder+"day.csv", index_col=0)
job_df = pd.read_csv(folder+"job.csv", index_col=0)
staff_df = pd.read_csv(folder+"staff.csv", index_col=0)
requirement_df = pd.read_csv(folder+"requirement.csv", index_col=0)

cost_df, violate_df, new_staff_df, job_assign, status = shift_scheduling(period_df, break_df, day_df, job_df, staff_df, requirement_df, theta=1,
                    lb_penalty =10000, ub_penalty =10000, job_change_penalty = 10, break_penalty = 10000, max_day_penalty = 5000, 
                    OutputFlag=False, TimeLimit=10, random_seed = 2)
 ================ Now solving the problem ================ 

 
penalty value
0 Cost 216512
1 Staff Lower Bound 0
2 Staff Upper Bound 0
3 Change Job 23
4 Break Number 0
5 Early Break 0
6 Late Break 0
7 Max Work Day 0
 
Day 0 Day 1 Day 2 Day 3 Day 4
Staff Lower Bound 0.0 0.0 0.0 0.0 0.0
Staff Upper Bound 0.0 0.0 0.0 0.0 0.0
Change Job 6.0 3.0 5.0 5.0 4.0
Break Number 0.0 0.0 0.0 0.0 0.0
Early Break 0.0 0.0 0.0 0.0 0.0
Late Break 0.0 0.0 0.0 0.0 0.0
 
name wage_per_period max_period max_day job_set day_off start end Shift for Day 0 Shift for Day 1 Shift for Day 2 Shift for Day 3 Shift for Day 4 max day violation
0 Ryan Gallagher 908 10 13 [2] [1] 2 7 None None None None 3_7 0.0
1 박영길 1284 11 10 [2, 1] [4] 2 7 None None None None None 0.0
2 Rachel Davis 1226 9 12 [2, 1] [2] 3 8 None None None None None 0.0
3 史建平 1124 8 10 [2, 1] [2] 3 10 7_9 4_6 None 5_10 None 0.0
4 김영희 972 12 10 [2, 1] [1] 4 8 4_6 None None 4_6 4_6 0.0
5 김현정 936 9 13 [1] [3] 2 9 None 2_7 3_5 None 5_7 0.0
6 王洋 1123 10 12 [1] [1] 3 9 None None 6_8 None None 0.0
7 Teresa James 1017 9 11 [1] [1] 0 9 5_7 None 3_7 0_4 0_2 0.0
8 Javier Johnson 960 9 13 [1] [2] 4 9 None 6_9 None 6_8 6_8 0.0
9 Jeffrey Simpson 911 8 10 [1, 2] [1] 4 9 4_9 None None 5_7 None 0.0
10 David Robinson 1021 11 13 [2] [3] 1 8 5_7 1_3 3_5 None None 0.0
11 이경수 1026 8 10 [1] [4] 4 11 None 4_6 4_6 4_6 None 0.0
12 罗玉英 1220 12 12 [2] [4] 3 9 None None None None None 0.0
13 진준호 1194 8 14 [1] [0] 0 11 None None 0_2 None None 0.0
14 杨洁 1201 10 14 [2, 1] [4] 1 8 4_6 5_7 5_7 3_5 None 0.0
15 谷红霞 1183 9 14 [2] [4] 0 10 4_6 None 0_2 2_4 None 0.0
16 장수진 1287 10 13 [2] [4] 3 11 8_10 7_9 7_11 None None 0.0
17 文雪梅 1142 9 10 [1, 2] [1] 0 10 1_3 None None None None 0.0
18 최경희 943 11 14 [1] [3] 4 11 8_11 8_11 8_10 None 7_11 0.0
19 안승현 1168 10 10 [2] [2] 2 7 2_4 4_6 None None None 0.0
20 Joshua Warner 1237 8 11 [2] [2] 2 11 6_9 6_11 None None None 0.0
21 이영희 1121 9 14 [2, 1] [3] 2 10 None 5_7 4_6 None 3_5 0.0
22 石玲 993 11 12 [2] [2] 0 11 0_2 0_2 None 0_4 0_3 0.0
23 杉山 晃 1199 8 13 [1] [2] 0 11 1_3 None None 7_11 1_6 0.0
24 한현준 1243 11 11 [1, 2] [3] 0 10 7_9 7_9 None None 5_10 0.0
25 佐々木 加奈 1063 12 13 [2, 1] [1] 4 11 7_11 None None 7_11 4_6 0.0
26 Vicki Townsend 985 8 13 [1, 2] [3] 3 7 None 3_7 None None None 0.0
27 山岸 くみ子 1144 10 14 [1, 2] [2] 0 11 0_5 0_3 None 1_3 8_11 0.0
28 解英 1056 10 10 [2, 1] [4] 2 11 None 8_10 6_11 5_7 None 0.0
29 陈萍 1015 11 12 [2, 1] [3] 1 9 None 1_4 1_6 None 2_4 0.0
#plotly.offline.plot(fig);
#plotly.offline.plot(fig);

可視化

必要人数を描画する関数 make_requirement_graph

引数:

  • day_df : 日データフレーム
  • period_df : 期間データフレーム
  • job_df : ジョブデータフレーム
  • staff_df : スタッフデータフレーム
  • requirement_df : 必要人数データフレーム
  • job_assign : 変数 $y$ の情報(スタッフiが日dの期tに割り当てられたジョブの番号)を入れた辞書(キーは文字列)
  • day : 描画したい日のid

返値:

  • fig : Plotlyのグラフオブジェクト

make_requirement_graph[source]

make_requirement_graph(day_df, period_df, job_df, staff_df, requirement_df, job_assign, day=0)

必要人数のグラフを生成する関数

make_requirement_graphの使用例

if TEST:
    print("Status",status)
    if status ==0: #SCOPが失敗していないときのみ表示
        fig = make_requirement_graph(day_df, period_df, job_df, staff_df, requirement_df, job_assign, day = 0)
    plotly.offline.plot(fig);
Image("../figure/requirement_for_shift.png")
Status 0

ガントチャートを描画する関数 make_gannt_for_shift

引数:

  • day_df : 日データフレーム
  • period_df : 期間データフレーム
  • staff_df : スタッフデータフレーム
  • job_df : ジョブデータフレーム
  • requirement_df : 必要人数データフレーム
  • job_assign : 変数 $y$ の情報(スタッフiが日dの期tに割り当てられたジョブの番号)を入れた辞書(キーは文字列)
  • day_off : 希望休日を入れた辞書
  • day : 描画したい日のid

返値:

  • fig : Plotlyのグラフオブジェクト

make_gannt_for_shift[source]

make_gannt_for_shift(day_df, period_df, staff_df, job_df, job_assign, day=0)

スタッフごとのガントチャートを生成する関数

make_gannt_for_shiftの使用例

if TEST:
    if status ==0: #SCOPが失敗していないときのみ表示
        fig = make_gannt_for_shift(day_df, period_df, staff_df, job_df, job_assign, day=0 )
    plotly.offline.plot(fig);
Image("../figure/gannt_for_shift.png")