時間戳記#

Arrow/Pandas 時間戳記#

Arrow 時間戳記儲存為 64 位元整數,並帶有欄位中繼資料以關聯時間單位(例如毫秒、微秒或奈秒)和選用的時區。Pandas (Timestamp) 使用 64 位元整數表示奈秒和選用的時區。不帶有關聯時區的 Python/Pandas 時間戳記類型稱為「時區感知」。帶有關聯時區的 Python/Pandas 時間戳記類型稱為「時區感知」。

時間戳記轉換#

Pandas/Arrow ⇄ Spark#

Spark 將時間戳記儲存為 64 位元整數,表示自 UNIX 紀元以來的微秒數。它不會在其時間戳記中儲存任何關於時區的中繼資料。

Spark 使用會話本機時區(即 spark.sql.session.timeZone)解譯時間戳記。如果未定義該時區,Spark 將轉為預設系統時區。為了簡化起見,以下始終定義會話本機時區。

這表示往返時間戳記時會發生幾件事

  1. 時區資訊遺失(所有從 spark 轉換為 arrow/pandas 的時間戳記都是「時區感知」)。

  2. 時間戳記會截斷為微秒。

  3. 會話時區可能對時間戳記值的轉換產生違反直覺的影響。

Spark 轉 Pandas (透過 Apache Arrow)#

以下案例假設 Spark 設定 spark.sql.execution.arrow.enabled 設定為 "true"

>>> pdf = pd.DataFrame({'naive': [datetime(2019, 1, 1, 0)],
...                     'aware': [Timestamp(year=2019, month=1, day=1,
...                               nanosecond=500, tz=timezone(timedelta(hours=-8)))]})
>>> pdf
       naive                               aware
       0 2019-01-01 2019-01-01 00:00:00.000000500-08:00

>>> spark.conf.set("spark.sql.session.timeZone", "UTC")
>>> utc_df = sqlContext.createDataFrame(pdf)
>>> utf_df.show()
+-------------------+-------------------+
|              naive|              aware|
+-------------------+-------------------+
|2019-01-01 00:00:00|2019-01-01 08:00:00|
+-------------------+-------------------+

請注意,感知時間戳記的轉換會偏移以反映假設 UTC 的時間(它表示時間中的同一瞬間)。對於感知遲鈍的時間戳記,Spark 會將它們視為系統本機時區,並將它們轉換為 UTC。回想一下,在內部,spark dataframe 的結構描述不會隨時間戳記儲存任何時區資訊。

現在,如果會話時區設定為美國太平洋時間 (PST),我們不會看到感知時區的顯示有任何偏移(它仍然表示時間中的同一瞬間)

>>> spark.conf.set("spark.sql.session.timeZone", "US/Pacific")
>>> pst_df = sqlContext.createDataFrame(pdf)
>>> pst_df.show()
+-------------------+-------------------+
|              naive|              aware|
+-------------------+-------------------+
|2019-01-01 00:00:00|2019-01-01 00:00:00|
+-------------------+-------------------+

再次查看 utc_df.show(),我們看到了會話時區的影響之一。感知遲鈍的時間戳記最初是假設 UTC 轉換的,它反映的瞬間實際上早於來自 PST 轉換資料框架的感知遲鈍時區

>>> utc_df.show()
+-------------------+-------------------+
|              naive|              aware|
+-------------------+-------------------+
|2018-12-31 16:00:00|2019-01-01 00:00:00|
+-------------------+-------------------+

Spark 轉 Pandas#

我們可以觀察轉換回 Arrow/Pandas 時會發生什麼事。假設會話時區仍然是 PST

>>> pst_df.show()
+-------------------+-------------------+
|              naive|              aware|
+-------------------+-------------------+
|2019-01-01 00:00:00|2019-01-01 00:00:00|
+-------------------+-------------------+


 >>> pst_df.toPandas()
 naive      aware
 0 2019-01-01 2019-01-01
 >>> pst_df.toPandas().info()
 <class 'pandas.core.frame.DataFrame'>
 RangeIndex: 1 entries, 0 to 0
 Data columns (total 2 columns):
 naive    1 non-null datetime64[ns]
 aware    1 non-null datetime64[ns]
 dtypes: datetime64[ns](2)
 memory usage: 96.0 bytes

請注意,除了成為「時區感知」時間戳記之外,當轉換為紀元偏移時,「感知」值現在也會有所不同。Spark 透過先轉換為會話時區(或系統本機時區,如果未設定會話時區),然後本機化以移除時區資訊來執行轉換。這導致時間戳記比原始時間早 8 小時

>>> pst_df.toPandas()['aware'][0]
Timestamp('2019-01-01 00:00:00')
>>> pdf['aware'][0]
Timestamp('2019-01-01 00:00:00.000000500-0800', tz='UTC-08:00')
>>> (pst_df.toPandas()['aware'][0].timestamp()-pdf['aware'][0].timestamp())/3600
-8.0

當會話時區為 UTC 時,轉換資料框架時也會發生相同的轉換類型。在這種情況下,感知遲鈍和感知都表示時間中的不同瞬間(感知遲鈍瞬間是由於在建立資料框架之間會話時區的變更所致)

>>> utc_df.show()
+-------------------+-------------------+
|              naive|              aware|
+-------------------+-------------------+
|2018-12-31 16:00:00|2019-01-01 00:00:00|
+-------------------+-------------------+

>>> utc_df.toPandas()
naive      aware
0 2018-12-31 16:00:00 2019-01-01

請注意,當會話時區為 UTC 時,感知方面令人驚訝的偏移不會發生(但時間戳記仍然變成「時區感知」)

>>> spark.conf.set("spark.sql.session.timeZone", "UTC")
>>> pst_df.show()
+-------------------+-------------------+
|              naive|              aware|
+-------------------+-------------------+
|2019-01-01 08:00:00|2019-01-01 08:00:00|
+-------------------+-------------------+

>>> pst_df.toPandas()['aware'][0]
Timestamp('2019-01-01 08:00:00')
>>> pdf['aware'][0]
Timestamp('2019-01-01 00:00:00.000000500-0800', tz='UTC-08:00')
>>> (pst_df.toPandas()['aware'][0].timestamp()-pdf['aware'][0].timestamp())/3600
0.0