特定の列でグループ化したいデータがいくつかあり、そのグループのローリングタイムウィンドウに基づいて一連のフィールドを集計します。
ここにいくつかのサンプルデータがあります:
df = spark.createDataFrame([Row(date='2016-01-01', group_by='group1', get_avg=5, get_first=1),
Row(date='2016-01-10', group_by='group1', get_avg=5, get_first=2),
Row(date='2016-02-01', group_by='group2', get_avg=10, get_first=3),
Row(date='2016-02-28', group_by='group2', get_avg=20, get_first=3),
Row(date='2016-02-29', group_by='group2', get_avg=30, get_first=3),
Row(date='2016-04-02', group_by='group2', get_avg=8, get_first=4)])
group_by
でグループ化し、最も早い日付から開始して、そのグループのエントリがない30日間になるまでの時間枠を作成します。これらの30日が経過すると、次の時間枠は、前の時間枠に含まれなかった次の行の日付から始まります。
次に、たとえばget_avg
の平均とget_first
の最初の結果を取得するなど、集計を行います。
したがって、この例の出力は次のようになります。
group_by first date of window get_avg get_first
group1 2016-01-01 5 1
group2 2016-02-01 20 3
group2 2016-04-02 8 4
編集:申し訳ありませんが、私の質問が適切に指定されていないことに気付きました。 30日間何も操作しないと終了するウィンドウが実際に必要です。それに応じて、例のgroup2の部分を変更しました。
修正された回答:
ここでは、単純なウィンドウ関数のトリックを使用できます。大量のインポート:
from pyspark.sql.functions import coalesce, col, datediff, lag, lit, sum as sum_
from pyspark.sql.window import Window
ウィンドウ定義:
w = Window.partitionBy("group_by").orderBy("date")
date
をDateType
にキャスト:
df_ = df.withColumn("date", col("date").cast("date"))
次の式を定義します。
# Difference from the previous record or 0 if this is the first one
diff = coalesce(datediff("date", lag("date", 1).over(w)), lit(0))
# 0 if diff <= 30, 1 otherwise
indicator = (diff > 30).cast("integer")
# Cumulative sum of indicators over the window
subgroup = sum_(indicator).over(w).alias("subgroup")
subgroup
式をテーブルに追加します。
df_.select("*", subgroup).groupBy("group_by", "subgroup").avg("get_avg")
+--------+--------+------------+
|group_by|subgroup|avg(get_avg)|
+--------+--------+------------+
| group1| 0| 5.0|
| group2| 0| 20.0|
| group2| 1| 8.0|
+--------+--------+------------+
first
は集計では意味がありませんが、列が単調増加している場合はmin
を使用できます。それ以外の場合は、ウィンドウ関数も使用する必要があります。
Spark 2.1を使用してテスト。2.1。以前のSparkリリースで使用すると、サブクエリとWindow
インスタンスが必要になる場合があります。
元の答え(指定されたスコープには関係ありません)
Spark 2.0なので、 a window
function を使用できるはずです:
タイムスタンプ指定列を指定して、行を1つ以上の時間枠にバケット化します。ウィンドウの開始は包括的ですが、ウィンドウの終了は排他的です。 12:05はウィンドウ[12:05,12:10)にありますが、[12:00,12:05)にはありません。
from pyspark.sql.functions import window df.groupBy(window("date", windowDuration="30 days")).count()
でも結果からわかる
+---------------------------------------------+-----+ |window |count| +---------------------------------------------+-----+ |[2016-01-30 01:00:00.0,2016-02-29 01:00:00.0]|1 | |[2015-12-31 01:00:00.0,2016-01-30 01:00:00.0]|2 | |[2016-03-30 02:00:00.0,2016-04-29 02:00:00.0]|1 | +---------------------------------------------+-----+
タイムゾーンに関しては少し注意する必要があります。