Pandas DataFrame转多独立Series及中学排课代码优化求助
Python自动排课实现指导
核心需求
- 遍历学生列表及对应年级
- 为每位学生随机分配6个时段的课程班级,每个时段1门课
- 避免同一学生被安排同一课程的多个班级
- 6门课中必须包含2门选修课(Elective)
- 每个班级学生人数不超过30人
期望输出格式
格式一:按班级展示
| Class 1 Section 1 | Class 1 Section 2 |
|---|---|
| Student 1 | Student 3 |
| Student 2 | Student 4 |
格式二:按学生+时段展示
| Students | 1st Period | 2nd Period |
|---|---|---|
| Student 1 | Course 1 Section 1 | Course 3 Section 2 |
| Student 2 | Course 2 Section 1 | Course 4 Section 1 |
| Student 3 | Course 3 Section 1 | Course 1 Section 2 |
当前代码问题
现有测试代码仅针对ELA 1课程,存在以下问题:
- 生成的DataFrame中存储的是学生列表而非单个学生字符串,不利于后续处理
- 未处理时段冲突,可能出现同一学生在同一时段被安排多门课程的情况
- 仅单课程处理,无法满足多课程(含选修课)的分配需求
个人思路:应按时段组织数据结构(如九年级第1时段的所有课程),而非按课程类型组织。
现有测试代码
import pandas as pd s_test = 'schedule_test.csv' # Roster of students and grade levels df_s_test = pd.read_csv(s_test) class_size_limit = 30 stop = 0 df_shuffle = df_s_test.sample(frac=1) ela1 = {'ELA Section 1':[],'ELA Section 2':[],'ELA Section 3':[],'ELA Section 4':[],'ELA Section 5':[],'ELA Section 6':[]} sections = ['Section 1','Section 2','Section 3','Section 4','Section 5','Section 6'] for s in df_shuffle.index: for sec in sections: if df_shuffle.loc[s,'Grade Level']=='9th': if stop==0: if df_shuffle.loc[s,'Student'] not in ela1[sec]: if len(ela1[sec]) < class_size_limit: ela1[sec]+=[df_shuffle.loc[s,'Student']] stop+=1 stop = 0 ela1 =pd.Series(ela1) df_ela1 = pd.DataFrame({}) df_ela1 = pd.concat([df_ela1,ela1.to_frame().T],ignore_index=True) df_ela1.to_csv('test.csv',index=False)
分步解决方案
1. 数据结构重构
创建分层数据结构,按「年级→时段→课程类型→班级」组织,同时跟踪每个班级的已选学生数:
# 定义各年级的必修/选修课程 grade_courses = { '9th': { 'required': ['ELA 1', 'Algebra 1', 'Biology', 'World History'], 'electives': ['Art 1', 'Music 1', 'PE 1', 'Computer Basics'] }, # 可扩展其他年级课程 '10th': { 'required': ['ELA 2', 'Geometry', 'Chemistry', 'US History'], 'electives': ['Art 2', 'Music 2', 'PE 2', 'Coding 1'] } } # 初始化班级容量跟踪 class_tracking = {} for grade, course_groups in grade_courses.items(): class_tracking[grade] = {} for period in range(1,7): # 6个时段 class_tracking[grade][period] = {} # 添加必修课班级 for course in course_groups['required']: class_tracking[grade][period][course] = { 'sections': [f"{course} Section {i}" for i in range(1,7)], 'student_counts': [0]*6 # 对应每个班级的人数 } # 添加选修课班级 for course in course_groups['electives']: class_tracking[grade][period][course] = { 'sections': [f"{course} Section {i}" for i in range(1,7)], 'student_counts': [0]*6 }
2. 学生排课逻辑实现
按年级分组学生,为每个学生分配课程,确保满足所有规则:
import random # 读取学生数据 df_students = pd.read_csv('schedule_test.csv') class_size_limit = 30 # 初始化学生排课结果 student_schedules = [] # 按年级分组处理 for grade, group in df_students.groupby('Grade Level'): courses = grade_courses[grade] required_courses = courses['required'].copy() electives = courses['electives'].copy() for _, student in group.iterrows(): student_name = student['Student'] schedule = {'Student': student_name} assigned_courses = set() elective_count = 0 # 遍历6个时段 for period in range(1,7): # 决定当前时段是必修还是选修(需满足2门选修) if elective_count < 2 and random.random() < 0.35: # 随机选择选修,控制数量 course_pool = electives elective_count +=1 else: course_pool = [c for c in required_courses if c not in assigned_courses] # 随机选择课程 selected_course = random.choice(course_pool) assigned_courses.add(selected_course) # 找到该时段该课程的未满班级 course_info = class_tracking[grade][period][selected_course] available_sections = [ idx for idx, count in enumerate(course_info['student_counts']) if count < class_size_limit ] if not available_sections: # 若所有班级满员,可抛出警告或扩展班级 print(f"Warning: All sections for {selected_course} (Period {period}) are full!") continue # 随机选一个未满班级 section_idx = random.choice(available_sections) section_name = course_info['sections'][section_idx] # 更新班级人数 class_tracking[grade][period][selected_course]['student_counts'][section_idx] +=1 # 记录该时段课程 schedule[f"{period}th Period"] = section_name student_schedules.append(schedule) # 转换为DataFrame(格式二) df_student_schedules = pd.DataFrame(student_schedules) print(df_student_schedules.head())
3. 生成按班级展示的输出(格式一)
从排课结果中提取每个班级的学生列表:
# 初始化班级学生字典 class_students = {} # 遍历所有学生的排课结果 for schedule in student_schedules: student_name = schedule['Student'] for period_col, section in schedule.items(): if 'Period' not in period_col: continue # 按班级分组 if section not in class_students: class_students[section] = [] class_students[section].append(student_name) # 转换为DataFrame(格式一) max_students = max(len(students) for students in class_students.values()) # 补全每个班级的列表长度,避免DataFrame对齐问题 for section, students in class_students.items(): class_students[section] += ['']*(max_students - len(students)) df_class_schedules = pd.DataFrame(class_students) print(df_class_schedules.head())
4. 保存结果
# 保存格式二的结果 df_student_schedules.to_csv('student_schedules.csv', index=False) # 保存格式一的结果 df_class_schedules.to_csv('class_schedules.csv', index=False)
内容的提问来源于stack exchange,提问作者Gandr23
相关产品推荐
相关产品推荐

