spark学习之旅(2)之之RDD常用方法

RDD(Resilient Distributed Dataset)叫做弹性分布式数据集,是Spark中最基本的数据抽象,它代表一个不可变、可分区、里面的元素可并行计算的集合。这里的弹性指的是RDD可以根据当前情况自动进行内存和硬盘存储的转换

简单点讲就是spark中对数据的一个封装,把数据封装进对象,容易操作

在spark中所有的计算都是围绕着RDD操作的,每个 RDD 都被分为多个分区,这些分区运行在集群中的不同节点上,并且RDD也可以缓存到内存中

RDD支持两种操作:转化操作和行动(计算)操作。

RDD 的转化操作是把原来的RDD换成新的,这样做主要是为了把数据转换成方便计算的格式,例如之前的学习中我们计算过文件中的单词数量

数据1

java~HTML~spark
java
scala

在上面文件中不方便计算机计算数量,所以这时候需要转换,转化成下面的格式

数据2

java,1
HTML,1
spark,1
java,1
scala,1

也就是说我们把封装了数据1的RDD转换成了封装了数据2的RDD。这就是转换这一类操作的作用。

新的RDD则包含了如何从其他RDDs衍生所必需的信息,所以说RDDs之间是有依赖关系的,简单点说刚才封装数据2的RDD由数据1RDD衍生过来的,可以简单理解为数据2RDD是数据1RDD的孩子,体内有数据1RDD的DNA,这个就是RDD中的血缘。

基于RDDs之间的依赖,RDDs会形成一个有向无环图DAG,该DAG描述了整个流式计算的流程,实际执行的时候,RDD是通过血缘关系(Lineage)一气呵成的,即使出现数据分区丢失,也可以通过血缘关系重建分区,

简单讲就是根据RDD中的血缘关系,工作的时候一个家族的RDD都要去执行工作,工作期间有的RDD跑了(数据丢失),也没关系,可以根据家族中其他成员再次找到他

如果说转换方法是把数据转换成合适的计算方式,那么行动操作就是最后的计算方式。

扫描二维码关注公众号,回复: 16866019 查看本文章

我们使用上面的方式把数据转换成下面的输出格式

java,1
html,1
。。。。

而计算操作就是把每个值的1都加起来,得到和后展示或保存。

注意:Spark采用惰性计算模式(懒执行),RDD只有第一次在一个行动操作中用到时,才会真正计算运行。

常用RDD方法

常用转换方法

首先介绍下环境背景

object Food {

  def main(args: Array[String]): Unit = {
  	// 创建spark上下文
    val spark = new SparkContext("local","food")
    // 读取文件中数据 A.csv文件是广东美食数据集
    val data = spark.textFile("data/A.csv")
    // 打印前10条
    data.take(10).foreach(println(_))
	
  }
}

后续代码都是基于此时的data变量编写

map(func)

经过函数转换后变成新的RDD,
例如在当前数据集中我只需要名称,评论数,人均价格,类别,商圈,此时就可以使用map
val result = data.map(
      x => {
        val arr = x.split(",")
        val mony = arr(2).split("元")
        (arr(0),arr(1),mony(0).toInt,arr(3),arr(4))
      }
    )
result.take(10).foreach(println(_))
打印结果》》》
(极炙·台灣精致炭火烤肉,3551,211,日本料理,天河城/体育中心)
(大滷爺(正佳店),151,54,粤菜,天河城/体育中心)
(白天鹅宾馆·玉堂春暖餐厅,2018,312,粤菜,沙面)
(Mr.Fish鱼鲜生海鲜放题(高德置地冬广场店),7703,354,自助餐,高德置地/花城汇)
......


mapPartitions(func)

同上,不同的是在分区上进行,函数Iterator,假设有N个元素,有M个分区,那么map的函数的将被调用N次,而mapPartitions被调用M次,一个函数一次处理所有分区
由于直接处理的分区中的所有数据,当数据量庞大的时候回造成内存溢出

val result = data.mapPartitions(
      x => {
        var list = List();
        while (x.hasNext){
          val arr = x.next().split(",")
          list :+ arr(3)
        }
        list.iterator  // 注意返回的是迭代器类型
      }
    )
    

mapPartitionsWithIndex(func)

类似于mapPartitions,但func带有一个整数参数表示分片的索引值,因此在类型为T的RDD上运行时,func的函数类型必须是(Int, Interator[T]) => Iterator[U]

flatMap(func)

类似map,结果是数据的话会把数据中每一个值取出作为单个的值,返回多个值

val r1 = data.map(_.split(","))
    .take(10)
    .foreach(println(_))

打印》》》
[Ljava.lang.String;@53a665ad
[Ljava.lang.String;@2c0b4c83
[Ljava.lang.String;@78525ef9
......

val r2 = data.flatMap(_.split(","))
    .take(10)
    .foreach(println(_))
    
打印》》》
极炙·台灣精致炭火烤肉
3551
211元
日本料理
......

filter(func)

主要用于过滤,匿名函数返回值是true,则返回,为false则不返回数据

val result = data.map(
  x => {
    val arr = x.split(",")
    val mony = arr(2).split("元")
    (arr(0),arr(1),mony(0).toInt,arr(3),arr(4))
  }
).filter(
  x => {
    x._4 == "日本料理"
  }
)
result.take(10).foreach(println(_))

sample(withReplacement, fraction, seed)

采样变换根据给定的随机种子,从RDD中随机地按指定比例选一部分记录,创建新的RDD

withReplacement : Boolean , True数据复制出去的,下次取值还是从元数据取,可能重复;False取出后删除原值,不会重复
fraction : Double, 在0~1之间的一个浮点值,表示要采样的记录在全体记录中的比例
seed :随机种子

val result = data.sample(false,0.5)
result.take(10).foreach(println(_))

distinct()

去除重复的数据

sortBy(func)

按照func对数据进行处理,然后把结果排序,第二个为true为升序,为false为降序

// 饭店最贵前10名
val result = data.map(
  x => {
    val arr = x.split(",")
    val mony = arr(2).split("元")
    (arr(0),arr(1),mony(0).toInt,arr(3),arr(4))
  }
)

result.sortBy(-_._3)
  .take(10)
  .foreach(println(_))

sortByKey([ascending], [numTasks])

根据key排序

union(otherDataset)

对源RDD和参数RDD求并集后返回一个新的RDD  不去重

subtract (otherDataset)

去除两个RDD中相同的元素,不同的RDD将保留下来

intersection(otherDataset)

对源RDD和参数RDD求交集后返回一个新的RDD

join(otherDataset, [numTasks])

在类型为(K,V)和(K,W)的RDD上调用,返回一个相同key对应的所有元素对在一起的(K,(V,W))的RDD

首先说明下数据address.csv是美食店铺对应的地址
val addressRDD = spark.textFile("data/address.csv")
addressRDD.take(10).foreach(println(_))

打印》》》
店铺ID,公共交通,纬度,经度,行政区名称
id10002222,,23.081253,113.328349,海珠区
id100412754,,23.095611,113.272257,海珠区
id101488702,,23.09487246,113.2727256,海珠区
.......

val foodRDD = footerData.map(
      line => line.split(",")
    ).filter(line => line.length>13)
      .map(line => (line(13),line(0)))

    val addressRDD = addressData.map(
      line => {
        val adds = line.split(",")
        (adds(0),adds(4))
      }
    )

    foodRDD.join(addressRDD).take(10)foreach(println(_))

cogroup(otherDataset, [numTasks])

在类型为(K,V)和(K,W)的RDD上调用,返回一个(K,(Iterable<V>,Iterable<W>))类型的RDD

groupBy()

接收一个函数,这个函数返回的值作为key,然后通过这个key来对里面的元素进行分组

groupByKey()

groupByKey也是对每个key进行操作,但只生成一个序列。

collect()

数组的形式返回数据集的所有元素

常用计算方法

reduce(func)

每次传入两个参数通过fun 的到一个返回值,该返回值继续与后面的值进行调用fun,直到所有的数据计算完成,最后返回一个计算结果

data.reduce(
      (a,b) => {
        println(a)
        println(b)
        ("和",a._2 + b._2)
      }
    )
两个参数分别代表RDD中的两个记录,返回值被RDD用来进行递归计算,其中a表示上一个数据

reduceByKey(func, [numTasks])

在一个(K,V)的RDD上调用,返回一个(K,V)的RDD,使用指定的reduce函数,将相同key的值聚合到一起,reduce任务的个数可以通过第二个可选的参数来设置。

countByKey()

针对(K,V)类型的RDD,返回一个(K,Int)的map,表示每一个key对应的元素个数

count()

返回元素数量

猜你喜欢

转载自blog.csdn.net/lihao1107156171/article/details/115057844