大数据实战-callLog项目(通话记录数据分析)之数据生产
作者:互联网
文章目录
前言
这个环节用到了两个工具来提高开发效率,而不是重复造轮子。一个是随机生成测试数据的 java-testdata-generator,另一个是数据处理的Joinery,我的博客中都有介绍,可以点进去了解一下。
生成基础数据
在随机生成数据之前,需要准备一些基础数据,否则直接随机的随机性太强,后面数据分析求和的意义就不大了。
Meaven依赖
其中POI是用DataFrame生成csv文件需要的依赖,如果自己手动写csv的话,joinery和poi都不需要了。
<dependency>
<groupId>joinery</groupId>
<artifactId>joinery-dataframe</artifactId>
<version>1.9</version>
</dependency>
<dependency>
<groupId>com.github.binarywang</groupId>
<artifactId>java-testdata-generator</artifactId>
<version>1.1.2</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.apache.poi/poi -->
<dependency>
<groupId>org.apache.poi</groupId>
<artifactId>poi</artifactId>
<version>3.17</version>
</dependency>
姓名-手机号列表
private static void geneNamNum(){
//准备生成器和df对象
ChineseNameGenerator cng = ChineseNameGenerator.getInstance();
ChineseMobileNumberGenerator cmng = ChineseMobileNumberGenerator.getInstance();
DataFrame<Object> df = new DataFrame<>("name", "telephone");
for(int i=0;i<10;i++){
//生成中文姓名并放入df
String generatedName = cng.generate();
String generatedMobileNum = cmng.generate();
df.append(Arrays.asList(generatedName, generatedMobileNum));
}
try {
//生成csv文件到运行环境的当前目录下
df.writeCsv("./nam_num.csv");
} catch (IOException e) {
e.printStackTrace();
}
}
在main方法中调用即可获得结果
时间维度表
由于可视化的web项目中需要用到,使用在这里先一起生成了。如果用自己的可视化工具,就不用生成。
private static void genDateDimention(){
DataFrame<Object> df = new DataFrame<>("year", "month","day");
Calendar calendar=Calendar.getInstance();
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
try {
//设置时间段的起止时间
Date start = sdf.parse("2017-01-01");
Date end = sdf.parse("2019-07-12");
calendar.setTime(start);
//先添加年月日
while(end.compareTo(calendar.getTime()) > 0){
//注意月份从0开始,需要+1
df.append(Arrays.asList(calendar.get(Calendar.YEAR),
calendar.get(Calendar.MONTH)+1 ,
calendar.get(Calendar.DATE)));
//日期+1
calendar.add(Calendar.DATE, 1);
}
//再添加年维度和月维度的,缺失的维度设为-1
for(int year=2017;year<2020;year++){
df.append(Arrays.asList(year,-1,-1));
for(int month=1;month<=12;month++){
df.append(Arrays.asList(year,month,-1));
}
}
//生成csv文件
df.writeCsv("./dateDimentions.csv");
} catch (ParseException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
直接在main方法调用即可
2019 7 11表示2019年7月11日很好理解,2017 -1 -1表示2017年,2017 1 -1表示2017年1月,为什么要这样呢?因为查询通话信息的时候,选定的范围可能是一天,也可能是一个月或者一年;这种设计能将三种维度放在一张表里,便于web项目的使用。
自动随机生成
接着用之前的数据来模拟数据生产。
需要生成的数据形式为:主叫人+主叫人号码+被叫人+被叫人号码+通话时间(时间戳)+通话时长
例如秦东,15178223582,姜惑努,18481678295,1547472607462,147
生成时间戳
这个比较麻烦,需要仔细留意一下,因为整型11位数解决不了时间戳运算的问题。
生成一定范围内的long数据
java中没有提供这样的方法,需要自己封装一下;其中n表示长整型的位数
代码
//生成一定范围内的长整数
public Long nextLong(Random rng, Long n) {
// error checking and 2^x checking removed for simplicity.
long bits, val;
do {
bits = (rng.nextLong() << 1) >>> 1;
val = bits % n;
} while (bits-val+(n-1) < 0L);
return val;
}
调用演示
获得随机的时间戳
代码
//生成一段时间内的时间戳
private Long getTimeStamp(SimpleDateFormat formatter, String startStr, String endStr){
Random random = new Random();
Long startTime = null, endTime = null, offset = null;
try {
startTime = formatter.parse(startStr).getTime();
endTime = formatter.parse(endStr).getTime();
//这里就用到了之前的nextLong,偏移的范围在起止时间相差的时间段内
offset = this.nextLong(random, endTime-startTime);
} catch (ParseException e) {
e.printStackTrace();
}
return startTime+offset;
}
调用示例
这样就能获得指定时间段类的任意时间了
生成一行记录
首先需要读取之前的nam_num.csv文件并放入Map,用于随机抽取通话人。
private Map<String,String> getNamNumMap(String pathname){
Map<String, String> map = new HashMap<String,String>();
//Java7的try-with-resources可以优雅关闭文件,异常时自动关闭文件
try (FileReader reader = new FileReader(pathname);
BufferedReader br = new BufferedReader(reader) // 建立一个对象,它把文件内容转成计算机能读懂的语言
) {
String line = br.readLine();
String[] strs;
while ((line = br.readLine()) != null) {
// 一次读入一行数据
strs = line.split(",");
map.put(strs[0], strs[1]);
}
} catch (IOException e) {
e.printStackTrace();
}
return map;
}
再准备一个写csv文件的方法
private void writeRecord(String outputFilePath, String line){
//构造好记录后就往文件中写,输出的文件通过主函数的参数传递进来
try(FileWriter writer = new FileWriter(outputFilePath, true);
BufferedWriter br = new BufferedWriter(writer)){
br.write(line+"\n");
br.flush();
//设置休眠时间来调整,数据生产的速度
Thread.sleep(10*1000);
}catch (IOException e) {
e.printStackTrace();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
然后就可以再Main方法中构造记录了,注意主函数需要两个参数,一个是nam_num.csv文件路径另一个是
public static void main(String[] args) {
AutoDataGen adg = new AutoDataGen();
//获得基础的姓名手机号映射
Map<String, String> map = adg.getNamNumMap(args[0]);
String[] keys = new String[map.size()];
//获得Key的数组用于随机取出通话人
map.keySet().toArray(keys);
SimpleDateFormat formatter = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
Random random = new Random();
String key1,key2,value1,value2;
while(true){
//获得主叫人被叫人姓名
key1 = keys[random.nextInt(keys.length)];
key2 = keys[random.nextInt(keys.length)];
//自己不能够自己打电话
if(key1==key2)
continue;
//获得主叫人被叫人手机号
value1 = map.get(key1);
value2 = map.get(key2);
//获得通话时间
Long timeStamp = adg.getTimeStamp(formatter, "2017-01-01 00:00:00", "2019-07-11 23:59:59");
//获得通话时间
int callTime = random.nextInt(9999);
String line = key1+","+value1+","+key2+","+value2+","+String.valueOf(timeStamp)+","+String.valueOf(callTime);
System.out.println(line);
adg.writeRecord(args[1], line);
}
}
可以现在Eclipse中测试一下,首先打开运行参数的设置菜单
设置两个路径,第二个是保存结果的
可以看到控制台间隔10S打印一行记录
打包上传
在Eclipse中打包很方便
然后可以在target下找到jar包将其上传到linux中,并且把nam_num.csv一同带过去
我在家目录下新建了callLogs文件夹,并将它们放在里面(上传的步骤省去,直接用Xterm或者WinSCP都能实现)
执行程序
java -cp calllogs-0.0.1-SNAPSHOT.jar product.AutoDataGen ./nam_num.csv ./calllog.csv
注意自己的包名和类名
只要这样一直开着就能一直不断产生数据
后记
终于实现了数据生成这个环节了,我也通过这次锻炼收获了不少,而且其中封装的一些代码在别的项目也能拿过去用。
标签:数据分析,map,String,通话记录,生成,callLog,new,line,csv 来源: https://blog.csdn.net/weixin_44112790/article/details/95721280