時間戳記#
Arrow/Pandas 時間戳記#
Arrow 時間戳記儲存為 64 位元整數,並帶有欄位中繼資料以關聯時間單位(例如毫秒、微秒或奈秒)和選用的時區。Pandas (Timestamp
) 使用 64 位元整數表示奈秒和選用的時區。不帶有關聯時區的 Python/Pandas 時間戳記類型稱為「時區感知」。帶有關聯時區的 Python/Pandas 時間戳記類型稱為「時區感知」。
時間戳記轉換#
Pandas/Arrow ⇄ Spark#
Spark 將時間戳記儲存為 64 位元整數,表示自 UNIX 紀元以來的微秒數。它不會在其時間戳記中儲存任何關於時區的中繼資料。
Spark 使用會話本機時區(即 spark.sql.session.timeZone
)解譯時間戳記。如果未定義該時區,Spark 將轉為預設系統時區。為了簡化起見,以下始終定義會話本機時區。
這表示往返時間戳記時會發生幾件事
時區資訊遺失(所有從 spark 轉換為 arrow/pandas 的時間戳記都是「時區感知」)。
時間戳記會截斷為微秒。
會話時區可能對時間戳記值的轉換產生違反直覺的影響。
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