pyspark 的“between”功能:时间戳的范围搜索不包含在内

Posted

技术标签:

【中文标题】pyspark 的“between”功能:时间戳的范围搜索不包含在内【英文标题】:pyspark's "between" function: range search on timestamps is not inclusive 【发布时间】:2017-04-14 01:10:51 【问题描述】:

pyspark 的 'between' 函数不包含时间戳输入。

例如,如果我们想要两个日期之间的所有行,比如“2017-04-13”和“2017-04-14”,那么当日期作为字符串传递时,它会执行“排他”搜索。即,它省略了“2017-04-14 00:00:00”字段

但是,文档似乎暗示它是inclusive(虽然没有提及时间戳)

当然,一种方法是从上限增加一微秒并将其传递给函数。但是,这不是一个很好的解决方案。进行包容性搜索的任何干净方式?

例子:

import pandas as pd
from pyspark.sql import functions as F
... sql_context creation ...
test_pd=pd.DataFrame(["start":'2017-04-13 12:00:00', "value":1.0,"start":'2017-04-14 00:00:00', "value":1.1])
test_df = sql_context.createDataFrame(test_pd).withColumn("start", F.col("start").cast('timestamp'))
test_df.show()

+--------------------+-----+
|               start|value|
+--------------------+-----+
|2017-04-13 12:00:...|  1.0|
|2017-04-14 00:00:...|  1.1|
+--------------------+-----+

test_df.filter(F.col("start").between('2017-04-13','2017-04-14')).show()

+--------------------+-----+
|               start|value|
+--------------------+-----+
|2017-04-13 12:00:...|  1.0|
+--------------------+-----+

【问题讨论】:

【参考方案1】:

找到答案了。 pyspark 的“between”函数在处理时间戳输入时不一致。

    如果您在没有时间的情况下以字符串格式提供输入,它将执行排他搜索(不是我们对上面链接的文档的期望)。 如果您将输入作为日期时间对象或精确时间(例如,'2017-04-14 00:00:00')提供,则它会执行包含性搜索。

对于上面的例子,这里是排他搜索的输出(使用 pd.to_datetime):

test_df.filter(F.col("start").between(pd.to_datetime('2017-04-13'),pd.to_datetime('2017-04-14'))).show()

+--------------------+-----+
|               start|value|
+--------------------+-----+
|2017-04-13 12:00:...|  1.0|
|2017-04-14 00:00:...|  1.1|
+--------------------+-----+

同样,如果我们以字符串格式提供日期和时间,它似乎执行了包容性搜索:

test_df.filter(F.col("start").between('2017-04-13 12:00:00','2017-04-14 00:00:00')).show()

+--------------------+-----+
|               start|value|
+--------------------+-----+
|2017-04-13 12:00:...|  1.0|
|2017-04-14 00:00:...|  1.1|
+--------------------+-----+

【讨论】:

有趣的地方。但是输出看起来是一样的。你能详细说明一下吗? 以上两种方法产生一个包容性搜索(因此输出相同)。但是,如果我们只是将日期作为字符串传递(见问题),我们会得到一个排他搜索。重点是:如果您想要包容性搜索,请使用上述任何一种方法,而不要只使用日期字符串(例如,F.between('2017-04-13','2017-04-14') @VinayKolar Source code for between() function 似乎暗示这是一个包容性搜索【参考方案2】:

.between() 方法始终是包容性的。您的示例中的问题是,当您将字符串传递给 .between() 方法时,它也将您的数据视为字符串。对于字符串比较,'2017-04-14 00:00:00' 严格大于 '2017-04-14' 因为前者的字符串比后者长,这就是为什么在您的示例中过滤掉第二个日期.为避免“不一致”,您应该将日期时间格式的参数传递给 .between(),如下所示:

filtered_df = (test_df.filter(F.col("start")
                .between(dt.strptime('2017-04-13 12:00:00', '%Y-%m-%d %H:%M:%S'), 
                         dt.strptime('2017-04-14 00:00:00', '%Y-%m-%d %H:%M:%S'))))

这将产生预期的结果:

+--------------------+-----+
|               start|value|
+--------------------+-----+
|2017-04-13 12:00:...|  1.0|
|2017-04-14 00:00:...|  1.1|
+--------------------+-----+

【讨论】:

'2017-04-14 00:00:00' 严格大于 '2017-04-14' - 这是一个字符串转换问题,无论如何我认为另一个然后你描述。在内部,Spark 似乎将'2017-04-14' 解析为'2017-04-14 00:00:00',这使得 OP 查询不包含结束范围。这与一个字符串长于另一个无关(否则您将不会得到任何搜索结果,因为所有时间戳都会大于您的中间条件。【参考方案3】:

要明确一点,如果您想从单个日期获取数据,最好指定确切时间

ex) 仅检索一天的数据 (2017-04-13)

test_df.filter(F.col("start").between('2017-04-13 00:00:00','2017-04-13 23:59:59.59') 

cf) 如果您将日期设置为“2017-04-13”、“2017-04-14”之间,这将包括 2017-04-14 00:00:00 数据,这在技术上不是数据你想退出,因为它是 2017-04-14 的数据。

【讨论】:

以上是关于pyspark 的“between”功能:时间戳的范围搜索不包含在内的主要内容,如果未能解决你的问题,请参考以下文章

pyspark 数据框删除具有较旧时间戳的重复值

如何在 PySpark 中复制 Pandas 的 between_time 函数

通过pyspark读取日期时间格式(2017-01-12t141206)

PySpark:不能使用日期时间年 = 0001 进行列操作

Pyspark:两个日期之间的差异(Cast TimestampType,Datediff)

使用 Window() 计算 PySpark 中数组的滚动总和?