火花均线

时间:2017-04-05 20:06:36

标签: apache-spark

我正在尝试为包含多个时间序列的数据集实现移动平均值。每列代表一个被测量的参数,而一行包含一秒钟内测量的所有参数。所以一行看起来像:

timestamp, parameter1, parameter2, ..., parameterN

我找到了一种使用window functions做类似事情的方法,但以下错误让我感到抱怨:

  

分区规范:控制哪些行与给定行位于同一分区中。此外,用户可能希望在订购和计算框架之前确保将具有相同值的类别列的所有行收集到同一台机器上。 如果没有给出分区规范,则必须将所有数据收集到一台计算机

问题是,我没有任何东西要分区。那么我可以使用这种方法来计算移动平均值而不存在在一台机器上收集所有数据的风险吗?如果没有,有什么更好的方法呢?

2 个答案:

答案 0 :(得分:1)

每个重要的Spark职位都需要分区。如果你想让你的工作在天启之前完成,那就没有办法了。问题很简单:当需要进行不可避免的聚合时(在您的情况下,平均值),您如何通过在同一台机器上尽可能多地分组相关数据来最小化混乱,从而对数据进行分区?

我的移动平均线的经验是股票。在那种情况下,这很容易;分区将在股票代码上。毕竟,库存A的50天移动平均线的计算与库存B的计算无关,因此这些数据不需要在同一台机器上。明显的分区比你的情况更简单 - 更不用说它每天只需要一个数据点(可能)(交易结束时股票的收盘价),而你每秒只有一个。

所以我只能说你需要考虑在数据集中添加一个功能,其唯一目的是作为分区键,即使它与你测量的内容无关。如果没有,我会感到惊讶,但如果没有,那么请考虑在日期基于时间的分区。

答案 1 :(得分:0)

如果您想拥有更细粒度的控件,则可以使用联接自己定义窗口函数,假设我们必须计算以下内容:res(n)= a * row(n-1)+ b * row(n )+ c *行(n + 1)。

我将展示一些示例,直到最极端的情况。假设我们有以下设置:

import org.apache.spark.sql.functions._

val df = spark.createDataFrame(Seq(
  (1, 2),
  (2, 4),
  (3, 6),
  (4, 5),
  (5, 6),
  (6, 8)
)).toDF("id", "c")
val (a, b, c) = (.25, .25, .5)

可以使用简单的DataFrame操作或标准sql以多种方式完成此操作:

  1. 通过加入DataFrames

    由于加入时名称重复,我需要创建其他数据框。然后,我基于id(必须为1)加入数据帧并跨列查询:

    val columns = df.columns
    var df1 = df.select(columns.map(col):_*)
    var df2 = df.select(columns.map(col):_*)
    columns.foreach(name => df1 = df1.withColumnRenamed(name, name + "1"))
    columns.foreach(name => df2 = df2.withColumnRenamed(name, name + "2"))
    df.join(df1, $"id" === $"id1" - 1)
      .join(df2, $"id" === $"id2" + 1)
      .withColumn("windowF", $"c" * b + $"c1" * c + $"c2" *a)
      .show()
    

    结果是:

    +---+---+---+---+---+---+-------+
    | id|  c|id1| c1|id2| c2|windowF|
    +---+---+---+---+---+---+-------+
    |  2|  4|  3|  6|  1|  2|    4.5|
    |  3|  6|  4|  5|  2|  4|    5.0|
    |  4|  5|  5|  6|  3|  6|   5.75|
    |  5|  6|  6|  8|  4|  5|   6.75|
    +---+---+---+---+---+---+-------+
    
  2. 通过使用sql:

    我正在从DataFrame创建一个视图:

    df.createTempView("tab")
    spark
        .sql(s"select $b * t1.c + $c * t2.c + $a * t3.c result " +
             "from tab t1 " +
             "join tab t2 on t1.id = t2.id - 1 " + 
             "join tab t3 on t1.id = t3.id + 1").show()
    

    这将导致:

    +------+
    |result|
    +------+
    |  4.50|
    |  5.00|
    |  5.75|
    |  6.75|
    +------+
    

所以结果是一样的。

在遇到极端情况时,需要手动处理它们,例如,在sql中执行full outer join并用正确的值替换null

例如,可以在连接的on部分进行更多过滤。

相关问题