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

本来想从网上找真实数据集的,但是网上的数据不合这个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. css font-face自定义字体

    font-face:自定义字体 1.找到一个字体文件 2.放入新建的的font的文件夹内: 3.在样式里面以下内容 注释:aa是字体名 <style> @font-face{     fo ...

  2. Spark相比Hadoop MapReduce的特点

    (1)中间结果输出     基于MapReduce的计算引擎通常会将中间结果输出到磁盘上,进行存储和容错. 出于任务管道承接的考虑,当一些查询翻译到MapReduce任务时,往往会产生多个Stage, ...

  3. CSS3每日一练之选择器-结构性伪类选择器

    <!DOCTYPE HTML> <html> <head> <meta charset="gb2312"> <title> ...

  4. MYSQL数据库性能调优之三:explain分析慢查询

    explain显示了mysql如何使用索引来处理select语句以及连接表.可以帮助选择更好的索引和写出更优化的查询语句.使用方法,在select语句前加上explain就可以了. 一.explain ...

  5. 在VS2012中实现Ext JS的智能提示

    Visual Studio 2012太强大了,居然能自己会去提取Ext JS的类的属性和方法,从而实现只能提示.下面就来介绍一下实现这个功能. 在Visual Studio 2012中随便创建一个We ...

  6. Codeforces 118 D. Caesar's Legions (dp)

    题目链接:http://codeforces.com/contest/118/problem/D 有n个步兵和m个骑兵要排成一排,其中连续的步兵不能超过k1个,连续的骑兵不能超过k2个. dp[i][ ...

  7. POJ 1160Post Office

    POJ 1160    Post Office 我不知道优化,我只知道最暴力的方法,O(V^3),居然100ms不到的过了 设DP[i][j][k]表示考虑前i个小镇,放了j个邮局,最后一个邮局的所在 ...

  8. AcceptEx与WSAEventSelect和Accept

    (转自论坛的一个帖子http://bbs.csdn.net/topics/280032853) AcceptEx主要用于向完成端口 投递一个或多个的连接请求..当有连接时进来,这里分两种情况: 1.A ...

  9. C#用串口接收事件接不全数据的处理

    问题描述:都知道用事件dataReceive来处理串口非常的方便,但当一次的数据过长时,就会出现截断数据的情况.比如说发一个指 令,返回一个30个字节的数据,但上位机则分两次来接收者30个数据. 解决 ...

  10. fastcgi 分布式

    以lighttpd fastcgi写一下自己对fastcgi分布式的理解. 假设一台机器A上运行lighttpd,在这台主机上只是对请求进行分发. 而在其他多台机器上运行多个fastcgi进程,用来接 ...