如何在Python中持久存储仿真结果?
背景:我正在对一组数据进行多次模拟。在每个模拟中,我会把项目分配给学生。每次模拟的不同之处在于,我会随机改变学生的顺序,这样每个学生都有机会获得他们想要的项目。我在一个电子表格(比如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 |
|----------|-----------|-----------|
这里建议我将Session
和Student
列作为复合键。这样我就可以访问特定学生在特定模拟中的记录,或者仅仅获取某个特定模拟的所有分配结果。
问题:
这个主意好吗?
如何使用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
的类,这个类只有name
和stud_id
。
因此,当分配函数给学生分配项目(通过一个唯一的ID来引用 - Project
是它自己的类)时,它会在allocated_project
中反映出这个值。如果某个学生在某次模拟中没有被分配到项目(因为其他人已经拿走了,哈哈...抱歉),allocated_project
会保持为None
(这对我来说是很有用的信息)。
3 个回答
我对数据库的事情帮不了你,因为我也是个新手,只知道怎么从sqlite表里查询数据...
不过,关于数据保存,你可以试试用pickle模块来存储你的对象吗?具体用法可以查查文档,但我记得大概是用file(filename, 'wb').write(pickle.pickle(myobject))来写入数据,用myobject = pickle.unpickle(file(filename, 'rb'))来读取数据。
这样你就可以把多个表或者其他东西读入多个变量,然后进行你想要的比较。
如果你不需要通过Python再读回来,你也可以手动把数据格式化成以制表符分隔的格式,然后把这个文件加载到你喜欢的电子表格软件里(OpenOffice Calc非常好用)。
一个二维数据展示就是一个关系数据库表,里面有两个关键字段。
在你的例子中,就是学生关键字段和会话关键字段。
所谓的“复合键”其实可以忽略不计。它没有什么用处,也不是必需的。复合键解决问题的效果不好,反而会带来很多麻烦。可以假装你从来没有听说过它。
我们做的事情是增加一个额外的列,里面放一个简单的“标识符”。这个标识符是为每一行自动生成的替代键。每一行都有一个独特的自动生成的键是件好事,而复合键则是件坏事。
你现在的逻辑结构可以看作是一个三元关系,其中你提到的推荐表对应的是Attendance
这个关系对象。因此,理想情况下,你应该创建一个类似于这个的对象模型:
(来源: databasedesignstudio.com)
.
在你的情况下,有人可能会问,既然每个Entity
表只有一个字段,为什么还需要多个表。但我还是建议你这样建模,因为这个模型更能反映现实世界的情况。而且你还需要存储学生们偏好的Project
,这就需要另一个表,并且这个表和Student
表之间是多对多的关系。
使用实体会让你更容易理解sqlalchemy
;如果你只保持一个表,你真的能深入了解数据库吗?
关于复合键
:S.Lott给出了很好的理由来避免使用它们,我完全同意他的看法。