记录一次处理数据处理的过程,涉及LINUX操作系统及相关指令以及Hadoop。
任务需求
数据需求
需要以下四个方面的数据:
学生成绩
A、以学生为单位:计算每个学生总的平均分(按年限分)
图书馆数据(按借阅为准)
A、以学生为单位:计算每个学生借阅总数 (按年限分)
B、以学生为单位:计算每个学生借阅类型ABCD(按年限分)
C、只统计学生借阅次数,忽略学生还书数据
消费数据
A、以学生为单位:消费总额(按年限分)
B、删除学生充值记录
门禁数据
A、以学生为单位:9点后进校门次数,11点进校门(按年限分)
输出格式
工作目录
.
├── Bill.sql
├── BookRecord.sql
├── Grade.tsv
├── Gurad.sql
├── OracleSync.sql
├── output (用于输出清洗的格式数据)
│ ├── require (用于输出目标需求格式数据)
├── Result.tsv
├── Student
├── 书目数据
│ ├── 2005.1.1-2009.12.31.xls
│ ├── 2010.1.1-2012.12.31书目数据.xls
│ ├── 2013.1.1-2014.12.31书目数据.xls
│ ├── 2015.1.1-2017.3.15书目数据.xls
│ ├── bookIndex1.txt
│ ├── bookIndex2.txt
│ ├── bookIndex3.txt
│ └── bookIndex4.txt
├── 原始数据
│ ├── db2017-03-14.rar
│ └── 书目数据.rar
├── 备份数据
│ ├── Bill - 副本.sql
│ ├── Book - 副本.sql
│ ├── Grade - 副本.tsv
│ ├── Gurad - 副本.sql
│ ├── OracleSync - 副本.sql
│ └── Result - 副本.tsv
├── 数据格式.xlsx
└── 数据需求.txt
清洗数据格式
通过清洗数据得到以空格为分隔符,以行为单位,以列为类别的数据文件。
姓名学号数据
源文件: 未直接给出,从图书馆借书数据中提取
查看了给出的几份文件,并没有直接给出学生姓名学号文件,但在图书馆的借阅记录文件中有相关的数据。
因此,我们可以先从这里输出一份学生姓名与学号对应的数据表,要注意的是需要对结果进行去重,因为一个学生可能存在多次借阅记录。
清洗命令如下:取学生姓名学号对应数据
cat BookRecord.sql
| awk -F ', ' '{print $2"\t"$1}'
| sed "s|INSERT INTO V_TSG_JYLOG VALUES (||g"
| sed "s|'||g"
> output/Student_temp.txt
| sort
| uniq
> output/Student.txt
# 第一根管道 awk命令 通过','(加空格)按行分割数据,取位置1和2对应的学生姓名和学生学号,输出时以制表符分隔
# 第二根管道 sed命令 通过正则全局替换残留的SQL语句字段“INSERT INTO V_TSG_JYLOG VALUES (”
# 第三根管道 sed命令 通过正则全局删除'符号
# 输出重定向 把学生信息存储到output文件夹下的Student_temp.txt文件中
# 第四根管道 对Student_temp.txt进行排序(为下一步去重做准备)
# 第五根管道 将排序后的结果进行取唯一值操作
# 输出重定向 输出排序和去重后的学生姓名学号对应数据到output文件夹下的Student.txt文件中
清洗结果 ( Student.txt ):
# 按列序对应分别为:(以制表符分割)
# $1:学生学号 / $2:学生姓名
163020417 蒋X刚
163020419 黄X旭
163020421 罗X武
163020423 杨X林
163020425 唐X雯
图书馆书目数据
源文件:多份同样格式的XLS文件
给出的XLS文件因为编码原因在linux操作系统中不能直接使用,我这里用了一个比较笨的办法来处理。
先用Excel打开,再保存为UTF-8格式的txt文件,这一步骤操作完成后文件目录如下图:
红色框起来的是按照时间顺序新生成的四份txt文件,内容格式如下:
接下来用如下命令来合并四分文件并进行清洗:
cat bookIndex1.txt
| sed '1d'
| awk -F '\t' '{print $2"\t"$3"\t"$4"\t"$5"\t"$6"\t"$7"\t"$8"\t"$9"\t"$10}'
> ../output/BookIndex.txt
cat bookIndex2.txt | sed '1d' | awk -F '\t' '{print $2"\t"$3"\t"$4"\t"$5"\t"$6"\t"$7"\t"$8"\t"$9"\t"$10}' >> ../output/BookIndex.txt
cat bookIndex3.txt | sed '1d' | awk -F '\t' '{print $2"\t"$3"\t"$4"\t"$5"\t"$6"\t"$7"\t"$8"\t"$9"\t"$10}' >> ../output/BookIndex.txt
cat bookIndex4.txt | sed '1d' | awk -F '\t' '{print $2"\t"$3"\t"$4"\t"$5"\t"$6"\t"$7"\t"$8"\t"$9"\t"$10}' >> ../output/BookIndex.txt
# 第一根管道 sed命令 删除第一行(无用的表头)
# 第二根管道 awk命令 按制表符分割输出$2-$12的文件,不输出$1是因为序号无效
# 输出重定向 >>追加输出四份书目索引文件到同一份名为BookIndex.txt文件中
清洗结果 ( BookIndex.txt ):
# 按列序对应分别为:
# $1:索书号 / $2:书名 / $3:作者 / $4:出版日期 / $5:标准编码 / $6:书籍编号 / $7:单价 / $8:出版者 / $9:借阅次数
F713.50/WBC 营销:市场营销操作1001法 王必成主编 2004 7-5078-2320-2 0002102 54 中国国际广播出版社 0
B848.4/LH 跟亿万富翁站在一起 鹿荷编著 2004 7-80109-995-8 0002103 13 中央编译出版社 0
F275.3/KPN 质量成本原理:原理、实施和应用 (美) Jack Campanella编著 2004 7-111-14776-6 0002104 28 机械工业出版社 0
F740.45/DYL 外贸会计习题与解答 丁元霖主编 2004 7-5429-1292-5 0002105 17.5 立信会计出版社 0
F270/CQM 股票期权理论与实务 "蔡启明, 钱焱著" 2004 7-5429-1230-5 0002106 17.5 立信会计出版社 0
F590/W388 旅游学导论 "王晓云, 张帆编著" 2004 7-5429-1199-6 0002107 22.6 立信会计出版社 0
图书馆借阅数据
源文件: BookRecord.sql
文件的SQL语句格式如下 :
INSERT INTO V_TSG_JYLOG VALUES ('XX东', '153100476', '504948', '还书,保留本', '0791593', '0791593', '2016-07-06 08:28:26', '威尼斯商人');
INSERT INTO V_TSG_JYLOG VALUES ('XX敏', '153100340', '507522', '还书', '0791600', '0791600', '2016-06-01 18:53:52', '黑塞诗选');
INSERT INTO V_TSG_JYLOG VALUES ('XX成', '153020068', '508306', '还书', '0791613', '0791613', '2016-11-29 10:03:01', '未拆的家书');
INSERT INTO V_TSG_JYLOG VALUES ('XX晴', '163020432', '508306', '还书', '0791614', '0791614', '2016-12-17 19:30:55', '未拆的家书');
清洗命令:取借阅记录
cat BookRecord.sql
| sed "s|INSERT INTO V_TSG_JYLOG VALUES (||g"
| sed "s|[');]||g"
| awk -F ', ' '{print $1"\t"$2"\t"$3"\t"$4"\t"$5"\t"$7"\t"$8}'
> output/BookRecord.txt
# 第一根管道 sed命令 替换残留的SQL语句字段“INSERT INTO V_TSG_JYLOG VALUES (”
# 第二根管道 sed命令 通过正则替全局换掉' ) ;这三种字符
# 第三根管道 awk命令 以","为分隔符,取$1-$8位置上的数据
# 输出重定向 输出清洗后的借阅记录
清洗结果 ( BookRecord.txt ):
# 按列序对应分别为:(以制表符分割)
# $1:姓名 / $2:学号 / $3:未知(ZJM) / $4:操作类型 / $5:书籍编号 / $6:借阅时间 / $7:书名
刘佳 050132021 6416 借书 0034679 2007-05-09 10:03:02 武则天传
苏娟 050132020 6416 借书 0034679 2007-05-09 10:03:02 武则天传
李玉 050132019 6416 借书 0034679 2007-05-09 10:03:02 武则天传
王都 050132018 6416 借书 0034679 2007-05-09 10:03:02 武则天传
吴茂 050132017 6416 借书 0034679 2007-05-09 10:03:02 武则天传
饭卡消费数据
源文件:Bill.sql
文件的SQL语句格式如下:
INSERT INTO YKTYHXFSJZL_NEW VALUES (NULL, NULL, NULL, NULL, NULL, NULL, '000207619', NULL, NULL, NULL, NULL, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL);
INSERT INTO YKTYHXFSJZL_NEW VALUES (NULL, NULL, NULL, NULL, NULL, NULL, '000207620', NULL, NULL, NULL, NULL, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL);
INSERT INTO YKTYHXFSJZL_NEW VALUES (77934979, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, '2017/03/09 18:27:29.527', 33, 1, NULL, NULL, NULL, NULL, '94043244', '163110195', '2032', '联网售饭', '0108027');
INSERT INTO YKTYHXFSJZL_NEW VALUES (77934980, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, '2017/03/09 18:27:30.670', 244.75, 7, NULL, NULL, NULL, NULL, '94043245', '140330105', '2032', '联网售饭', '0108011');
清洗命令如下:
cat Bill.sql
| awk -F ', ' '{print $11"\t"$12"\t"$13"\t"$19"\t"$21}'
| sed "s|[']||g"
> output/Bill.txt
# 第一根管道 awk命令 取以', '(加空格)分隔符分割后位置为7,12,13,18,21的数据,输出时以制表符分割
# 第二根管道 sed命令 在每一行中全面替换'符号(替换学号的左右符号)
# 输出重定向 把处理后的输出结果输出到output文件夹下的Bill.txt文件中
清洗结果 ( Bill.txt ):
# 按列序对应分别为:(以制表符分割)
# $1:消费消费时间 / $2:卡上余额 / $3:交易金额 / $4:学号 / $5:消费类型
2016/03/29 12:50:47.453 4.25 2.50 153020240 联网售饭
2016/03/29 12:36:13.630 6.75 10 153020240 联网售饭
2016/03/29 17:31:42.377 104.25 100 153020240 银行转入
2016/04/05 12:11:26.800 6.05 11 153020240 联网售饭
2016/04/05 08:15:18.307 17.05 2.50 153020240 联网售饭
学生成绩数据
源文件:Grade.tsv
拿到的这份制表符分割文件用head命令查看了一下,发现中文读不出来,重新存储了一次编码格式化UTF-8可以正常显示中文了。
还需要注意:部分课程的名称里面包含空格,如果按空格分割会出现列数多的情况,可以用sed命令再次处理。
清洗命令如下:
cat Grade.tsv
| sed '1d'
| awk -F '\t' '{print $1"\t"$2"\t"$3"\t"$4"\t"$5"\t"$6"\t"$7"\t"$8"\t"$9}'
> output/Grade.txt
# 第一根管道 sed命令 d命令用于按行删除内容,1d表示删除掉第一行(表头)
# 第二根管道 sed命令 替换课程名称中的空格
# 第三根管道 awk命令 同样地,以\t(制表符)为分割符,取$1-$9
# 输出重定向命令 输出清洗后的成绩信息
清洗结果 ( Grade.txt ):
# 按列序对应分别为:
# $1:学号 / $2:班级 / $3:学年 / $4:专业 / $5:学科名 / $6:正考成绩 / $7:补考成绩
130680186 视觉2013-02班 2013 视觉传达设计 商业摄影 75 0
130680188 视觉2013-02班 2013 视觉传达设计 商业摄影 67 0
130680192 视觉2013-02班 2013 视觉传达设计 商业摄影 76 0
154080002 物联网2015-01班 2015 物联网应用技术 物联网概论 80 0
154080004 物联网2015-01班 2015 物联网应用技术 物联网概论 87 0
154080005 物联网2015-01班 2015 物联网应用技术 物联网概论 87 0
154080007 物联网2015-01班 2015 物联网应用技术 物联网概论 86 0
154080010 物联网2015-01班 2015 物联网应用技术 物联网概论 67 0
门禁刷卡数据
源文件:Gurad.sql
门禁记录sql文件格式如下:
INSERT INTO MJXXB VALUES ('1001', '1', '控制器06门1', '1', '大门门禁', '20150604', '18:56:02:000');
INSERT INTO MJXXB VALUES ('1001', '1', '控制器06门1', '1', '大门门禁', '20150604', '18:56:13:000');
INSERT INTO MJXXB VALUES ('1001', '1', '控制器06门1', '1', '大门门禁', '20150604', '19:06:57:000');
INSERT INTO MJXXB VALUES ('1001', '1', '控制器06门1', '1', '大门门禁', '20150604', '19:07:15:000');
INSERT INTO MJXXB VALUES ('1001', '1', '控制器06门1', '1', '大门门禁', '20150604', '19:20:36:000');
如法炮制,用以下命令清洗:
cat Gurad.sql
| sed "s|INSERT INTO MJXXB VALUES (||g"
| sed "s|[');]||g"
| awk -F ', ' '{print $1"\t"$2"\t"$3"\t"$4"\t"$5"\t"$6"\t"$7}'
> output/Gurad.txt
# 第一根管道 sed命令 删除SQL语句残留
# 第二根管道 sed命令 删除多余符号
# 第三根管道 awk命令 以','分割,取$1-$7的数据
# 输出重定向命令 输出清洗后的门禁记录数据
清洗结果 ( Gurad.txt ):
# 按列序对应分别为:(以制表符分割)
# $1:学号 / $2:设备编号 / $3:设备名称 / $4:设备轮询编号 / $5:刷卡位置 / $6:刷卡日期 / $7:刷卡时间
130840171 1 A1 2 宿舍门禁 20150902 22:34:48:000
130840171 1 A1 2 宿舍门禁 20150902 22:34:51:000
130840171 1 A1 2 宿舍门禁 20150902 22:34:52:000
130520103 1 A1 2 宿舍门禁 20150902 22:37:33:000
130820119 1 A1 2 宿舍门禁 20150902 22:38:29:000
关联提取数据
数据清洗完成后我们下一步开始提取数据 —— 提取处理需求中所需数据。
这里用到了awk的文件关联操作,过段时间会在其他文章中对awk文件关联操作的详解。
成绩平均分
awk -F '\t' '
NR==FNR{student[$1]=$2}
NR!=FNR{if($6>=0){print "Grade\t"student[$1]"\t"$1"\t"$3"\t"$6}}
'
Student.txt Grade.txt
> require/Grade.txt
注意:在做后续 Map Reduce 步骤中出现了数组越界的情况,排查发现是因为数据清洗格式没有统一有的成绩存在空的情况。
数据格式如下:
# 按列序对应分别为:(以制表符分割)
# $1:数据类型标识 / $2:姓名 / $3:学号 / $4:年份 / $5:成绩
Grade 董文静 140680107 2014 87
Grade 宗升正 140680108 2014 72
Grade 朱文莉 140680110 2014 86
Grade 葛玲玮 140680111 2014 84
Grade 刘佳洁 140680114 2014 72
图书借阅
awk -F '\t' '
NR==FNR{bookInfo[$6]=$1}
NR!=FNR{print "BookRecord\t"$1"\t"$2"\t"substr(bookInfo[$5],1,1)"\t"$4"\t"substr($6,1,4)}
'
BookIndex.txt BookRecord.txt
> require/BookRecord.txt
数据格式如下:
# 按列序对应分别为:(以制表符分割)
# $1:数据类型标识 / $2:姓名 / $3:学号 / $4:图书类型 / $5:操作类型 /$6:年份
BookRecord 彭瑶 100410750 I 借书 2012
BookRecord 权婷 100410749 I 借书 2012
BookRecord 孙彦 100410748 I 借书 2012
BookRecord 刘姝娜 100410747 I 借书 2012
BookRecord 周忆菲 100410746 I 借书 2012
BookRecord 钟疆慧 100410745 I 借书 2012
饭卡消费
awk '
NR==FNR{student[$1]=$2}
NR!=FNR{if($5!="NULL"){print "Bill\t"student[$4]"\t"$4"\t"substr($1,1,4)"\t"$3"\t"$5}}
'
Student.txt Bill.txt
> require/Bill.txt
数据类型如下:
# 按列序对应分别为:(以制表符分割)
# $1:数据类型标识 / $2:姓名 / $3:学号 / $4:年份 / $5:交易额 / $6:操作类型
Bill 张燕芳 153070194 2016 2 联网售饭
Bill 何枫玲 163070207 2016 1 联网售饭
Bill 163060430 2016 1 联网售饭
Bill 潘捷 140920106 2016 3.50 联网售饭
Bill 罗蕊 154100051 2016 4 联网售饭
门禁刷卡
awk '
NR==FNR{student[$2]=$1}
NR!=FNR{print "Gurad\t"student[$1]"\t"$1"\t"substr($6,1,4)"\t"substr($7,1,2)}
'
Student.txt Gurad.txt
> require/Gurad.txt
数据类型如下:
# 按列序对应分别为:(以制表符分割)
# $1:数据类型标识 / $2:姓名 / $3:学号 / $4:年份 / $5:刷卡时间(小时)
Gurad 刘畅 120350240 2017 19
Gurad 梁潇 120350303 2017 19
Gurad 严鉥文 120350307 2017 17
Gurad 高子涵 120350329 2017 19
Gurad 朱兴奇 120350330 2017 19
Gurad 周力君 120930130 2017 19
Gurad 黄丹 130110118 2017 19