I would like to obtain the last value an attribute takes per group over the previous month.
I can achieve this with a self-join like so:
from pyspark.sql import SparkSession, functions as F
from pyspark.sql.window import Window
df = (
spark.createDataFrame(
[
("2022-07-29", 1, 1),
("2022-07-30", 1, 2),
("2022-07-31", 1, 3),
("2022-08-01", 1, 4),
("2022-08-02", 1, 5),
("2022-08-03", 1, 6),
("2022-09-10", 1, 8),
("2022-09-11", 1, 9),
("2022-09-12", 1, 10),
("2022-07-29", 2, 7),
("2022-07-30", 2, 6),
("2022-07-31", 2, 5),
("2022-08-01", 2, 4),
("2022-08-02", 2, 3),
("2022-08-03", 2, 2),
("2022-09-10", 2, 8),
("2022-09-11", 2, 9),
("2022-09-12", 2, 10),
],
["date","id","value"]
)
.withColumn("date", F.to_date(F.col("date")))
)
w = Window.partitionBy("id", "month").orderBy(F.col("date").desc())
df = (
df
.withColumn("month", F.date_trunc("month", F.col("date")))
.join(
df
.withColumn("month", F.add_months(F.date_trunc("month", F.col("date")), 1))
.withColumn("last_value_prev_month", F.first(F.col("value")).over(w))
.select("id", "month", "last_value_prev_month")
.drop_duplicates(subset=["id", "month"]),
on=["id", "month"],
how="left"
)
.drop("month")
.orderBy(["id", "date"])
)
df.show()
+---+----------+-----+---------------------+
| id| date|value|last_value_prev_month|
+---+----------+-----+---------------------+
| 1|2022-07-29| 1| null|
| 1|2022-07-30| 2| null|
| 1|2022-07-31| 3| null|
| 1|2022-08-01| 4| 3|
| 1|2022-08-02| 5| 3|
| 1|2022-08-03| 6| 3|
| 1|2022-09-10| 8| 6|
| 1|2022-09-11| 9| 6|
| 1|2022-09-12| 10| 6|
| 2|2022-07-29| 7| null|
| 2|2022-07-30| 6| null|
| 2|2022-07-31| 5| null|
| 2|2022-08-01| 4| 5|
| 2|2022-08-02| 3| 5|
| 2|2022-08-03| 2| 5|
| 2|2022-09-10| 8| 2|
| 2|2022-09-11| 9| 2|
| 2|2022-09-12| 10| 2|
+---+----------+-----+---------------------+
This seems inefficient to me.
Can this be done with just a window, avoiding a self-join?