我有一个带有startTime和一些要素向量的设备ID数据,需要根据hour
或weekday_hour
进行合并。样本数据如下:
+-----+-------------------+--------------------+
|hh_id| startTime| hash|
+-----+-------------------+--------------------+
|dev01|2016-10-10 00:01:04|(1048576,[121964,...|
|dev02|2016-10-10 00:17:45|(1048576,[121964,...|
|dev01|2016-10-10 00:18:01|(1048576,[121964,...|
|dev10|2016-10-10 00:19:48|(1048576,[121964,...|
|dev05|2016-10-10 00:20:00|(1048576,[121964,...|
|dev08|2016-10-10 00:45:13|(1048576,[121964,...|
|dev05|2016-10-10 00:56:25|(1048576,[121964,...|
这些功能基本上是SparseVectors,它们由自定义函数合并。当我尝试按以下方式创建键列时:
val columnMap = Map("hour" -> hour($"startTime"), "weekday_hour" -> getWeekdayHourUDF($"startTime"))
val grouping = "hour"
val newDF = oldDF.withColumn("dt_key", columnMap(grouping))
我得到java.io.NotSerializableException
。完整的堆栈跟踪如下:
Caused by: java.io.NotSerializableException: org.apache.spark.sql.Column
Serialization stack:
- object not serializable (class: org.apache.spark.sql.Column, value: hour(startTime))
- field (class: scala.collection.immutable.Map$Map3, name: value1, type: class java.lang.Object)
- object (class scala.collection.immutable.Map$Map3, Map(hour -> hour(startTime), weekday_hour -> UDF(startTime), none -> 0))
- field (class: linef03f4aaf3a1c4f109fce271f7b5b1e30104.$read$$iw$$iw$$iw$$iw$$iw$$iw$$iw$$iw$$iw$$iw, name: groupingColumnMap, type: interface scala.collection.immutable.Map)
- object (class linef03f4aaf3a1c4f109fce271f7b5b1e30104.$read$$iw$$iw$$iw$$iw$$iw$$iw$$iw$$iw$$iw$$iw, linef03f4aaf3a1c4f109fce271f7b5b1e30104.$read$$iw$$iw$$iw$$iw$$iw$$iw$$iw$$iw$$iw$$iw@4f1f9a63)
- field (class: linef03f4aaf3a1c4f109fce271f7b5b1e30104.$read$$iw$$iw$$iw$$iw$$iw$$iw$$iw$$iw$$iw, name: $iw, type: class linef03f4aaf3a1c4f109fce271f7b5b1e30104.$read$$iw$$iw$$iw$$iw$$iw$$iw$$iw$$iw$$iw$$iw)
- object (class linef03f4aaf3a1c4f109fce271f7b5b1e30104.$read$$iw$$iw$$iw$$iw$$iw$$iw$$iw$$iw$$iw, linef03f4aaf3a1c4f109fce271f7b5b1e30104.$read$$iw$$iw$$iw$$iw$$iw$$iw$$iw$$iw$$iw@207d6d1e)
但是当我尝试执行相同的逻辑而不显式创建列时,使用if-else,我不会遇到任何此类错误。
val newDF = if(groupingKey == "hour") {
oldDF.withColumn("dt_key", hour($"startTime")
} else {
oldDF.withColumn("dt_key", getWeekdayHourUDF($"startTime")
}
使用Map-way来实现它会非常方便,因为可能有更多类型的密钥提取方法。请帮我弄清楚为什么会引起这个问题。
答案 0 :(得分:0)
您可以使用{
"status": "Active"
}
内置函数
when
udf功能
或者,您可以创建val groupingKey = //"hour" or "weekday_hour"
import org.apache.spark.sql.functions._
df.withColumn("dt_key", when(lit(groupingKey) === "hour", hour($"startTime")).otherwise(when(lit(groupingKey) === "weekday_hour", getWeekdayHourUDF($"startTime")).otherwise(lit(0)))).show(false)
函数以创建地图列
udf
并将其用作
import org.apache.spark.sql.functions._
def mapUdf = udf((hour: Int, weekdayhour: Int, groupingKey: String) => if(groupByKey.equalsIgnoreCase("hour")) hour else if(groupByKey.equalsIgnoreCase("weekday_hour")) weekdayhour else 0)
我希望答案很有帮助
答案 1 :(得分:0)
也许有点晚了,但我在 Spark 2.4.6 并且无法重现该问题。我猜测代码为多个键调用 columnMap
。如果您提供一个易于重现的示例,包括数据(1 行数据集就足够了),它会有所帮助。但是,正如堆栈跟踪所说,Column
类确实不是Serializable
,我将根据我目前的理解尝试详细说明。
TLDR;避免这种情况的一种简单方法是将 val
转换为 def
。
我相信很清楚为什么用 when
案例或 UDF 表达同样的事情是有效的。
第一次尝试:这样的事情可能不起作用的原因是因为 (a) Column
类是不可序列化的(我认为这是一个有意识的设计选择,因为它的预期是Spark API 中的角色),并且 (b) 表达式中没有任何内容
oldDF.withColumn("dt_key", columnMap(grouping))
这告诉 Spark Column
的第二个参数的实际具体 withColumn
是什么,这意味着具体的 Map[String, Column]
对象需要通过网络发送给执行程序,当引发这样的异常时。
第二次尝试:第二次尝试成功的原因是关于定义 groupingKey
所需的此 DataFrame
参数的相同决定可以完全发生在驱动程序上。< /p>
考虑使用 DataFrame
API 作为查询构建器的 Spark 代码,或者保存执行计划的东西,而不是数据本身,会有所帮助。一旦您对其调用操作(write
、show
、count
等),Spark 会生成将任务发送给执行程序的代码。那时,实现 DataFrame
/Dataset
所需的所有信息必须已经在查询计划中正确编码,或者需要可序列化,以便可以通过网络发送。
def
通常可以解决此类问题,因为
def columnMap: Map[String, Column] = Map("a" -> hour($"startTime"), "weekday_hour" -> UDF($"startTime"))
不是具体的 Map
对象本身,而是在每次调用时创建一个新的 Map[String, Column]
这个Map
。
This 和 this 似乎是有关该主题的好资源。我承认我明白为什么使用 Function
like
val columnMap = () => Map("a" -> hour($"startTime"), "b" -> UDF($"startTime"))
然后 columnMap()("a")
会起作用,因为反编译的字节码显示 scala.Function
被定义为 Serializable
的具体实例,但我不明白为什么 {{1}有效,因为这对他们来说似乎并非如此。无论如何,我希望这会有所帮助。