使用Apache-Spark分析时间序列

时间:2015-11-16 05:23:08

标签: python apache-spark time-series pyspark

我有非常大的时间序列数据,数据格式是: (arrival_time,key,value),时间单位为秒,例如:

0.01, k, v
0.03, k, v
....
1.00, k, v
1.10, k, v
1.20, k, v
1.99, k, v
2.00, k, v
...

我需要做的是获得整个数据的每秒行数。 到现在为止,我使用pySpark,我的代码就像:

linePerSec = []
lo = rdd.take(1)[0]
hi = lo + 1.0
end = rdd.collect()[-1][0]
while(hi < end):
     number = rdd.filter(lambda (t, k, v): t >= lo and t < hi).count()
     linePerSec.append(number)
     lo = hi
     hi = lo + 1.0

但是它非常慢,甚至比在for循环中逐行数据更慢。我想这是因为rdd.filter()遍历整个rdd以找到满足过滤条件的行。但是对于时间序列,我们不需要在代码中的hi边界之后遍历数据。在我的情况下,是否有任何解决方案让火花停止通过rdd? 谢谢!

2 个答案:

答案 0 :(得分:3)

首先让我们创建一些虚拟数据:

rdd = sc.parallelize(
    [(0.01, "k", "v"),
    (0.03, "k", "v"),
    (1.00, "k", "v"),
    (1.10, "k", "v"),
    (1.20, "k", "v"),
    (1.99, "k", "v"),
    (2.00, "k", "v"),
    (3.10, "k", "v"),
    (4.50, "k", "v")])

从RDD中提取时间字段:

def get_time(x):
    (start, _, _) = x
    return start

times = rdd.map(get_time)

接下来,我们需要从时间到密钥的函数映射:

def get_key_(start):
    offset = start - int(start)
    def get_key(x):
        w = int(x) + offset
        return w if x >= w else int(x - 1) + offset
    return get_key

找到最短和最长时间

start = times.takeOrdered(1)[0]
end = times.top(1)[0]

生成实际的关键功能:

get_key = get_key_(start)

并计算平均值

from operator import add

total = (times
  .map(lambda x: (get_key(x), 1))
  .reduceByKey(add)
  .values()
  .sum())

time_range = get_key(end) - get_key(start) + 1.0

mean = total / time_range

mean
## 1.8

快速检查:

  • [0.01,1.01]:3
  • [1.01,2.01]:4
  • [2.01,3.01]:0
  • [3.01,4.01]:1
  • [4.01,5.01]:1

它给出9/5 = 1.8

等效的数据框可以如下所示:

from pyspark.sql.functions import count, col, sum, lit, min, max

# Select only arrival times
arrivals = df.select("arrival_time")

# This is almost identical as before
start = df.agg(min("arrival_time")).first()[0]
end = df.agg(max("arrival_time")).first()[0]

get_key = get_key_(start)
time_range = get_key(end) - get_key(start) + 1.0

# But we'll need offset as well
offset = start - int(start)

# and define a bucket column
bucket = (col("arrival_time") - offset).cast("integer") + offset

line_per_sec = (df
    .groupBy(bucket)
    .agg(count("*").alias("cnt"))
    .agg((sum("cnt") / lit(time_range)).alias("mean")))

line_per_sec.show()

 ## +----+
 ## |mean|
 ## +----+
 ## | 1.8|
 ## +----+

请注意,这与the solution提供的Nhor非常相似,主要有两点不同:

  • 使用与代码相同的启动逻辑
  • 正确处理空间隔

答案 1 :(得分:0)

我要做的是第一次将时间值设置为:

from pyspark.sql.functions import *
df = df.select(floor(col('arrival_time')).alias('arrival_time'))

现在你已经arrival_time了,你已准备好计算每秒的行数:

df = df.groupBy(col('arrival_time')).count()

现在,当您计算每秒的行数时,您可以获取所有行并将其除以计数以获得每秒的平均行数:

lines_sum = df.select(sum(col('count')).alias('lines_sum')).first().lines_sum
seconds_sum = df.select(count(col('arrival_time')).alias('seconds_sum')).first().seconds_sum
result = lines_sum / seconds_sum