Python Pyspark:重新采样频率降至毫秒

Python Pyspark:重新采样频率降至毫秒,python,pyspark,resampling,Python,Pyspark,Resampling,我需要在时间戳列上连接两个spark数据帧。问题是它们有不同的频率:第一个数据帧(df1)每10分钟观察一次,而第二个数据帧(df2)是25赫兹(每秒25次观察,比df1的频率高15000倍)。每个数据帧有100多列和数百万行。为了实现平滑连接,我尝试将df1重采样到25 hz,填充重采样导致的空值,然后在数据帧处于相同频率时连接数据帧。数据帧太大,这就是为什么我尝试使用spark而不是pandas 因此,问题是:假设我有以下spark数据帧: 我想把它重新采样到25赫兹(每秒25次观测),这

我需要在时间戳列上连接两个spark数据帧。问题是它们有不同的频率:第一个数据帧(df1)每10分钟观察一次,而第二个数据帧(df2)是25赫兹(每秒25次观察,比df1的频率高15000倍)。每个数据帧有100多列和数百万行。为了实现平滑连接,我尝试将df1重采样到25 hz,填充重采样导致的空值,然后在数据帧处于相同频率时连接数据帧。数据帧太大,这就是为什么我尝试使用spark而不是pandas

因此,问题是:假设我有以下spark数据帧:

我想把它重新采样到25赫兹(每秒25次观测),这样它看起来像这样:

如何在pyspark中高效地实现这一点

注意:

我尝试使用前面问题()中的代码对我的df1重新采样,如下所示:

from pyspark.sql.functions import col, max as max_, min as min_

freq = x   # x is the frequency in seconds

epoch = (col("timestamp").cast("bigint") / freq).cast("bigint") * freq 

with_epoch  = df1.withColumn("dummy", epoch)

min_epoch, max_epoch = with_epoch.select(min_("dummy"), max_("dummy")).first()

new_df = spark.range(min_epoch, max_epoch + 1, freq).toDF("dummy")

new_df.join(with_epoch, "dummy", "left").orderBy("dummy")
.withColumn("timestamp_resampled", col("dummy").cast("timestamp"))
似乎,上述代码仅在预期频率大于或等于1秒时有效。例如,当freq=1时,它生成下表:

但是,当我通过25 hz作为频率(即freq=1/25)时,代码失败,因为spark.range功能中的“阶跃”不能小于1


是否有解决此问题的方法?或者以任何其他方式将频率重新采样到毫秒?

如果您的目标是连接2个数据帧,我建议直接使用内部连接:

df = df1.join(df2, df1.Timestamp == df2.Timestamp)
但是,如果要尝试对数据帧进行下采样,可以将时间戳转换为毫秒,并保留
mod(timestamp,25)=0的行。只有在确保数据采样完美的情况下,才能使用此选项

from pyspark.sql.functions import col
df1 = df1.filter( ((col("Timestamp") % 25) == 0 )
另一种选择是对每行进行编号,每25行保留1。使用此解决方案,您将在不考虑时间戳的情况下减少行数。此解决方案的另一个问题是需要对数据进行排序(效率不高)

PD:过早优化是万恶之源

编辑:时间戳到int 让我们使用带有毫秒的epoch标准创建一个充满时间戳的假数据集

>>>  df = sqlContext.range(1559646513000, 1559646520000)\
                    .select( (F.col('id')/1000).cast('timestamp').alias('timestamp'))
>>> df
DataFrame[timestamp: timestamp]
>>> df.show(5,False)
+-----------------------+
|timestamp              |
+-----------------------+
|2019-06-04 13:08:33    |
|2019-06-04 13:08:33.001|
|2019-06-04 13:08:33.002|
|2019-06-04 13:08:33.003|
|2019-06-04 13:08:33.004|
+-----------------------+
only showing top 5 rows
现在,将其转换回整数:

>>> df.select( (df.timestamp.cast('double')*1000).cast('bigint').alias('epoch') )\
      .show(5, False)
+-------------+
|epoch        |
+-------------+
|1559646513000|
|1559646513001|
|1559646513002|
|1559646513003|
|1559646513004|
+-------------+
only showing top 5 rows

嗨,丹尼尔,谢谢你的好主意。我考虑将直接连接作为一个选项B(不过,考虑使用“外部”连接来保留所有可能的行),看看你关于重采样的评论,似乎重采样会有很多问题,所以我可能会选择连接选项。不过,你能不能把时间戳转换成毫秒?怎么做?谢谢我有一个类似的例子,没有使用步长为1/25的范围,您是否尝试过先将历元转换为毫秒(乘以1000),然后将新的范围步长转换为40,然后您可以使用:from_unixtime和格式:“yyyy-MM-dd'T'HH:MM:ss.SSS”将历元转换回时间戳?在我的SparkSQL版本中,时间戳存储在秒中,但我有另一个后端捕获时间序列存储在纳秒中。为了加入并重新取样,我必须先将后者重新缩放10^9倍,然后它才能工作。