Spark Dataframe用Null替换一行中特定列的值

时间:2018-08-29 05:15:15

标签: scala apache-spark dataframe null apache-spark-sql

在尝试用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的任何其他简便方法也可以解决我的问题。预先感谢

2 个答案:

答案 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)))