Scala 查找每年的最大分数总和

Scala 查找每年的最大分数总和,scala,apache-spark,Scala,Apache Spark,我是Scala和Spark的新手,是否有人可以优化Scala代码以查找学生每年的最高分数 val m=sc.textFile("marks.csv") val SumOfMarks=m.map(_.split(",")).mapPartitionsWithIndex {(idx, iter) => if (idx == 0) iter.drop(1) else iter}.map(l=>((l(0),l(1)),l(3).toInt)).reduceByKey(_+_).sortBy

我是Scala和Spark的新手,是否有人可以优化Scala代码以查找学生每年的最高分数

val m=sc.textFile("marks.csv")
val SumOfMarks=m.map(_.split(",")).mapPartitionsWithIndex {(idx, iter) => if (idx == 0) iter.drop(1) else iter}.map(l=>((l(0),l(1)),l(3).toInt)).reduceByKey(_+_).sortBy(line => (line._1._1, line._2), ascending=false)
var s:Int=0
var y:String="0"
for(i<-SumOfMarks){ if((i._1._1!=y) || (i._2==s && i._1._1==y)){ println(i);s=i._2;y=i._1._1}}


Input : marks.csv
year,student,sub,marks
2016,ram,maths,90
2016,ram,physics,86
2016,ram,chemistry,88
2016,raj,maths,84
2016,raj,physics,96
2016,raj,chemistry,98
2017,raghu,maths,96
2017,raghu,physics,98
2017,raghu,chemistry,94
2017,rajesh,maths,92
2017,rajesh,physics,98
2017,rajesh,chemistry,98

我不确定你所说的“优化”到底是什么意思,但更“scala-y”和“spark-y”的方法可能如下:

import org.apache.spark.sql.expressions.Window
//将数据文件读取为带有行标题的CSV文件。
val marksDF=spark.read.option(“header”、“true”).csv(“marks.csv”)
//计算每个学生每年的总分。新的总分列将被称为“总分”
val marksByStudentYear=marksDF.groupBy(col(“年”)、col(“学生”).agg(sum(col(“分数”)).as(“totMark”))
//每年对分数进行排名。最高分数将获得排名1,第二高的排名2,依此类推。
//排名的好处是,如果两个分数有相同的分数,他们都会得到相同的分数
//同级。
val marksRankedByYear=marksByStudentYear.withColumn(“rank”,densite_rank()。over(Window.partitionBy(“year”)。orderBy($“totMark.desc)))
//最后进行筛选,以便我们每年只获得“最高分”(排名=1),
//按年份和学生姓名排序并显示结果。
val topstudens=marksRankedByYear.filter($“rank”==1.orderBy($“year”,$“student”).show
最佳学生秀
这将在Spark shell中产生以下输出:

+----+-------+-------+----+
|year|student|totMark|rank|
+----+-------+-------+----+
|2016|    raj|  278.0|   1|
|2017|  raghu|  288.0|   1|
|2017| rajesh|  288.0|   1|
+----+-------+-------+----+

如果您需要根据您的问题显示CSV,您可以使用:

topstudens.collect.map(u.mkString(“,”).foreach(println)
产生:

2016,raj,278.0,1
2017,raghu,288.0,1
2017,rajesh,288.0,1
我已经将这个过程分解为各个步骤。这将允许您通过简单地在中间结果上运行show来查看每个步骤中发生了什么。例如,要查看spark.read.option。。。则只需在spark shell中输入marksDF.show

scala> val df = spark.read.format("csv").option("header", "true").load("/CSV file location/marks.csv")
scala> df.registerTempTable("record")
scala> sql(" select year, student, marks from (select year, student, marks, RANK() over (partition by year order by marks desc) rank From ( Select year, student, SUM(marks) as marks from record group by Year, student)) where rank =1 ").show
因为OP想要一个RDD版本,下面是一个例子。它可能不是最优的,但它确实给出了正确的结果:

import org.apache.spark.rdd.rdd
//一个帮助函数,使查看RDD内容稍微容易一些。
def dump[R](rdd:rdd[R])=rdd.collect.foreach(println)
val marksRdd=sc.textFile(“marks.csv”)
//用于注释RDD中内容的case类
案例课堂报告(年份:整数,学生:字符串,子:字符串,分数:整数)
//将RDD创建为一系列报告对象-忽略标题。
val marksReportRdd=marksRdd.map(u.split(“,”).mapPartitionsWithIndex{
(idx,iter)=>if(idx==0)iter.drop(1)else iter
}.map(r=>Report(r(0.toInt,r(1),r(2),r(3.toInt))
//将数据按年份和学生分组。
val marksgroupped=marksReportRdd.groupBy(report=>(report.year,report.student))
//通过将分数相加,计算每个学生每年的总分
//这名学生当年所学的每门学科。
val totalMarkStudentYear=marksgroupped.map{case(key,marks:Iterable[Report])=>(key,marks.foldLeft(0)((acc,rep)=>acc+rep.mark))}
//确定每年的最高分数。
val yearScoreHighest=totalMarkStudentYear.map{case(key,score:Int)=>(key._1,score)}.reduceByKey(math.max(_,_))
//确定每年得分最高的学生名单。
//这是通过加入每个学生每年获得的总分来实现的
//达到每年的最高分数。
//连接在键上执行,该键必须是Tuple2(年份,分数)。
//要实现这一点,必须映射两个RDD以生成具有数据属性的密钥。
//最高分数的数据属性是一个伪值“x”。
//学生成绩的数据属性是学生的姓名。
val highestrankstudentbyear=totalMarkStudentYear.map{case(key,score)=>((key.\u 1,score),key.\u 2)}.join(yearScoreHighest.map(k=>(k,x)))
//最后从加入的RDD中提取年份、学生姓名和分数
//按年份和名称排序。
val result=highestrank studentbyyear.map{case(key,score)=>(key.\u 1,score.\u 1,key.\u 2)}.sortBy(r=>(r.\u 1,r.\u 2))
//显示最终结果。
转储(结果)
val result=highestrank studentbyyear.map{case(key,score)=>(key.\u 1,score.\u 1,key.\u 2)}.sortBy(r=>(r.\u 1,r.\u 2))
转储(结果)
上述结果是:

(2016,raj,278)
(2017,raghu,288)
(2017,rajesh,288)
与前面一样,只需使用dump函数转储中间RDD即可查看它们。注意:转储函数采用RDD。如果要显示数据帧或数据集的内容,请使用其show方法

也许有一个比上面提到的更好的解决方案,但它确实起到了作用

希望RDD版本能够鼓励您尽可能使用数据帧和/或数据集。不仅代码更简单,而且:

  • Spark将评估数据帧和数据集,并可以优化整个转换过程。RDD不是(即,它们在没有优化的情况下一个接一个地执行)。翻译数据帧和基于数据集的过程可能会运行得更快(假设您没有手动优化RDD等价物)
  • 数据集和数据帧允许不同程度的模式(例如命名列和数据类型)
  • 可以使用SQL查询数据帧和数据集
  • DataFrame和DataSet操作/方法更符合SQL构造
  • 数据帧和数据集比RDD更易于使用
  • 数据集(和RDD)提供编译时错误检测
  • 数据集是未来的发展方向
有关更多信息,请查看以下两个链接:

或者干脆谷歌“spark我应该使用rdd还是dataframe?”


祝您的项目一切顺利。

在SCALA spark shell上试用

scala> val df = spark.read.format("csv").option("header", "true").load("/CSV file location/marks.csv")
scala> df.registerTempTable("record")
scala> sql(" select year, student, marks from (select year, student, marks, RANK() over (partition by year order by marks desc) rank From ( Select year, student, SUM(marks) as marks from record group by Year, student)) where rank =1 ").show
它将生成下表

+----+-------+-----+
|year|student|marks|
+----+-------+-----+
|2016|    raj|278.0|
|2017|  raghu|288.0|
|2017| rajesh|288.0|
+----+-------+-----+

谢谢,这个很好用。但我正在寻找非DF解决方案。我认为我写的代码是简单的暴力之一,我想它可以得到改进。数据帧增加了很多火花的能力。请允许我问一下,您为什么选择不使用它们???我正在探索如何在Spark Scala中实现Oracle分析/窗口功能,而不使用数据帧。谢谢你的时间。我已经用RDD版本更新了答案。我希望
import org.apache.spark.rdd.RDD
import org.apache.spark.SparkConf
import org.apache.spark.SparkContext
import org.apache.spark.sql.SparkSession
import org.apache.spark.sql.functions
//Finding Max sum of marks each year
object Marks2 {
  def getSparkContext() = {
    val conf = new SparkConf().setAppName("MaxMarksEachYear").setMaster("local")
    val sc = new SparkContext(conf)
    sc
  }

  def dump[R](rdd: RDD[R]) = rdd.collect.foreach(println)

  def main(args: Array[String]): Unit = {
   // System.setProperty("hadoop.home.dir", "D:\\Setup\\hadoop_home")
    val sc = getSparkContext()

    val inpRDD = sc.textFile("marks.csv")
    val head = inpRDD.first()
    val marksRdd = inpRDD.filter(record=> !record.equals(head)).map(rec => rec.split(","))
    val marksByNameyear = marksRdd.map(rec =>((rec(0).toInt,rec(1)),rec(3).toInt))
    
    //marksByNameyear.cache()

    val aggMarksByYearName = marksByNameyear.reduceByKey(_+_)
    val maxMarksByYear = aggMarksByYearName.map(s=> (s._1._1,s._2))reduceByKey(math.max(_, _))
    
    
    val markYearName = aggMarksByYearName.map(s => (s._2.toInt,s._1._2))
    val marksAndYear = maxMarksByYear.map(s => (s._2.toInt,s._1))
    
    val tt = sc.broadcast(marksAndYear.collect().toMap)
    marksAndYear.flatMap {case(key,value) => tt.value.get(key).map {other => (other,value, key)  } } 
    val yearMarksName = marksAndYear.leftOuterJoin(markYearName) 
    
    val result = yearMarksName.map(s =>(s._2._1,s._2._2,s._1)).sortBy(f=>f._3, true)
   
   //dump(markYearName);'
   dump(result)

  }
}