去年大数据分析课程的选题项目,最近考研不顺整理之前的杂碎半成品等丰富一下简历,准备找工作二战了,真是一个悲伤的现实,唉。
原题目链接:DC竞赛:学生成绩排名预测 【已挂】
相关数据:https://github.com/typeisgod/CSDN
题目背景和意义:
我们希望通过借助大数据相关的挖掘技术和基础算法,从学生的校园行为数据中,根据学生出入图书馆的次数,以及借书和消费情况等,挖掘用户作息规律、兴趣爱好等,精准地预测学生之间的相对排名。通过对这些日常行为的建模来预测学生的学业成绩,发现学生成绩和日常校园行为之间的潜在关系,可以实现提前预警学生的异常情况,并进行适当的干预,因而对学生的培养、管理工作将会起到极其重要的作用。
题目描述:
本次竞赛中,我们将从某高校的某个学院随机抽取一定比例学生,提供这些学生在三个学期的图书馆进出记录、一卡通消费记录、图书馆借阅记录、以及综合成绩的相对排名。这一部分数据将作为训练数据。我们从另外的某学院随机抽取一定比例的学生,然后提供他们在三个学期的图书馆进出记录、一卡通消费记录、图书借阅记录、以及前两个学期的成绩排名。第三学期的成绩排名作为预测目标。
提供文件:
训练\成绩.txt。训练集的成绩文件,包含学期、学号、以及相对排名
训练\借书.txt。训练集的图书借阅信息,包含学期、学号、书号、日期
训练\图书门禁.txt。 训练集的图书门禁进入,包含学期、学号、日期、时间
训练\消费.txt。训练集的消费数据,包含学期、学号、地点、日期、时间、金额
测试\成绩.txt。 测试集的成绩文件。字段同上 测试\借书.txt。 测试集的图书借阅信息。字段同上
测试\图书门禁.txt。 测试集的图书门禁进入。字段同上
测试\消费.txt。 测试集的消费数据。字段同上
评估标准:
算法通过衡量预测排名和实际排名的Spearman相关性,为[0,1]之间的值,值越大,表示越相关,排名的预测就越准确。若要考虑n个学生的排名,学生i的预测排名为pi,而实际的排名为ri,di = pi - ri,那么
本数据统计模型(最后评分并不是很高,仅供参考):
后来觉得次数不一定决定成绩,应该还有稳定性。后来对门禁和借书计算了每个学期的月均值、月方差、日均值、日方差(第一、三学期五个月 第二学期六个月 每个月三十一天计算),扩展到了317维。之后归一化到了[0,1]之间。
我做的工作:提取成绩、图书馆门禁、借书;分析预测
汪东启的工作:提取图书类别(供借书使用)、消费;多维合并
一.书籍信息读取、检查与存储(汪东启)
0. 图书类别.txt 文件预览与格式:
1.文件的预读取与检查(test_check_book.py)
# -*- coding: utf-8 -*-
"""
Created on Mon May 7 14:53:24 2018
@author: wangdongqi
读取书籍信息,检查数据中可能出现的问题
"""
Book = dict()
ClassNum = []
err = []
with open('图书类别.txt', encoding = 'utf-8') as f:
f.readline();
for line in f:
(BookNumber, BookClass) = line.split('\t');
if BookNumber not in Book.keys():
Book[BookNumber] = [BookClass]
else:
Book[BookNumber].append(BookClass)
print(BookNumber)
err.append(BookNumber)
if BookClass not in ClassNum:
ClassNum.append(BookClass);
# 测试重复的那些数据是否有变化
err_list = []
for i in err:
tmp_list = Book[i]
tmp = tmp_list[0]
for j in tmp_list[1:]:
if j != tmp:
err_list.append(i)
break
# 结论:所有重复的数据的信息相同,对结果无影响
2.数据提取(test_read_book)
首先导入文件,将文件按行切分(split,代码使用replace直接替换)为BookNumber和bookclass,检查BookNumber是否为数字(防止图书编号中出现错切分的其他符号),如果是则放入字典BookInfo中;同时BookClass为list类型,用于存放一共出现的bookclass的种类数(文件中只有42种,但在其他文件中出现未在字典中的书籍,收集作为第43类)。最后通过pickle.load函数将BookInfo和BookClass数组存放下pkl中。
# -*- coding: utf-8 -*-
"""
Created on Mon May 7 15:49:55 2018
@author: wangdongqi
读取书籍信息
"""
BookInfo = dict()
errInfo = dict()
BookClass = []
with open('图书类别.txt', encoding = 'utf-8') as f:
f.readline();
for line in f:
line = line.replace('\n', '')
(BookNumber, bookclass) = line.split('\t');
if not BookNumber.isdigit():
errInfo[BookNumber] = bookclass
continue
BookInfo[BookNumber] = bookclass
if bookclass not in BookClass:
BookClass.append(bookclass)
# 排序
BookClass.sort()
# 打印42类书的类名
for i in BookClass:
print(i, end = ' ')
# 保存信息
import pickle
# 保存
pickle.dump(BookInfo, open('BookInfo.pkl', 'wb'))
pickle.dump(BookClass, open('BookClass.pkl', 'wb'))
# 读取
BookInfo = pickle.load(open('BookInfo.pkl', 'rb'))
BookClass = pickle.load(open('BookClass.pkl', 'rb'))
二.成绩、图书馆门禁、借书提取(Type) (pre.py)
0.文件预览与格式
1.
依次读入成绩、图书馆门禁、借书三个txt文件,分别存入file_rank、file_ibrary、file_borrow变量中。
(1)file_rank:
每次读取一行(readline()),对每行未使用split等切割,而是手动实现的“字符切割”:(代码25-39行)
pre为某个字符段(数字段)的开始,初始化0,end为某个字符段(数字段)的结束,当没有扫描到行末尾时循环查找,当下标为pre的字符不为空(/t)时,end一直循环到下一个空(/t)或末尾的上一个字符(数字段结尾),然后取[pre:end](end不取),依次取数组段直到pre到行末,得到的多个数字段放入temp中,并合并到总data里(二维数组),读取下一行。全部读完后以学号为主key,学期为副key对data排序:data=sorted(data,key=lambda x:(x[1],x[0]))
此时data格式为:
学期 | 学号 | 排名 |
(2)file_library:
新增 月平均 月方差 月最大 月最小
日平均 日方差 日最大 日最小
06-22 小时点次数
(PS:月平均:平均每个月去图书馆的门禁次数,月最大:最多一个月去的门禁次数,其他同理
小时点次数:这个学期每个小时门禁次数汇总情况,并非每天每小时的次数)
新增共 25 维(最开始的模型只有下标3 新增4-28)
下标(从0开始) | 0 | 1 | 2 | 3 | 4-7 | 8-11 | 12-28 |
维/属性 | 学期 | 学号 | 排名 | 本学期图书馆门禁次数 | 月平均 月方差 月最大 月最小 | 日平均 日方差 日最大 日最小 | 06-22 小时点次数 |
从file_library提取的数据共26维(data下标对应3-28),建立一个和data等长(len(data)的)三维数组data3,用于存放每个学期每个学生每个月每天的图书馆门禁次数。
data3=np.zeros(shape=(len(data),6,31)).tolist();
用split('\t')分割每行为四个数字段,分别对应学期、学号、日期、时间。(代码中ent为回车符),通过学期和学号即可计算出对应data中的下标:index=(sid-1)*3+seme-1; (sid=学号,seme=学期)。
从时间提取出前二位数为小时hour([0:2]),同理从日期中提取出月份month和天day。并根据学期将每个学期的月份最低数规划到0,分别将每个月每天的次数存入data3中,将每小时(06时-22时)的门禁次数写入data中。(此上代码75-90行)
统计完毕后计算月均值mmean、月最大mmax、月最大mmax、月最小mmin、月方差mvar、日均值mmean、日最大mmax、日最大mmax、日最小mmin、日方差mvar并放入data的[4:12]中。
(3) file_borrow:(使用 一 中已经写好的pkl作为字典索引类别)
下标(从0开始) | 0 | 1 | 2 | 3-28 | 29-71 | 72-75 | 76-79 |
维/属性 | 学期 | 学号 | 排名 | file_library提取的属性列 | 每类书的借阅总次数(本数) | 月平均 月方差 月最大 月最小 | 日平均 日方差 日最大 日最小 |
读入pkl和file_library类似地统计每个人每个学期每类书(43类,用ABCD等表示)的总次数,和所有类型书的借阅月总次数、最大、最小、方差、日同理。
代码pre.py:
# -*- coding: utf-8 -*-
"""
Created on Mon May 7 13:28:12 2018
@author: Type真是太帅了
"""
import numpy as np
import pickle
file_rank=open('成绩.txt','r')
file_library=open('图书馆门禁.txt','r')
file_borrow=open('借书.txt','r')
file_consume=open('消费.txt','r')
#file_type=open('图书类别.txt','r')
'''
学期 学号 图书馆门禁次数 食堂总消费 交通总消费 宿舍总消费 超市总消费 书类别 排名
'''
data=[]
'''
读入成绩
'''
line = file_rank.readline()
line = file_rank.readline()#直接读取第二行数据
while line:
temp=[]
pre=0
while pre<len(line)-2: #对于每行line的每个字符 将其转化为数字形式并存储于数组中 最后\n两个字符不读
if line[pre]!='\t':
end=pre+1
while end<len(line)-1:
if line[end]=='\t':
temp=temp+[int(line[pre:end])]
pre=end+1
break
else:
end=end+1
else:
pre=pre+1
end=pre+1
data=data+[temp]
line = file_rank.readline()
'''
以学号为主key 学期为负key进行排序
'''
data=sorted(data,key=lambda x:(x[1],x[0]))
'''
读入图书馆门禁次数(学期计)
'''
'''
新增 月平均 月方差 月最大 月最小
日平均 日方差 日最大 日最小
06-22 小时点次数
共 25 维
0 1 2 3 4567 891011 12-28 29-71
学期 学号 排名 总数 月 日 06-22 1-43
'''
line = file_library.readline()
line = file_library.readline()#直接读取第二行数据
zero26=np.zeros(26,int).tolist();#新建26维列表
i=0
while i<len(data):
data[i]=data[i]+zero26;
i=i+1
data3=np.zeros(shape=(len(data),6,31)).tolist();#统计每天的次数 每个月取31天
'''1/3学期 data3[index][0-4]表示 9-1月 9~0 10~1 11~2 12~3 1~4
2学期 data3[index][0-5]表示 2-7月
'''
n_1=5;#1 3 学期 5个月
n_2=6;# 2 学期 6个月
while line:
read