Spark 标签生成(Java和Scala的两种实现)
气温数据多重聚合
[Scala]实现聚合气温数据。聚合出Max,Min.AVG
/**
* 气温数据聚合应用
*/
object TempAggDemo{
def main(args:Array[String]):Unit={
//配置一下
val conf=new SparkConf()
conf.setAppName("tempAgg")
conf.setMaster("local")
val sc=new SparkContext(conf)
//1.加载文件
val rdd1=sc.textFile("file:///d:/mr/temp3.dat")
//2.切割成对(1930,54)
val rdd2=rdd1.map(line=>{
var arr=line.split(" ")
(arr(0).toInt,arr(1).toInt)
PairRDD
//3.分组 按key(年度)分组RDD一但变成了二元组就变成了PairRDD
//(19300->{23,34,67},1931->{.......})
val rdd3=rdd2.groupByKey()
//4.对组内元素进行统计聚合 mapValues()可以迭代的量 K不需要变,看V变
val rdd4=rdd3.mapValues(it=>{
val mx= it.max //max[B >:Int](implicit cmp:Ordering[B]) 最大值
val mn=it.min
val sum=it.sun
val size=it.size
(mx,mn,sum.toFloat /size) //把每一个年度的气温数据聚合成了三元组
})
//5.按照年度排序
val rdd5=rdd4.sortByKey(true) //true升序
//6.输出
rdd5.collect().foreach(println)
}
}
reduceByKey的特点:reduceByKey不会改变K,V的类型。进来是什么类型,出去还是什么类型。
如果要聚合三个值,max,min,(sum,size)通过reduceByKey一次出来。原来的K,V,由于reduceByKey不能改变类型。而我们要的结果至少包含三个结果。它们起码是三元组。
[Scala]
object TempAggDemo2{
def main(args:Array[String]):Unit={
val conf=new SparkConf()
conf.setAppName("tempAgg")
conf.setMaster("local")
val sc=new SparkContext(conf)
//1.加载文件
val rdd1=sc.textFile("file:///d:/mr/temp3.dat")
//2.切割成对(1930,54)
val rdd2=rdd1.map(line=>{
var arr=line.split(" ")
//(mx,mn,sum,count)
val year=arr(0).toInt
val tmp=arr(1).toInt
//这里又是一个元组(temp最高气温,temp最低气温,temp气温总和,)在变换的时候我们把V当成一个元组来代替,K就是年份。按K聚合,所以所有年份相同的V都要在一起聚合。而此刻的V不是一个单纯的数字了。它是一个元组。把它重向的捏在一起。reduceByKey的特点就是它不能改变V的类型。
(year,(temp,temp,temp,1))
})
//每一元素后面都带1
// rdd2.collect().foreach(println) //(1953,(23,23,23,1))(1951,(26,26,26,1))
//3.聚合 reduceByKey(func:((Int,Int,Int,Int),(Int,Int,Int,Int))=>(Int,Int,Int,Int)) 它是两个元组组成一个元组.聚合是纵向捏合的过程。两个元组,它捏合完后产生的新值。还要跟第三个再聚,不断的取最大值最小值和avg
val rdd3=rdd2.rdd2.reduceByKey((a,b)=>{
import scla.math._
(max(a._1,b_1),min(a._2,b_2),a._3+b._3,a._4+b._4)
})
//rdd3.collect().foreach(println) //(1921,(48,-50,348,53))(1933,(41,-44,-38,49))
//4.变换 K不需要变K是年度,把V变掉就行了 V是一个元组
val rdd4=rdd3.mapValue(t=>{
(t._1,t._2,t._3.toFloat /t.-4)
}).sortByKey()
}
}
[Java]
map(Functionf) JavaRDD它就没有各种ByKey的操作
mapToPair(PairFunction)JavaPairRDD
String 输入的行 Integer K 四元组Tuple4<>
import scala.Float;
import scala.Tuple2;
import scala.Tuple3;
import scala.Tuple4;
public class TempAggDemeJava2{
public static void main(String[] args){
SparkConf conf=new SparkConf();
conf.setAppName("tempAgg");
conf.setMaster("local");
JavaSparkContext sc=new JavaSparkContext(conf);
//1.加载文件
JavaRDDrdd1=sc.textFile(path:"file:///d:/mr/temp3.dat");
//2.变换(1903,(32,23,23,1))
JavaRDD>rdd2=
rdd1.mapToPair(newPairFunction
Tuple4>(){
public Tuple2>
call(String s)throws Exception{
String[] arr=s.plit(regex:" ");
int year=Integer.parseInt(arr[0]);
int tmp=Integer.parseInt(arr[1]);
Tuple4 v=new Tuple4(tmp,tmp,temp,_4:1);
return new Tuple2>(year,v);
}
});
//3.聚合
JavaPairRDD>redd2.reduceByKey(
new Function2,
Tuple4,
Tuple4>(){
public Tuple4call
(Tuple4v1,
Tuple4v2)throws Exception{
//v1和v2是返回一个新的元组
int mx=Math.max(v1._1(),v2._1());
int mn=Math.min(v1._2(),v2._2());
int sum=v1._3()+v2._3();
int count=v1._4()+v2._4();
return new Tuple4(mx,mn,sum,count);
}
});
//4.map取出avg K是年度 V变成了三元组
JavaPairRDD rdd4=rdd3.mapValues(new Function,
Tuple3>(){
public Tuple3call
(Tuple4v1)throws Exception{
return new Tuple3(v1._1(),v1._2(),_3: (float)v1._3() /v1._4());
}
});
//5.排序
JavaPairRDD>rdd5=rdd4.sortByKey();
//6.列表
List>> list=rdd5.collect();
for(Tuple2> t:list){
System.out.println(t);
}
}
}
[标签生成Scala版本]
[TaggejScala.scala]标签生成Scala
```scala
/**
*
*标签生成
*/
object TaggenScala{
def main(args:Array[String]):Unit={
val conf=new SparkConf()
conf.setAppName("Taggen")
conf.setMaster("local")
val sc=new SparkContext(conf)
//1.加载文件,将泛型加全
val rdd1:RDD[(String,util.List[String])]=
sc.textFile("file:///d:/mr/temptags.txt")
//2.解析每行的json数据成为集合 //map这种是变换
val rdd2:RDD[(String)]=rdd1.map(line=>{
val arr:Array[String]=line.split("\t")
//提取商家id
val busii:String=arr(0)
//json对象
val json:String=arr(1)
val list:java.util.List[String]=TagUtil.extractTag(json)
Tuple2[String,java.util.List[String]](busid,list)
})
//3.过滤空集合 filter(f:((String,util.List[String]))=>Boolean),(String,util.List[String])这个参数是二元组,返回值是Boolean,rdd2.filter()这里可以用一个高阶函数传进去。传清楚的话它就是一个元组rdd2.filter(t=>{ }),要把这个元组定义齐全的话。类型可以声明,它的类型是一个二元组rdd2.filter((t:Tuple2[String,java.util.List[String]])=>
val rdd3:RDD[(String,util.List[String])]=
rdd2.filter((t:Tuple2[String,java.util.List[String]])=>{
!t._2.isEmpty //二元组一旦进来就可以拿到它第二个元素,看它是否为空
})
//4.将值压扁 flatMapValues[U](f:(util.List[String]))=>TraversableOnce[U]):这个是对值进行压扁,如果对值一旦压扁。值压扁之后值压扁之后,每一值都会跟K从新组合新的K。 flatMap:
val rdd4:RDD[(String,String)]rdd3.flatMapValues((list:java.util.List[String])=>{
//导入隐式转换
import scala.collection.JavaConversions._
//返回list的话它需要返回可以迭代的量,需要导入隐式转换
})
//5.滤掉数组的tag,它的类型没有变还是元组
val rdd5:RDD[(String,String)]=rdd4.filter((t:Tuple2[String,String])=>{
try{
//
Integer.pparseInt(t._2)
false
}catch{
case _=>true
}
})
//6.标一成对,第5步已经是一对了但是不是想要的。需要重新配对成新的一对。
val rdd6:RDD[Tuple2[Tuple2[String,String],Int]]=
rdd5.map((t:Tuple2[String,String])=>{
Tuple2[Tuple2[String,String],Int](t,1)
}) //新的二元组是一个嵌套的二元组,元组是Optin的方法
//7.聚合
val rdd7:RDD[Tuple2[Tuple2[String,String],Int]]=
rdd6.reduceByKey((a:Int,b:Int)=>{
a+b
})
//8.重组
val rdd8:RDD[Tuple2[String,Tuple2[String,Int]]]=
rdd7.map((t:Tuple2[Tuple2[String,String],Int])=>{
Tuple2[String,List[Tuple2[String,Int]]](t._1._1,Tuple2[String,Int](t._1._2,t._2)::Nil) //List里面的元素是二元组
//Tuple2[String,Tuple2[String,Int]](t._1._1,Tuple2[String,Int](t._1._2,t._2)::Nil) //在reduceByKey它是纵向捏合,把一个K下所有的V聚合在一起形成一个值。但又不能改变V的类型。如果原来的K是一个二元组。最终把所有的V聚完之后还是一个二元组。这是不合适的。此刻的目的是把它同一商家下的所有评论都放一起。每一个二元组都做到集合里面去。这样一来两个集合就可以聚到一个集里面去了。当我们变换的时候,我们不会把它变成一个二元组。 而是把它变成集合。(Tuple2[String,Int](t._1._2,t._2)::Nil)
}) //
//9.reduceByKey
val rdd9:RDD[Tuple2[String,List[Tuple2[String,Int]]]] =
rdd8.reduceByKey((a:List[Tuple2[String,Int]],b:List[Tuple2[String,Int]])=>{
a:::b //::是一个数字和一个集合的添加,[B >:(String,Int)](x:B):它是一个元组结尾的操作符是右操作符。
//:::[B >: (String,Int)](prefix: List[B])这是一个界定,B是List 它是柯里化函数
})
//10.分组内排序
val rdd10:RDD[Tuple2[String,List[Tuple2[String,Int]]]] =
rdd9.mapValues((list:List[Tuple2[String,Int]]))=>{
//sortBy[B](f:((String,Int))=>B)(implicit ord:Ordering[B]),sortBy它要排序它的元素是一个元组,把它的类型(二元组)给它。 List集合是不可变集合(immutable) 源码 typeList[+A]=scala.collection.immutable.List[A] val List=scala.collection.immutable.List 所以它排序完之后产生的是新集合。
val list2:List[Tuple2[String,Int]]=list.sortBy((t:Tuple2[String,Int])=>{
-t._2
})
//取前5
list2.take(5)
})
//11.商家间排序 sortBy[K](f:((String,List[(String,Int)]))=>K,A,ASCENDING:Boolean=true,numpartitions:Int...)它是一个高阶
val rdd11:RDD[Tuple2[String,List[Tuple2[String,Int]]]] =
rdd10.sortBy((t:Tuple2[String,List[Tuple2[String,Int]]])=>{
t.-2(0)._2 //t.-2就是集合(0)就是商家的评论最大数。再把它取出来。
},false)
//收集 打印
rdd11 .collect().foreach(println)
}
}
总结:把所有泛型类型都定义上。
public class TagUtil{
/*
*从json中抽取评论集合
*/
public static ListextractTag(String json){
Listlist=new ArrayList();
//将字符串解析成json对象
JSONObject obj=JSON.parseObject(json);
JSONArray arr=obj.getJSONArray(key:"extInfoList");
if(arr !=null && arr.size()>0){
//得到数组的第一json对象
JSONObject firstObj=arr.getJSONObject(index:0);
JSONArray values=firstObj.getJSONArray(key:"values");
if(arr !=null &&arr.size()>0){
//得到数组的第一个json对象
JSONObject firstObj=arr.getJSONObject(index:0);
JSONArray values=firstObj.getJSONArray(key:"values");
if(values !=null && arr.size()>0){
//得到字符串解析成json对象
JSONObject firstObj=arr.getJSONObject(index:0);
JSONArray values=firstObj.getJSONArray(key:"values");
if(values !=null &&values.size()>0){
for(int i=0;i
String tag=values.getString(i);
list.add(tag);
}
}
}
return list;
}
}
}
[TagUtil.java]
public class TagUtil{
//从json中抽取评论集合
public static ListextractTag(String json){
Listlist=new ArrayList();
//将字符串解析成json对象
JSONObject obj=JSON.parseObject(json);
JSONArray arr=obj.getJSONArray("extInfoList");
if(arr !=null && arr.size()>0){
//得到数组的第一个json对象
JSONObject firstObj=arr.getJSONObject(0);
JSONArray values=firstObj.getJSONArray("values");
if(arr !=null && arr.size()>0){
//得到数组的第一个json对象
JSONObject firstObj=arr.getJSONObject(0);
JSONArray values=firstObj.getJSONArray("values");
if(values !=null &&values.size()>0){
for(int i=0;i
String tag=values,getString(i);
list.add(tag);
}
}
}
return list;
}
}
}
//java方法定义
public staic T getMiddle(Listlist){
return list.get(list.size() /2);
}
@Test
public void testMethod(){
Listlist=new ArrayList();
list.add(1);
list.add(2);
list.add(3);
System.out.println(getMiddle(list));
}
Maven
com.alibaba
fastjson
1.2.24
[标签生成Java版本]
import org.apache.spark.SparkConf;
import org.apache.spark.api.java.JavaRDD;
import org.apache.spark.api.java.JavaSparkContext;
public class TaggenJava2{
public static void main(String[] args){
SparkConf conf=new SparkConf();
conf.setAppName("tempAgg");
conf,setMaster("local");
JavaSparkContext sc=new JavaSparkContext(conf);
//1.加载文件
JavaRDDrdd1=sc.textFile(path:"file:///d:/tenotags.txt");
//变换 2. 切割
JavaPairRDD>rdd2=
rdd1.mapToPair(new PairFunction>(){
public Tuple2>call(String s)throws Exception{
String[] arr=s.split(regex:"\t");
String busid=arr[0];
Listtags=TagUtil.extractTag(arr[1]);
return new Tuple2>(busid,tags); //现在已经把它变成集合了,空集合要把它滤掉
}
});
//注意:操作过程期间,集合过滤越往前越好。
//3.过滤空集合
JavaPairRDD>rdd3=
rdd2.filter(new Function>,Boolean>(){
public Boolean call(Tuple2>t)throws Exception{
return !t._2().isEmpty();
}
});
//4.压扁值
JavaPairRDDrdd4=
rdd3.flatMapValues(new Function,Iterable>(){
public Iterable call(List v1)throws Exception{
return v1;
}
});
//5.过滤掉数字标签,过滤的话需要把K和V传进来,它是过滤不会改变内容
rdd4.filter(new Function,Boolean>(){
public Boolean call(Tuple2t)throws Exception{
try{
Integer.parseInt(t._2());
return false;
}catch(Exception e){
e.printStackTrace();
}
return true;
}
});
//6.重组,标1成对 新K是二元组
JavaPairRDD,Integer> rdd6=
rdd5.mapToPair(new PairFunction,
Tuple2,Integer>(){
public Tuple2,Integer>call
(Tuple2t)throws Exception{
return new Tuple2,Integer>(t,1);
}
});
//7.聚合值
JavaPairRDD,Integer> rdd7=
rdd6.reduceByKey(new Function2(){
public Integer call(Integer v1,Integer v2)throws Exception{
return v1+v2;
}
});
//8.重组组员(busid,(tag,num)) 不是把它变成元组是把它变成集合
JavaPairRDD>>rdd8=
rdd7.mapToPair(new PairFunction,Integer>,
String,Tuple2>(){
public Tuple2>>call
(Tuple2,Integer>t)thros Exception{
List>list=new ArrayList>();
list.add(new Tuple2(t._1._2,t._2));
return new Tuple2>(t._1._1,
new Tuple2(t._1._2,t._2));
}
});
//9.聚合 变成集合了,把两个集合变成一个集合
JavaPairRDD>> rdd9=rdd8.reduceByKey(
new Function2>,List>,
List>>(){
public List>call(List>
v1,List>v2)throws Exception{
v1.addAll(v2); //v1的所有集合添加所有v2的集合,没有返回值
return v1;
}
});
//10.商家内排序,对V变换,此时list是V
JavaPairRDD>> rdd10=
rdd9.mapValues(new Funciton>,
List>>(){
public List>call(List>v1)
throws Exception{
//Comparator对比器二元元组sort(Comparator<?super Tuple2>>c])
v1.sort(new Comparator>(){
//比较集合中的两个元素的大小有三家评论和数量
public int compare(Tuple2o1,Tuple2o2){
return -(o1._2-o2._2); //-()降序
}
});
//subList返回的列表是不能串行化的,要用一个串行化的方法返回这个值。
List>newList=new ArrayList>();
newList.addAll(v1.subList(0,v1.size()>5?5:v1.size()));
//subList(int fromIndex,int toIndex)
// return v1;//sort是没有返回值的,它是对V本身排序的
//return v1.subList(0,4);//前5 有些集合里面可能没有5个元素会报错要先判断一下
//return v1.subList(0,v1.size()>5?5:v1.size());//>5就5否则的话就v1.size
return newList;
}
});
//11.变换pairRDD到普通RDD,否则没有sortBy方法,这里先把它转成非JavaPairRDD的 JavaPairRDD它没有排序的方法,只有按Key
//第一个是二元组,第二个是List 这个是评论数量
JavaRDD>>>rdd11=
rdd10.map(newFunction
Integer>>>,Tuple2>>>(){
public Tuple2>>call
(Tuple2>>v1)throws Exception{
return t;
}
});
//12.商家间排序
//Object这是你要返回的值,Function这是一个元组,它返回的是一个整数
//按照每一个商家它第一个评论的数量,来倒排
rdd11.sortBy(new Function>>,
Integer(){
public Integer call(Tuple2>>t)
throw Exception{
return -t._2.get(0)._2;
}
},true,2);// sortBy
List>list=rdd5.collect();
for(Tuple2 t:list){
System.out.println(t);
}
//输出rdd7
List list=rdd8.collect();
for(Object o:list){
System.out.println(o);
}
}
}
[sort源码]//sort没有返回值
default void sort(Comparator<?super E>c){
Object[] a=this.toArray();
Arrays.sort(a,(Comparator)c);
ListIteratori=this.listIterator();
for(Object e : a){
i.next();
i.set((E)e);
}
}
JavaRDD>>>.....
[sortBy]
//JFunction[T,S]参数,ascending:升降序 numPartition:分区
def sortBy[S](f:JFunction[T,S],ascending:Boolean,numPartition:Int):JavaRDD[T]