Scala Spark数据帧填充
我想在数据帧上执行“filldown”类型的操作,以删除空值,并确保最后一行是一种摘要行,根据Scala Spark数据帧填充,scala,apache-spark,pyspark,azure-synapse,Scala,Apache Spark,Pyspark,Azure Synapse,我想在数据帧上执行“filldown”类型的操作,以删除空值,并确保最后一行是一种摘要行,根据时间戳(按项目ID分组)包含每列的最后已知值。当我使用Azure Synapse笔记本时,语言可以是Scala、Pyspark、SparkSQL甚至是c。然而这里的问题是,真正的解决方案有数百万行和数百列,因此我需要一个能够利用Spark的动态解决方案。我们可以提供一个大型集群,以确保我们能够充分利用它 样本数据: // Assign sample data to dataframe val df =
时间戳
(按项目ID
分组)包含每列的最后已知值。当我使用Azure Synapse笔记本时,语言可以是Scala、Pyspark、SparkSQL甚至是c。然而这里的问题是,真正的解决方案有数百万行和数百列,因此我需要一个能够利用Spark的动态解决方案。我们可以提供一个大型集群,以确保我们能够充分利用它
样本数据:
// Assign sample data to dataframe
val df = Seq(
( 1, "10/01/2021", 1, "abc", null ),
( 2, "11/01/2021", 1, null, "bbb" ),
( 3, "12/01/2021", 1, "ccc", null ),
( 4, "13/01/2021", 1, null, "ddd" ),
( 5, "10/01/2021", 2, "eee", "fff" ),
( 6, "11/01/2021", 2, null, null ),
( 7, "12/01/2021", 2, null, null )
).
toDF("eventId", "timestamp", "itemId", "attrib1", "attrib2")
df.show
第4行和第7行作为摘要行的预期结果:
+-------+----------+------+-------+-------+
|eventId| timestamp|itemId|attrib1|attrib2|
+-------+----------+------+-------+-------+
| 1|10/01/2021| 1| abc| null|
| 2|11/01/2021| 1| abc| bbb|
| 3|12/01/2021| 1| ccc| bbb|
| 4|13/01/2021| 1| ccc| ddd|
| 5|10/01/2021| 2| eee| fff|
| 6|11/01/2021| 2| eee| fff|
| 7|12/01/2021| 2| eee| fff|
+-------+----------+------+-------+-------+
我已经检查了这个选项,但在适应我的用例时遇到了困难
我有一种可以工作的SparkSQL解决方案,但是对于大量的列来说,它会非常冗长,希望有更容易维护的东西:
%%sql
WITH cte (
SELECT
eventId,
itemId,
ROW_NUMBER() OVER( PARTITION BY itemId ORDER BY timestamp ) AS rn,
attrib1,
attrib2
FROM df
)
SELECT
eventId,
itemId,
CASE rn WHEN 1 THEN attrib1
ELSE COALESCE( attrib1, LAST_VALUE(attrib1, true) OVER( PARTITION BY itemId ) )
END AS attrib1_xlast,
CASE rn WHEN 1 THEN attrib2
ELSE COALESCE( attrib2, LAST_VALUE(attrib2, true) OVER( PARTITION BY itemId ) )
END AS attrib2_xlast
FROM cte
ORDER BY eventId
对于许多
列
,您可以创建一个表达式
,如下所示
val window = Window.partitionBy($"itemId").orderBy($"timestamp")
// Instead of selecting columns you could create a list of columns
val expr = df.columns
.map(c => coalesce(col(c), last(col(c), true).over(window)).as(c))
df.select(expr: _*).show(false)
更新:
val mainColumns = df.columns.filterNot(_.startsWith("attrib"))
val aggColumns = df.columns.diff(mainColumns).map(c => coalesce(col(c), last(col(c), true).over(window)).as(c))
df.select(( mainColumns.map(col) ++ aggColumns): _*).show(false)
结果:
+-------+----------+------+-------+-------+
|eventId|timestamp |itemId|attrib1|attrib2|
+-------+----------+------+-------+-------+
|1 |10/01/2021|1 |abc |null |
|2 |11/01/2021|1 |abc |bbb |
|3 |12/01/2021|1 |ccc |bbb |
|4 |13/01/2021|1 |ccc |ddd |
|5 |10/01/2021|2 |eee |fff |
|6 |11/01/2021|2 |eee |fff |
|7 |12/01/2021|2 |eee |fff |
+-------+----------+------+-------+-------+
有没有排除某些列的方法?您可以
drop
将列作为df.columns.drop(0)
带索引的列,或者可以使用filterNot
df.columns.filterNot(uu.endsWith(“eventId”))Ah ok,我的意思是,我只想在某些列上运行填充,并创建一个结果数据框,其中一些列未被触及,一些列应用了填充,有意义吗?如果您查看我的SQL示例,您可以看到eventId、timestamp和itemId未被更改,只有attrib1和2被更改。比如“只填充[attrib1,attrib2]列”或者“只填充以attrib开头的列”。有意义吗?请检查更新,您也可以定义自定义字段列表,而不是以这种方式进行准备。感谢更新。如果你认为这是一个有趣的问题,不要害怕对它投反对票!