web-dev-qa-db-ja.com

pyspark上のSparkSQL:時系列を生成する方法は?

PysparkでSparkSQLを使用していくつかのPostgreSQLテーブルをDataFrameに格納し、タイプstartstop列とdate列に基づいていくつかの時系列を生成するクエリを作成しています。

仮定 my_table含まれています:

 start      | stop       
-------------------------
 2000-01-01 | 2000-01-05 
 2012-03-20 | 2012-03-23 

PostgreSQLでは、これを行うのは非常に簡単です。

SELECT generate_series(start, stop, '1 day'::interval)::date AS dt FROM my_table

そしてそれはこのテーブルを生成します:

 dt
------------
 2000-01-01
 2000-01-02
 2000-01-03
 2000-01-04
 2000-01-05
 2012-03-20
 2012-03-21
 2012-03-22
 2012-03-23

しかし、プレーンなSparkSQLを使用してそれを行う方法は? UDFまたはいくつかのDataFrameメソッドを使用する必要がありますか?

10
pietrop

spark sqlからのデータフレームdfがあるとします、これを試してください

from pyspark.sql.functions as F
from pyspark.sql.types as T

def timeseriesDF(start, total):
    series = [start]
    for i xrange( total-1 ):
        series.append(
            F.date_add(series[-1], 1)
        )
    return series

df.withColumn("t_series", F.udf(
                timeseriesDF, 
                T.ArrayType()
            ) ( df.start, F.datediff( df.start, df.stop ) ) 
    ).select(F.explode("t_series")).show()
0
Rakesh Kumar

@Rakeshの答えは正しいですが、より冗長でない解決策を共有したいと思います。

import datetime
import pyspark.sql.types
from pyspark.sql.functions import UserDefinedFunction

# UDF
def generate_date_series(start, stop):
    return [start + datetime.timedelta(days=x) for x in range(0, (stop-start).days + 1)]    

# Register UDF for later usage
spark.udf.register("generate_date_series", generate_date_series, ArrayType(DateType()) )

# mydf is a DataFrame with columns `start` and `stop` of type DateType()
mydf.createOrReplaceTempView("mydf")

spark.sql("SELECT explode(generate_date_series(start, stop)) FROM mydf").show()
9
pietrop

既存の回答は機能しますが、非常に非効率的です。代わりに、rangeを使用してからデータをキャストすることをお勧めします。 Pythonの場合

from pyspark.sql.functions import col
from pyspark.sql import SparkSession

def generate_series(start, stop, interval):
    """
    :param start  - lower bound, inclusive
    :param stop   - upper bound, exclusive
    :interval int - increment interval in seconds
    """
    spark = SparkSession.builder.getOrCreate()
    # Determine start and stops in Epoch seconds
    start, stop = spark.createDataFrame(
        [(start, stop)], ("start", "stop")
    ).select(
        [col(c).cast("timestamp").cast("long") for c in ("start", "stop")
    ]).first()
    # Create range with increments and cast to timestamp
    return spark.range(start, stop, interval).select(
        col("id").cast("timestamp").alias("value")
    )

使用例:

generate_series("2000-01-01", "2000-01-05", 60 * 60).show(5)  # By hour
+-------------------+
|              value|
+-------------------+
|2000-01-01 00:00:00|
|2000-01-01 01:00:00|
|2000-01-01 02:00:00|
|2000-01-01 03:00:00|
|2000-01-01 04:00:00|
+-------------------+
only showing top 5 rows
generate_series("2000-01-01", "2000-01-05", 60 * 60 * 24).show()  # By day
+-------------------+
|              value|
+-------------------+
|2000-01-01 00:00:00|
|2000-01-02 00:00:00|
|2000-01-03 00:00:00|
|2000-01-04 00:00:00|
+-------------------+
2
user10938362

Sparkv2.4はsequence関数をサポートします:

sequence(start、stop、step)-開始から停止まで(両端を含む)要素の配列を生成し、ステップごとに増分します。返される要素のタイプは、引数式のタイプと同じです。

サポートされているタイプは、バイト、ショート、整数、ロング、日付、タイムスタンプです。

例:

SELECTシーケンス(1、5);

[1,2,3,4,5]

SELECTシーケンス(5、1);

[5,4,3,2,1]

SELECTシーケンス(to_date( '2018-01-01')、to_date( '2018-03-01')、間隔1か月);

[2018-01-01,2018-02-01,2018-03-01]

https://docs.databricks.com/spark/latest/spark-sql/language-manual/functions.html#sequence

1
Roman Tkachuk