统计影音视频网站的常规指标,各种TopN指标

Hive大数据分析

作者:陶运道

概述:大数据特点是数据量大,数据为半结构化以及完全非结构化数据类型,在应用之前要对数据进行清洗规整后,存放在数据仓库中。以供应用。

本文通过实例项目,详细讨论了数据清洗过程、清洗后数据以hive表形式存放在hdfs中、以及如何应用数据。

    本项目已通过实验验证了项目可行性。

关键字:清洗,wordMap类,TOPN指标

1项目要求

统计影音视频网站的常规指标,各种TopN指标:(热搜)

(1)统计视频观看数Top10

(2)统计视频类别热度Top10

(3)统计视频观看数Top20所属类别

(4)统计视频观看数Top50所关联视频的所属类别Rank

(5)统计每个类别中的视频热度Top10

(6)统计每个类别中视频流量Top10

(7)统计上传视频最多的用户Top10以及他们上传的视频

(8)统计每个类别视频观看数Top10

2实验数据分析

2.1实验数据分析

 项目数据在 video、user目录中,如图所示,在目录video有五个数据文件0.txt~4.txt。

为了便于研究0.txt等文件结构,将文件导入到EXCEL。数据文件格式如图1所示。

图1  数据文件格式

文件每行由多列组成,每列之间用‘\t’隔开,第4列中间有空格。每行列数不同。对这样数据,无法处理。

2.2 数据清洗规则

为了处理数据,首先对数据进行重组清洗操作。

处理原则:(1)每行9列或10列,列与列之间用’\t’分隔。

         (2)多于10列将第10列及后面列并成一列,并以”&”分隔。

         (3)第4列由于中间有空隔,去空格。

 清洗规整后每行数据典型格式

图2  清洗后的数据格式

2.3 hive表结构设计

为了方便数据应用,创建hive表。

表1 视频表结构

字段

备注

详细描述

videoid

视频唯一id(String)

11位字符串

uploader

视频上传者(String)

上传视频的用户名String

age

视频年龄(int)

视频在平台上的整数天

category

视频类别(Array<String>)

上传视频指定的视频分类

length

视频长度(Int)

整形数字标识的视频长度

views

观看次数(Int)

视频被浏览的次数

rate

视频评分(Double)

满分5分

Ratings

流量(Int)

视频的流量,整型数字

conments

评论数(Int)

一个视频的整数评论数

related ids

相关视频id(Array<String>)

相关视频的id,最多20个

表2 用户表结构

字段

备注

字段类型

uploader

上传者用户名

string

videos

上传视频数

int

friends

朋友数量

int

3建立java项目完成对数据清洗

为了实现数据清洗,利用IDEA创建maven项目。

3.1建立maven:JAVA项目

(1)新建项目

 File-New-Project-Java-Maven-create

图4 创建项目向导

(2)配置maven依赖

 File-Setting-Build,Execution,Deployment-Build Tools-Maven

                    图5 配置maven依赖向导

 按实际配置三项

Maven home path:    maven安装路径

User setting file:  settings.xml文件路径(一般在maven安装路径的conf目录中)

Local repository:  本地仓库目录(由用户在settings.xml设置)            

(3)配置 pom.xml

将以下内容复制到pom.xml中。

<!--依赖管理-->
<dependencies>
    <dependency>
        <groupId>
org.apache.hadoop</groupId>
        <artifactId>
hadoop-common</artifactId>
        <version>
3.2.3</version>
    </dependency>
   
<!-- https://mvnrepository.com/artifact/org.apache.hadoop/hadoop-hdfs -->
   
<dependency>
        <groupId>
org.apache.hadoop</groupId>
        <artifactId>
hadoop-hdfs</artifactId>
        <version>
3.2.3</version>
    </dependency>
    <dependency>
        <groupId>
org.apache.maven.plugins</groupId>
        <artifactId>
maven-install-plugin</artifactId>
        <version>
3.1.0</version>
    </dependency>
    <dependency>
        <groupId>
org.apache.hadoop</groupId>
        <artifactId>
hadoop-mapreduce-client-core</artifactId>
        <version>
3.2.3</version>
    </dependency>
    <dependency>
        <groupId>
org.apache.hadoop</groupId>
        <artifactId>
hadoop-client</artifactId>
        <version>
3.2.3</version>
    </dependency>
    <dependency>
        <groupId>
org.testng</groupId>
        <artifactId>
testng</artifactId>
        <version>
7.8.0</version>
        <scope>
compile</scope>
    </dependency>
</dependencies>

3.2 新建两个类Main、WordMapper,如图所示

                            图6  建立类图示

  1. WordMapper类程序

import java.io.IOException;

import org.apache.hadoop.io.NullWritable;

import org.apache.hadoop.io.Text;

import org.apache.hadoop.mapreduce.Mapper;

//mapper进程,每一个splitblock)会启动该类,

public class WordMapper extends Mapper<Object, Text, Text, NullWritable> {

    Text text = new Text();

    @Override

  // map()处理行 由上下文机制传 一行数据,格式键值对<key(行号)Text(一行文本)>

  protected void map(Object key, Text value, Context context) throws IOException, InterruptedException {

        String ori=value.toString(); //取一行

        StringBuilder etlString1 = new StringBuilder(); //创建字符串变量

        String[] splits = ori.split("\t"); //将行数据"\t"分割,每个部分数据形成数组splits

        if(splits.length < 9) return ;   //数组长度小于9返回,不处理本行 (每行数组长度>=9)

        splits[3] = splits[3].replace(" ", "");   //3列去除空格

        /*下面将字符串数组整合一个字符串整合规则:

splits[0]”\t” splits[1] ”\t”…… ”\t”splits[8]                           splits数组长度=9

splits[0]”\t” splits[1] ”\t”…… ”\t”splits[8]& splits[9]& splits[9] splits数组长度>9

*/

        for(int i = 0; i < splits.length; i++) {     //数组元素以一定分隔形式形成字符串。

            if(i < 9){  //处理小于9列(0-8列)

                if(i == splits.length - 1){ //如果只有9列有数据,将最后一列写入

                     etlString1.append(splits[i]);

                }else{

                    etlString1.append(splits[i] + "\t");

                }

            }else{

                if(i == splits.length - 1){

                    etlString1.append(splits[i]); //写入最后一列数据

                }else{

                    etlString1.append(splits[i] + "&");//不是最后一列加&写入

                }

            }

        }

        String  etlString= etlString1.toString();

       // if(etlString.isEmpty()) return;  //如果字符串是空串,返回,不处理

        text.set(etlString);  //设置对象text

        context.write(text,NullWritable.get());

    }

}

(2)Main类程序

import java.io.IOException;
import java.net.URI;
import java.net.URISyntaxException;
import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.fs.*;
import org.apache.hadoop.fs.Path;
import org.apache.hadoop.io.IntWritable;
import org.apache.hadoop.io.NullWritable;
import org.apache.hadoop.mapreduce.Job;
import org.apache.hadoop.io.Text;
import org.apache.hadoop.mapreduce.lib.input.FileInputFormat;
import org.apache.hadoop.mapreduce.lib.output.FileOutputFormat;
public class Main {
    public static   void main(String[] args) throws IOException, URISyntaxException, InterruptedException, ClassNotFoundException,Exception
      {  Configuration conf = new Configuration();
         conf.set("fs.defaultFS","hdfs://192.168.100.100:9000");
          Job job = Job.getInstance(conf, "word count");  //
创建job将文件以行为单位进行处理
           job.setJarByClass(Main.class);   //设置job主类
           job.setMapperClass(WordMapper.class);  //设置job mapper
           job.setMapOutputValueClass(NullWritable.class);
           job.setMapOutputKeyClass(Text.class);
           job.setNumReduceTasks(0); //
设置源目录下多个文件
           FileInputFormat.addInputPaths(job, "/video");  //原文件路径

//输出路径,注意运行前在/video下不能有out文件夹。
           FileOutputFormat.setOutputPath(job, new Path("/kk/output")); 

System.exit(job.waitForCompletion(true) ? 0 : 1);//结果本次任务
        }
}

(3)程序打包运行

按图操作,项目加入依赖向导,File-Project structure

图7 加入依赖

选择主类向导

图8 加入主类

打包向导

                           图9  打包

3.3.运行程序

    # hadoop fs –put /home/video   /   //将数据文件发至hdfs上

# hadoop fs –mkdir  /kk    // 创建hdfs输出目录

#hadoop fs –put /home/user.txt  /kk    //将数据文件发至hdfs上

    将jar包文件复制到 本地/home  并改名为1122.jar 

# cd /home

# hadoop jar 1122.jar    //运行程序

#hadoop fs –ls  /kk/output   //查看文件

                   图10  程序运行后数据文件

注意:数据清洗也可以用 spark 的RDD编程实现

代码如下:代码简单

运行spark-shell

#spark-shell

Scala> import java.lang
val f=sc.textFile("/video/*.txt")

scala>val f1 = f.map(x => {

         val etlString1 = new lang.StringBuilder 

         val splits =x.split("\t")

         //处理列数>=9列

         if(splits.length<=8) etlString1.append("aaaaa")

          if(splits.length>8)

            {  splits(3) = splits(3).replace(" ", "")  //去第4列空格

               for (i <- 0 until splits.length) {

                     if (i < 9) {  if (i == splits.length - 1)  etlString1.append(splits(i))

                               else etlString1.append(splits(i) + "\t")

                             }

               else if (i == splits.length - 1) etlString1.append(splits(i))

               else etlString1.append(splits(i) + "&")   }

              }

         etlString1  //map()将一行数据变成标准形式字符串

       } )

val f2=f1.filter(x=>x.toString != "aaaaa")   //过滤

f2.saveAsTextFile("/aaa")

导入hive表后总共743569行数据

注意:(1)在处理过程由于去空格语句位置不对,出现错误,最后想到,也可能splits数组元素不足3,所造成错误。

(2)在读取多个文件时出错:Caused by: org.apache.spark.SparkException: Kryo serialization failed: Buffer overflow. Available: 0, required: 88. To avoid this, increase spark.kryoserializer.buffer.max value.

解决方法:编辑spark下conf/ spark-defaults.conf  增加一行

spark.kryoserializer.buffer.max=128m     //默认为64m   也可以再增加点

(3)val f2=f1.filter(x=>x.toString != "aaaaa") 语句中类型转换

(4)etlString1一定要定义为lang.StringBuilder,可随时将字符串插入该对象末尾

   如果使用字符串变量,是没办法将串插入到变量中

4创建表并导入数据

4.1创建表

创建test数据库

创建表:gulivideo_ori,gulivideo_user_ori,

创建表:gulivideo_orc,gulivideo_user_orc

gulivideo_ori:

create table gulivideo_ori(

    videoId string,

    uploader string,

    age int,

category array<string>,

    length int,

    views int,

    rate float,

    ratings int,

    comments int,

relatedId array<string>)

row format delimited fields terminated by "\t"

collection items terminated by "&"

stored as textfile;

gulivideo_user_ori

create table gulivideo_user_ori(

    uploader string,

    videos int,

    friends int)

row format delimited

fields terminated by "\t"

stored as textfile;

把原始数据插入到orc表中

gulivideo_orc

create table gulivideo_orc(

    videoId string,

    uploader string,

    age int,

    category array<string>,

    length int,

    views int,

    rate float,

    ratings int,

    comments int,

    relatedId array<string>)

row format delimited fields terminated by "\t"

collection items terminated by "&"

stored as orc;

gulivideo_user_orc

create table gulivideo_user_orc(

    uploader string,

    videos int,

    friends int)

row format delimited

fields terminated by "\t"

stored as orc;

4.2导入ETL后的数据

gulivideo_ori

load data inpath "/kk/output" into table gulivideo_ori;

gulivideo_user_ori

load data inpath "/kk/user.txt" into table gulivideo_user_ori;

4.3ORC表插入数据

gulivideo_orc

insert into table gulivideo_orc select * from gulivideo_ori;

gulivideo_user_orc

insert into table gulivideo_user_orc select * from gulivideo_user_ori;

5 业务分析

5.1统计视频观看数Top10

思路:使用order by按照views字段做一个全局排序即可,同时我们设置只显示前10条。

最终代码:

select videoId,views  from  gulivideo_ori  order by views desc  limit 10

5.2统计视频类别热度Top10

类别category字段值包含多个视频 ,统计每个视频最多前10个类别

1) 一个videoId对应一个category字段。一个category字段值对应多个视频,需要先将category字段进行列转行(展开)。

2) 按照视频名称分组,依videoId统计个数。

3) 最后按照热度(点击次数即统计每个视频的videid之和)排序,显示前10条。

关键要理解以下函数:

lateral view explode(category) t_catetory  as category_name

将一行category字段值变成多行

其中:转换虚拟表名t_catetory

例如 有一行:

   videoid          category

1xbSFrHzFQ0      ["Film","Animation"]

变成

videoid      cate

1xbSFrHzFQ0 Film

1xbSFrHzFQ0 Animation

查询代码:

select  category_name as category, count(t1.videoId) as hot

from ( select  videoId,  category_name

    from gulivideo_orc lateral view explode(category) t_catetory  as category_name) t1

group by t1.category_name  order by  hot  desc limit  10;

5.3统计出视频观看数最高的20个视频的所属类别以及类别包含Top20视频的个数

1) 先找到观看数最高的20个视频所属条目的所有信息,降序排列

2) 把这20条信息中的category分裂出来(列转行)

3) 最后查询视频分类名称和该分类下有多少个Top20的视频

最终代码:

SELECT cate category, COUNT(videoid) hot  FROM

(SELECT videoid,cate  FROM

(SELECT videoid, category, views  FROM test.gulivideo_orc

ORDER BY   views DESC   limit 20)t1

Lateral view explode(category) tb1 as cate)t2

GROUP BY  cate  ORDER BY  hot desc;

注解 1. 找出观看视频数前20名的记录集(包含字段,videoid, category, views)

SELECT videoid, category, views  FROM  gulivideo_orc

ORDER BY   views  DESC   limit 20 

查询结果:

videoid     category    views

dMH0bHeiRNg ["Comedy"]   42513417

0XxI-hvPRRA ["Comedy"]   20282464

2.再将category字段拆分成行记录集 (字段包括videoid, cate(视频名称) )

SELECT videoid, cate FROM

(SELECT  videoid, category, views  FROM  gulivideo_orc

注意:查询时要有t1  别名

ORDER BY  views DESC  limit 20)t1

Lateral view explode(category) tb1 as cate

videoid        cate

dMH0bHeiRNg     Comedy

0XxI-hvPRRA     Comedy

1dmVU08zVpA     Entertainment

5.4统计视频观看数Top50所关联视频所属类别排序

1.查询观看数前50视频信息

SELECT    videoid,  views,  relatedid  FROM   gulivideo_orc

             ORDER BY views DESC LIMIT 50) :t1

2.将t1字段relatedid展开(一行变多行),并去重

SELECT  DISTINCT   (relatedid_name)

FROM  t1 lateral VIEW explode (relatedid) table_temp AS relatedid_name; t2

 3.t2为视频观看top50所关联视频videoid字段表,将该表与原表inner join得到关联视频展开relatedid字段表。

SELECT   category,relatedid   FROM  gulivideo_orc t3

   INNER JOIN t2   ON t3.videoid = t2.relatedid_name; t4

 4.将上述表中category字段行转列得到最终的展开表。

SELECT   category_name  FROM

  t4 lateral VIEW explode (category) table_temp AS category_name; t5

5. 对最终展开表的category_name字段group by之后再count一下即可得到每个类别的总数最后排序即可。

SELECT    category_name,    COUNT(*) hot

  FROM   t5   GROUP BY t5.category_name   ORDER BY hot DESC ;

最终代码

SELECT  category_name,hot

FROM  (SELECT  category_name,  COUNT(*) hot

  FROM   (SELECT    category_name

    FROM  (SELECT   category,relatedid

      FROM    gulivideo_orc t3

        INNER JOIN

          (SELECT    DISTINCT(relatedid_name)

          FROM (SELECT    videoid,views,relatedid

            FROM     gulivideo_orc

             ORDER BY views DESC LIMIT 50) t1 lateral VIEW explode (relatedid) table_temp AS relatedid_name) t2

          ON t3.videoid = t2.relatedid_name) t4 lateral VIEW explode (category) table_temp AS category_name) t5    GROUP BY t5.category_name) t6  ORDER BY hot DESC;

5.5统计每个类别中的视频热度Top10,以Music为例

思路:

1) 要想统计Music类别中的视频热度Top10,需要先找到Music类别,那么就需要将category展开,所以可以创建一张表用于存放categoryId展开的数据。

2) 向category展开的表中插入数据。

3) 统计对应类别(Music)中的视频热度。

创建表类别表:

create table video_category( videoId string, uploader string, age int,

    cate string, length int, views int, rate float, ratings int,

    comments int, relatedId array<string>)

row format delimited fields terminated by "\t"

collection items terminated by "&"

stored as orc;

向类别表中插入数据

insert into table video_category 

select videoid,uploader,age,cate,length,views,rate,ratings,comments, relatedId

from gulivideo_orc lateral view explode(category) tb1 as cate;

统计Music类别的Top10(也可以统计其他)

SELECT videoId, views FROM video_category

WHERE cate = "Music"

ORDER BY views DESC limit 10;

5.6统计每个类别中视频流量Top10,以Music为例

思路:

1) 创建视频类别展开表(categoryId列转行后的表)

2) 按照ratings排序即可

最终代码:

SELECT videoid,ratings FROM  video_category

WHERE cate="Music"

ORDER  BY ratings desc limit 10;

5.7统计上传视频最多的用户Top10以及他们上传的观看次数在前20的视频

思路:

1)先找到上传视频最多的10个用户的用户信息

Select * from gulivideo_user_orc  order by videos desc limit 10;t1 

2) 通过uploader字段与gulivideo_orc表进行join,得到的信息按照views观看次数进行排序即可。

最终代码

SELECT t1.uploader, t2.videoid,  t2.views, t2.ratings

FROM( SELECT * FROM gulivideo_user_orc ORDER BY videos DESC  limit 10)t1

JOIN gulivideo_orc  t2  ON t1.uploader = t2.uploader

ORDER BY views DESC limit  20;

5.8统计每个类别视频观看数Top10

1)categoryId展开表video_category

2)分组排序  

排序函数用法 rank()  over(partition by A order by B)  按A分组,每组按B排序 ,名次并列。

SELECT cate, videoid, views,

RANK() OVER(PARTITION BY cate   ORDER BY  views  DESC) hot   FROM   video_category;t1

3)查询rank值小于等于10的数据行即可

SELECT cate,   videoid,  views   FROM t1    WHERE hot <= 10;

查询代码:

SELECT cate,videoid,views

FROM (SELECT cate, videoid,views,

RANK() OVER(PARTITION BY cate ORDER BY views DESC) hot

FROM video_category)t1    WHERE hot <= 10;

  • 25
    点赞
  • 21
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值