看了以后学了不少通信运营商关于用户数据记录的知识啊。

本来想从网上找真实数据集的,但是网上的数据不合这个DEMO的场景要求,于是用作者提供的python脚本生成一定数据量的数据来实践(当然,这些数据结构是简化了的)。


生成数据:

查看数据:

第一个是位置数据,是手机定期想基站通信报告情况产生的;第二个是上网数据,是手机上网产生的记录;

数据格式:

两种数据都共同包含了分析计算用到的IMSI、LOC、TIME。形成时间互补。


目标是根据数据分析计算指定日期指定时间分割里用户在不同基站的停留时间(我觉得这种计算结果没什么用,要说有用的话估计是一种分析用户下一个地点的预测应用的基础数据)


分析过程:

不同文件提取需要字段并转换时间格式的工具对象类:

 package org.admln.LBS;

 import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date; import org.apache.hadoop.io.Text; /**
* @author admln
* 定义异常类
*
*/
class LineException extends Exception { private static final long serialVersionUID = 1L;
int flag;
public LineException(String msg,int flag) {
super(msg);
this.flag = flag;
}
public int getFlag() {
return flag;
}
} public class DataLine {
private String IMSI,LOC,TIME,TIMEFLAG;
private Date day;
private SimpleDateFormat formatter = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); public static DataLine parser(String line,boolean source,String date,String[] timeFlag) throws LineException {
DataLine dataLine = new DataLine();
String[] lineSplit = line.split("\t");
if(source) {
dataLine.setIMSI(lineSplit[0]);
dataLine.setLOC(lineSplit[3]);
dataLine.setTIME(lineSplit[4]);
}else {
dataLine.setIMSI(lineSplit[0]);
dataLine.setLOC(lineSplit[2]);
dataLine.setTIME(lineSplit[3]);
} //检查日期合法性
if(!dataLine.getTIME().startsWith(date)) {
throw new LineException("",-1);
}
try {
dataLine.setDay(dataLine.getFormatter().parse(dataLine.getTIME()));
}catch(ParseException e) {
throw new LineException("",0);
} //计算所属时段
// int hour = Integer.valueOf(dataLine.getTIME().split(" ")[1].split(":")[1]);
// System.out.println(hour + " " + Integer.valueOf(timeFlag[0]));
// for(int i=0;i<timeFlag.length;i++) {
// if(i==0 && hour<Integer.valueOf(timeFlag[0])) {
// dataLine.setTIMEFLAG("00-" + timeFlag[i]);
// break;
// }else if(hour>Integer.valueOf(timeFlag[timeFlag.length-1])){
// throw new LineException("time is error",-1);
// }else if(hour < Integer.valueOf(timeFlag[i])){
// dataLine.setTIMEFLAG(timeFlag[i-1] + "-" + timeFlag[i]);
// break;
// }
// }
int i = 0, n = timeFlag.length;
int hour = Integer.valueOf( dataLine.getTIME().split(" ")[1].split(":")[0] );
while ( i < n && Integer.valueOf( timeFlag[i] ) <= hour )
i++;
if ( i < n )
{
if ( i == 0 )
dataLine.setTIMEFLAG( "00-" + timeFlag[i] );
else
dataLine.setTIMEFLAG( timeFlag[i-1] + "-" + timeFlag[i] );
}
else //Hour大于最大的时间点
throw new LineException("", -1); return dataLine;
} //输出KEY
public Text outKey() {
return new Text(this.IMSI + "|" + this.TIMEFLAG);
} //输出VALUE
public Text outValue() {
long time = this.day.getTime()/1000L;
return new Text(this.LOC + "|" + String.valueOf(time));
} //测试主函数
public static void main(String[] args) throws LineException {
//位置数据。 IMSI IMEI UPDATETYPE LOC TIME
String pos = "0000000000 0047483647 3 00000044 2013-09-12 00:09:17";
//网络数据. IMSI IMEI LOC TIME URL
String net = "0000000000 0047483647 00000063 2013-09-12 00:20:14 www.baidu.com";
String[] str = {"09","17","24"};
DataLine temp = DataLine.parser("0000000000 0047483647 3 00000044 2013-09-12 00:15:17", true, "2013-09-12", str);
System.out.println(temp.getIMSI() + " " + temp.getLOC() + " " + temp.getTIME() + " " + temp.getTIMEFLAG());
System.out.println(temp.outKey()); } public String getIMSI() {
return IMSI;
} public void setIMSI(String iMSI) {
IMSI = iMSI;
} public String getLOC() {
return LOC;
} public void setLOC(String lOC) {
LOC = lOC;
} public String getTIME() {
return TIME;
} public void setTIME(String tIME) {
TIME = tIME;
} public String getTIMEFLAG() {
return TIMEFLAG;
} public void setTIMEFLAG(String tIMEFLAG) {
TIMEFLAG = tIMEFLAG;
} public Date getDay() {
return day;
} public void setDay(Date day) {
this.day = day;
} public SimpleDateFormat getFormatter() {
return formatter;
} public void setFormatter(SimpleDateFormat formatter) {
this.formatter = formatter;
} }

主角MR:

 package org.admln.LBS;

 import java.io.IOException;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map.Entry;
import java.util.TreeMap; import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.fs.Path;
import org.apache.hadoop.io.NullWritable;
import org.apache.hadoop.io.Text;
import org.apache.hadoop.mapreduce.Job;
import org.apache.hadoop.mapreduce.Mapper;
import org.apache.hadoop.mapreduce.Reducer;
import org.apache.hadoop.mapreduce.lib.input.FileInputFormat;
import org.apache.hadoop.mapreduce.lib.input.FileSplit;
import org.apache.hadoop.mapreduce.lib.output.FileOutputFormat; /**
* @author admln
*
*/
public class BaseStationDataPreprocess { /**
* 计数器
* 用于计数各种异常数据
*/
enum Counter {
TIMESKIP, //时间格式有误
OUTOFTIMESKIP,//时间不在参数指定的时间段内
LINESKIP, //源文件行有误
USERSKIP //某个用户某个时间段被整个放弃
}
public static class baseMapper extends Mapper<Object,Text,Text,Text>{
private String date;
private String[] timeFlag;
private boolean dataSource;
@Override
public void setup(Context context) throws IOException {
//取出指定日期和时间间隔
this.date = context.getConfiguration().get("date");
this.timeFlag = context.getConfiguration().get("timeFlag").split("-"); //提取文件名
FileSplit fs = (FileSplit) context.getInputSplit();
String fileName = fs.getPath().getName();
if(fileName.startsWith("POS")) {
this.dataSource = true;
}else if(fileName.startsWith("NET")) {
this.dataSource = false;
}else {
throw new IOException("file error");
}
}
/**
* MAP任务
* 读取基站数据
* 找出数据所对应时间段
* 以IMSI和时间段作为 KEY
* CGI和时间作为 VALUE
* @throws InterruptedException
* @throws IOException
*/
@Override
public void map(Object key,Text value,Context context) throws IOException, InterruptedException {
DataLine line;
try {
line = DataLine.parser(value.toString(), this.dataSource, this.date, this.timeFlag);
}catch(LineException e) {
if(e.getFlag()==-1) {
context.getCounter(Counter.OUTOFTIMESKIP).increment(1);
}else {
context.getCounter(Counter.TIMESKIP).increment(1);
}
return;
}catch(Exception e) {
context.getCounter(Counter.LINESKIP).increment(1);
return;
} context.write(line.outKey(), line.outValue());
}
}
/**
* 统计同一个IMSI在同一时间段
* 在不同CGI停留的时长
*/
public static class baseReducer extends Reducer<Text,Text,NullWritable,Text> {
private String date;
private SimpleDateFormat formatter = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
@Override
public void setup(Context context) {
this.date = context.getConfiguration().get("date");
}
public void reduce(Text key,Iterable<Text> values,Context context) throws IOException, InterruptedException {
String imsi = key.toString().split("\\|")[0];
String timeFlag = key.toString().split("\\|")[1]; TreeMap<Long,String> tree = new TreeMap<Long,String>();
String treeKey,treeValue;
for(Text val : values) {
treeKey = val.toString().split("\\|")[1];
treeValue = val.toString().split("\\|")[0];
try {
tree.put(Long.valueOf(treeKey),treeValue);
}catch (NumberFormatException e) {
context.getCounter(Counter.TIMESKIP).increment(1);
continue;
}
}
try {
tree.put((formatter.parse(this.date + " " + timeFlag.split("-")[1] + ":00:00").getTime()/1000L), "OFF");
HashMap<String, Float> locs = getStayTime(tree); //输出
for( Entry<String, Float> entry : locs.entrySet() )
{
StringBuilder sb = new StringBuilder();
sb.append(imsi).append("|");
sb.append(entry.getKey()).append("|");
sb.append(timeFlag).append("|");
sb.append(entry.getValue()); context.write( NullWritable.get(), new Text(sb.toString()) );
}
} catch (ParseException e) {
e.printStackTrace();
} }
//获取位置停留时间方法
private HashMap<String,Float> getStayTime(TreeMap<Long,String> tree) {
HashMap<String,Float> hashMap = new HashMap<String,Float>();
Entry<Long,String> branch,nextBranch;
Iterator<Entry<Long,String>> it = tree.entrySet().iterator();
branch = it.next();
while(it.hasNext()) {
nextBranch = it.next();
float stay = (nextBranch.getKey()-branch.getKey())/60.0f;
if(stay<60.0) {
if(hashMap.containsKey(branch.getValue())) {
hashMap.put(branch.getValue(),(float)(Math.round((hashMap.get(branch.getValue())+stay)*100))/100);
}else {
hashMap.put(branch.getValue(), (float)(Math.round(stay*100))/100);
}
}
branch = nextBranch;
}
return hashMap;
}
}
@SuppressWarnings("deprecation")
public static void main(String[] args) throws Exception {
Path input = new Path("hdfs://hadoop:8020/input/baseStation/");
Path output = new Path("hdfs://hadoop:8020/output/baseStation/"); Configuration conf = new Configuration(); //把输入参数中的日期和一天内的时间间隔标志传入配置对象
conf.set("date", args[1]);
conf.set("timeFlag", args[2]); Job job = new Job(conf,"BaseStationDataPreprocess"); job.setJarByClass(BaseStationDataPreprocess.class); job.setOutputKeyClass(Text.class);
job.setOutputValueClass(Text.class); job.setMapperClass(baseMapper.class);
job.setReducerClass(baseReducer.class); FileInputFormat.addInputPath(job, input);
FileOutputFormat.setOutputPath(job, output); System.exit(job.waitForCompletion(true)?0:1); } }

计算结果:

(环境是centos6.4;hadoop2.2.0;JDK1.7)


我觉得作者的做法也不是最好的,作者肯定没有透露所有细节,肯定有更好的方法去处理误差。比如记录中第一时间和时间段的起始时间的时间差。

这个视频留了作业,关于作业1就是上面的过程。至于作业二是要求计算出每个时间段中停留时间最长的三个基站并输出。

这其实就是把每个基站的输出再根据停留时间算一个topK。


我觉得自己需要温故和补习的知识点:

1.自定义异常

好处是自己可以定义异常中的各种动作以迎合自己程序的需要。(类java.lang.Throwable是所有异常类的基类,它包括两个子类:Exception和Error,Exception类用于描述程序能够捕获的异常,如ClassNotFoundException;自定义异常类可以继承Throwable类或者Exception,而不要继承Error类。自定义异常类之间也可以有继承关系。)需要为自定义异常类设计构造方法,以方便构造自定义异常对象。

2.setup方法

3.关于竖线的转义,不转义士没有语法错误的,但是实际错误。以及转义的方式,以及其他需要转移的字符也要注意,如括号、单引号等。


特别佩服作者,不愧参加了那么多的实践项目,对数据的异常处理的很细致很全面也特别有利于事后分析修正。这是我最受用的一点。


InAction-根据LBS数据手机用户移动轨迹的更多相关文章

  1. 苹果iOS苹果公司的手机用户都有权索赔

    大家知道.手机中的操作系统(基础软件)存储在手机固(firm,ware)之中,一般而言,手机用户自己是不能修改的. 苹果iOS手机的系统后门(服务程序)也存储在手机固件之中.手机用户自己是无法删除的. ...

  2. Mysql 数据备份与恢复,用户创建,授权

    Mysql 数据备份与恢复,用户创建,授权 1. Mysqldump >outfile.sql 2. Mysql –uxxx –pxxx < backfile.sql 3. Create  ...

  3. apache通过.htaccess(rewrite)判断手机电脑跳转-手机用户重定向到手机版

    自动判断.重定向的办法也有几种: 使用网站构建的程序(例如PHP)来判断.重定向:使用服务器上的Web服务(例如Apache)来判断.重定向. 在Apache中设置重定向有两个办法: 在网站的http ...

  4. Python爬虫入门教程 43-100 百思不得姐APP数据-手机APP爬虫部分

    1. Python爬虫入门教程 爬取背景 2019年1月10日深夜,打开了百思不得姐APP,想了一下是否可以爬呢?不自觉的安装到了夜神模拟器里面.这个APP还是比较有名和有意思的. 下面是百思不得姐的 ...

  5. 大数据freestyle: 共享单车轨迹数据助力城市合理规划自行车道

    编者按:近年来,异军突起的共享单车极大地解决了人们共同面临的“最后一公里”难题,然而,共享单车发展迅猛,自行车道建设却始终没有能够跟上脚步.幸运的是摩拜单车大量的轨迹数据为我们提供了一种新的思路:利用 ...

  6. 使用mongodb存取lbs数据

    1,在mongodb中创建lbs_db数据库,collection名称lbs_info,要使用lbs查询功能,需要对二维数据列建立索引 db.lbs_info.ensureIndex( { locs ...

  7. 4.windows和Linux下创建oracleusername表空间,表,插入数据,用户管理表等操作

    进入超级管理员,运行下面命令 Window下创建数据库.表空间,用户,插入数据等操作 -- 01 创建表空间 -- 注意表空间的路径 依据实际安装环境进行调整 CREATE TABLESPACE ts ...

  8. 【Salvation】——登录注册存储数据&验证用户

    写在前面:登录注册功能是在纯Unity3D环境内实现的,用到UGUI绘制界面技术,数据库的部分是后面拓展加进来的,这里数据存储是指存在XML用户文件中. 注册用户名和密码 zc() 用户名和密码登录 ...

  9. Python爬虫入门教程 42-100 爬取儿歌多多APP数据-手机APP爬虫部分

    1. 儿歌多多APP简单分析 今天是手机APP数据爬取的第一篇案例博客,我找到了一个儿歌多多APP,没有加固,没有加壳,没有加密参数,对新手来说,比较友好,咱就拿它练练手,熟悉一下Fiddler和夜神 ...

随机推荐

  1. 第二百二十八天 how can I 坚持

    hibernate 还有好多不会搞啊,本来很简单的东西,没用过就不会. 今天... 只是感觉很累,昨天爬山爬的,不知道该写点啥了,买的羽绒服到了,还行吧,凑合穿吧. 睡觉了.今天貌似又发脾气了.哎.. ...

  2. Django中如何使用django-celery完成异步任务2(转)

    原文链接: http://www.weiguda.com/blog/74/ 在上一篇博文中, 我们介绍了如何在开发环境中使用Celery. 接下来我们介绍一下如何在部署环境使用Celery. 1. 简 ...

  3. Event Functions

    [Event Functions] A key concept in games programming is that of making changes to position, state an ...

  4. ubuntu 64位系统下加速Android模拟器

    安装KVM: sudo apt-get install qemu-kvm libvirt-bin ubuntu-vm-builder bridge-utils 在Postfix Configurati ...

  5. 对比AMD 890、AMD 880、 AMD 790、AMD 785、 AMD 780、AMD 7

    770无集显.中低端独显主流. 780G带集显.现在可以无视. 785G现在是带集显的主流. 790GX高端带集显. 790FX专高端,无集显. 790X带集显.基本无视. 870 大板,无集显 88 ...

  6. 如何在C#中模拟C++的联合(Union)?[C#, C++] How To Simulate C++ Union In C#?

    1 什么是联合? 联合(Union)是一种特殊的类,一个联合中的数据成员在内存中的存储是互相重叠的.每个数据成员都在相同的内存地址开始.分配给联合的存储区数量是“要包含它最大的数据成员”所需的内存数. ...

  7. ssh 框架整合试例 (spring+struts2+hibernate)

    1.首先用Eclipse创建一个web项目(Eclipse EE 版) new->Other-> 输入web 然后选择Dynamic Web Project->next-> 输 ...

  8. ASP.NET MVC- Upload File的例子

    上传文件是一项基本功能,一定要了解的.先来看一下使用ASP.NET MVC实现简单的上传. 一.简单的例子 Controller的例子 public ActionResult UploadDemo() ...

  9. 关于favicon.ico的使用

    http://www.cnblogs.com/LoveJenny/archive/2012/05/22/2512683.html

  10. SQLServer获取随机数据

    1.比较常见和好用的一种 SELECT TOP 10 *, NEWID() AS randomFROM tableORDER BY random --newid函数会随机生成一个guid,很长的一个字 ...