java reducebykey_Spark入门(五)--Spark的reduce和reduceByKey

reduce和reduceByKey的区别

reduce和reduceByKey是spark中使用地非常频繁的,在字数统计中,可以看到reduceByKey的经典使用。那么reduce和reduceBykey的区别在哪呢?reduce处理数据时有着一对一的特性,而reduceByKey则有着多对一的特性。比如reduce中会把数据集合中每一个元素都处理一次,并且每一个元素都对应着一个输出。而reduceByKey则不同,它会把所有key相同的值处理并且进行归并,其中归并的方法可以自己定义。

例子

在单词统计中,我们采用的就是reduceByKey,对于每一个单词我们设置成一个键值对(key,value),我们把单词作为key,即key=word,而value=1,因为遍历过程中,每个单词的出现一次,则标注1。那么reduceByKey则会把key相同的进行归并,然后根据我们定义的归并方法即对value进行累加处理,最后得到每个单词出现的次数。而reduce则没有相同Key归并的操作,而是将所有值统一归并,一并处理。

spark的reduce

我们采用scala来求得一个数据集中所有数值的平均值。该数据集包含5000个数值,数据集以及下列的代码均可从github下载,数据集名称为"avg"。为求得这个数据集中的平均值,我们先用map对文本数据进行处理,将其转换成long类型。

数据集内容:

28a2e560d0688ba72bdddee8acd53597.png

reduce求平均值scala实现

import org.apache.spark.{SparkConf, SparkContext}

object SparkReduce {

def main(args: Array[String]): Unit = {

val conf = new SparkConf().setMaster("local").setAppName("SparkReduce")

val sc = new SparkContext(conf)

//将String转成Long类型

val numData = sc.textFile("./avg").map(num => num.toLong)

//reduce处理每个值

println(numData.reduce((x,y)=>{

println("x:"+x)

println("y:"+y)

x+y

})/numData.count())

}

}

复制代码

reduce求平均值Java实现

import org.apache.spark.SparkConf;

import org.apache.spark.api.java.JavaRDD;

import org.apache.spark.api.java.JavaSparkContext;

import org.apache.spark.api.java.function.Function;

import org.apache.spark.api.java.function.Function2;

public class SparkReduceJava {

public static void main(String[] main){

SparkConf conf = new SparkConf().setAppName("SparkReduceJava").setMaster("local");

JavaSparkContext sc = new JavaSparkContext(conf);

reduceJava(sc);

reduceJava8(sc);

}

public static void reduceJava(JavaSparkContext sc){

JavaRDDtextData = sc.textFile("./avg").map(new Function() {

@Override

public Long call(String s) throws Exception {

return Long.parseLong(s);

};

});

System.out.println(

textData.reduce(new Function2() {

@Override

public Long call(Long aLong, Long aLong2) throws Exception {

System.out.println("x:"+aLong);

System.out.println("y:"+aLong2);

return aLong+aLong2;

}

})/textData.count()

);

}

public static void reduceJava8(JavaSparkContext sc){

JavaRDDtextData = sc.textFile("./avg").map(s->Long.parseLong(s));

System.out.println(textData.reduce((x,y)->x+y)/textData.count());

}

}

复制代码

reduce求平均值python实现

from pyspark import SparkConf,SparkContext

conf = SparkConf().setMaster("local").setAppName("SparkReduce")

sc = SparkContext(conf=conf)

numData = sc.textFile("./avg").map(lambda s:int(s))

print(numData.reduce(lambda x,y:x+y)/numData.count())

复制代码

运行结果

观察运行结果,我们不难发现,x存放的是累加后的值,y是当前值,x初始为0。事实上,x正是存放上次处理的结果,而y则是本次的数值。不断做x+y就并且放回累加后的结果作为下一次x的值。这样就可以得

到数值总和。最后将总和除以总数就能够得到平均值。

scala或java运行结果

平均值只保留了整数

x:222783

y:48364

x:271147

y:204950

x:476097

y:261777

x:737874

y:166827

x:904701

y:154005

x:1058706

y:150029

x:1208735

y:140158

x:1348893

y:404846

x:1753739

y:542750

...

...

平均值是:334521

复制代码

9d6a63c37fd1a702620703e6b4cc9a28.png

python运行结果

python默认保留了小数

334521.2714

复制代码

b4666e26327f358d6ebc7ecb4e3ad38f.png

spark的reduceByKey

spark的reduceByKey对要处理的值进行了差别对待,只有key相同的才能进行reduceByKey,则也就要求了进行reduceByKey时,输入的数据必须满足有键有值。由于上述的avg我们是用随机数生成的,那么我们可以用reduceByKey完成一个其他功能,即统计随机数中末尾是0-9各个数值出现的个数。

scala实现

import org.apache.spark.{SparkConf, SparkContext}

object SparkReduceByKey {

def main(args: Array[String]): Unit = {

val conf = new SparkConf().setMaster("local").setAppName("SparkReduce")

val sc = new SparkContext(conf)

//将String转成Long类型

val numData = sc.textFile("./avg").map(num => (num.toLong%10,1))

numData.reduceByKey((x,y)=>x+y).foreach(println(_))

}

}

复制代码

java实现

import org.apache.spark.SparkConf;

import org.apache.spark.api.java.JavaPairRDD;

import org.apache.spark.api.java.JavaSparkContext;

import org.apache.spark.api.java.function.Function2;

import org.apache.spark.api.java.function.PairFunction;

import scala.Tuple2;

public class SparkReduceByKeyJava {

public static void main(String[] main){

SparkConf conf = new SparkConf().setAppName("SparkReduceJava").setMaster("local");

JavaSparkContext sc = new JavaSparkContext(conf);

reduceByKeyJava(sc);

reduceByKeyJava8(sc);

}

public static void reduceByKeyJava(JavaSparkContext sc){

JavaPairRDD numData = sc.textFile("./avg").mapToPair(new PairFunction() {

@Override

public Tuple2 call(String s) throws Exception {

return new Tuple2(Integer.parseInt(s)%10,1);

}

});

System.out.println(numData.reduceByKey(new Function2() {

@Override

public Integer call(Integer integer, Integer integer2) throws Exception {

return integer+integer2;

}

}).collectAsMap());

}

public static void reduceByKeyJava8(JavaSparkContext sc){

JavaPairRDD numData = sc.textFile("./avg").mapToPair(s->new Tuple2<>(Integer.parseInt(s)%10,1));

System.out.println(numData.reduceByKey((x,y)->x+y).collectAsMap());

}

}

复制代码

python实现

from pyspark import SparkConf,SparkContext

conf = SparkConf().setMaster("local").setAppName("SparkReduce")

sc = SparkContext(conf=conf)

print(sc.textFile("./avg").map(lambda s:(int(s)%10,1)).reduceByKey(lambda x,y:x+y).collectAsMap())

复制代码

运行结果

scala运行结果

(4,522)

(0,462)

(1,495)

(6,519)

(3,463)

(7,544)

(9,518)

(8,533)

(5,483)

(2,461)

复制代码

b90b47510ce5ebdd71c08fbfaf997532.png

java运行结果

{8=533, 2=461, 5=483, 4=522, 7=544, 1=495, 9=518, 3=463, 6=519, 0=462}

复制代码

55c3e4ce1efa3d80c2bc25400a72d0b7.png

python运行结果

{3: 463, 4: 522, 0: 462, 7: 544, 5: 483, 9: 518, 8: 533, 6: 519, 2: 461, 1: 495}

复制代码

96962b2b41c68683ae7668145853e516.png

我们注意到三个程序输出的顺序不一样,但是本质的结果都是一致的。这里体现了spark的一个优点,由于是在单机本地上,该优点表现出来的是相同输入输出结果顺序不同。但是在集群中,该优点表现出来的是在集群中各自处理,而后返回结果。当数量足够大的时候,这个优点就更加明显。

对结果进行排序

那么为了能够使得输出结果顺序一致,我们可以对数据进行排序后输出,那么这里就涉及到了sortByKey。

scala实现

import org.apache.spark.{SparkConf, SparkContext}

object SparkReduceByKey {

def main(args: Array[String]): Unit = {

val conf = new SparkConf().setMaster("local").setAppName("SparkReduce")

val sc = new SparkContext(conf)

//将String转成Long类型

val numData = sc.textFile("./avg").map(num => (num.toLong%10,1))

//根据key排序后输出

numData.reduceByKey((x,y)=>x+y).sortByKey().foreach(println(_))

}

}

复制代码

java实现

特别注意这里用的是collect,而不是collectMap,因为java中转换成Map会打乱顺序

import org.apache.spark.SparkConf;

import org.apache.spark.api.java.JavaPairRDD;

import org.apache.spark.api.java.JavaSparkContext;

import org.apache.spark.api.java.function.Function2;

import org.apache.spark.api.java.function.PairFunction;

import scala.Tuple2;

public class SparkReduceByKeyJava {

public static void main(String[] main){

SparkConf conf = new SparkConf().setAppName("SparkReduceJava").setMaster("local");

JavaSparkContext sc = new JavaSparkContext(conf);

reduceByKeyJava(sc);

reduceByKeyJava8(sc);

}

public static void reduceByKeyJava(JavaSparkContext sc){

JavaPairRDD numData = sc.textFile("./avg").mapToPair(new PairFunction() {

@Override

public Tuple2 call(String s) throws Exception {

return new Tuple2(Integer.parseInt(s)%10,1);

}

});

System.out.println(numData.reduceByKey(new Function2() {

@Override

public Integer call(Integer integer, Integer integer2) throws Exception {

return integer+integer2;

}

}).sortByKey().collect());

}

public static void reduceByKeyJava8(JavaSparkContext sc){

JavaPairRDD numData = sc.textFile("./avg").mapToPair(s->new Tuple2<>(Integer.parseInt(s)%10,1));

System.out.println(numData.reduceByKey((x,y)->x+y).sortByKey().collect());

}

}

复制代码

python实现

from pyspark import SparkConf,SparkContext

conf = SparkConf().setMaster("local").setAppName("SparkReduce")

sc = SparkContext(conf=conf)

print(sc.textFile("./avg").map(lambda s:(int(s)%10,1)).reduceByKey(lambda x,y:x+y).sortByKey().collectAsMap())

复制代码

得到结果,这里只给出scala输出的结果,其他输出的结果一致,只是表现形式不同

(0,462)

(1,495)

(2,461)

(3,463)

(4,522)

(5,483)

(6,519)

(7,544)

(8,533)

(9,518)

复制代码

数据集以及代码都可以在github上下载。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值