RDD / Scala从RDD获取一列

时间:2017-02-27 11:39:03

标签: scala apache-spark rdd

我有一个RDD[Log]文件,其中包含各种字段(username,content,date,bytes),我希望为每个字段/列找到不同的内容。

例如,我想获得RDD中找到的最小/最大和平均字节数。当我这样做时:

val q1 = cleanRdd.filter(x => x.bytes != 0)

我得到了带有字节的RDD的完整行!= 0.但是我怎样才能实际求和它们,计算平均值,找到最小值/最大值等?如何从RDD中只选择一列并在其上应用转换?

编辑:普拉萨德告诉我有关更改类型数据帧时,他就如何使虽然没有做说明,而我不能在网站上找到了坚实的答案。任何帮助都会很棒。

编辑:LOG类:

case class Log (username: String, date: String, status: Int, content: Int)

使用cleanRdd.take(5).foreach(println)给出类似这样的东西

Log(199.72.81.55 ,01/Jul/1995:00:00:01 -0400,200,6245)
Log(unicomp6.unicomp.net ,01/Jul/1995:00:00:06 -0400,200,3985)
Log(199.120.110.21 ,01/Jul/1995:00:00:09 -0400,200,4085)
Log(burger.letters.com ,01/Jul/1995:00:00:11 -0400,304,0)
Log(199.120.110.21 ,01/Jul/1995:00:00:11 -0400,200,4179)

2 个答案:

答案 0 :(得分:2)

嗯......你有很多问题。

所以...你有一个日志的抽象

case class Log (username: String, date: String, status: Int, content: Int, byte: Int)

Que - 如何从RDD中只选择一列。

答案 - 你有一个map函数与RDD' s。因此,对于RDD[A]map会使用A => B类型的地图/转换函数将其转换为RDD[B]

val logRdd: RDD[Log] = ...

val byteRdd = logRdd
  .filter(l => l.bytes != 0)
  .map(l => l.byte)

阙 - 我怎么能真正总结它们呢?

答案 - 您可以使用reduce / fold / aggregate来完成。

val sum = byteRdd.reduce((acc, b) => acc + b)

val sum = byteRdd.fold(0)((acc, b) => acc + b)

val sum = byteRdd.aggregate(0)(
  (acc, b) => acc + b,
  (acc1, acc2) => acc1 + acc2
)

注意::这里需要注意的一点是,Int的总和可能会比Int能够处理的更大。因此,在大多数现实生活中,我们应该至少使用Long作为累加器,而不是Int,这实际上会删除reducefold作为选项。而且我们只剩下聚合。

val sum = byteRdd.aggregate(0l)(
  (acc, b) => acc + b,
  (acc1, acc2) => acc1 + acc2
)

现在,如果您必须计算多个内容,例如min,max,avg,那么我建议您使用单个aggregate而不是像这样的多个来计算它们,

// (count, sum, min, max)
val accInit = (0, 0, Int.MaxValue, Int.MinValue)

val (count, sum, min, max) = byteRdd.aggregate(accInit)(
  { case ((count, sum, min, max), b) => 
      (count + 1, sum + b, Math.min(min, b), Math.max(max, b)) },
  { case ((count1, sum1, min1, max1), (count2, sum2, min2, max2)) => 
      (count1 + count2, sum1 + sum2, Math.min(min1, min2), Math.max(max1, max2)) }
})

val avg = sum.toDouble / count

答案 1 :(得分:0)

查看DataFrame API。您需要将RDD转换为DataFrame,然后您可以使用如下所示的min,max,avg函数:

val rdd = cleanRdd.filter(x => x.bytes != 0)
val df = sparkSession.sqlContext.createDataFrame(rdd, classOf[Log])

假设您想要对列bytes进行操作,那么

import org.apache.spark.sql.functions._

df.select(avg("bytes")).show
df.select(min("bytes")).show
df.select(max("bytes")).show

更新

在spark-shell中尝试以下内容。检查结果截图...

case class Log (username: String, date: String, status: Int, content: Int)

val inputRDD = sc.parallelize(Seq(Log("199.72.81.55","01/Jul/1995:00:00:01 -0400",200,6245), Log("unicomp6.unicomp.net","01/Jul/1995:00:00:06 -0400",200,3985), Log("199.120.110.21","01/Jul/1995:00:00:09 -0400",200,4085), Log("burger.letters.com","01/Jul/1995:00:00:11 -0400",304,0), Log("199.120.110.21","01/Jul/1995:00:00:11 -0400",200,4179)))

val rdd = inputRDD.filter(x => x.content != 0)

val df = rdd.toDF("username", "date", "status", "content")

df.printSchema

import org.apache.spark.sql.functions._

df.select(avg("content")).show
df.select(min("content")).show
df.select(max("content")).show

enter image description here

enter image description here