web-dev-qa-db-ja.com

Spark

特定の列でグループ化したいデータがいくつかあり、そのグループのローリングタイムウィンドウに基づいて一連のフィールドを集計します。

ここにいくつかのサンプルデータがあります:

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の部分を変更しました。

9
Mike S

修正された回答

ここでは、単純なウィンドウ関数のトリックを使用できます。大量のインポート:

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")

dateDateTypeにキャスト:

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    |
+---------------------------------------------+-----+

タイムゾーンに関しては少し注意する必要があります。

17
user6910411