我有一个DF,其中有SELECT TOP (1) WITH TIES StudentName, CourseName
FROM (SELECT s.StudentName, c.CourseName,
SUM(CASE WHEN c.CourseName = 'XXX' THEN 1 ELSE 0 END) OVER (PARTITION BY StudentName) as cnt_xxx
FROM Student s INNER JOIN
StudentCourse sc
ON s.StudentId = sc.StudentID INNER JOIN
Course c
ON sc.CourseID = c.CourseID
) sc
WHERE cnt_xxx > 0
ORDER BY StudentName;
和bookingDt
列。我需要找到这两个日期之间的所有日期。
示例代码:
arrivalDt
代码输出:
df = spark.sparkContext.parallelize(
[Row(vyge_id=1000, bookingDt='2018-01-01', arrivalDt='2018-01-05')]).toDF()
diffDaysDF = df.withColumn("diffDays", datediff('arrivalDt', 'bookingDt'))
diffDaysDF.show()
我试图找到两个日期之间的天数,并使用+----------+----------+-------+--------+
| arrivalDt| bookingDt|vyge_id|diffDays|
+----------+----------+-------+--------+
|2018-01-05|2018-01-01| 1000| 4|
+----------+----------+-------+--------+
函数和timedelta
函数计算所有日期。
explode
预期输出:
基本上,我需要构建一个DF,其中要记录dateList = [str(bookingDt + timedelta(i)) for i in range(diffDays)]
和bookingDt
之间(包括两端)的每个日期。
arrivalDt
答案 0 :(得分:6)
只要您使用的是Spark 2.1版或更高版本,就可以利用以下事实:我们在使用column values as arguments时可以使用pyspark.sql.functions.expr()
:
diffDays
','
,以将其转换为大小为diffDays
的数组pyspark.sql.functions.posexplode()
爆炸该数组及其索引pyspark.sql.functions.date_add()
将索引值天数添加到bookingDt
代码:
import pyspark.sql.functions as f
diffDaysDF.withColumn("repeat", f.expr("split(repeat(',', diffDays), ',')"))\
.select("*", f.posexplode("repeat").alias("txnDt", "val"))\
.drop("repeat", "val", "diffDays")\
.withColumn("txnDt", f.expr("date_add(bookingDt, txnDt)"))\
.show()
#+----------+----------+-------+----------+
#| arrivalDt| bookingDt|vyge_id| txnDt|
#+----------+----------+-------+----------+
#|2018-01-05|2018-01-01| 1000|2018-01-01|
#|2018-01-05|2018-01-01| 1000|2018-01-02|
#|2018-01-05|2018-01-01| 1000|2018-01-03|
#|2018-01-05|2018-01-01| 1000|2018-01-04|
#|2018-01-05|2018-01-01| 1000|2018-01-05|
#+----------+----------+-------+----------+
答案 1 :(得分:4)
好吧,您可以执行以下操作。
创建仅包含日期的数据框:
dates_df
#,从开始bookingDt
至最后arrivalDt
,然后在两个条件之间加入这些df:
df.join(dates_df,
on=col('dates_df.dates').between(col('df.bookindDt'), col('dt.arrivalDt'))
.select('df.*', 'dates_df.dates')
它可能比使用explode
的解决方案更快,但是您需要弄清楚该df的开始日期和结束日期。
10年df仅有3650条记录,不必担心太多。
答案 2 :(得分:3)
对于 Spark 2.4 + ,sequence可用于创建包含bookingDt
和arrivalDt
之间的所有日期的数组。然后可以分解该数组。
from pyspark.sql import functions as F
df = df \
.withColumn('bookingDt', F.col('bookingDt').cast('date')) \
.withColumn('arrivalDt', F.col('arrivalDt').cast('date'))
df.withColumn('txnDt', F.explode(F.expr('sequence(bookingDt, arrivalDt, interval 1 day)')))\
.show()
输出:
+-------+----------+----------+----------+
|vyge_id| bookingDt| arrivalDt| txnDt|
+-------+----------+----------+----------+
| 1000|2018-01-01|2018-01-05|2018-01-01|
| 1000|2018-01-01|2018-01-05|2018-01-02|
| 1000|2018-01-01|2018-01-05|2018-01-03|
| 1000|2018-01-01|2018-01-05|2018-01-04|
| 1000|2018-01-01|2018-01-05|2018-01-05|
+-------+----------+----------+----------+
答案 3 :(得分:0)
按照@vvg的建议:
# I assume, bookindDt has dates range including arrivalDt,
# otherwise you have to find intersection of unique dates of bookindDt and arrivalDt
dates_df = df.select('bookindDt').distinct()
dates_df = dates_df.withColumnRenamed('bookindDt', 'day_of_listing')
listing_days_df = df.join(dates_df, on=dates_df.day_of_listing.between(df.bookindDt, df.arrivalDt))
输出:
+----------+----------+-------+-------------------+
| arrivalDt| bookingDt|vyge_id|day_of_listing |
+----------+----------+-------+-------------------+
|2018-01-05|2018-01-01| 1000|2018-01-01 |
+----------+----------+-------+-------------------+
|2018-01-05|2018-01-01| 1000|2018-01-02 |
+----------+----------+-------+-------------------+
|2018-01-05|2018-01-01| 1000|2018-01-03 |
+----------+----------+-------+-------------------+
|2018-01-05|2018-01-01| 1000|2018-01-04 |
+----------+----------+-------+-------------------+
|2018-01-05|2018-01-01| 1000|2018-01-05 |
+----------+----------+-------+-------------------+