获取每周和每日平均时间戳数据

时间:2016-06-16 17:44:29

标签: apache-spark dataframe apache-spark-sql sql-timestamp

我目前拥有Spark数据框架的数据,格式如下:

Timestamp    Number
.........    ......
M-D-Y        3
M-D-Y        4900

时间戳数据绝不是统一的或一致的(即,我可以在2015年3月1日出现一个值,表中的下一个值是2015年9月1日......也是,我每个日期可以有多个条目)。

所以我想做两件事

  1. 计算每周的条目数。所以我基本上想要一个新表,它表示时间戳列所在的行所对应的行数。如果存在多年,我理想的是希望平均每年的值以获得单个值。
  2. 平均每周的数字列。因此,对于一年中的每个星期,我都会有一个代表数字列平均值的值(如果该周内没有条目,则为0)。

1 个答案:

答案 0 :(得分:1)

通过组合unix_timestamp和简单类型转换,使用内置函数解析日期相对容易:

sqlContext.sql(
  "SELECT CAST(UNIX_TIMESTAMP('March 1, 2015', 'MMM d, yyyy') AS TIMESTAMP)"
).show(false)

// +---------------------+
// |_c0                  |
// +---------------------+
// |2015-03-01 00:00:00.0|
// +---------------------+

使用DataFrame DSL等效代码将是这样的:

import org.apache.spark.sql.functions.unix_timestamp

unix_timestamp($"date", "MMM d, yyyy").cast("timestamp")

要填写缺失的条目,您可以使用不同的技巧。最简单的方法是使用与上面相同的解析逻辑。首先让我们创建一些帮助者:

def leap(year: Int) = {
  ((year % 4 == 0) && (year % 100 != 0)) || (year % 400 == 0)
}
def weeksForYear(year: Int) = (1 to 52).map(w => s"$year $w")
def daysForYear(year: Int) = (1 to { if(leap(2000)) 366 else 366 }).map(
  d => s"$year $d"
)

和示例参考数据(这里持续数周但你可以做几天相同的事情):

import org.apache.spark.sql.functions.{year, weekofyear}'

val exprs = Seq(year($"date").alias("year"), weekofyear($"date").alias("week"))

val weeks2015 = Seq(2015)
  .flatMap(weeksForYear _)
  .map(Tuple1.apply)
  .toDF("date")
  .withColumn("date", unix_timestamp($"date", "yyyy w").cast("timestamp"))
  .select(exprs: _*)

最后,您可以转换原始数据:

val df = Seq(
  ("March 1, 2015", 3), ("September 1, 2015", 4900)).toDF("Timestamp", "Number")

val dfParsed = df
  .withColumn("date", unix_timestamp($"timestamp", "MMM d, yyyy").cast("timestamp"))
  .select(exprs :+ $"Number": _*)

合并和汇总:

weeks2015.join(dfParsed, Seq("year", "week"), "left")
  .groupBy($"year", $"week")
  .agg(count($"Number"), avg($"Number"))
  .na.fill(0)