1、项目背景:本人是轮滑社的干部,负责安排大一新社员训练的时间,由于人数多,且每个人的晚上空闲时间不同,按照传统笨拙的手写安排十分复杂且耗费大量时间和精力(我之前每届的负责人都是手写安排,要耗费好几天的时间),然后刚刚学完Python的我萌生了用python代码来自动化这一工作的想法。。。
2、目的: Python代码自动化安排社员晚训时间
3、需求分析:1)晚训时间为周一到周六,每个人一周训练两个晚上;
2)有舍友的尽量安排在一起
3)每晚训练人数应尽量平均
4、算法步骤:1)利用pandas读取已经收集好的大一晚课表(无课的即可安排训练),并提取出 每个人的晚上无课空闲时间
2)利用随机模块random在每个人的无课时间随机挑选两天作为晚训时间,并对 每晚训练的人的姓名分类到各晚训时间列表中(六个列表,周一至周六)
3)遍历人数较多的日期的训练名单(即上述列表),将其重新分配到人数较少的 日期晚训名单列表中从而达到每晚人数均衡
5、过程难点分析:1)如何将舍友的安排在一起
2)由于是随机,可能导致每晚人数过度不均衡,如何使他们均衡
6、晚课表格式:
7、代码:本人较懒,不一一介绍步骤了,封装的最后源码如下(jupyter notebook 上实现)
import numpy as np
import pandas as pd
import random
class HD():
# 此些均为类变量
free_date ={} # 存储每个人的有空日期
wanxun_date = {} # 存储每个人的晚训日期
wanxunbiao = 0
周一晚训 = []
周二晚训 = []
周三晚训 = []
周四晚训 = []
周五晚训 = []
周六晚训 = []
晚训日期 = [周一晚训, 周二晚训, 周三晚训, 周四晚训, 周五晚训, 周六晚训]
日期 = ['周一','周二','周三','周四','周五','周六']
def __init__(self,dizhi):
# 此些为实例变量
self.dizhi = dizhi
self.data = pd.read_excel(self.dizhi)
# 统一小写宿舍号
self.data["宿舍号"] = list(map(lambda x:x.lower(),self.data["宿舍号"]))
self.data = self.data.sort_values('宿舍号').reset_index(drop = True)
# 生成每个人空闲日期的字典
def Free_date(self):
# 按照宿舍人数进行升序,进行此步骤的目的是为了宿舍有多人的人员慢安排进晚训日期列表,
# 有助于之后人员转移时(人数多的日期内按顺序转移到少人数日期),优先转移没有舍友的人,有利于同宿舍的人分在一起
sort_sushe_index = self.data.groupby('宿舍号').transform(np.size).sort_values('姓名').index
data = self.data.reindex(index = sort_sushe_index)
# 遍历每一行作为一个series数据类型
for i in range(data.shape[0]):
# row:series类型;存储每一行即每个人的信息
row = data.iloc[i,:]
date = []
# 遍历每个人的
for g in row[2:].index:
if row[g] == '无':
date.append(g)
self.free_date[row['姓名']] = date
# 生成每个人的晚训日期字典
def Wanxun_date(self):
# 重新初始化此些类变量使每次调用重新生成‘新的晚训日期(wanxun_date)’,
# 用于renshupingheng中通过多次新的wanxun_date才可能使人数平衡
self.周一晚训 = []
self.周二晚训 = []
self.周三晚训 = []
self.周四晚训 = []
self.周五晚训 = []
self.周六晚训 = []
self.晚训日期 = [self.周一晚训, self.周二晚训, self.周三晚训, self.周四晚训, self.周五晚训, self.周六晚训]
self.wanxun_date = {}
# 要先调用生成free_date,才能生成wanxun_date
self.Free_date()
# 遍历每个人的姓名跟空闲日期
for people,free_date in self.free_date.items():
# 条件语句:如果该名字未在晚训时间字典里,即还未被安排晚训时间
if people not in self.wanxun_date.keys():
# 在空闲日期里随机选定两天作为晚训时间
date = random.sample(free_date,2)
self.wanxun_date[people] = date
# 取出该people整个宿舍的dataframe
sushe = self.data[self.data['宿舍号'] == self.data[self.data['姓名'] == people].宿舍号.values[0]]
#遍历每个舍友的名字
for name in sushe.姓名:
# 判断该晚训日期是否是其他舍友的空闲日期的子集
if set(date).issubset(self.free_date[name]):
# 给舍友安排相同的晚训日期
self.wanxun_date[name] = date
# 将每个人安排好后的晚训日期按日期将姓名添加到晚训日期的列表中
for key, val in self.wanxun_date.items():
for g in val:
if g == '周一':
self.周一晚训.append(key)
elif g == '周二':
self.周二晚训.append(key)
elif g == '周三':
self.周三晚训.append(key)
elif g == '周四':
self.周四晚训.append(key)
elif g == '周五':
self.周五晚训.append(key)
elif g == '周六':
self.周六晚训.append(key)
# 使晚训时间安排到每天人数尽量平均
def Renshupingheng(self):
# 得先生成晚训日期wanxun_date才能进行这一步
self.Wanxun_date()
# 函数:返回包含每天晚训人数的列表
def date_sum_people(x):
len_ = []
for i in x:
len_.append(len(i))
return len_
# 初始化每一天的晚训人数列表len_
len_ = date_sum_people(self.晚训日期)
# 总人数
sum_people = len(self.data)*2
# 初始化日期中最多的总人数
max_len = max(len_)
# 当人数最多的一天的总人数小于平均人数+1时,停止转移
while max_len > (sum_people//6)+1:
# 最少人数日期的索引
min_date_suoyin = len_.index(min(len_))
# 最多人数日期的索引
max_date_suoyin = len_.index(max(len_))
# 遍历最多人数的日期,将人转移到最少人数的日期上
for people in self.晚训日期[max_date_suoyin]:
# 条件语句:判断最少人数的日期是否在该people的空闲日期内,是的话转移
if self.日期[min_date_suoyin] in self.free_date[people] and people not in self.晚训日期[min_date_suoyin]:
# 向人数最少的日期转移people
self.晚训日期[min_date_suoyin].append(people)
# 转移后在原本晚训日期内删除该people
self.晚训日期[max_date_suoyin].remove(people)
# 每转移一个人,跳过此循环
break
# 此步骤很重要,防止因为最多人数的日期中没有人可以安排在最少人数日期中而死循环,换下一个人数最多的日期进行转移人员
else:
# 删除len_中人数最多的数
len_.remove(max_len)
# 使第二人数多的日期作为下一个转移的日期
max_len = max(len_)
continue
# 每转移一个人,重新计算每天的晚训人数
len_ = date_sum_people(self.晚训日期)
max_len = max(len_)
# 多次循环使人数尽量达到更平衡状态
# 如果运行不出来,可能是因为人数太少导致有些日期空闲的人数太少(比如在安排干部晚训表的时候因人数少会出现此现象)
# 从而一直死循环,可以加大下面行代码的(sum_people//6)+{key}的key值,然后观察晚训表作出办法
if max(date_sum_people(self.晚训日期))> (sum_people//6)+2:
self.Renshupingheng()
def Wanxunbiao(self):
self.Renshupingheng()
# 转换成dataframe并写入excel表格到桌面,注意路径改成自己桌面
self.wanxunbiao= pd.DataFrame(self.晚训日期).T
self.wanxunbiao.columns = self.日期
# 保存成Excel
def baocun(self,dizhi):
self.wanxunbiao.to_excel(dizhi)
# 搜寻一个人的空闲时间
def souxun_freedate(self,x):
try:
print('{}的空闲时间为:'.format(x),self.free_date[x])
except:
print("输入错误(输入的非姓名或姓名不存在)")
# 搜寻一个人的晚训时间
def souxun_wanxundate(self,x):
try:
print('{}的晚训时间为:'.format(x),self.wanxun_date[x])
except:
print("输入错误(输入的非姓名或姓名不存在)")
# 最后修改日期:2021/7/27
8、调用:
# 改成自己的桌面地址
dizhi = r"C:\Users\86198\Desktop\大一晚课表.xlsx"
# 调用HD类生成一个hd对象,注意对象名尽量不要跟类名一样
hd = HD(dizhi)
# 调用实例方法生成晚训表
hd.Wanxunbiao()
hd.wanxunbiao
9、结果:
8、保存晚训表:
9、晚训表结果:
10、感兴趣可私聊我要晚课表以及源码文件