使用 Python 持久地存储模拟结果

40 阅读3分钟

正在对数据集运行多个模拟。

  • 每次模拟都会将项目分配给学生。
  • 模拟之间的区别在于随机化学生顺序,以便所有学生都有机会被分配到想要的项目。
  • 以前将部分分配结果写入电子表格(即 Excel),类似于以下内容(只展示了部分,实际表格扩展到了几千次模拟,大约 100 名学生):
Session 1Session 2Session 3
Stu1Proj_AAProj_ABProj_AB
-------------------------------------------
Stu2Proj_ABProj_AAProj_AC
-------------------------------------------
Stu3Proj_ACProj_ACProj_AA
-------------------------------------------
  • 处理分配的代码当前将模拟结果存储在一个对象中。
  • 下次运行分配时,对象会被覆盖。
  • 想要存储所有分配结果,这很重要,因为稍后需要从数据中获取信息,例如:Stu1 分配到最多的项目或者 Proj_AC 有多受欢迎(被分配的次数 / 模拟次数)。

问题:

  • 有哪些方法可以将这种模拟信息持久地存储起来?
  • 基本上,每个模拟输出在结束并开始下一个分配周期之前需要将自身添加到存储库中。
  • 一位朋友建议使用 SQLAlchemy 将这些结果映射到关系型数据库。喜欢这个想法,因为它确实给了深入研究数据库的机会。
  • 推荐的数据库结构如下:

|----------|-----------|-----------|

SessionStudentProject
1Stu1Proj_AA
--------------------------------
1Stu2Proj_AB
--------------------------------
1Stu3Proj_AC
--------------------------------
2Stu1Proj_AB
--------------------------------
2Stu2Proj_AA
--------------------------------
2Stu3Proj_AC
--------------------------------
3Stu1Proj_AB
--------------------------------
3Stu2Proj_AC
--------------------------------
3Stu3Proj_AA
--------------------------------
  • 在这里,建议将 Session 和 Student 列设为组合键。
  • 这样可以访问特定学生在特定模拟中的特定记录。
  • 或者可以仅仅获取特定模拟的整个分配运行。

问题:

  • 这个主意好吗?
  • 如何使用 SQLAlchemy 实现和查询组合键?
  • 如果某个学生没有被分配项目(如果他想要的所有项目都被占用了,就会发生这种情况),数据库会发生什么情况?
  • 在代码中,如果学生没有为特定运行分配项目,则他不会获得项目 ID,而只是获取该字段/对象的值 None。

抱歉询问多个问题,但由于这些问题密切相关,所以认为在同一个空间里询问它们。

解决方案

方法 1:

  • 使用关系数据库表(具有两个键)显示数据。
  • 在示例中,Student 键和 Session 键。
  • 可以忽略“组合键”这个概念,它没有帮助,也没有必要。组合键不能很好地解决任何问题,还会制造许多困难。
  • 引入一个带有简单“标识符”的附加列。它是一个“自动生成的代理键”。自动生成的唯一键对每一行来说都是件好事。组合键是件坏事。

方法 2:

  • 可以将这种逻辑结构视为三元关系,其中建议使用表格对应于出勤关系对象。
  • 因此,理想情况下,还应当创建类似于此的对象模型:

(source: databasedesignstudio.com)

  • 现在,可能有人会说,如果每个实体表只有一个字段,为什么还需要一张以上的表格?
  • 但仍然会采用这种方式进行建模,因为这种模型更好地表示了现实世界,并且仍然需要在某个地方存储学生更愿意做的项目,这将是另一个与学生表具有多对多关系的表格。
  • 使用实体对于理解 sqlalchemy 来说更好、更容易;而如果只保留一张表格,又真的能深入研究数据库吗?

有关组合键:

  • S.Lott 给出了避免使用组合键的充分理由,完全同意他的观点。

注释:

  • 以下代码仅供参考:
  session = db.session

  # 创建一个新的分配运行
  allocation_run = AllocationRun()
  session.add(allocation_run)

  # 为每个学生分配一个项目
  for student in students:
    project = allocate_project(student)
    session.add(Allocation(allocation_run, student, project))

  # 提交更改
  session.commit()

  # 读取分配结果
  results = session.query(Allocation).all()

  # 打印结果
  for result in results:
    print(f"{result.student.name} was allocated {result.project.name}")