求连续登陆天数
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的汇总到一块