【问题标题】:pyspark's "between" function: range search on timestamps is not inclusivepyspark 的“between”功能:时间戳的范围搜索不包含在内
【发布时间】: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|
+--------------------+-----+

【问题讨论】:

    标签: python datetime range pyspark between


    【解决方案1】:

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

    1. 如果您在没有时间的情况下以字符串格式提供输入,它将执行排他搜索(不是我们对上面链接的文档的期望)。
    2. 如果您将输入作为日期时间对象或精确时间(例如,'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 的数据。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2011-09-04
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2023-03-03
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多