Scala 数据帧转换

Scala 数据帧转换,scala,apache-spark,Scala,Apache Spark,我不熟悉Spark和Scala。我有一个拥有大量数据的数据框架。模式与此类似 让我们调用此数据帧empDF: id name emp_id code date 1 Andrew D01 C101 2012-06-14 2 James D02 C101 2013-02-26 3 James D02 C102 2013-12-29 4 J

我不熟悉Spark和Scala。我有一个拥有大量数据的数据框架。模式与此类似

让我们调用此
数据帧empDF

       id   name      emp_id  code  date
       1    Andrew    D01     C101  2012-06-14
       2    James     D02     C101  2013-02-26
       3    James     D02     C102  2013-12-29
       4    James     D02     C101  2010-09-27
       5    Andrew    D01     C101  2013-10-12
       6    Andrew    D01     C102  2011-10-13
我以
DataFrame[Row]
对象的形式从数据库中读取这些数据。现在,我必须执行以下步骤:

对于代码C101的每一行,必须设置级别大于1,对于其他代码,级别应为0。如果没有以前的记录, 该级别设置为
1
。如果以前的记录比该记录早两年或两年以上,则级别设置为
2
。 在此步骤之后,dataframe应该如下所示

       id   name      emp_id  code  date         level
       1    Andrew    D01     C101  2012-06-14     2
       2    James     D02     C101  2013-02-26     2
       3    James     D02     C102  2012-12-29     0
       4    James     D02     C101  2010-09-27     1
       5    Andrew    D01     C101  2009-10-12     1
       6    Andrew    D01     C102  2010-10-13     0
第一行和第二行具有级别
2
,因为该员工的记录较旧,且两行之间的日期差超过两年。级别为
1
的行是因为没有以前日期的记录,级别为“0”的行是因为我们已将所有代码标记为除C101之外的0级别

现在,对于级别为2的行,我们必须检查代码C102是否在去年内应用于这些员工,如果应用,则将级别设置为3,否则不要更改级别。在最终结果数据框中,除了代码C101之外的所有行都应该被删除

经过以上两个步骤后,生成的数据帧应如下所示:

     id name    emp_id  code  date       level
     1  Andrew  D01     C101  2012-06-14   2
     2  James   D02     C101  2013-02-26   3
     4  James   D02     C101  2010-09-27   1
     5  Andrew  2013    C101  2013-10-12   1
请注意,第一行具有级别
2
,因为该员工去年没有C102,而第二行具有去年的C102。 如何在Scala中使用dataframe api以及诸如
map
flatmap
reduce
等函数实现这一点?

您可以使用:

import org.apache.spark.sql.expressions.Window
导入org.apache.spark.sql.functions_
val window=window.partitionBy(“名称”).orderBy(“日期”)
val lagCol=滞后(列(“日期”),1)。结束(窗口)
val diff=datediff(col(“日期”),col(“以前的日期”))
val级别=何时(
col(“previousDate”).isNull | |(diff您可以使用:

import org.apache.spark.sql.expressions.Window
导入org.apache.spark.sql.functions_
val window=window.partitionBy(“名称”).orderBy(“日期”)
val lagCol=滞后(列(“日期”),1)。结束(窗口)
val diff=datediff(col(“日期”),col(“以前的日期”))
val级别=何时(

col(“previousDate”).isNull | | |(diff)你尝试了什么?你得到了什么结果,与你预期的结果相比?看。此外,我试图清理英语,但你应该再次检查我的解释是否准确。以后,记得使用短句,清楚你提到的内容。(使用'record'而不是'it'。你对'history'的使用也很奇怪。)顺便说一句,如果你没有显示“尝试过”的代码,你不太可能得到帮助——你的问题可能就结束了。“华而不实”为什么,是的!我是最花哨的。你尝试过什么?你得到了什么结果,与你期望的相比?看。我也尝试过清理英语,但你应该再次检查我的解释是否准确。以后,记得使用短句,清楚你提到的内容。(使用'record'而不是'it'。你对'history'的使用也很奇怪。)顺便说一句,如果你没有显示“尝试过”的代码,你不太可能得到帮助——你的问题可能就结束了。“华而不实”?为什么,是的!我是最花哨的。@thanx Daniel de Paula。我真的很感激你的回答。我还有一个步骤,就是在你的答案中新的数据框上,我需要检查去年的员工是否有代码C102,如果有,那么将这一行标记为3级。@AtifShahzad,但在第一步中,你删除了所有代码为dif的行与C101不同,因此将不存在任何代码为C102的行。我不太明白。在第一步中,我们只需要C101进行处理,但当我们处理它时,在最后一步中,我们需要检查第一步中仅处理的行上是否有C102。第一个数据帧也可能有C103、C104和其他行,但我们只需要在最后一步中检查C102@AtifShahzad对不起,我还是不明白。你能用你的解释和一个例子来编辑你的问题吗?@thanx Daniel de Paula。我真的很感激你的回答。我还有一步要做的就是在你的答案中添加新的数据框,我需要检查去年员工身上是否有代码C102,如果有,然后标记这个row为3级。@AtifShahzad,但在第一步中,您删除了所有代码与C101不同的行,因此将不存在任何代码为C102的行。我不太明白。在第一步中,我们只需要C101进行处理,但在处理时,在最后一步中,我们需要检查第一步中仅处理的行上是否有C102。第一个数据帧e也可能有C103、C104和其他,但我们只需要在最后一步中检查C102。@AtifShahzad对不起,我仍然不明白。您能用您的解释和示例编辑您的问题吗?
import org.apache.spark.sql.expressions.Window
import org.apache.spark.sql.functions._

val window = Window.partitionBy("name").orderBy("date")
val lagCol = lag(col("date"), 1).over(window)
val diff = datediff(col("date"), col("previousDate"))
val level = when(
  col("previousDate").isNull || (diff <= 730), 1
).otherwise(2)

val newDF = empDF
  .where(col("code") === "C101")
  .withColumn("previousDate", lagCol)
  .withColumn("level", level)
  .drop("previousDate")

newDF.orderBy("id").show

+---+------+------+----+----------+-----+
| id|  name|emp_id|code|      date|level|
+---+------+------+----+----------+-----+
|  1|Andrew|   D01|C101|2012-06-14|    1|
|  2|James |   D02|C101|2013-02-26|    2|
|  4|James |   D02|C101|2010-09-27|    1|
+---+------+------+----+----------+-----+
// Input data
val df = {
    import org.apache.spark.sql._
    import org.apache.spark.sql.types._
    import scala.collection.JavaConverters._
    import java.time.LocalDate

    val simpleSchema = StructType(
        StructField("id", IntegerType) ::
        StructField("name", StringType) ::
        StructField("emp_id", StringType) ::
        StructField("code", StringType) ::
        StructField("date", DateType) :: Nil)

    val data = List(
        Row(1, "Andrew", "D01", "C101", java.sql.Date.valueOf(LocalDate.of(2012, 6, 14))),
        Row(2, "James", "D02", "C101", java.sql.Date.valueOf(LocalDate.of(2013, 2, 26))),
        Row(3, "James", "D02", "C102", java.sql.Date.valueOf(LocalDate.of(2013, 12, 29))),
        Row(4, "James", "D02", "C101", java.sql.Date.valueOf(LocalDate.of(2010, 9, 27)))
    )    

    spark.createDataFrame(data.asJava, simpleSchema)
}
df.show()
// Filter and level calculation.
val df2 = df.filter(col("code") === "C101").
    withColumn("level", when(datediff(col("date"), min(col("date")).over(Window.partitionBy("emp_id"))) >= 365 * 2, 2).otherwise(1))
df2.show()