大数据之spark_流量统计案例

数据

用户id,开始时间,结束时间,所用流量
1,2020-02-18 14:20:30,2020-02-18 14:46:30,20
1,2020-02-18 14:47:20,2020-02-18 15:20:30,30
1,2020-02-18 15:37:23,2020-02-18 16:05:26,40
1,2020-02-18 16:06:27,2020-02-18 17:20:49,50
1,2020-02-18 17:21:50,2020-02-18 18:03:27,60
2,2020-02-18 14:18:24,2020-02-18 15:01:40,20
2,2020-02-18 15:20:49,2020-02-18 15:30:24,30
2,2020-02-18 16:01:23,2020-02-18 16:40:32,40
2,2020-02-18 16:44:56,2020-02-18 17:40:52,50
3,2020-02-18 14:39:58,2020-02-18 15:35:53,20
3,2020-02-18 15:36:39,2020-02-18 15:24:54,30

需求

求每个用户每个阶段用的总流量,并显示该阶段的开始时间和结束时间,阶段划分规则为:该次登录时间减去上去结束时间超过10分钟,如果超过10分钟就判定为下一个阶段,没超过就所属当前阶段

SQL思想

在这里插入图片描述
写sql时将先将结束时间往下压一行,然后就可以使用当前行的开始时间减结束时间,判断是否大于10分钟,大于就给1,小于就给个0,然后调用sum over()将当前行的0或1往上一路叠加,然后就能将每个阶段分开,从而分组聚合

RDD代码实现

package com.doit.spark.day01

import java.text.SimpleDateFormat
import java.util.logging.SimpleFormatter
import org.apache.spark.{SparkConf, SparkContext}
import org.apache.spark.rdd.RDD
import scala.collection.mutable

object FlowStatistics {
  def main(args: Array[String]): Unit = {
    //传入是否在本地运行
    val setMaster = args(0).toBoolean
    //传入求的每个学科的受欢迎的老师的数量
    val conf: SparkConf = new SparkConf().setAppName("FlowStatistics")
    //判断是否在本地运行
    if (setMaster){
      conf.setMaster("local[*]")
    }
    val sc = new SparkContext(conf)
    //传入文件路径
    val filePath = args(1)
    val line: RDD[String] = sc.textFile(filePath)
    val userFlow: RDD[(String, String, String, String)] = line.map(x => {
      val arr: Array[String] = x.split(",")
      val uid: String = arr(0)
      val startTime: String = arr(1)
      val endTime: String = arr(2)
      val flow: String = arr(3)
      (uid, startTime, endTime, flow)
    })
    //对用户分组之后,调用flatMapValues对迭代器value处理后再对它压平
    val value: RDD[(String, (String, String, String, Int, Int))] = userFlow.groupBy(_._1).flatMapValues((it: Iterable[(String, String, String, String)]) => {
      val sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss")
      //定义一个阶段标识,初始值为0
      var index = 0
      //定义一个时间的中间值,起始值为null
      var timeTemp: String = null
      //立一个flag,判断是否为该组内的第一条数据
      var flag: Boolean = true
      //用map变量组内数据
      it.map(flow => {
        //判断是否为第一条数据
        if (flag) {
          //是就将flag = false
          flag = false
          //并当前的结束时间赋给中间值
          timeTemp = flow._3
        } else {
          //不是第一条数据,就该条数据的登录时间和上条数据的结束时间(中间值)是否超过十分钟
          val startTime: Long = sdf.parse(flow._2).getTime
          val endTime: Long = sdf.parse(timeTemp).getTime
          val l: Long = (startTime -endTime) / 1000 / 60
          if (l > 10) {
            //是,就给阶段标识加1
            index += 1
          }
          //处理完之后,也要将当前的结束时间赋给中间值
          timeTemp = flow._3
        }
        //返回:用户,开始时间,结束时间,流量,分组标识
        (flow._1, flow._2, flow._3, flow._4.toInt, index)
      })
    })
    //调用reduceByKey获取当前阶段内起始时间的和结束时间并加总阶段流量
    //因为reduceByKey要求输入类型和输出类型相同,所以需要用map转换,并注意将用户和阶段标识统一作为key
    val maped: RDD[((String, Int), (String, String, Int))] = value.map(x => ((x._1, x._2._5), (x._2._2, x._2._3, x._2._4)))
    val reduceByKeyed: RDD[((String, Int), (String, String, Int))] = maped.reduceByKey((v1, v2) => {
      (Ordering[String].min(v1._1, v2._1), Ordering[String].max(v1._2, v2._2), v1._3 + v2._3)
    }).sortByKey()
    println(reduceByKeyed.collect().toBuffer)
  }
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值