如何在Python中持久存储仿真结果?

2 投票
3 回答
864 浏览
提问于 2025-04-15 20:50

背景:我正在对一组数据进行多次模拟。在每个模拟中,我会把项目分配给学生。每次模拟的不同之处在于,我会随机改变学生的顺序,这样每个学生都有机会获得他们想要的项目。我在一个电子表格(比如Excel)里写下了一些分配的结果,基本上是这样的(这只是一个小快照,实际的表格有几千次模拟,大约有100个学生)。

|          | Session 1 | Session 2 | Session 3 |
|----------|-----------|-----------|-----------|
|Stu1      |Proj_AA    |Proj_AB    |Proj_AB    |
|----------|-----------|-----------|-----------|
|Stu2      |Proj_AB    |Proj_AA    |Proj_AC    |
|----------|-----------|-----------|-----------|
|Stu3      |Proj_AC    |Proj_AC    |Proj_AA    |
|----------|-----------|-----------|-----------|

现在,处理分配的代码目前是把每次模拟的结果存储在一个对象里。下一次进行分配时,这个对象会被覆盖。

所以我真正想做的是保存所有的分配结果。这很重要,因为我之后需要从这些数据中提取信息,比如:哪个项目是Stu1最常被分配的,或者Proj_AC有多受欢迎(被分配的次数/模拟的总次数)。

问题:
我可以用什么方法来持久保存这些模拟信息?基本上,每次模拟结束后,输出结果需要添加到存储库中,然后再开始下一轮分配。

一个朋友建议的解决方案是使用SQLAlchemy将这些结果映射到一个关系数据库。我觉得这个主意不错,因为这让我有机会深入了解数据库。

我被推荐的数据库结构是:

|----------|-----------|-----------|
|Session   |Student    |Project    |
|----------|-----------|-----------|
|1         |Stu1       |Proj_AA    |
|----------|-----------|-----------|
|1         |Stu2       |Proj_AB    |
|----------|-----------|-----------|
|1         |Stu3       |Proj_AC    |
|----------|-----------|-----------|
|2         |Stu1       |Proj_AB    |
|----------|-----------|-----------|
|2         |Stu2       |Proj_AA    |
|----------|-----------|-----------|
|2         |Stu3       |Proj_AC    |
|----------|-----------|-----------|
|3         |Stu1       |Proj_AB    |
|----------|-----------|-----------|
|3         |Stu2       |Proj_AC    |
|----------|-----------|-----------|
|3         |Stu3       |Proj_AA    |
|----------|-----------|-----------|

这里建议我将SessionStudent列作为复合键。这样我就可以访问特定学生在特定模拟中的记录,或者仅仅获取某个特定模拟的所有分配结果。

问题:
这个主意好吗?
如何使用SQLAlchemy实现和查询复合键?
如果某个学生没有被分配到项目(比如他想要的项目都被其他人拿走了),数据库会发生什么?在代码中,如果一个学生没有被分配项目,他在那个字段/对象中会得到None


抱歉问了多个问题,但因为这些问题是紧密相关的,我想在同一个地方问。


编辑 - 2010年3月25日

目前,学生的类结构是这样的:

class Student(DeptPerson):
    def __init__(self, name, stud_id):
        super(Student, self).__init__(name, stud_id)
        self.preferences = collections.defaultdict(set)
        self.allocated_project = None
        self.allocated_rank = 0

(杂项) 它继承自一个叫DeptPerson的类,这个类只有namestud_id

因此,当分配函数给学生分配项目(通过一个唯一的ID来引用 - Project是它自己的类)时,它会在allocated_project中反映出这个值。如果某个学生在某次模拟中没有被分配到项目(因为其他人已经拿走了,哈哈...抱歉),allocated_project会保持为None(这对我来说是很有用的信息)。

3 个回答

0

我对数据库的事情帮不了你,因为我也是个新手,只知道怎么从sqlite表里查询数据...

不过,关于数据保存,你可以试试用pickle模块来存储你的对象吗?具体用法可以查查文档,但我记得大概是用file(filename, 'wb').write(pickle.pickle(myobject))来写入数据,用myobject = pickle.unpickle(file(filename, 'rb'))来读取数据。

这样你就可以把多个表或者其他东西读入多个变量,然后进行你想要的比较。

如果你不需要通过Python再读回来,你也可以手动把数据格式化成以制表符分隔的格式,然后把这个文件加载到你喜欢的电子表格软件里(OpenOffice Calc非常好用)。

3

一个二维数据展示就是一个关系数据库表,里面有两个关键字段。

在你的例子中,就是学生关键字段和会话关键字段。

所谓的“复合键”其实可以忽略不计。它没有什么用处,也不是必需的。复合键解决问题的效果不好,反而会带来很多麻烦。可以假装你从来没有听说过它。

我们做的事情是增加一个额外的列,里面放一个简单的“标识符”。这个标识符是为每一行自动生成的替代键。每一行都有一个独特的自动生成的键是件好事,而复合键则是件坏事。

1

你现在的逻辑结构可以看作是一个三元关系,其中你提到的推荐表对应的是Attendance这个关系对象。因此,理想情况下,你应该创建一个类似于这个的对象模型:

三元关系
(来源: databasedesignstudio.com)
.

在你的情况下,有人可能会问,既然每个Entity表只有一个字段,为什么还需要多个表。但我还是建议你这样建模,因为这个模型更能反映现实世界的情况。而且你还需要存储学生们偏好的Project,这就需要另一个表,并且这个表和Student表之间是多对多的关系。

使用实体会让你更容易理解sqlalchemy;如果你只保持一个表,你真的能深入了解数据库吗?

关于复合键:S.Lott给出了很好的理由来避免使用它们,我完全同意他的看法。

撰写回答