我需要在一个时间戳列上加入两个spark数据帧。问题在于它们具有不同的频率:第一个数据帧(df1)每10分钟观察一次,而第二个数据帧(df2)为25 hz(每秒观察25次,比df1高15000倍)。每个数据框都有100多个列和数百万行。为了使连接平滑,我尝试将df1重新采样至25 Hz,预先填充由重采样导致的Null值,然后在数据帧处于相同频率时将它们加入。数据框太大,这就是为什么我要使用Spark而不是熊猫的原因。
所以,这是一个问题:假设我有以下Spark数据框:
我想将其重新采样到25 Hz(每秒25次观察),这样看起来就这样:
如何在pyspark中有效地做到这一点?
注意:
我尝试使用来自先前问题(PySpark: how to resample frequencies)的代码重新采样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"))
似乎,以上代码仅在预期频率大于或等于秒时有效。例如,当freq = 1时,将产生下表:
但是,当我通过25 hz作为频率(即freq = 1/25)时,代码将失败,因为spark.range函数中的“步长”不能小于1。
是否有解决此问题的解决方法?还是以其他方式将频率重新采样到毫秒?
答案 0 :(得分: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:过早的优化是万恶之源
让我们使用纪元标准(以毫秒为单位)创建一个充满时间戳的假数据集。
>>> 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