MapReduce 通信数据清洗处理
现获取到一份相关人员的通信数据,进行简单的预处理整合数据,最终输出。
需求:
- 将电话号码替换成人名
- 将拨打、接听电话的时间戳转换成日期
- 求出电话的通话时间,以秒做单位
- 将省份编码替换成省份名称
数据集:
phone
:呼叫者手机号,接受者手机号,开始时间戳,接受时间戳,呼叫者地址省份编码,接受者地址省份编码
city
:地址id,省份编码,省份名称
person
:电话ID,电话号码,姓名
最后数据的样例:
任宗阳,邓二,1970年01月18日 06:41:23,1970年01月18日 06:41:23,186秒,浙江省,新疆维吾尔自治区
1.封装Bean类
- 创建Bean类:封装数据输出内容
- 重写toString():定义输出格式
- 继承自WritableComparable:实现序列化、反序列化方法
- 定义setter、getter方法
- 自定义set()方法:用于赋值
package 通信数据处理;
import org.apache.hadoop.io.WritableComparable;
import java.io.DataInput;
import java.io.DataOutput;
import java.io.IOException;
public class BeanTest implements WritableComparable<BeanTest> {
// 封装属性
private String sender; // 呼叫者
private String receiver; // 接受者
private String start; // 开始时间
private String end; // 结束时间
private String interval; // 间隔
private String p; // 省份
private String c; // 城市
// toString()
@Override
public String toString() {
StringBuffer buffer = new StringBuffer();
buffer.append(sender).append(",");
buffer.append(receiver).append(",");
buffer.append(start).append(",");
buffer.append(end).append(",");
buffer.append(interval).append(",");
buffer.append(p).append(",");
buffer.append(c);
return buffer.toString();
}
// 无参构造
public BeanTest() {
}
@Override
public int compareTo(BeanTest o) {
return 0;
}
@Override
public void write(DataOutput dataOutput) throws IOException {
dataOutput.writeUTF(sender);
dataOutput.writeUTF(receiver);
dataOutput.writeUTF(start);
dataOutput.writeUTF(end);
dataOutput.writeUTF(interval);
dataOutput.writeUTF(p);
dataOutput.writeUTF(c);
}
@Override
public void readFields(DataInput dataInput) throws IOException {
sender = dataInput.readUTF();
receiver = dataInput.readUTF();
start = dataInput.readUTF();
end = dataInput.readUTF();
interval = dataInput.readUTF();
p = dataInput.readUTF();
c = dataInput.readUTF();
}
public void set(String sender, String receiver, String start, String end, String interval, String p, String c) {
this.sender = sender;
this.receiver = receiver;
this.start = start;
this.end = end;
this.interval = interval;
this.p = p;
this.c = c;
}
public String getSender() {
return sender;
}
public void setSender(String sender) {
this.sender = sender;
}
public String getReceiver() {
return receiver;
}
public void setReceiver(String receiver) {
this.receiver = receiver;
}
public String getStart() {
return start;
}
public void setStart(String start) {
this.start = start;
}
public String getEnd() {
return end;
}
public void setEnd(String end) {
this.end = end;
}
public String getInterval() {
return interval;
}
public void setInterval(String interval) {
this.interval = interval;
}
public String getP() {
return p;
}
public void setP(String p) {
this.p = p;
}
public String getC() {
return c;
}
public void setC(String c) {
this.c = c;
}
}
2.MapperTest类进行数据转换
- 在这里我们通过缓存小文件的形式进行操作,相对来说
person.txt
和city.txt
文件的内容较少,就将他们作为缓存文件。 - 首先通过
context.getCacheFiles()
获取缓存文件组。拿person数据集举例说明:原数据集中有三列,分别是电话ID
,电话号码
,姓名
,我们目标是将phone.txt
中的前两列换成对应的号码拥有者姓名,所以在person数据集中第一列电话ID显得不那么重要(使用不到),所以在处理过程中忽略。同样的city数据集中的第一列也可以忽略~ - 我们通过缓存的形式分别处理
归属人
、归属地
信息,并将其以电话->人名
、地址ID->省份
的键值对形式存在两组HashMap
中,这样做的好处就是后面可以通过原数据中有的信息去get()
对应键的值,完成替换操作。 - 时间数据的转换,就使用SimpleDateFormat类(指定输出样式),由于原先是
String
类型的数据,所以我们要先将其转为Long
型的时间类型数据(Long.parseLong(String型原始数据)
)。时间间隔就是用结束的时间减去开始的时间即可。
package 通信数据处理;
import org.apache.commons.lang.StringUtils;
import org.apache.hadoop.io.IOUtils;
import org.apache.hadoop.io.LongWritable;
import org.apache.hadoop.io.NullWritable;
import org.apache.hadoop.io.Text;
import org.apache.hadoop.mapreduce.Mapper;
import java.io.*;
import java.net.URI;
import java.text.SimpleDateFormat;
import java.util.HashMap;
public class MapperTest extends Mapper<LongWritable, Text, BeanTest, NullWritable> {
String sender; // 拨号人
String receiver; // 接收人
String p; // 拨号人居住地
String c; // 接受者居住地
String start; // 拨号时间
String end; // 挂机时间
String interval; // 间隔时间
HashMap<String, String> people = new HashMap(); // 存储人员数据缓存的内容
HashMap<String, String> provience = new HashMap(); // 存储省份数据缓存的内容
BeanTest k = new BeanTest();
@Override
protected void setup(Context context) throws IOException, InterruptedException {
//分别获取缓存文件中的数据
// 1.获取所有缓存文件集
URI[] cacheFiles = context.getCacheFiles();
String line;
// 2.获取人员文件中的数据
String path1 = cacheFiles[0].getPath().toString();
BufferedReader person = new BufferedReader(new InputStreamReader(new FileInputStream(path1),"UTF-8"));
while (StringUtils.isNotEmpty(line = person.readLine())){
// 7,18000696806,赵贺彪
String[] fields = line.split(",");
people.put(fields[1],fields[2]); // 存储18000696806-->赵贺彪
}
// 3.获取省份文件中的数据
String path2 = cacheFiles[1].getPath().toString();
BufferedReader city = new BufferedReader(new InputStreamReader(new FileInputStream(path2),"UTF-8"));
while (StringUtils.isNotEmpty(line = city.readLine())){
// 1,110000,北京市
String[] fields = line.split(",");
provience.put(fields[1],fields[2]); // 存储110000-->北京市
}
// 4.关闭资源
IOUtils.closeStream(person);
IOUtils.closeStream(city);
}
@Override
protected void map(LongWritable key, Text value, Context context) throws IOException, InterruptedException {
// 18620192711,15733218050,1506628174,1506628265,650000,810000 获取一行数据
String line = value.toString();
// 拆分
String[] fields = line.split(",");
// 替换拨号及接收人名信息\以及他们的所在省份信息
sender = people.get(fields[0]);
receiver = people.get(fields[1]);
p = provience.get(fields[4]);
c = provience.get(fields[5]);
// 转换时间日期格式
SimpleDateFormat sdf = new SimpleDateFormat("yyyy年MM月dd日 HH:mm:ss");
start = sdf.format(Long.parseLong(fields[2]));
end = sdf.format(Long.parseLong(fields[3]));
interval = Long.parseLong(fields[3]) - Long.parseLong(fields[2])+"秒";
k.set(sender,receiver,start,end,interval,p,c);
System.out.println(k);
// 写出
context.write(k,NullWritable.get());
}
}
3.Reducer类输出数据
package 通信数据处理;
import org.apache.hadoop.io.NullWritable;
import org.apache.hadoop.mapreduce.Reducer;
import java.io.IOException;
public class ReducerTest extends Reducer<BeanTest, NullWritable,BeanTest, NullWritable> {
@Override
protected void reduce(BeanTest key, Iterable<NullWritable> values, Context context) throws IOException, InterruptedException {
// 直接写出
for (NullWritable v:values){
context.write(key,NullWritable.get());
}
}
}
4.DriverTest类配置job
package 通信数据处理;
import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.fs.Path;
import org.apache.hadoop.io.NullWritable;
import org.apache.hadoop.mapreduce.Job;
import org.apache.hadoop.mapreduce.lib.input.FileInputFormat;
import org.apache.hadoop.mapreduce.lib.output.FileOutputFormat;
import java.net.URI;
public class DriverTest {
public static void main(String[] args) {
Job job;
Configuration conf = new Configuration();
try {
// 获取job
job = Job.getInstance(conf);
// 基础配置
job.setMapperClass(MapperTest.class);
job.setReducerClass(ReducerTest.class);
job.setJarByClass(DriverTest.class);
job.setMapOutputKeyClass(BeanTest.class);
job.setMapOutputValueClass(NullWritable.class);
job.setOutputKeyClass(BeanTest.class);
job.setOutputValueClass(NullWritable.class);
// 配置缓存文件
URI[] uris = new URI[2];
uris[0] = new URI("file:///G:/Projects/IdeaProject-C/MapReduce/src/main/java/通信数据处理/cache/person.txt");
uris[1] = new URI("file:///G:/Projects/IdeaProject-C/MapReduce/src/main/java/通信数据处理/cache/city.txt");
job.setCacheFiles(uris);
// 输入输出文件
FileInputFormat.setInputPaths(job, new Path("G:\\Projects\\IdeaProject-C\\MapReduce\\src\\main\\java\\通信数据处理\\data\\phone.txt"));
FileOutputFormat.setOutputPath(job, new Path("G:\\Projects\\IdeaProject-C\\MapReduce\\src\\main\\java\\通信数据处理\\output"));
boolean b = job.waitForCompletion(true);
System.exit(b ? 0 : 1);
} catch (Exception e){
e.printStackTrace();
}
}
}