项目要求
使用IDEA编写Mapreduce代码将HDFS中的数据读取并处理再上传至HDFS
数据示例
数据及数据类型如下表所示
字段 | 备注 | 详细描述 |
video id | 视频唯一id | 11位字符串 |
uploader | 视频上传者 | 上传视频的用户名String |
age | 视频年龄 | 视频在平台上的整数天 |
category | 视频类别 | 上传视频指定的视频分类 |
length | 视频长度 | 整形数字标识的视频长度 |
views | 观看次数 | 视频被浏览的次数 |
rate | 视频评分 | 满分5分 |
ratings | 流量 | 视频的流量,整型数字 |
conments | 评论数 | 一个视频的整数评论数 |
related ids | 相关视频id | 相关视频的id,最多20个 |
实际数据展示
代码编写
package videodata;
import org.apache.hadoop.io.NullWritable;
import org.apache.hadoop.io.Text;
import org.apache.hadoop.mapreduce.Mapper;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.StringTokenizer;
public class VideoMap extends Mapper<Object, Text, Text, NullWritable> {
public void map(Object key, Text value,Context context) throws IOException, InterruptedException {
String line = value.toString();
StringTokenizer tokenizer = new StringTokenizer(line, "\t");
List<String> list = new ArrayList<>();
while (tokenizer.hasMoreTokens()) {
String video_id = tokenizer.nextToken();
String uploader = tokenizer.nextToken();
String age = tokenizer.nextToken();
String category = tokenizer.nextToken();
String length = tokenizer.nextToken();
String view_count = tokenizer.nextToken();
String rate = tokenizer.nextToken();
String ratings = tokenizer.nextToken();
String comments = tokenizer.nextToken();
list.add(video_id);
list.add(uploader);
list.add(age);
list.add(category);
list.add(length);
list.add(view_count);
list.add(rate);
list.add(ratings);
list.add(comments);
while (tokenizer.hasMoreTokens()) {
String[] relate_id = new String[]{tokenizer.nextToken()};
for (int i = 0; i < relate_id.length; i++) {
list.add(relate_id[i]);
}
}
String result = String.join(",", list);
context.write(new Text(result), NullWritable.get());
}
}
}
运行过后控制台报错:
观察错误,发现其原因是map任务读取到到某一行时停止,分析得出可能是原始数据中发生了数据丢失导致字段无法读入,在代码重写map方法之前添加一个变量i并将其初始化,并在循环外添加System.out.println(i++)代码,目的是map任务进行到第几行数据打印出来,这样就可以轻松找到原始数据是哪一行发生了错误。
缺少字段的数据:
找到原始数据对比可知原来是原始数据中的字段发生了缺失,于是观察数据并重新编写能将原始数据进行清洗的代码,在读取原始数据之前加一条判断语句来判断该行数据是否完整,以此来进行数据清洗。
代码优化
package videodata;
import org.apache.hadoop.io.NullWritable;
import org.apache.hadoop.io.Text;
import org.apache.hadoop.mapreduce.Mapper;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.StringTokenizer;
public class VideoMap extends Mapper<Object, Text, Text, NullWritable> {
int i = 1;
public void map(Object key, Text value,Context context) throws IOException, InterruptedException {
String line = value.toString();
if (line.length() >= 13) {
StringTokenizer tokenizer = new StringTokenizer(line, "\t");
List<String> list = new ArrayList<>();
System.out.println(i++);
while (tokenizer.hasMoreTokens()) {
String video_id = tokenizer.nextToken();
String uploader = tokenizer.nextToken();
String age = tokenizer.nextToken();
String category = tokenizer.nextToken();
String length = tokenizer.nextToken();
String view_count = tokenizer.nextToken();
String rate = tokenizer.nextToken();
String ratings = tokenizer.nextToken();
String comments = tokenizer.nextToken();
list.add(video_id);
list.add(uploader);
list.add(age);
list.add(category);
list.add(length);
list.add(view_count);
list.add(rate);
list.add(ratings);
list.add(comments);
while (tokenizer.hasMoreTokens()) {
String[] relate_id = new String[]{tokenizer.nextToken()};
for (int i = 0; i < relate_id.length; i++) {
list.add(relate_id[i]);
}
}
String result = String.join(",", list);
context.write(new Text(result), NullWritable.get());
}
}
}
}
将数据清洗后的数据数量与原始数据的数量进行对比:
从上图中可以看出小部分数据清洗的结果与原始数据的差别还是有很大出入的,如果一个一个数据进行清洗,未免有些麻烦。