我有一个类似于((String, String), TimeStamp)
的RDD。我有大量的记录,我想为每个键选择 具有最新TimeStamp值的记录。我已经尝试了以下代码,仍然在努力解决这个问题。有人可以帮我这么做吗?
我尝试下面的代码是错误的,并且不能正常工作
val context = sparkSession.read.format("jdbc")
.option("driver", "com.mysql.jdbc.Driver")
.option("url", url)
.option("dbtable", "student_risk")
.option("user", "user")
.option("password", "password")
.load()
context.cache();
val studentRDD = context.rdd.map(r => ((r.getString(r.fieldIndex("course_id")), r.getString(r.fieldIndex("student_id"))), r.getTimestamp(r.fieldIndex("risk_date_time"))))
val filteredRDD = studentRDD.collect().map(z => (z._1, z._2)).reduce((x, y) => (x._2.compareTo(y._2)))
答案 0 :(得分:6)
直接在DataFrame上进行操作很简单(在这里奇怪地命名为context
):
val result = context
.groupBy("course_id", "student_id")
.agg(min("risk_date_time") as "risk_date_time")
然后您可以像之前那样将其转换为RDD(如果需要) - 结果具有相同的架构。
如果您想通过RDD执行此操作,请使用reduceByKey
:
studentRDD.reduceByKey((t1, t2) => if (t1.before(t2)) t1 else t2)
答案 1 :(得分:2)
首先,您的代码提供的结果不正确,因为reduce不正确。 reduce函数返回一个int(来自compareTo)而不是x,y,但int没有._2成员。 要更正此尝试:
studentRDD.collect().map(z => (z._1, z._2)).reduce((x ,y) => if (x._2.compareTo(y._2) < 0) x else y)._1
基本上这个新函数会以较小的时间返回记录,然后返回你取得密钥的整体结果(最小的)。
请注意,由于收集,您在驱动程序上执行了所有这些操作。没有理由收集,映射和减少RDD上的工作,因此您可以通过执行以下操作获得相同的结果(并且仍然可以扩展): studentRDD.map(z =&gt;(z._1,z._2))。reduce((x,y)=&gt; if(x._2.compareTo(y._2)&lt; 0)x else y)。 _1
您可以直接从上下文数据框执行此操作:
val targetRow = context.agg(min(struct('risk_date_time, 'course_id, 'student_id)) as "rec").select($"rec.*").collect()(0)
val key = (targetRow.getString(1), targetRow.getString(2))