在尝试用null替换Spark数据帧的特定列的值时遇到问题。 我有一个包含五十多个列的数据框,其中两个是关键列。我想创建一个具有相同架构的新数据框,并且新数据框应具有键列中的值和非键列中的空值。 我尝试了以下方法,但遇到了问题:
//old_df is the existing Dataframe
val key_cols = List("id", "key_number")
val non_key_cols = old_df.columns.toList.filterNot(key_cols.contains(_))
val key_col_df = old_df.select(key_cols.head, key_cols.tail:_*)
val non_key_cols_df = old_df.select(non_key_cols.head, non_key_cols.tail:_*)
val list_cols = List.fill(non_key_cols_df.columns.size)("NULL")
val rdd_list_cols = spark.sparkContext.parallelize(Seq(list_cols)).map(l => Row(l:_*))
val list_df = spark.createDataFrame(rdd_list_cols, non_key_cols_df.schema)
val new_df = key_col_df.crossJoin(list_df)
当我在old_df
中只有字符串类型的列时,这种方法很好。但是我有一些double类型和int类型的列,这会引发错误,因为rdd是空字符串的列表。
为避免这种情况,我尝试将list_df
作为一个空数据框,将模式设为non_key_cols_df
,但是crossJoin
的结果是一个空数据框,我相信是因为一个数据框为空。
我的要求是将non_key_cols
作为具有Null的单行数据帧,以便可以对crossJoin
执行key_col_df
并形成所需的new_df
。
将数据框的关键列以外的所有列更新为null的任何其他简便方法也可以解决我的问题。预先感谢
答案 0 :(得分:2)
crossJoin
是一项昂贵的操作,因此您希望尽可能避免使用它。
一个更简单的解决方案是遍历所有非键列,并使用lit(null)
插入null。使用foldLeft
可以按以下步骤完成:
val keyCols = List("id", "key_number")
val nonKeyCols = df.columns.filterNot(keyCols.contains(_))
val df2 = nonKeyCols.foldLeft(df)((df, c) => df.withColumn(c, lit(null)))
输入示例:
+---+----------+---+----+
| id|key_number| c| d|
+---+----------+---+----+
| 1| 2| 3| 4.0|
| 5| 6| 7| 8.0|
| 9| 10| 11|12.0|
+---+----------+---+----+
将给出:
+---+----------+----+----+
| id|key_number| c| d|
+---+----------+----+----+
| 1| 2|null|null|
| 5| 6|null|null|
| 9| 10|null|null|
+---+----------+----+----+
答案 1 :(得分:0)
Shaido答案的缺点很小-列类型将丢失。 可以通过使用模式来解决,例如:
val nonKeyCols = df.schema.fields.filterNot(f => keyCols.contains(f.name))
val df2 = nonKeyCols.foldLeft(df)((df, c) => df.withColumn(c.name, lit(null).cast(c.dataType)))