【问题标题】:How to stop timestamp in pyspark from dropping trailing zeroes如何阻止pyspark中的时间戳丢弃尾随零
【发布时间】:2021-01-30 01:03:56
【问题描述】:

我有 Spark 数据帧,其中时间戳以毫秒为单位。

+-----------------------+
|CALC_TS                |
+-----------------------+
|2021-01-27 01:35:05.043|
|2021-01-27 01:35:05.043|    
|2021-01-27 01:35:05.043|

我想让它像这样显示微秒:

+--------------------------+
|CALC_TS                   |
+--------------------------+
|2021-01-27 01:35:05.043000|
|2021-01-27 01:35:05.043000|
|2021-01-27 01:35:05.043000|

所以基本上我希望毫秒部分以微秒为单位显示。在上面的示例中,第一个数据帧的43 毫秒为43 thousand 微秒,如秒数据帧所示。

我试过了:

df.withColumn('TIME', to_timestamp('CALC_TS', 'yyyy-MM-dd HH:mm:ss.SSSSSS'))

df.withColumn('TIME', col('CALC_TS').cast("timestamp"))

但是他们给出了相同的结果并去掉了最后 3 个零。有没有办法做到这一点?

【问题讨论】:

标签: apache-spark pyspark apache-spark-sql


【解决方案1】:

to_timestamp(timestamp_str[,fmt]) 接受一个字符串并返回一个时间戳(类型)。如果您的CALC_TS 已经是您所说的时间戳,您应该使用df.withColumn('TIME', date_format('CALC_TS','yyyy-MM-dd HH:mm:ss.SSSSSS')) 将其格式化为字符串,精度为微秒。来自Spark reference

o 分数:使用一个或多个(最多 9 个)连续的 'S' 字符,例如 SSSSSS,解析和格式化秒的小数部分。对于解析, 可接受的分数长度可以是 [1,连续‘S’的数量]。 对于格式化,分数长度将被填充到 带有零的连续“S”。 Spark 支持微秒的日期时间 精度,最多有 6 位有效数字,但可以解析 纳秒级,超出部分被截断。

对于 Spark 2.4,只是为了使其看起来像时间戳字段的精度为微秒,也许您可​​以在格式化时“伪造”尾随零:date_format('CALC_TS','yyyy-MM-dd HH:mm:ss.SSS000')

【讨论】:

  • 所以我想我可能没有清楚地表达我的要求。我尝试了你的方法,它的结果与@blackbishop 相同。我得到2021-01-27 01:35:05.000043 而不是2021-01-27 01:35:05.043000。这个想法是以微秒为单位来描述结果。所以43毫秒是43,000微秒
  • @thentangler 你的 Spark 版本是什么?
  • Spark 版本为 2.4.6
  • issues.apache.org/jira/browse/SPARK-26424 可能相关吗?我更新了答案。
  • JIRA 中的相关部分是Fix Version/s: 3.0.0,即升级你的 Spark 到 3.0 应该会自动修复它 :)
【解决方案2】:

您可以使用rpad

右填充尾随零达到您的时间戳的预期长度。在您的情况下,长度为 26 个字符(格式为 yyyy-MM-dd HH:mm:ss.SSSSSS

from pyspark.sql.functions import *

df.withColumn('CALC_TS_1', col('CALC_TS').cast("timestamp"))\
    .withColumn('CALC_TS_1', rpad(col('CALC_TS_1').cast('string'),26,'0'))\
    .show(truncate=False)

+--------------------------+--------------------------+
|CALC_TS                   |CALC_TS_1                 |
+--------------------------+--------------------------+
|2021-01-27 01:35:05.043   |2021-01-27 01:35:05.043000|
|2021-01-27 01:35:05.043567|2021-01-27 01:35:05.043567|
+--------------------------+--------------------------+

【讨论】:

    【解决方案3】:

    如果列CALC_TS 是字符串类型,首先使用to_timestampunix_timestamp 函数转换为TimestampType,然后使用date_format,您可以将其格式化为以毫秒为单位的6 个小数:

    from pyspark.sql import functions as F
    
    df.printSchema()
    
    #root
    # |-- CALC_TS: string (nullable = true)
    
    df1 = df.withColumn(
       'TIME',
       F.to_timestamp(
           F.unix_timestamp('CALC_TS', "yyyy-MM-dd HH:mm:ss.SSS") # seconds
           + F.substring_index('CALC_TS', '.', -1).cast('float') / 1000 # milliseconds part
       )
    ).withColumn(
       "TIME_FORMAT",
       F.date_format("TIME", "yyyy-MM-dd HH:mm:ss.SSSSSS")
    )
    
    df1.show(truncate=False)
    
    #+-----------------------+-----------------------+--------------------------+
    #|CALC_TS                |TIME                   |TIME_FORMAT               |
    #+-----------------------+-----------------------+--------------------------+
    #|2021-01-27 01:35:05.043|2021-01-27 01:35:05.043|2021-01-27 01:35:05.000043|
    #|2021-01-27 01:35:05.043|2021-01-27 01:35:05.043|2021-01-27 01:35:05.000043|
    #|2021-01-27 01:35:05.043|2021-01-27 01:35:05.043|2021-01-27 01:35:05.000043|
    #+-----------------------+-----------------------+--------------------------+
    
    #root
    # |-- CALC_TS: string (nullable = true)
    # |-- TIME: timestamp (nullable = true)
    # |-- TIME_FORMAT: string (nullable = true)
    

    如果列已经是时间戳类型,只需使用date_format,如上面的代码。

    【讨论】:

    • 感谢您的回答。但正如下面在回复 mazaneicha 的回答时提到的,我正在寻找一种以微秒为单位显示毫秒的方法。使用您建议的方法,它将 43 milliseconds 转换为 43 microseconds,这是不准确的。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2012-04-30
    • 2013-06-14
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-09-26
    • 1970-01-01
    相关资源
    最近更新 更多