前几个月的最后日期 ID
Last Date Id Of Previous Months
我的数据框有一个 DateId(即一个整数列,将日期定义为自 1993 年 6 月 25 日以来的天数)。 Objective是计算列中每个日期之前的月份最后一天的日期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
仍然是spark Column类型并且.cast(Int)
不适用。
请注意:在 SO 问题中解决了类似的问题,但是当在此处应用 timeStart
常量时,相同的方法失败了。此外,使用函数比表达式更可取,因为相同的计算使用具有真实数据的多列。
在使用 Scala 转换函数测试了许多选项之后,基于 UDF 的 Java 字符串和 SimpleDateFormat
我唯一能弄清楚的是:
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))
})
添加另一个验证支持函数和一个简单的 select:
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
你能从 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);
}
编辑:根据您(Dan,提问者)的说法,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 作为期望的结果,但我认为这是一个错字?
请欣赏代码的清晰和不言自明。
我的数据框有一个 DateId(即一个整数列,将日期定义为自 1993 年 6 月 25 日以来的天数)。 Objective是计算列中每个日期之前的月份最后一天的日期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
仍然是spark Column类型并且.cast(Int)
不适用。
请注意:在 timeStart
常量时,相同的方法失败了。此外,使用函数比表达式更可取,因为相同的计算使用具有真实数据的多列。
在使用 Scala 转换函数测试了许多选项之后,基于 UDF 的 Java 字符串和 SimpleDateFormat
我唯一能弄清楚的是:
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))
})
添加另一个验证支持函数和一个简单的 select:
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
你能从 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);
}
编辑:根据您(Dan,提问者)的说法,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 作为期望的结果,但我认为这是一个错字?
请欣赏代码的清晰和不言自明。