前提・実現したいこと
現在、シフトの作成を行おうとしています。
これまで質問した内容に関する回答をもとに、以下のプログラムを作成しました。
該当のソースコード
python
1import numpy as np 2import pandas as pd 3from pulp import LpBinary, LpMinimize, LpProblem, LpVariable, lpSum, value 4 5#アルバイトの集合 6n_member = 5 7member = pd.Series(f"member{m+1}" for m in range(n_member)) 8 9#日にちの集合 10n_day = 2 11day = pd.Series(f"day{d+1}" for d in range(n_day)) 12 13#時刻の集合 14n_time = 9 15time = pd.Series(f"{t+12}時" for t in range(n_time)) 16#t時 : t時~t+1時 17 18#シフト番号 19n_shiftnumber = 6 20shiftnumber = pd.Series(f"shift{sn+1}" for sn in range(n_shiftnumber)) 21 22#シフトの一覧 23shift = pd.read_csv('exshift.csv', header=None) 24shift.index = shiftnumber 25shift.columns = time 26 27shiftdaycount = [int(sum(x) > 0) for x in shift.values] 28 29#各シフトの労働時間 30wtime = pd.read_csv('wtime.csv', header=None) 31wtime.index = shiftnumber 32wtime.columns = day 33 34#必要人数 35need = pd.DataFrame([[2, 2, 2, 2, 2, 2, 2, 2, 2]], index=day, columns=time) 36 37#希望出勤日 38kibou = pd.DataFrame([[1, 1], 39 [1, 1], 40 [1, 1], 41 [1, 1], 42 [1, 1]], index=member, columns=day) 43 44#各メンバーの時給 45jikyu = pd.DataFrame([[900, 900], 46 [900, 900], 47 [900, 900], 48 [900, 900], 49 [900, 900]], index=member, columns=day) 50 51#1日の人件費 52jinkenhi = pd.DataFrame([[16200], 53 [16200]], index=day) 54 55 56#モデルの作成 57prob = LpProblem(sense=LpMinimize) 58 59#変数 60X = LpVariable.dicts('X', ([m for m in range(member.size)], [d for d in range(day.size)], [s for s in range(n_shiftnumber)]), 0, 1, 'Integer') 61 62#目的関数 63prob += kibou.values.sum() - lpSum(shiftdaycount[s] * X[m][d][s] for s in range(n_shiftnumber) for d in range(day.size) for m in range(member.size)) 64 65for m in range(member.size): 66 #希望出勤日数 67 ks = kibou.iloc[m].sum() 68 #実際に働いた日数 69 actual = lpSum(shiftdaycount[s] * X[m][d][s] for s in range(n_shiftnumber) for d in range(day.size)) 70 #(制約1)実際に働いた日数が希望出勤日数を超えない制約 71 prob += actual <= ks 72 73 74#制約条件 75#(制約2)必要最低人数を満たす制約 76for d in range(day.size): 77 for t in range(time.size): 78 prob += lpSum(shift.values[s][t] * X[m][d][s] for s in range(n_shiftnumber) for m in range(member.size)) == need.iloc[d, t] 79 80#(制約4)各メンバーが1日に取りうるシフトは必ず1つ 81for m in range(member.size): 82 for d in range(day.size): 83 prob += lpSum(X[m][d][s] for s in range(n_shiftnumber)) == 1 84 85#(制約5)人件費を超えない 86for s in range(n_shiftnumber): 87 for d in range(day.size): 88 prob += lpSum(jikyu.iloc[m, d] * wtime.iloc[s, d] * X[m][d][s] for m in range(member.size)) <= jinkenhi.iloc[d] 89 90#(制約6)6日以上の連続勤務を禁止する 91for m in range(member.size): 92 for s in range(n_shiftnumber): 93 prob += lpSum(X[m][d][s] for d in range(day.size)) <= 6 94 95prob.solve() 96 97 98scheduling = pd.DataFrame(index=member, columns=day) 99for m in range(member.size): 100 for d in range(day.size): 101 for s in range(n_shiftnumber): 102 if X[m][d][s].value() == 1: 103 scheduling.iloc[m, d] = shiftnumber[s] 104 105scheduling.to_csv("scheduling.csv") 106 107
各csvファイルのデータ内容
shift 12時 13時 14時 15時 16時 17時 18時 19時 20時 shift1 0 0 0 0 0 0 0 0 0 shift2 1 1 1 1 1 1 1 1 1 shift3 1 1 1 0 0 0 0 0 0 shift4 0 0 0 1 1 1 0 0 0 shift5 0 0 0 0 0 0 1 1 1 shift6 1 1 1 0 0 0 1 1 1
wtime day1 day2 shift1 0 0 shift2 9 9 shift3 3 3 shift4 3 3 shift5 3 3 shift6 6 6
上記のプログラムでは日ごとに各memberが出勤可能かどうか(上記の希望出勤日)をもとに実行してきました。
この日ごとで出勤可能かどうかを判別していたものを、時間ごとに判別しシフトを組むものを作成したいと考えています。(〇日は出勤できます→〇日の×時は出勤できますというように変更したい)
そこで上記の希望出勤日の部分を以下の希望出勤時間のように変更してみました。
試したこと
python
1#希望出勤時間 2kibou = pd.DataFrame([[0, 0, 0, 1, 1, 1, 0, 0, 0], 3 [0, 0, 0, 1, 1, 1, 0, 0, 0], 4 [1, 1, 1, 0, 0, 0, 1, 1, 1], 5 [1, 1, 1, 0, 0, 0, 1, 1, 1], 6 [0, 0, 0, 0, 0, 0, 0, 0, 0]], index=member, columns=time) 7#0 : 出勤不可の時間 8#1 : 出勤可能な時間 9 10kiboutimecount = [int(sum(y) > 0) for y in kibou.values]
変更後、エラーは発生せず、以下のような結果が得られました。
実行結果
python
1 day1 day2 2member1 shift3 shift4 3member2 shift5 shift4 4member3 shift4 shift6 5member4 shift2 shift6 6member5 shift1 shift1
実行結果のday2は問題ないのですが、day1のように出勤不可の時間帯に出勤するようなシフトを選択してしまいます。
これはプログラムに問題があるのか、希望出勤時間のデータの書き方に問題があるのか、判断ができないため質問させていただきました。
一方的な質問になってしまい申し訳ありません。よろしくお願いします。
バッドをするには、ログインかつ
こちらの条件を満たす必要があります。