Spark案例

 求连续登陆天数


object Demo1{
  //练习登陆天数
  //guid01,2018-02-28
  //guid01,2018-03-01
  //guid01,2018-03-02
  //guid01,2018-03-04
  //guid01,2018-03-05
  //guid01,2018-03-06
  //guid01,2018-03-07
  //guid02,2018-03-01
  //guid02,2018-03-02
  //guid02,2018-03-03
  //guid02,2018-03-06
  def main(args: Array[String]): Unit = {
    val sc: SparkContext = new SparkContext(new SparkConf().setAppName("LoginNum").setMaster("local[*]"))
    val fileRDD: RDD[String] = sc.textFile("data1.txt")
    val value = fileRDD.map(str => {
      val strings = str.split(",")
      val name: String = strings(0)
      val date: String = strings(1)
      val time: LocalDate = LocalDate.parse(date)
      (name,date)
    })

    //1.对数据进行处理,取出有用数据
    val groupL = value.groupByKey()//根据uid进行分组,将共同uid的数据进行分组
    //然后将值压平,获得一个(uid,(登记时间,基准时间))的数据
    val resultMap: RDD[(String, (String, String))] = groupL.flatMapValues(s => {
      val sorted: List[String] = s.toList.sorted //将时间变成list,并且排序默认
      val format = new SimpleDateFormat("yyyy-MM-dd")
      var index = 0
      //定义一个第增值
      //对list中的数据进行操作,将其转换为//guid01,访问时间,基准时间,以便后面对基准时间进行统计
      val tuples = sorted.map(ele => {
        val date: Date = format.parse(ele)
        //将string类型转为date
        val calendar: Calendar = Calendar.getInstance()
        calendar.setTime(date) //设置时间
        calendar.add(Calendar.DATE, -index) //递增减一
        index += 1 //加一
        //访问时间,基准时间
        (ele, format.format(calendar.getTime))
      })
      tuples
    })
    //  姓名  itrator(string,string)
    //然后根据姓名,和时间进行分组
    //将数据进行处理,划分成((uid,基准时间),登陆时间)),这样就可以根据基准时间+uid进行分区,将相同的uid和相同的基准时间的值放到同一个迭代器中,获得一个(uid,基准时间),itrator
    //接着mapVlues,获得((uid,基准时间),(itratorsize,head,last))的数据,然后过滤size<3的,map最后整理成最后的格式
    val timeAndNum = resultMap.map(t => {
      ((t._1, t._2._2), t._2._1)
      //(uid,基准时间)   登记时间
    }).groupByKey().mapValues(it => {
      //对时间进行统计   这时候数据是   ((uid,基准时间),itrator("多个登记时间")
      //要统计出来每个uid的同一个基准时间下,登记时间的数量
      var loginSize = it.size
      //开始时间,结束时间
      val last = it.last
      val first = it.head
      (loginSize, first, last)
    }).filter(f=>f._2._1>=3)
        .map(f=>{
          (f._1._1,f._2._1,f._2._2,f._2._3)
        })
    println(timeAndNum.collect().toBuffer)

  }
}

计算每个页面下的子页面访问TopN 

方法一

    //http://分页.51doit.cn/子页
  //统计出每个分页下,访问最多的子页面
  def main(args: Array[String]): Unit = {
    val sc: SparkContext = new SparkContext(new SparkConf().setAppName("最受欢迎老师").setMaster("local[*]"))
    val info = sc.textFile("teache.log")
    val teacheToOne: RDD[((String, String), Int)] = info.map(f => {
      val info = f.split("/")
      val subject = info(2).split("\\.")(0)
      val name = info(3)
      ((subject, name), 1)
    })
    val reduce = teacheToOne.reduceByKey(_+_).map(f=>{
      (f._1._1,(f._1._2,f._2))
    }).sortBy(t=>t._2._2).groupByKey().mapValues(f=>{f.take(2)})
    println(reduce.collect().toBuffer)
  }

方法2

 def main(args: Array[String]): Unit = {
    val sc: SparkContext = new SparkContext(new SparkConf().setAppName("最受欢迎老师").setMaster("local[*]"))
    val info = sc.textFile("teache.log")
    val teacheToOne: RDD[((String, String), Int)] = info.map(f => {
      val info = f.split("/")
      val subject = info(2).split("\\.")(0)
      val name = info(3)
      ((subject, name), 1)
    })
    val reduce = teacheToOne.reduceByKey(_+_).map(f=>{(f._1._1,(f._1._2,f._2))}).groupByKey().flatMapValues(f=>{f.toList.sortBy(g=>{g._2}).take(2).iterator})
    println(reduce.collect().toBuffer)

电影统计案例

package com.dt.spark.cores

import org.apache.log4j.{Level, Logger}
import org.apache.spark.broadcast.Broadcast
import org.apache.spark.rdd.RDD
import org.apache.spark.{SparkConf, SparkContext}

import scala.collection.mutable

//统计同一用户id评论的前20条电影
object Movie_User_Analyzer_RDD {


  要想从多个数据源获取信息,可以将多个数据处理成(主键,信息)类型的信息,通过join来连接查询
  def main(args: Array[String]): Unit = {

    //设置日志打印界别
    Logger.getLogger("org").setLevel(Level.ERROR)
    //默认本地运行模式
    var masterUrl="local[*]"
    //数据存储目录
    var dataPath="K:\\WORKSOURCE\\mrdata\\movie\\"

    if (args.length>0){
      masterUrl=args(0)//0参数指定master的url
    }else if  (args.length>1){
      dataPath=args(1)//指定数据地址
    }

    /**
      * 创建sc
      */
    val sc = new SparkContext(new SparkConf().setMaster(masterUrl).setAppName("Movie_User_Analyzer_RDD"))
    //读取数据
    val moviesRDD = sc.textFile(dataPath+"movies.dat")
    val occupationsRDD = sc.textFile(dataPath+"occupations.dat")
    val ratingsRDD = sc.textFile(dataPath+"ratings.dat")
    val usersRDD = sc.textFile(dataPath+"users.dat")

    /**
      * 分析某部电影的用户观看信息 
      * 
      * 1::F::1::10::48067
      * 
      * 
      */
    val userBasic: RDD[(String, (String, String, String))] = usersRDD.map(_.split("::")).map(user => {
      (user(3),( user(0), user(1), user(2)))
    })
    //形成以用户职业id作为key,其他信息作为values的元组信息
    userBasic
    //将数据进行切分,然后直接放入tuple4中

    for (elem <- userBasic.collect().take(2)) {
      println("userBasic  ----(职业id,(用户id,性别,年龄))"+elem)
    }
    //切分职业信息(职业id,职业信息)
    val jobBasic: RDD[(String, String)] = occupationsRDD.map(_.split("::")).map(job => {
      (job(0),job(1))
    })
    //将其切割成
    for (elem <- jobBasic.collect().take(2)) {
      println("jobBasic  ----职业id,职业信息)"+elem)
    }


    //找到1193的电影
    val targetMovie: RDD[(String, String)] = ratingsRDD.map(_.split("::")).map(ratings => {
      //返回  用户id,电影
      (ratings(0), ratings(1))
    }).filter(_._2.equals("1193"))
    //将其切割成
    for (elem <- targetMovie.collect().take(2)) {
      println("targetMovie  ----用户id,电影id)"+elem)
    }
    //  用户和职业关联
    //职业id,(用户id,性别,年龄),职业名称
    val userInfomationRDD: RDD[(String, ((String, String, String), String))] = userBasic.join(jobBasic)
    userInfomationRDD.cache()
    for (elem <- userInfomationRDD.collect().take(2)) {
      println("userInfomationRDD  ----职业id,(用户id,性别,年龄),职业名称"+elem)
    }
    // 用户id,((用户id,性别,年龄),职业名称)
    val targetUser: RDD[(String, ((String, String, String), String))] = userInfomationRDD.map(userinfo => {
      (userinfo._2._1._1, userinfo._2)
    })

//    for (elem <- targetUser.collect().take(2)) {
//      println("targetUser  ----用户id((用户id,性别,年龄),职业名称)"+elem)
//    }

    println("统计出用户ID为1193用户的用户信息,  用户id,性别,用户年龄,职业")
    val userInfoMation: RDD[(String, (String, ((String, String, String), String)))] = targetMovie.join(targetUser)
    val userAllInfo = userInfoMation.map(f => {
      (f._1, f._2._1, f._2._2._1._2, f._2._2._1._3, f._2._2._2)
    })
    println("电影id,用户id,性别,年龄,职业")
//    for (elem <- userAllInfo.collect().take(10)) {
//      println(elem)
//    }

    // TODO 统计出最受欢迎的电影和TOPN
    //1.取出   用户id,电影id,评分
    val ratings = ratingsRDD.map(_.split("::")).map(movi => {
      (movi(0), movi(1), movi(2))
    }).cache()
    //将电影作为key,(评分,次数1)

    val favoriteMovie = ratings.map(rat => {
      (rat._2, (rat._3.toDouble, 1))
    })
      .reduceByKey((x, y) => {
        (x._1 + y._1, x._2 + y._2)
      }) //将根据key进行分组的value(评分,次数)相加,获得   电影id,(总评分,总次数)
      .map(movie => {
      (movie._1, movie._2._1 / movie._2._2)
    }) //获得   电影,平均分
      .map(motrate => {
      (motrate._2, motrate._1)
    }).sortByKey(false) //获得降序,根据评分获得
      //.collect().take(10).toBuffer
    println("统计出最受欢迎的电影和TOP20")
   // println(favoriteMovie)
//   TODO 观看人数最多的电影
    val morestLooked = ratingsRDD.map(rate => {
      rate.split("::")
    }).map(info => {
      (info(1), 1)
    }) //获得评分次数
      .reduceByKey(_ + _).map(f => {
      (f._2, f._1)
    }).sortByKey(false)
    //println("找出观看次数最多的电影TOP10")
   // println(morestLooked.collect().take(10).toBuffer)

    // TODO 最受男性欢迎和女性欢迎的电影

    val male="M"
    val female="F"
    val genderRatings: RDD[(String, ((String, String, String), String))] = ratings.map(f => {
      (f._1, (f._1, f._2, f._3))
    }) //获得(用户id,(用户id,电影id,评分))
      .join(usersRDD.map(_.split("::")).map(user => {
      (user(0), user(1))
    }))
      //join用户id,用户性别,获得  uid,((用户id,(用户id,电影id,评分),性别))
      .cache()
    //genderRatings.take(2).foreach(println)
    val Mgrating: RDD[(String, String, String)] = genderRatings.filter(u => {
      u._2._2.equals("M")
    }) //筛选出M的
      .map(in => {
      (in._2._1)
    })
    Mgrating   //获得m的   (用户id,电影id,评分)  在此基础上筛选出虽受欢迎的电影

    val Fgrating: RDD[(String, String, String)] = genderRatings.filter(u => {
      u._2._2.equals("F")
    }) //筛选出F的
      .map(in => {
      in._2._1
    })
    Fgrating  //获得m的   (用户id,电影id,评分)  在此基础上筛选出虽受欢迎的电影
    val MFavertite = Mgrating.map(inf => {
      (inf._2, 1)
    }).reduceByKey(_ + _).map(fin => {
      (fin._2, fin._1)
    })
      .sortByKey(false).map(f=>{(f._2,f._1)})
//      .take(10)
//    println("男性最喜欢的电影的Top 10")
//    println(MFavertite.toBuffer)
    val FFavertite = Fgrating.map(inf => {
      (inf._2, 1)
    }).reduceByKey(_ + _).map(fin => {
      (fin._2, fin._1)
    })
      .sortByKey(false).map(f=>{(f._2,f._1)})
//      .take(10)
//    println("女性最喜欢的电影的Top 10")
//    println(FFavertite.toBuffer)


      // TODO 不同年龄段人员最受欢迎的TOPN
    //  user 中
    //      18以下  1
    //        18 -24   18
    //        25-34    25
    //        35-44    35
    //        45-49    45
    //        50-55    50
    //        56  +    56
    //思路,使用广播变量,将18年龄的用户id(qq),和25的(微信),把这两个变量广播到每个excutor中
    //避免每次的task都消耗资源传输这部分数据
    //每个exe过滤出对应的符合uid的数据

    //筛选出18的用户id,添加到广播变量

    //用户id,((用户id,性别,年龄),职业名称)
    val QQUser: RDD[(String, String)] = usersRDD.map(_.split("::")).map(info=>{(info(0),info(2))}).filter(per=>{per._2.equals("18")})
    val WXUser: RDD[(String, String)] = usersRDD.map(_.split("::")).map(info=>{(info(0),info(2))}).filter(per=>{per._2.equals("25")})
    //实现map阶段的join,
    val qqSet: mutable.Set[String] = mutable.HashSet() ++= QQUser.map(_._1).collect()
    val wxSet: mutable.Set[String] = mutable.HashSet() ++= WXUser.map(_._1).collect()

    val qqBroad: Broadcast[mutable.Set[String]] = sc.broadcast(qqSet)
    val wxBroad: Broadcast[mutable.Set[String]] = sc.broadcast(wxSet)
    //获得广播变量
    val movieIdAndName = moviesRDD.map(_.split("::")).map(f => {
      (f(0), f(1))
    }).collect().toMap //获得每个电影的id和名称
    ratings.map(f=>{(f._2,f._1)}).filter(f=>{qqBroad.value.contains(f._2)})
      .map(f=>(f._1,1)).reduceByKey(_+_)//将出现的次数相加
      .map(f=>{(f._2,f._1)}).sortByKey(false)//根据次数降序
      .take(10).map(x=>{movieIdAndName.getOrElse(x._2,null)})//获得join,得到电影名称.driver端
      .foreach(println)
      //总体思路,先从评分过滤出电影及评论的uid,然后过滤出来broadcast包含的值,
    //根据出现次数统计出欢迎程度,然后根据欢迎程度,倒序排列,最后找出来前10条,找出电影名字
  }
}

Spark行与行之间关系处理

总体思路,利用自定义分区器,将每个用户的数据进行分区排序操作,然后用mapPartition将行与行之间进行处理,符合一定条件的打上相同的属性,为后面的聚合操作做准备

例子

将相同id的流量时间在10分钟以内的汇总成一条数据

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
1,2020-02-18 19:37:23,2020-02-18 20:05:26,200
1,2020-02-18 20:06:27,2020-02-18 20:20:49,200
1,2020-02-18 20:21:50,2020-02-18 20:33:27,200
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
object FlowCount {

  def main(args: Array[String]): Unit = {
    /**
      * 其实就是将同一个id的数据放到同一个分区,
      * 然后对这个分区进行mappartiotion操作,在分区内处理行与行之间的关系,如果符合某种条件就将内容打上标签,然后后面的进行聚合操作
      */

    val conf = new SparkConf().setAppName(this.getClass.getCanonicalName).setMaster("local[*]")
    val sc = new SparkContext(conf)

    val lines: RDD[String] = sc.textFile("data.csv")

    //整理数据
    val tupleRdd: RDD[((String, Long, Long, Long), Null)] = lines.mapPartitions(it => {
      val dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss")
      it.map(line => {
        val fields = line.split(",")
        val uid = fields(0)
        val startTime = fields(1)
        val endTime = fields(2)
        val downFlow = fields(3).toLong
        val startTimestamp = dateFormat.parse(startTime).getTime
        val endTimestamp = dateFormat.parse(endTime).getTime
        ((uid, startTimestamp, endTimestamp, downFlow), null)
      })
    })
    //定义一个MapPartition,在map阶段完成数据的汇总,不要连续的调用map
    //计算出用户的ID,返回一个数组
    val uids = tupleRdd.map(_._1._1).distinct().collect()//将uid去重之后然后汇总,传递给分区器
    implicit val sorter = Ordering[Long].on[(String, Long, Long, Long)](t => t._2)
    //隐式转换,引入排序规则,根据时间进行排序
    val repartitionedAndSorted = tupleRdd.repartitionAndSortWithinPartitions(new UidPartitioner(uids))
    //进行分区,并且排序,将每个id进行分区
    val uidAndFlag: RDD[((String, Int), (Long, Long, Long))] = repartitionedAndSorted.mapPartitions(it => {
      var temp = 0L
      var flag = 0   //分条相加的
      it.map(t => {
        val startTimestamp = t._1._2
        val endTimestamp = t._1._3
        if(temp != 0) {
          if((startTimestamp - temp) / 1000 / 60 > 10) {
            flag += 1   //通过判断,如果时间大于10分钟,就是一条新数据
          } else {
            flag += 0   //如果小于10分钟,说明跟上面的数据是同一个flag
          }
        }
        temp = endTimestamp   //每条数据过来,都将保存在temp中,下一条数据在进行判断,如果是在数据范围内的

        println(((t._1._1, flag), (startTimestamp, endTimestamp, t._1._4)))

        ((t._1._1, flag), (startTimestamp, endTimestamp, t._1._4))
      })
    })
    //(uid,分组flag),(开始时间,结束时间,流量)
    val res = uidAndFlag.reduceByKey((t1, t2) => {
      (Math.min(t1._1, t2._1), Math.max(t1._2, t2._2), t1._3 + t2._3)
    }).mapPartitions(it => {
      val dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss")
      it.map(t => {
        (t._1._1, dateFormat.format(new Date(t._2._1)), dateFormat.format(new Date(t._2._2)), t._2._3)
      })
    }).collect()

    println(res.toBuffer)


    sc.stop()

  }
}

class UidPartitioner(val uids: Array[String]) extends Partitioner {

  val uidToNum = new mutable.HashMap[String, Int]()
  var i = 0
  for(uid <- uids) {
    uidToNum(uid) = i
    i += 1
  }

  override def numPartitions: Int = uids.length

  override def getPartition(key: Any): Int = {
    val uid = key.asInstanceOf[(String, Long, Long, Long)]._1
    uidToNum(uid)
  }
}

通过时间判断是否跟上面的一条数据为同一时间段,如果是同一时间段,flag的数量维持原来的,如果不是同一个时间段,就+1

最后将相同flag,相同uid的汇总到一块

 

 

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值