LoginSignup
0
0

More than 1 year has passed since last update.

シフト作成メモ

Last updated at Posted at 2022-12-31

作成途中
メモ用
プログラムがちがち初心者

qiita.rb

import pandas as pd
import numpy as np
from random import random
import time, os
import openpyxl as opx
pd.options.display.precision = 0


def read_excel():
 
 df = pd.read_excel(r"C:\Users\keisu\OneDrive\デスクトップ\月次シフト.xlsx", index_col = 0, header=[1,])
 df = df.fillna("0")
 df = df.replace("0", 0).replace("", 3)

 kiso = df.iloc[2:, 0:31].reset_index(drop = True)
 holiday = df.iloc[2:,31:32].reset_index(drop = True)
 holiday.columns = ["休日数"]
 kouritu = df.iloc[2:,32:33].reset_index(drop = True)
 kouritu.columns = ["効率"]
 people = df.iloc[0:2, 0:31].reset_index(drop = True)
 people.index = ["日勤", "夜勤"]
 
 return kiso, holiday, kouritu, people


def first_gene(kiso, holiday):
    days = len(kiso.columns)
    kiso_copy = kiso.copy()
    
    for k in range(len(kiso_copy)):
     h = []
     while len(h) < holiday.loc[k][0]:
         n = np.random.randint(1, days + 1)
         if not n in h:
             h.append(n)
     #h以外のリスト=出勤数 filter(n = n !== h)  
     not_h =  [i for i in range(1, days + 1) if i not in h]
     for i in not_h:
         #日勤が1、夜勤が2
         x = 1 if 0.8 > random() else 2
         if kiso_copy.loc[k,i] == 0:
            kiso_copy.loc[k,i] = x

    return kiso_copy
  
  
def holiday_fix(kiso, holiday):
    
    #人ごとに見る、kiso_copyで出勤をすべて1にして、判定用に使う、希望休を消さないため
    kiso_copy = kiso.copy()
    kiso_copy.replace(2,1)
    for k in range(len(kiso)):
     days = len(kiso.columns)
     if np.count_nonzero(kiso_copy.iloc[k:k + 1] == 0) + np.count_nonzero(kiso_copy.iloc[k:k + 1] == 3) != holiday.iloc[k][0]:
         s = np.count_nonzero(kiso_copy.iloc[k:k + 1] == 0) + np.count_nonzero(kiso_copy.iloc[k:k + 1] == 3) - holiday.iloc[k][0]
         buf = 0
         c1 = 0 if s > 0 else 1
         c2 = 1 if c1 == 0 else 0
         #増減したい休日数に達するまでループ 夜勤数は固定するため、休み⇔日勤のみ
         while buf < abs(s):
            n = np.random.randint(1, days)
            if kiso.loc[k][n] == c1:
                    buf += 1
                    #休日数を操作
                    kiso.loc[k,n] = c2
    return kiso


def yakin_fix(kiso, holiday):
    
    #日ごとにみる、kiso_copyで出勤をすべて1にして、判定用に使う、希望休を消さないため
    kiso_copy = kiso.copy()
    for k in range(len(kiso.columns)):
     if np.count_nonzero(kiso_copy.iloc[:,k:k + 1] == 2) != people.iloc[1,k]:
         s = np.count_nonzero(kiso_copy.iloc[:,k:k + 1] == 2) - people.iloc[1,k]
         buf = 0
         c1 = 2 if s > 0 else 1
         c2 = 1 if c1 == 2 else 2
         #増減したい夜勤数に達するまでループ 休日数は固定するため、夜勤⇔日勤のみ
         while buf < abs(s):
            n = np.random.randint(0, len(kiso))
            if kiso.iloc[n,k] == c1:
                    buf += 1
                    #夜勤数を操作
                    kiso.iloc[n,k] = c2
    return kiso


def evaluation_function(kiso):

 # 評価
 eva1 = kiso.copy()
 eva1.replace(2, 1)
 eva2 = kiso.copy()
 score = 0

 # メンバーごとに評価
 for k in range(len(eva1)):
     # 結合
     x = ''.join([str(i) for i in np.array(eva1.iloc[k:k + 1]).flatten()])
     # 5連勤以上
     score += np.sum([((2 - len(i))**2)*-1 for i in x.split("0") if len(i) > 5])
     # 3連休以上
     score += np.sum([((1 - len(i))**2)*-1 for i in x.split("1") if len(i) > 3])
     # 飛び石連休
     score += -10*(len(x.split("010"))-1)
 
 # 日勤必要数
 score += np.sum([abs(people.iloc[0,k] - np.sum(eva2.iloc[:,k] == 1)) * -4 for k in range(len(eva2.columns))])
 
 # 効率
 K = []
 Kouritu = []
 for i in range(len(eva1.columns)):
    for j in range(len(eva1)):
        if eva1.iloc[j,i] == 1:
          K.append(j)
    if len(K) != 0:
     Kouritu.append(round(np.mean([kouritu.iloc[k,0] for k in K])))

    
 score += np.var(Kouritu)* -4

    
 return score


def crossover(ep, sd, p1,p2):
 
    days = len(p1.columns)
    p1 = np.array(p1).flatten()
    p2 = np.array(p2).flatten()
    
    ch1 = []
    ch2 = []
    
    for p1_, p2_ in zip(p1,p2):
      x = True if ep > random() else False
      if x == True:
        ch1.append(p1_)
        ch2.append(p2_)
      else:
        ch1.append(p2_)
        ch2.append(p1_)
    
    ch1, ch2 = mutation(sd, np.array(ch1).flatten(), np.array(ch2).flatten())
    
    ch1 = pd.DataFrame(ch1.reshape(int(len(ch1)/days), days))
    ch2 = pd.DataFrame(ch2.reshape(int(len(ch2)/days), days))
    ch1.columns = [i+1 for i in range(len(ch1.columns))]
    ch2.columns = [i+1 for i in range(len(ch2.columns))]
    
    return ch1, ch2


def mutation(sd, ch1, ch2):
  
  #夜勤は変えない
  x = True if sd > random() else False
  if x == True:
        rand = np.random.permutation([i for i in range(len(ch1))])
        rand = rand[:int(len(ch1)//10)]
        for i in rand:
         if ch1[i] == 1:
            ch1[i] = 0
         
         elif ch1[i] == 0:
            ch1[i] = 1
            
  x = True if sd > random() else False
  if x == True:
        rand = np.random.permutation([i for i in range(len(ch1))])
        rand = rand[:int(len(ch2)//10)]
        for i in rand:
         if ch2[i] == 1:
            ch2[i] = 0
         
         elif ch2[i] == 0:
            ch2[i] = 1
  return ch1, ch2

first_length = 100
elite_length = 20
gene_length = 10
ep = 0.5
sd = 0.1

kiso, holiday, kouritu, people = read_excel()

parent = []

for i in range(first_length):
  kiso = first_gene(kiso, holiday)
  kiso = holiday_fix(kiso, holiday)
  kiso = yakin_fix(kiso, holiday)
  score = evaluation_function(kiso)
  parent.append([score,kiso])

for i in range(gene_length):
  korenp = np.array(parent, dtype=object)
  parent = sorted(korenp,key=lambda x: -x[0])
  parent = parent[:elite_length]

  if i == 0 or top[0] < parent[0][0]:
    top = parent[0]
  else:
    parent.append(top)

  print(f"{i+1}世代")
  print(top[0])

  children = []
  for k1,v1 in enumerate(parent):
   for k2,v2 in enumerate(parent):
     if k1 < k2:
       ch1,ch2 = crossover(ep,sd, v1[1], v2[1])
       ch1 = yakin_fix(ch1, holiday)
       ch2 = yakin_fix(ch2, holiday)
       ch1 = holiday_fix(ch1, holiday)
       ch2 = holiday_fix(ch2, holiday)   
       score1 = evaluation_function(ch1)
       score2 = evaluation_function(ch2)
       children.append([score1,ch1])
       children.append([score2,ch2])
  
  parent = children.copy()
    
x = top[1].replace(1, "").replace(2, "").replace(0, "").replace(3, "")
x

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0