上个月的最后日期ID

时间:2018-07-25 21:22:58

标签: scala date apache-spark-sql days

我的数据框有一个DateId(即,将日期定义为从1993-06-25开始的天数的整数列)。目的是计算列中每个日期之前一个月的最后一天的日期ID:

DateId ->  _intermittent calc Date_       -> _result LastDayOfPriorMonthId_
9063   ->  2018-04-18                     -> 9045 (i.e. 2018-03-31)
8771   ->  2017-06-30                     -> 8741 (i.e. 2017-05-31)
9175   ->  2018-08-08                     -> 9167 (i.e. 2018-07-31)

解决方案真的很简单,但是我遇到类型转换问题:

val a = Seq(9063, 8771, 9175).toDF("DateId")
val timeStart = to_date(lit("1993-06-25"))


val dateIdAdd : (Column) => Column = x => {x - date_add(timeStart, x).DATE_OF_MONTH}

函数编译失败,并出现以下错误:

notebook:2: error: type mismatch;
found   : org.apache.spark.sql.Column
required: Int
   x - date_add(timeStart, x).DATE_OF_MONTH

.cast(IntegerType)之类的表达式不会改变结果(x仍然是火花列类型,.cast(Int)不适用。

请注意:类似的问题在this SO问题中得到解决,但是当在此处应用timeStart常量时,相同的方法将失败。与表达式相比,也首选使用函数,因为使用相同的计算会使用具有实际数据的多列。

2 个答案:

答案 0 :(得分:1)

可以从Java翻译吗?抱歉,我还没有编写Scala代码。

private static final LocalDate baseDate = LocalDate.of(1993, Month.JUNE, 25);

public static long dateIdAdd(long dateId) {
    LocalDate date = baseDate.plusDays(dateId);
    LocalDate lastOfPrevMonth = YearMonth.from(date).minusMonths(1).atEndOfMonth();
    return ChronoUnit.DAYS.between(baseDate, lastOfPrevMonth);
}

编辑:根据您的意见(丹,问问者),Scala版本为:

val baseDate = LocalDate.of(1993, Month.JUNE, 25)
val lastDayIdOfPriorMonth = udf((dateId : Long) => {
    val date = baseDate.plusDays(dateId)
    val lastOfPrevMonth = YearMonth.from(date).minusMonths(1).atEndOfMonth()
    ChronoUnit.DAYS.between(baseDate, lastOfPrevMonth)
})

让我们尝试使用示例日期(再次使用Java):

    System.out.println("9063 -> " + dateIdAdd(9063));
    System.out.println("8771 -> " + dateIdAdd(8771));
    System.out.println("9175 -> " + dateIdAdd(9175));

此打印:

9063 -> 9045
8771 -> 8741
9175 -> 9167

在您的问题中,您在上一种情况下给出了9176的理想结果,但我认为那是错字?

请享受代码的清晰和不言自明。

答案 1 :(得分:0)

在使用Scala转换功能测试了许多选项之后,我只能弄清楚基于带有Java字符串和SimpleDateFormat的UDF的攻击:

val dateIdAdd = udf((dateId : Long) => {
   val d = new SimpleDateFormat("yyyy-MM-dd")
   val ts = d.parse("1993-06-25")
   val tc = d.format(new Date(ts.getTime() + (24 * 3600 * 1000 * dateId)))
   dateId - Integer.parseInt(tc.substring(tc.length()-2))
})

在添加了另一个用于验证的支持功能并进行了简单选择之后:

val dateIdToDate = udf((dateId : Long) => {
  val d = new SimpleDateFormat("yyyy-MM-dd")
  val ts = d.parse("1993-06-25")
  d.format(new Date(ts.getTime() + (24 * 3600 * 1000 * dateId)))
})

val aa = a.select($"*"
             , dateIdToDate($"DateId") as "CalcDateFromId"
             , dateIdAdd($"DateId") as "CalcLastDayOfMonthId")

display(aa)

产生了预期的结果(但我怀疑这是最有效的方法):

DateId  CalcDateFromId  CalcLastDayOfMonthId
9063    4/18/2018       9045
8771    6/30/2017       8741
9175    8/8/2018        9167