项目背景

网站、app的运营者需要知道自己的产品或服务的运营状况,就需要对使用自己产品的用户进行各种角度的数据分析,比如:
用户数量
新增用户
留存用户
活跃用户
地域分析
渠道分析

要做这样的分析,数据来源应该是用户的产品使用的行为日志,行为日志是由app或者网站的页面获取用户相关信息后,发送给后台服务器记录下来的:

项目步骤

1.采集数据 (待完善)

从服务器通过flume agent 采集日志,将数据采集到HDFS,生产目录
/applog/2017-09-20/… (512M一个文件)

2.数据预处理(MapReduce)

预处理

需求:

1.清洗:检查每条日志的必选字段是否完整,不完整的日志应该滤除

2.数据解析成原始:
为每条日志添加一个用户唯一标识字段:user_id
user_id的取值逻辑:
如果是ios设备,user_id=device_id
如果是android设备, user_id = android_id
如果android_id为空,则user_id = device_id

3、将event字段抛弃,将header中的各字段解析成普通文本行

主要技术点:json解析 gson/fastjson/jackson/…

4.还有一个变态需求:
需要将清洗后的结果数据,分ios和android和其他 三种类别,输出到3个不同的文件夹;

/app_clean_log/2017-09-20/ios/part-r-00000
/ios/part-r-00000
/ios/part-r-00001

/android/part-r-00000
/android/part-r-00001

/other/part-r-00000
/other/part-r-00001

百度:multipleOutputs

实现:

MapReduce代码

public class AppLogDataClean {public static class AppLogDataCleanMapper extends Mapper<LongWritable, Text, Text, NullWritable> { //在这里提前声明需要的东西,不需要map()方法里面反复声明浪费资源,但是不可以传入外部参数Text k = null;NullWritable v = null;SimpleDateFormat sdf = null;MultipleOutputs<Text,NullWritable> mos = null;  //多路输出器@Overrideprotected void setup(Mapper<LongWritable, Text, Text, NullWritable>.Context context)throws IOException, InterruptedException {//在这里提前声明需要的东西,不需要map()方法里面反复声明浪费资源,在setup()方法中可以传入外部参数k = new Text();v = NullWritable.get();sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");mos = new MultipleOutputs<Text,NullWritable>(context);}@Overrideprotected void map(LongWritable key, Text value, Mapper<LongWritable, Text, Text, NullWritable>.Context context)throws IOException, InterruptedException {JSONObject jsonObj = JSON.parseObject(value.toString());  //将value变成一个jasonObject,可理解为一个hashmapJSONObject headerObj = jsonObj.getJSONObject(GlobalConstants.HEADER);   //首先先get header这个key对应的value,作为jsonObject//GlobalConstants.HEADER意思是将这个参数改成一个常量类/*** 过滤缺失必选字段的记录* 1、检查每条日志的必选字段是否完整,不完整的日志应该滤除*/if (StringUtils.isBlank(headerObj.getString("sdk_ver"))) {return;   //如果根据"sdk_ver"这个key get到的value为null或者空字符串“”,那么什么都不返回}if (null == headerObj.getString("time_zone") || "".equals(headerObj.getString("time_zone").trim())) {return;}if (null == headerObj.getString("commit_id") || "".equals(headerObj.getString("commit_id").trim())) {return;}if (null == headerObj.getString("commit_time") || "".equals(headerObj.getString("commit_time").trim())) {return;}else{// 练习时追加的逻辑,替换掉原始数据中的时间戳String commit_time = headerObj.getString("commit_time");String format = sdf.format(new Date(Long.parseLong(commit_time)+38*24*60*60*1000L));headerObj.put("commit_time", format);}if (null == headerObj.getString("pid") || "".equals(headerObj.getString("pid").trim())) {return;}if (null == headerObj.getString("app_token") || "".equals(headerObj.getString("app_token").trim())) {return;}if (null == headerObj.getString("app_id") || "".equals(headerObj.getString("app_id").trim())) {return;}if (null == headerObj.getString("device_id") || headerObj.getString("device_id").length()<17) {return;}if (null == headerObj.getString("device_id_type")|| "".equals(headerObj.getString("device_id_type").trim())) {return;}if (null == headerObj.getString("release_channel")|| "".equals(headerObj.getString("release_channel").trim())) {return;}if (null == headerObj.getString("app_ver_name") || "".equals(headerObj.getString("app_ver_name").trim())) {return;}if (null == headerObj.getString("app_ver_code") || "".equals(headerObj.getString("app_ver_code").trim())) {return;}if (null == headerObj.getString("os_name") || "".equals(headerObj.getString("os_name").trim())) {return;}if (null == headerObj.getString("os_ver") || "".equals(headerObj.getString("os_ver").trim())) {return;}if (null == headerObj.getString("language") || "".equals(headerObj.getString("language").trim())) {return;}if (null == headerObj.getString("country") || "".equals(headerObj.getString("country").trim())) {return;}if (null == headerObj.getString("manufacture") || "".equals(headerObj.getString("manufacture").trim())) {return;}if (null == headerObj.getString("device_model") || "".equals(headerObj.getString("device_model").trim())) {return;}if (null == headerObj.getString("resolution") || "".equals(headerObj.getString("resolution").trim())) {return;}if (null == headerObj.getString("net_type") || "".equals(headerObj.getString("net_type").trim())) {return;}/*** 生成user_id* 为每条日志添加一个用户唯一标识字段:user_id*user_id的取值逻辑:*如果是ios设备,user_id=device_id*如果是android设备, user_id = android_id如果android_id为空,则user_id = device_id*/String user_id = "";if ("android".equals(headerObj.getString("os_name").trim())) {user_id = StringUtils.isNotBlank(headerObj.getString("android_id")) ? headerObj.getString("android_id"): headerObj.getString("device_id");} else {user_id = headerObj.getString("device_id");}/*** 输出结果*  JsonToStringUtil.toString()是工具类,把一条记录的所有value拼接起来,把jsonObject 转化为String,然后输出* 需要将清洗后的结果数据,分ios和android和其他  三种类别,输出到3个不同的文件夹;*/headerObj.put("user_id", user_id);k.set(JsonToStringUtil.toString(headerObj));if("android".equals(headerObj.getString("os_name"))){mos.write(k, v, "android/android");        //第三个参数是输出路径}else{mos.write(k, v, "ios/ios");}}@Overrideprotected void cleanup(Mapper<LongWritable, Text, Text, NullWritable>.Context context)throws IOException, InterruptedException {mos.close();}}public static void main(String[] args) throws Exception {Configuration conf = new Configuration();Job job = Job.getInstance(conf);job.setJarByClass(AppLogDataClean.class);job.setMapperClass(AppLogDataCleanMapper.class);job.setOutputKeyClass(Text.class);job.setOutputValueClass(NullWritable.class);job.setNumReduceTasks(0);// 避免生成默认的part-m-00000等文件,因为,数据已经交给MultipleOutputs输出了LazyOutputFormat.setOutputFormatClass(job, TextOutputFormat.class);FileInputFormat.setInputPaths(job, new Path(args[0]));FileOutputFormat.setOutputPath(job, new Path(args[1]));boolean res = job.waitForCompletion(true);System.exit(res ? 0 : 1);}}

JsonToStringUtil工具类

package cn.edu360.app.log.mr;import com.alibaba.fastjson.JSONObject;public class JsonToStringUtil {public static String toString(JSONObject jsonObj) {StringBuilder sb = new StringBuilder();
//然后将所有value拼接成一个长String串,转化为String数据,sb.append(jsonObj.get("sdk_ver")).append("\001").append(jsonObj.get("time_zone")).append("\001").append(jsonObj.get("commit_id")).append("\001").append(jsonObj.get("commit_time")).append("\001").append(jsonObj.get("pid")).append("\001").append(jsonObj.get("app_token")).append("\001").append(jsonObj.get("app_id")).append("\001").append(jsonObj.get("device_id")).append("\001").append(jsonObj.get("device_id_type")).append("\001").append(jsonObj.get("release_channel")).append("\001").append(jsonObj.get("app_ver_name")).append("\001").append(jsonObj.get("app_ver_code")).append("\001").append(jsonObj.get("os_name")).append("\001").append(jsonObj.get("os_ver")).append("\001").append(jsonObj.get("language")).append("\001").append(jsonObj.get("country")).append("\001").append(jsonObj.get("manufacture")).append("\001").append(jsonObj.get("device_model")).append("\001").append(jsonObj.get("resolution")).append("\001").append(jsonObj.get("net_type")).append("\001").append(jsonObj.get("account")).append("\001").append(jsonObj.get("app_device_id")).append("\001").append(jsonObj.get("mac")).append("\001").append(jsonObj.get("android_id")).append("\001").append(jsonObj.get("imei")).append("\001").append(jsonObj.get("cid_sn")).append("\001").append(jsonObj.get("build_num")).append("\001").append(jsonObj.get("mobile_data_type")).append("\001").append(jsonObj.get("promotion_channel")).append("\001").append(jsonObj.get("carrier")).append("\001").append(jsonObj.get("city")).append("\001").append(jsonObj.get("user_id"));return sb.toString();}}

预处理程序的脚本开发

功能:每日00:20启动预处理程序(MapRedcue),对数据进行解析,处理数据## 3. 数据入库
在同一hdfs,等预处理过程结束,将预处理之后的数据,导入hive中的事先建 好的表(外部表),导入当日分区
提示:
1、 在hive中建表ods_app_log(外部表、分区表)映射预处理阶段生成的清洗数据
2、 每天定时将预处理之后的天数据 导入到 ods_app_log的天分区 中

实现

4.统计与分析(hive)

1. 活跃用户(日活)

(1) 活跃用户信息表(比较原始的表)

把当天的活跃用户信息抽取出来,存入一个日活用户信息表

CREATE TABLE etl_user_active_day (sdk_ver string,time_zone string,commit_id string,commit_time string,pid string,app_token string,app_id string,device_id string,device_id_type string,release_channel string,app_ver_name string,app_ver_code string,os_name string,os_ver string,language string,country string,manufacture string,device_model string,resolution string,net_type string,account string,app_device_id string,mac string,android_id string,imei string,cid_sn string,build_num string,mobile_data_type string,promotion_channel string,carrier string,city string,user_id string) partitioned BY (day string) row format delimited fields terminated BY '\001';
(2) 活跃用户总表


概念:某一天使用过app的用户就是活跃用户

计算过程:

 源表——ods_app_log
 目标表:日活表
create table etl_user_active_day like ods_app_log;

注意:

  1. 每个活跃用户抽取他当天所有记录中时间最早的一条(用分组排序的语法)

  2. 从ods_app_log原始数据表的当天分区中,抽取当日的日活用户信息插入日活用户信息表etl_user_active_day

代码实现:

INSERT INTO TABLE etl_user_active_day PARTITION (day = '2017-09-21',)
SELECT sdk_ver,time_zone,commit_id,commit_time,pid,app_token,app_id,device_id,device_id_type,release_channel,app_ver_name,app_ver_code,os_name,os_ver,LANGUAGE,country,manufacture,device_model,resolution,net_type,account,app_device_id,mac,android_id,imei,cid_sn,build_num,mobile_data_type,promotion_channel,carrier,city,user_id
FROM (
SELECT *
,row_number() OVER (PARTITION BY user_id ORDER BY commit_time) AS rn  --分组加排序FROM ods_app_log WHERE day = '2017-09-21') tmp
WHERE rn = 1;
(3)各维度统计活跃用户总表

根据四个重要指标,以不同维度统计活跃用户总表
–各维度组合分析:
不区分操作系统os_name 不区分城市city 不区分渠道release_channel 不区分版本app_ver_name 活跃用户
区分操作系统os_name 不区分城市city 不区分渠道release_channel 不区分版本app_ver_name 活跃用户
不区分操作系统os_name 区分城市city 不区分渠道release_channel 不区分版本app_ver_name 活跃用户
不区分操作系统os_name 不区分城市city 区分渠道release_channel 不区分版本app_ver_name 活跃用户
不区分操作系统os_name 不区分城市city 不区分渠道release_channel 区分版本app_ver_name 活跃用户
区分操作系统os_name 区分城市city 不区分渠道release_channel 不区分版本app_ver_name 活跃用户

像这样

维度组合统计
0 0 0 0
0 0 0 1
0 0 1 0
0 0 1 1
0 1 0 0
0 1 0 1
0 1 1 0
0 1 1 1
1 0 0 0
1 0 0 1
1 0 1 0
1 0 1 1
1 1 0 0
1 1 0 1
1 1 1 0
1 1 1 1
注意:
根据dim和day分区,表示以什么为组合和时间

  1. 统计不区分操作系统os_name 不区分城市city 不区分渠道release_channel 不区分版本app_ver_name活跃用户 这个维度的日活表

    -- 1 日新维度统计报表--数据建模 create table dim_user_new_day(os_name string,city string,release_channel string,app_ver_name string,cnts
    int) partitioned by (day string, dim string);-- 2 日新维度统计报表sql开发(利用多重插入语法) from etl_user_new_dayinsert into table dim_user_new_day
    partition(day='2017-09-21',dim='0000') select
    'all','all','all','all',count(1) -- where day='2017-09-21'
  2. 不区分操作系统os_name 不区分城市city 不区分渠道release_channel 区分版本app_ver_name 活跃用户 这个维度的日活表

    partition(day='2017-09-21',dim='0001') select
    'all','all','all',app_ver_name,count(1) where day='2017-09-21'
    group by app_ver_name
  3. 不区分操作系统os_name 区分城市city 不区分渠道release_channel 区分版本app_ver_name 活跃用户 这个维度的日活表

    partition(day='2017-09-21',dim='0001') select
    'all','all',city,app_ver_name,count(1) where day='2017-09-21'
    group by app_ver_name,city

1. 新增用户统计

新增用户的定义:
比如,在2017-08-28日出现了一些以前从没出现过的用户,则这些用户就是2017-08-28日的新增用户

需求:
1、将每日的新增用户从ods_app_log表中抽取出来,存入一个新用户信息表:
dw_new_user_day的日分区中

2、统计如下报表:
某日 城市 渠道 版本 新增用户数
2017-08-28 all all all ?
2017-08-28 具体城市 all all ?
2017-08-28 all 具体渠道 all ?
2017-08-28 all all 具体版本 ?
2017-08-28 all 具体渠道 具体版本 ?
2017-08-28 具体城市 all 具体版本 ?
2017-08-28 具体城市 具体渠道 all ?
2017-08-28 具体城市 具体渠道 具体版本 ?

整体思路:

  • a、应该建立一个历史用户表(只存user_id,第一天全部存入)

  • b、将当日的活跃用户去 比对 历史用户表, 就知道哪些人是今天新出现的用户 --> 当日新增用户(用联结的方式去对比,如果今日的用户表userID没有,那就是新用户)

  • c、将当日新增用户追加到历史用户表

(1)建立日新维度表实现

-- 1 历史用户表
create table etl_user_history(user_id string);  --(只存user_id)-- 2 当日新增用户表:存所有字段(每个人时间最早的一条),带有一个分区字段:day string;
create table etl_user_new_day like etl_user_active_day;-- 统计实现 *********************************-- 1 当日活跃-历史用户表 --> 新增用户表的当日分区
insert  into etl_user_new_day partition(day='2017-09-21')
SELECT sdk_ver,time_zone,commit_id,commit_time,pid,app_token,app_id,device_id,device_id_type,release_channel,app_ver_name,app_ver_code,os_name,os_ver,LANGUAGE,country,manufacture,device_model,resolution,net_type,account,app_device_id,mac,android_id,imei,cid_sn,build_num,mobile_data_type,promotion_channel,carrier,city,a.user_id
from  etl_user_active_day a left join  etl_user_history b on a.user_id = b.user_id
where a.day='2017-09-21' and b.user_id is null;-- 2 将当日新增用户的user_id追加到历史表
insert into table etl_user_history
select user_id from etl_user_new_day where day='2017-09-21';
(2)日新用户表维度统计
from etl_user_new_dayinsert into table dim_user_new_day partition(day='2017-09-21',dim='0000')
select 'all','all','all','all',count(1)
where day='2017-09-21'insert into table dim_user_new_day partition(day='2017-09-21',dim='0001')
select 'all','all','all',app_ver_name,count(1)
where day='2017-09-21'
group by app_ver_nameinsert into table dim_user_new_day partition(day='2017-09-21',dim='0010')
select 'all','all',release_channel,'all',count(1)
where day='2017-09-21'
group by release_channelinsert into table dim_user_new_day partition(day='2017-09-21',dim='0011')
select 'all','all',release_channel,app_ver_name,count(1)
where day='2017-09-21'
group by release_channel,app_ver_nameinsert into table dim_user_new_day partition(day='2017-09-21',dim='0100')
select 'all',city,'all','all',count(1)
where day='2017-09-21'
group by city

留存用户统计

概念:
比如,15号的新增用户,在16号又活跃了,这些用户就是次日留存用户;
比如,12号的新增用户,在15号又活跃了,这些用户就是3日留存用户;

需求:
——ETL:先抽取出次日留存用户,存入一个次日留存用户信息表,记录跟活跃用户表相同的字段;
——维度分析:统计各种维度下的留存用户数、留存用户比例

逻辑思路:昨天在新用户表中,今天在活跃用户表中 --> 今日的“次日留存用户”
12号在新增用户中,在15号在活跃用户表,这些用户就是3日留存用户;
那么将用id将两表内联结,两表都存在的话,那么就是留存用户(用左半联结效率略高 吧)

实现:

内联结:

-- 数据建模
--建次日留存etl信息表:记录跟活跃用户表相同的字段create table etl_user_keepalive_nextday like etl_user_active_day;-- etl开发
insert into table etl_user_keepalive_nextday partition(day='2017-09-22')
selectactuser.sdk_ver ,actuser.time_zone ,actuser.commit_id ,actuser.commit_time ,actuser.pid ,actuser.app_token ,actuser.app_id ,actuser.device_id ,actuser.device_id_type ,actuser.release_channel ,actuser.app_ver_name ,actuser.app_ver_code ,actuser.os_name ,actuser.os_ver ,actuser.language ,actuser.country ,actuser.manufacture ,actuser.device_model ,actuser.resolution ,actuser.net_type ,actuser.account ,actuser.app_device_id ,actuser.mac ,actuser.android_id ,actuser.imei ,actuser.cid_sn ,actuser.build_num ,actuser.mobile_data_type ,actuser.promotion_channel ,actuser.carrier ,actuser.city ,actuser.user_id from etl_user_new_day newuser join etl_user_active_day actuser
on newuser.user_id = actuser.user_id
where newuser.day='2017-09-21' and actuser.day='2017-09-22';

用左半联结的方法实现(用左半连接效率略高):

insert into table etl_user_keepalive_nextday partition(day='2017-09-22')
select sdk_ver
,time_zone
,commit_id
,commit_time
,pid
,app_token
,app_id
,device_id
,device_id_type
,release_channel
,app_ver_name
,app_ver_code
,os_name
,os_ver
,language
,country
,manufacture
,device_model
,resolution
,net_type
,account
,app_device_id
,mac
,android_id
,imei
,cid_sn
,build_num
,mobile_data_type
,promotion_channel
,carrier
,city
,user_id
from etl_user_new_day a left semi join etl_user_active_day b
on a.user_id = b.user_id and a.day='2017-09-21' and b.day='2017-09-22';where a.day='2017-09-21' and b.day='2017-09-22'; // 注意:left semi join中,右表的引用不能出现在where条件中

沉默用户分析

概念:创建用户后,一段时间内(连续7天)没有使用过app的用户

思路:
比如,现在运算的是20号的报表,
用13号的新增用户 left join 活跃用户表的(14-20号分区)
取右表join后结果为null的用户

-- 1 创建沉默用户表
create table silent_user_7_day like etl_user_active_day;-- 统计实现 *********************************-- 2 7日活跃-历史用户表 -->沉默用户表的当日分区
insert  into silent_user_7_day partition(day='2017-09-21')
SELECT sdk_ver,time_zone,commit_id,commit_time,pid,app_token,app_id,device_id,device_id_type,release_channel,app_ver_name,app_ver_code,os_name,os_ver,LANGUAGE,country,manufacture,device_model,resolution,net_type,account,cid_sn,build_num,mobile_data_type,promotion_channel,carrier,city,a.user_id
from  eetl_user_new_day a left join  etl_user_history b on a.user_id = b.user_id
where b.day='2017-09-21' and  b.day='2017-09-20'  b.day='2017-09-19' and b.day='2017-09-18' and b.day='2017-09-17' and b.day='2017-09-16' and b.day='2017-09-15' and b.user_id is null;

版本升级轨迹

需求:每天统计出如下报表
日期 user_id app_token channel city source_ver curr_ver
示例:
2017-08-14,许老师,共享女友,360应用,北京,v1.0
2017-08-14,赵老师,共享女友,安智市场,北京,v1.2
2017-08-14,许老师,共享女友,360应用,天津,v1.2
2017-08-14,许老师,共享女友,小米应用,天津,v2.0

2017-08-15,许老师,共享女友,360应用,北京,v2.0
2017-08-15,赵老师,共享女友,安智市场,北京,v1.2
2017-08-15,赵老师,共享女友,安智市场,北京,v1.5

变为一下格式,表示用户今日版本升级的过程

2017-08-14 许老师 共享女友 360应用 天津 v1.0 v1.2
2017-08-14 许老师 共享女友 小米应用 天津 v1.2 v2.0

思路:
将最左的字段版本信息新生成一行相同的但是向下移动一位,然后将左边版本号小于右边的提取出来。可用窗口分析函数实现:lag(app_ver_name,1,null) over(partition by user_id order by app_ver_name)

lag(参数一:要移动的字段,参数二:下移的行数,参数三:空缺的用啥填充) over((partition by 参数四:组内根据什么排序 order by 参数五:按什么分组)
解决方案:

–创建表格,输入数据
create table t_lag_test(day string,user_id string,app_token string,release_channel string,city string,app_ver_name string)
row format delimited fields terminated by ‘,’;

load data local inpath ‘/root/hivetest/ver.test’ into table t_lag_test;

– 数据统计
select
day,user_id,app_token,release_channel,city,ver_2,app_ver_name
from
(
select
day,user_id,app_token,release_channel,city,app_ver_name,
lag(app_ver_name,1,null) over(partition by user_id order by app_ver_name) as ver_2
from t_lag_test) tmp
where ver_2 is not null and app_ver_name>ver_2
;

更多窗口分析函数使用方法,点这里看更多窗口分析函数

将hive上统计好的数据表导出至mysql

将app数据仓库中的 日新用户维度统计报表:dim_user_new_day 导出到mysql的表中去

– 1 在mysql中建库建表

create database app;
create table dim_user_new_day(
os_name varchar(20),city varchar(20),release_channel varchar(20),app_ver_name varchar(20),cnts int,dt varchar(20)
);

–注意:将库和表的编码集改成utf8,命令如下:
修改库的编码:
mysql> alter database db_name character set utf8;
修改表的编码:
mysql> ALTER TABLE table_name CONVERT TO CHARACTER SET utf8 COLLATE utf8_general_ci;

– 2 用sqoop将hive中的 dim_user_new_day 中的指定日分区的数据导出到mysql 的

dim_user_new_day
#!/bin/bash
day=`date -d '-1 day' +'%Y-%m-%d'`/root/apps/sqoop/bin/sqoop export \
--connect "jdbc:mysql://hdp-04:3306/app?useUnicode=true&characterEncoding=utf-8" \
--username root \
--password root \
--input-fields-terminated-by '\001' \
--table dim_user_new_day \
--export-dir /user/hive/warehouse/app.db/dim_user_new_day_1p/day=${day} /

hadoop系列十八——案例 App数据统计与报表统计相关推荐

  1. Reflex WMS入门系列十八:如何看年终盘点的Inventory report数据?

    Reflex WMS入门系列十八:如何看年终盘点的Inventory report数据? Reflex WMS系统中的盘点分为cycle count(循环盘点)和physical inventory( ...

  2. 学习ASP.NET Core Razor 编程系列十八——并发解决方案

    原文:学习ASP.NET Core Razor 编程系列十八--并发解决方案 学习ASP.NET Core Razor 编程系列目录 学习ASP.NET Core Razor 编程系列一 学习ASP. ...

  3. Java学习系列(十八)Java面向对象之基于UDP协议的网络通信

    UDP协议:无需建立虚拟链路,协议是不可靠的. A节点以DatagramSocket发送数据包,数据报携带数据,数据报上还有目的目地地址,大部分情况下,数据报可以抵达:但有些情况下,数据报可能会丢失 ...

  4. Android产品研发(八)--App数据统计

    转载请标明出处:一片枫叶的专栏 上一篇文章中我们介绍了Android社区中比较火的热修复功能,并介绍了目前的几个比较流行的热修复框架,以及各自的优缺点,同时也介绍了一下自身项目中对热修复功能的实践.目 ...

  5. Java调用大数据接口,学习Hadoop第二十八课(java通过调用接口来操作HBase)

    上节课我们一起简单学习了HBase的一些理论,这节课我们一起学习用java调HBase的接口来操作HBase. 我们首先建一个工程,这里我们还用原始的新建一个lib包.然后我们把下载的hbase-0. ...

  6. 十八、Matplotlib数据可视化

    @Author:Runsen @Date:2019年05月13日 作者介绍:Runsen目前大三下学期,专业化学工程与工艺,大学沉迷日语,Python, Java和一系列数据分析软件.导致翘课严重,专 ...

  7. Hadoop系列之二:大数据、大数据处理模型及MapReduce

    1.大数据(big data) 什么是大数据?wikipedia上面给出了这样的定义: In information technology, big data is a collection of d ...

  8. 电气器件系列十八:电能表

    电能表是用来测量电能的仪表,又称电度表,火表,千瓦小时表,指测量各种电学量的仪表. 使用电能表时要注意,在低电压(不超过500伏)和小电流(几十安)的情况下,电能表可直接接入电路进行测量.在高电压或大 ...

  9. Python工具箱系列(十八)

    非对称加解密应用广泛,它的存在是致力于解决密钥通过公共信道传输这一经典难题.对称加密有一个天然的缺点,就是加密方和解密方都要持有同样的密钥,而这个密钥在传递过程中有可能会被截获,从而使加解密失效.难不 ...

最新文章

  1. Git简介以及与SVN的区别
  2. Python入门学习方法有哪些?
  3. 2 原生Zookeeper 实现分布式锁
  4. string.Format 指定字符串宽度
  5. android 媒体库扫描,如何扫描出Android系统媒体库中视频文件
  6. jquery ajax xml attribute,获得jQuery ajax和asp.net webmethod xml响应工作
  7. 大数据分析必须要会的python函数操作!!!
  8. bundle中vim相关快捷键的使用
  9. SAP License:GB01中替代字段释放
  10. 计量分析类论文如何创新-化柏林
  11. win11正式版iso镜像如何安装 windows11正式版iso镜像安装方法
  12. 21日请假一天陪妈妈去国博
  13. 【Python】包管理工具pip
  14. 新中大如何修改服务器地址,新中大GE10.0安装配置手册
  15. CryptoJS加密使用
  16. C语言实现推箱子游戏
  17. 安装mediawiki维基百科
  18. js事件冒泡与事件捕获、阻止事件冒泡和浏览器默认行为
  19. 微信小程序 live-player 无声音
  20. 程序员怒怼产品经理最新表情包,叫我改Bug这辈子是不可能的

热门文章

  1. ssm校园失物招领系统毕业设计源码080008
  2. P7选修 深度学习介绍
  3. 百度正式推出外链工具beta版本
  4. 用计算机录音并播放教学设计,声音的采集与处理(教学设计)
  5. 扩散模型与生成模型详解
  6. 用html写除法竖式代码,模拟竖式除法
  7. Python之解决”千年虫“问题篇
  8. 《大型网站技术架构》——第三章 大型网站核心架构要素
  9. 程序员手把手教你Mac M1Pro的java开发环境安装--jdk、git、maven、及tomcat
  10. 【AI 简报20201018期】英伟达开源「Imaginaire」、智能手表争夺战是如何打响的?