Scala 如何根据以前记录的值更新spark dataframe的列
我在df中有三列Scala 如何根据以前记录的值更新spark dataframe的列,scala,apache-spark,apache-spark-sql,spark-dataframe,Scala,Apache Spark,Apache Spark Sql,Spark Dataframe,我在df中有三列 Col1,col2,col3 X,x1,x2 Z,z1,z2 Y, X,x3,x4 P,p1,p2 Q,q1,q2 Y 我想做以下几件事 当col1=x时,存储col2和col3的值 当col1=y时,将这些列值分配给下一行 预期产量 X,x1,x2 Z,z1,z2 Y,x1,x2 X,x3,x4 P,p1,p2 Q,q1,q2 Y,x3,x4 任何帮助都将不胜感激 注意:-spark 1.6是,有一个功能需要订购 import org.apach
Col1,col2,col3
X,x1,x2
Z,z1,z2
Y,
X,x3,x4
P,p1,p2
Q,q1,q2
Y
我想做以下几件事
当col1=x时,存储col2和col3的值
当col1=y时,将这些列值分配给下一行
预期产量
X,x1,x2
Z,z1,z2
Y,x1,x2
X,x3,x4
P,p1,p2
Q,q1,q2
Y,x3,x4
任何帮助都将不胜感激
注意:-spark 1.6是,有一个功能需要订购
import org.apache.spark.sql.expressions.Window.orderBy
import org.apache.spark.sql.functions.{coalesce, lag}
case class Temp(a: String, b: Option[String], c: Option[String])
val input = ss.createDataFrame(
Seq(
Temp("A", Some("a1"), Some("a2")),
Temp("D", Some("d1"), Some("d2")),
Temp("B", Some("b1"), Some("b2")),
Temp("E", None, None),
Temp("C", None, None)
))
+---+----+----+
| a| b| c|
+---+----+----+
| A| a1| a2|
| D| d1| d2|
| B| b1| b2|
| E|null|null|
| C|null|null|
+---+----+----+
val order = orderBy($"a")
input
.withColumn("b", coalesce($"b", lag($"b", 1).over(order)))
.withColumn("c", coalesce($"c", lag($"c", 1).over(order)))
.show()
+---+---+---+
| a| b| c|
+---+---+---+
| A| a1| a2|
| B| b1| b2|
| C| b1| b2|
| D| d1| d2|
| E| d1| d2|
+---+---+---+
下面是一种使用窗口函数的方法,步骤如下:
last/rowsBetween
Window函数创建带有条件空值的tmp1
,并使用last/rowsBetween
窗口函数对最后一个非空值进行回填cols
和tmp2
foldLeft
val df = Seq(
("X", "x1", "x2"),
("Z", "z1", "z2"),
("Y", "", ""),
("X", "x3", "x4"),
("P", "p1", "p2"),
("Q", "q1", "q2"),
("Y", "", "")
).toDF("col1", "col2", "col3")
import org.apache.spark.sql.functions._
import org.apache.spark.sql.expressions.Window
val colList = df.columns.filter(_ != "col1")
val df2 = df.select($"col1", monotonically_increasing_id.as("id"),
struct(colList.map(col): _*).as("cols")
)
val df3 = df2.
withColumn( "tmp1", when($"col1" === "X", $"cols") ).
withColumn( "tmp2", last("tmp1", ignoreNulls = true).over(
Window.orderBy("id").rowsBetween(Window.unboundedPreceding, 0)
) )
df3.show
// +----+---+-------+-------+-------+
// |col1| id| cols| tmp1| tmp2|
// +----+---+-------+-------+-------+
// | X| 0|[x1,x2]|[x1,x2]|[x1,x2]|
// | Z| 1|[z1,z2]| null|[x1,x2]|
// | Y| 2| [,]| null|[x1,x2]|
// | X| 3|[x3,x4]|[x3,x4]|[x3,x4]|
// | P| 4|[p1,p2]| null|[x3,x4]|
// | Q| 5|[q1,q2]| null|[x3,x4]|
// | Y| 6| [,]| null|[x3,x4]|
// +----+---+-------+-------+-------+
val df4 = df3.withColumn( "newcols",
when($"col1" === "Y", $"tmp2").otherwise($"cols")
).select($"col1", $"newcols")
df4.show
// +----+-------+
// |col1|newcols|
// +----+-------+
// | X|[x1,x2]|
// | Z|[z1,z2]|
// | Y|[x1,x2]|
// | X|[x3,x4]|
// | P|[p1,p2]|
// | Q|[q1,q2]|
// | Y|[x3,x4]|
// +----+-------+
val dfResult = colList.foldLeft( df4 )(
(accDF, c) => accDF.withColumn(c, df4(s"newcols.$c"))
).drop($"newcols")
dfResult.show
// +----+----+----+
// |col1|col2|col3|
// +----+----+----+
// | X| x1| x2|
// | Z| z1| z2|
// | Y| x1| x2|
// | X| x3| x4|
// | P| p1| p2|
// | Q| q1| q2|
// | Y| x3| x4|
// +----+----+----+
[更新]
对于Spark 1.x,last(colName,ignoreNulls)
在DataFrame API中不可用。解决方法是恢复使用Spark SQL,该SQL在其last()方法中支持ignore null:
这在分布式框架中很难解决。。你怎么知道哪一个是其上一个X?即使我有这个疑问…但可能会有类似于滞后函数的东西我得到了一些异常“错误:重载方法值last with Alternations:”我正在删除from last的第二个参数(“tmp1”,ignoreNulls=true),然后我得到了这个异常“38:错误:value unbounddpreceiding不是object org.apache.spark.sql.expressions.Window的成员”我正在使用spark 1.6您可以将
Window.unbounddpreceiding
替换为Long.MinValue
。不幸的是last(colName,ignoreNulls)
在Spark 2.0之前不可用。请查看我的更新答案以获得解决方法。我仍然获得org.apache.Spark.sql.AnalysisException:最后找不到窗口函数;如果我做错了,请告诉我,我没有Spark 1.6环境,因此无法重现上述问题。要恢复使用Spark sql,请确保导入sqlContext.implicits.\u
。这里有一个相关的示例供您参考。
df2.
withColumn( "tmp1", when($"col1" === "X", $"cols") ).
createOrReplaceTempView("df2table")
// might need to use registerTempTable("df2table") instead
val df3 = spark.sqlContext.sql("""
select col1, id, cols, tmp1, last(tmp1, true) over (
order by id rows between unbounded preceding and current row
) as tmp2
from df2table
""")