从聚合数据的领先DCT系数创建要素的最有效方法-PySpark

时间:2019-05-06 12:20:32

标签: python pyspark signal-processing feature-engineering

我目前正在为一个数据集创建功能,该数据集包含一些设备的各种传感器读数的时间序列数据,这可能与同一设备的故障事件有关。 这些数据的基本结构是我们有一个表,其中包含设备ID,时间戳和传感器读数。

| ID | Cycle_ID | Timestamp  | sensor_1 | sensor_2 |
|----|----------|------------|----------|----------|
| 1  | 1        | 1547142555 | 123      | 641      |
| 1  | 1        | 1547142556 | 123      | 644      |
| 1  | 2        | 1547142557 | 124      | 643      |

现在的想法是根据周期汇总数据,以创建与这些周期相对应的序列(和相应的特征)。原始数据量很大,需要利用spark,但是聚合后的结果数据集足够小,可以用于将其存储在Pandas DF中并使用keras构建模型。 除其他事项外,一个想法是收集一些传感器的主要DCT组件,以将其用作功能。 为了做到这一点,我们(除其他外)进行以下汇总:


from pyspark.sql import Row, window
import pyspark.sql.functions as func

W = window.Window.partitionBy('ID', 'Cycle_ID').orderBy('Timestamp')

df_collect = pfr_flight_match.withColumn('sensor_1_coll', 
                 func.collect_list('sensor_1').over(W)) \
                 .groupBy('ID', 'Cycle_ID') \ 
                 .agg(func.max("sensor_1_coll").alias('sensor_1_coll'))

这为我分别针对每个设备的每个周期提供了传感器时间序列,作为一个数组。现在的想法是对它执行DCT,仅保留前导n系数,并将它们分别添加为新特征列。我想出了一种方法来执行此操作,但是性能似乎很糟糕,这就是为什么我寻求帮助。

由于不幸的是,无法在数组上使用Pyspark的DCT(根据文档,此功能必须为DenseVector类型),因此我们需要将收集的数组转换为DenseVector。在我看来,没有有效的方法,所以我正在使用UDF来做到这一点:

import pyspark.ml
to_vec = func.udf(lambda x: pyspark.ml.linalg.DenseVector(x),
                  pyspark.ml.linalg.VectorUDT())

下一步是使用以下方法自行执行DCT:

# Determine which column is the target of DCT
col_to_transform = 'sensor_1_coll'
df = df_collect.withColumn('vec', to_vec(col_to_transform))

# After switching the column type to DenseVector, we can apply DCT
dct = pyspark.ml.feature.DCT(inverse=False, inputCol='vec', outputCol='vec_dct')
df_dct = dct.transform(df)

# Drop intermediate columns
df_dct = df_dct.drop('vec', col_to_transform)

现在我担心的陷阱是:我们需要将DCT向量截断为一定数量的系数,然后将其分解为单独的列,以便稍后将它们传递到Pandas DF / Numpy数组中。

我担心使用UDF在性能方面不佳;而且无论如何DenseVector都不表示为数组类型。所以这在这里不起作用:

import pyspark.ml
trunc_vec = func.udf(lambda x: x[0:n],
                  pyspark.ml.linalg.VectorUDT())

所以我最后要做的是将一个合适的函数映射到上述DF的RDD版本上,并将其作为数据帧返回。这就是我现在正在使用的:

# State columns used for grouping
idx = ['ID', 'Cycle_ID']
keep_coeffs = 30 # How many of the leading coefficients shall be kept?

from functools import partial

# To be mapped onto rdd: Return auxillary columns plus the DCT coeffs as 
# individual columns, which are named serially
 def truncate_dct_vec(vec, coeffs):
    return tuple(vec[i] for i in idx) + tuple(vec.vec_dct.toArray()[0:coeffs+1].tolist())
truncate_dct_vec = partial(truncate_dct_vec, coeffs=keep_coeffs)

# Perform the mapping to get the truncated DCT coefficients, each in an individual column
df_dct = df_dct.rdd.map(truncate_dct_vec).toDF(idx)

问题是,这似乎运行起来非常慢(可能是由于在执行所有这些步骤时JVM和python之间进行了序列化和转换),这几乎是禁止的。我主要是在寻找更快的选择。感谢您提供任何帮助。

1 个答案:

答案 0 :(得分:0)

这是一个旧线程,但是,我希望这将来对其他人有帮助。 VectorAssembler会将一列或多列编码为密集的矢量表示形式。如果需要稀疏表示,请查看FeatureHasher。它还支持分类和布尔值。

无论如何,这应该可以解决问题:

from pyspark.sql import Row
from pyspark.sql.types import StructType, StructField, IntegerType
from pyspark.ml.feature import VectorAssembler, DCT

rows = [Row(id=1, cycle_id=1, sensor_1=123, sensor_2=641), 
        Row(id=1, cycle_id=1, sensor_1=123, sensor_2=644), 
        Row(id=1, cycle_id=2, sensor_1=124, sensor_2=643)]

data_schema = StructType([StructField("id", IntegerType(), True), 
         StructField("cycle_id", IntegerType(), True), 
         StructField("sensor_1", IntegerType(), True),
         StructField("sensor_2", IntegerType(), True)])

df = spark.createDataFrame(rows, data_schema)

cols = ["id", "cycle_id", "sensor_1", "sensor_2"]       
assembler = VectorAssembler(inputCols=cols, outputCol="features")
df = assembler.transform(df)

dct = DCT(inverse=False, inputCol="features", outputCol="features_dct")
dct_df = dct.transform(df)
dct_df.select("features_dct").show(truncate=False)

以下内容会将DCT反转为原始信号:

dct_inv = DCT(inverse=True, inputCol="features_dct", outputCol="features_dct_inverse")
dct_df_inv = dct_inv.transform(dct_df)
dct_df_inv.select("features_dct_inverse").show(truncate=False)