web-dev-qa-db-ja.com

Pysparkの時系列データ上でスライディングウィンドウを使用してデータを変換する方法

時系列データのスライディングウィンドウに基づいて特徴を抽出しようとしています。 Scalaでは、 この投稿ドキュメント に基づいたsliding関数があるようです。

import org.Apache.spark.mllib.rdd.RDDFunctions._

sc.parallelize(1 to 100, 10)
  .sliding(3)
  .map(curSlice => (curSlice.sum / curSlice.size))
  .collect()

私の質問は、PySparkに同様の機能がありますか?または、そのような関数がまだない場合、どのようにして同様のスライディングウィンドウ変換を実現しますか?

11
Bin

私の知る限り、sliding関数はPythonから利用できず、SlidingRDDはプライベートクラスであり、MLlibの外部からはアクセスできません。

既存のRDDでslidingを使用する場合は、次のように貧乏人のslidingを作成できます。

def sliding(rdd, n):
    assert n > 0
    def gen_window(xi, n):
        x, i = xi
        return [(i - offset, (i, x)) for offset in xrange(n)]

    return (
        rdd.
        zipWithIndex(). # Add index
        flatMap(lambda xi: gen_window(xi, n)). # Generate pairs with offset
        groupByKey(). # Group to create windows
        # Sort values to ensure order inside window and drop indices
        mapValues(lambda vals: [x for (i, x) in sorted(vals)]).
        sortByKey(). # Sort to makes sure we keep original order
        values(). # Get values
        filter(lambda x: len(x) == n)) # Drop beginning and end

あるいは、このようなことを試すことができます( toolz の小さな助けを借りて)

from toolz.itertoolz import sliding_window, concat

def sliding2(rdd, n):
    assert n > 1

    def get_last_el(i, iter):
        """Return last n - 1 elements from the partition"""
        return  [(i, [x for x in iter][(-n + 1):])]

    def slide(i, iter):
        """Prepend previous items and return sliding window"""
        return sliding_window(n, concat([last_items.value[i - 1], iter]))

    def clean_last_items(last_items):
        """Adjust for empty or to small partitions"""
        clean = {-1: [None] * (n - 1)}
        for i in range(rdd.getNumPartitions()):
            clean[i] = (clean[i - 1] + list(last_items[i]))[(-n + 1):]
        return {k: Tuple(v) for k, v in clean.items()}

    last_items = sc.broadcast(clean_last_items(
        rdd.mapPartitionsWithIndex(get_last_el).collectAsMap()))

    return rdd.mapPartitionsWithIndex(slide)
13
zero323

venuktanの答えに追加するには、Spark SQLとウィンドウの集計を取得するのではなく、ウィンドウのコンテンツ全体を保持します。これは、Spark MLに入力するために時系列データをスライディングウィンドウに前処理する私のユースケースで必要でした。

このアプローチの1つの制限は、時間の経過とともにスライディングウィンドウを使用することを想定していることです。

まず、たとえばCSVファイルを読み込むことでSpark DataFrameを作成できます。

df = spark.read.csv('foo.csv')

CSVファイルには2つの列があると想定しています。1つはUNIXタイムスタンプで、もう1つはスライドウィンドウを抽出する列です。

from pyspark.sql import functions as f

window_duration = '1000 millisecond'
slide_duration = '500 millisecond'

df.withColumn("_c0", f.from_unixtime(f.col("_c0"))) \
    .groupBy(f.window("_c0", window_duration, slide_duration)) \
    .agg(f.collect_list(f.array('_c1'))) \
    .withColumnRenamed('collect_list(array(_c1))', 'sliding_window')

ボーナス:この配列列をSpark ML、 ここでUDFアプローチを参照 に必要なDenseVector形式に変換します。

追加ボーナス:スライディングウィンドウの各要素が独自の列を持つように、結果の列のネストを解除するには、 ここでこのアプローチを試してください

これがお役に立てば幸いです。何か明確にできることがあれば教えてください。

2
Shane Halloran

ここで説明するように、spark 1.4にはウィンドウ関数があります: https://databricks.com/blog/2015/07/15/introducing-window-functions-in-spark-sql.html

お役に立てば幸いです。お知らせください。

0
venuktan