Learning Spark笔记4-常见的转换与动作

基本RDDs


不管数据如何,我们先将介绍一下我们可以对RDDs做哪些转换和操作


1.元素转换


这里有两个常用的转换map()和filter()。


map()转换会应用到RDD中的每个元素上,函数的结果是RDD,该RDD中的每个元素都有一个新值。
filter转换返回的结果是RDD,该RDD只包含通过filter()的元素。


我们可以使用map()执行任意数量的事情,map()返回的类型不一定与输入类型一致。


让我们看一个基本的例子:




Example 3-26. Python squaring the values in an RDD


nums = sc.parallelize([1, 2, 3, 4])
squared = nums.map(lambda x: x * x).collect()
for num in squared:
print "%i " % (num)


Example 3-27. Scala squaring the values in an RDD


val input = sc.parallelize(List(1, 2, 3, 4))
val result = input.map(x => x * x)
println(result.collect().mkString(","))


Example 3-28. Java squaring the values in an RDD


JavaRDD<Integer> rdd = sc.parallelize(Arrays.asList(1, 2, 3, 4));
JavaRDD<Integer> result = rdd.map(new Function<Integer, Integer>() {
public Integer call(Integer x) {
return x*x; 
}
});


System.out.println(StringUtils.join(result.collect(), ","));


有的时候我们想要从一个输入元素得到多个输出元素,这样的操作就可以调用flatMap()。像map()一样,对于输入的每个元素都会单独调用flatMap()。而不是返回单个元素,我们返回一个值得迭代器。而不是生成一个RDD的迭代器,我们得到一个包含所有迭代器元素的RDD。flatMap()一个简答的用法就是,将输入的字符串切分成单词,像下面一样


Example 3-29. flatMap() in Python, splitting lines into words
lines = sc.parallelize(["hello world", "hi"])
words = lines.flatMap(lambda line: line.split(" "))
words.first() # returns "hello"


Example 3-30. flatMap() in Scala, splitting lines into multiple words
val lines = sc.parallelize(List("hello world", "hi"))
val words = lines.flatMap(line => line.split(" "))
words.first() // returns "hello"


JavaRDD<String> lines = sc.parallelize(Arrays.asList("hello world", "hi"));
JavaRDD<String> words = lines.flatMap(new FlatMapFunction<String, String>() {
public Iterable<String> call(String line) {
return Arrays.asList(line.split(" "));
}
});
words.first(); // returns "hello"


flatMap()和map()的不同,flatMap()可以理解为将迭代器扁平化,不会返回一个RDD的列表,而是返回一个RDD包含所有列表中的元素。




2.伪设置操作


RDDs提供很多数学集操作,像是联合和交叉,即使RDDs没有正确设置。


RDD1
{coffee,coffee,panda,monkey,tea}


RDD2
{coffee,money,kitty}


RDD1.distinct() 
{coffee,panda,monkey,tea}


RDD1.union(RDD2)
{coffee,coffee,coffee,panda,monkey,monkey,tea,kitty}


RDD1.intersection(RDD2)
{coffee,monkey}


RDD1.subtract(RDD2)
{panda,tea}


我们的RDD最常丢失的集合属性是元素的唯一性,因为我们经常有重复的元素。如果我们想要唯一的元素,那么我们可以使用RDD.distinct()。注意distinct()是很浪费资源的,因为它需要通过网络混洗所有的数据,然后确保我们只接收到每个元素的一个副本。


最简单的设置操作时union(),它可以返回包含所有源数据的RDD。这在一些情况下是有用的,像是从多个来源处理日志文件。与数学的联合不同,如果有重复在输入RDDs中,那么Spark的union结果也将包括这些重复的。


Spark也提供了一个intersection(),它可以返回共同存在与两个RDDs中的元素。intersection()也会移除所有重复的。insersection()的性能会比较差,因为它需要通过网络混洗确定相同的元素。


有时我们需要考虑删除一些数据。subtract()返回一个只在第一个RDD中存在,不在第二个RDD中存在。像intersection(),它也执行混洗。


我们可以计算两个RDDs的笛卡尔积。例如,计算每个用户的预期兴趣的时候cartesian很有用。


3.动作


基本的RDDs最常见的操作时reduce(),它可以操作RDD中的两个元素,然后返回一个新的相同类型的元素。一个简单的例子像是“+”函数,我们可以使用它对我们的RDD就和。使用reduce(),我们可以简单的对我们的RDD求和,计算元素的个数,执行其它类型的聚合。


Example 3-32. reduce() in Python
sum = rdd.reduce(lambda x, y: x + y)


Example 3-33. reduce() in Scala
val sum = rdd.reduce((x, y) => x + y)


Example 3-34. reduce() in Java
Integer sum = rdd.reduce(new Function2<Integer, Integer, Integer>() {
public Integer call(Integer x, Integer y) {
return x + y;
}
});


fold()和reduce()都要求我们的结果的返回类型与我们正在运行的RDD中的元素的类型相同。这对于像和这种操作很好,但是有些时候我们想要返回不同类型。例如,当计算平均值时,我们要跟踪到目前为止的元素的个数,这要求我们返回一对。我们可以解决这个问题,首先使用map(),我们将每个元素转换为元素和个数1,这是我们要返回的类型,这样reduce函数可以在其上工作。


aggregate()函数可以让我们避免使返回值与我们正在处理的RDD相同的类型。aggregate()像fold(),我们提供一个返回的初始化的0值。然后我们提供一个函数组合RDD中元素的累加器,最后我们需要提供第二个函数合并两个累加器,每个节点的累加器以本地的方式计算自己的结果。


可以使用aggregate()计算RDD的平均值,避免在fold之前使用map()。


Example 3-35. aggregate() in Python


sumCount = nums.aggregate((0, 0),
(lambda acc, value: (acc[0] + value, acc[1] + 1),
(lambda acc1, acc2: (acc1[0] + acc2[0], acc1[1] + acc2[1]))))
return sumCount[0] / float(sumCount[1])


Example 3-36. aggregate() in Scala


val result = input.aggregate((0, 0))((acc, value) => (acc._1 + value, acc._2 + 1),
(acc1, acc2) => (acc1._1 + acc2._1, acc1._2 + acc2._2))


val avg = result._1 / result._2.toDouble




Example 3-37. aggregate() in Java


class AvgCount implements Serializable {
public AvgCount(int total, int num) {
this.total = total;
this.num = num;
}
public int total;
public int num;
public double avg() {
return total / (double) num;
}
}
Function2<AvgCount, Integer, AvgCount> addAndCount =
new Function2<AvgCount, Integer, AvgCount>() {
public AvgCount call(AvgCount a, Integer x) {
a.total += x;
a.num += 1;
return a;
}
};
Function2<AvgCount, AvgCount, AvgCount> combine =
new Function2<AvgCount, AvgCount, AvgCount>() {
public AvgCount call(AvgCount a, AvgCount b) {
a.total += b.total;
a.num += b.num;
return a;
}
};
AvgCount initial = new AvgCount(0, 0);
AvgCount result = rdd.aggregate(initial, addAndCount, combine);
System.out.println(result.avg());


一些动作返回以常规的集合或值形式将一些或全部的数据返回给我们的驱动程序


最简单的,最常用的操作是使用collect()将数据返回到我们的驱动程序。它会返回整个RDD的内容。collect()通常用在单元测试,整个RDD的内容是否可以装载内存中。可以轻松的将RDD的值与我们预期的结果进行比较。


take(n)从RDD中返回n个元素,然后最小化它访问的分区数量,所以它可能代表一部分集合。注意这些操作不会按照您期望的顺序返回元素。


这些操作对于单元测试和快速的排错是有用的,但是在处理大量数据时可能会引起瓶颈。


如果在我们的数据有顺序标识,我们可以从RDD中使用top()得到最上面的元素,我们可以定义自己的比较函数。


有时候我们需要在驱动程序中使用数据样本。takeSample(withReplacement,num,seed)




有时对RDD中的所有元素执行操作是有用的,但不向驱动程序返回任何结果。 一个很好的例子是将JSON发布到Web服务器或将数据插入到数据库中。 在任何一种情况下,foreach()操作都允许我们对RDD中的每个元素执行计算,而不会将其返回到本地。


RDD类型之间转换


一些功能只使用与某种类型的RDDs,像mean()和variance()用在数字RDDs中,join()用在键值RDDs中。在Scala和Java中,这些方法没有定义在标准RDD类中,所以访问这些方法我们需要确定这些类的位置。


Scala
RDDs特殊功能的转换是隐式的,自动完成的。我们需要导入org.apache.spark.SparkContext._。SparkContext对象中的这些隐式转换将RDD转化为各种包装类,像是DoubleRDDFunctions和PairRDDFunction.


Java


Example 3-38. Creating DoubleRDD in Java


JavaDoubleRDD result = rdd.mapToDouble(
 new DoubleFunction<Integer>() {
 public double call(Integer x) {
 return (double) x * x;
 }
});
System.out.println(result.mean());


Python API的结构与Java和Scala的结构不同。 在Python中,所有这些功能都在基础RDD类上实现,但如果RDD中的数据类型不正确,则在运行时会失败。


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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

艺菲

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值