LoginSignup
3
2

More than 3 years have passed since last update.

PySparkのTmeStampTypeに対してstringで演算するときの注意点

Last updated at Posted at 2019-11-25

はじめに

PySparkのTimeStampTypeの演算時に、不用意に日付形式の文字列型を使うと意図しない挙動をするパターンがあるという話。

そのため、TimeStampTypeに対してstringで演算することもできるが、datetimeを使う方が無難。

具体例で説明する。

ここで示す例は、PySPark 2.4.4によるものである。

検証用データ

次のコードで、2000/1/1 ~ 2000/1/5の日付データをもつSpark DataFrameを作成し、この日付データに対する条件処理を行ってみる。

検証用データ
import pandas as pd
from pyspark.sql import functions as F

pdf = pd.DataFrame(pd.date_range(start='1/1/2000', periods=5), columns=['date'])
sdf = spark.createDataFrame(pdf, ['timestamp'])

TimeStampTypeとdatetimeの演算

TimeStampTypeに対してdatetimeを用いた演算は、正常に動作する。

TimeStampTypeとdatetimeの演算
target_datetime = datetime.strptime('2000-01-03', '%Y-%m-%d')

print('== datetime(2000-01-03)')
sdf.where(F.col('timestamp') == datetime.strptime('2000-01-03', '%Y-%m-%d')).show()
print('>  datetime(2000-01-03)')
sdf.where(F.col('timestamp') >  datetime.strptime('2000-01-03', '%Y-%m-%d')).show()
print('>= datetime(2000-01-03)')
sdf.where(F.col('timestamp') >= datetime.strptime('2000-01-03', '%Y-%m-%d')).show()
print('<  datetime(2000-01-03)')
sdf.where(F.col('timestamp') <  datetime.strptime('2000-01-03', '%Y-%m-%d')).show()
print('<= datetime(2000-01-03)')
sdf.where(F.col('timestamp') <= datetime.strptime('2000-01-03', '%Y-%m-%d')).show()
print('between datetime(2000-01-02) and datetime(2000-01-04)')
sdf.where(F.col('timestamp').between(datetime.strptime('2000-01-02', '%Y-%m-%d'), datetime.strptime('2000-01-04', '%Y-%m-%d'))).show()
出力結果
== datetime(2000-01-03)
+-------------------+
|          timestamp|
+-------------------+
|2000-01-03 00:00:00|
+-------------------+

>  datetime(2000-01-03)
+-------------------+
|          timestamp|
+-------------------+
|2000-01-04 00:00:00|
|2000-01-05 00:00:00|
+-------------------+

>= datetime(2000-01-03)
+-------------------+
|          timestamp|
+-------------------+
|2000-01-03 00:00:00|
|2000-01-04 00:00:00|
|2000-01-05 00:00:00|
+-------------------+

<  datetime(2000-01-03)
+-------------------+
|          timestamp|
+-------------------+
|2000-01-01 00:00:00|
|2000-01-02 00:00:00|
+-------------------+

<= datetime(2000-01-03)
+-------------------+
|          timestamp|
+-------------------+
|2000-01-01 00:00:00|
|2000-01-02 00:00:00|
|2000-01-03 00:00:00|
+-------------------+

between datetime(2000-01-02) and datetime(2000-01-04)
+-------------------+
|          timestamp|
+-------------------+
|2000-01-02 00:00:00|
|2000-01-03 00:00:00|
|2000-01-04 00:00:00|
+-------------------+

TimeStampTypeとstring(datetime形式)の演算

次に、datetime形式(yyyy-mm-dd hh:flag_mm:ss)でstringを与えたときの結果を示す。
stringは暗黙的にキャストされるらしく、問題なく演算が行える。

TimeStampTypeとstring(datetime形式)の演算
print('== string(2000-01-03 00:00:00)')
sdf.where(F.col('timestamp') == '2000-01-03 00:00:00').show()
print('>  string(2000-01-03 00:00:00)')
sdf.where(F.col('timestamp') >  '2000-01-03 00:00:00').show()
print('>= string(2000-01-03 00:00:00)')
sdf.where(F.col('timestamp') >= '2000-01-03 00:00:00').show()
print('<  string(2000-01-03 00:00:00)')
sdf.where(F.col('timestamp') <  '2000-01-03 00:00:00').show()
print('<= string(2000-01-03 00:00:00)')
sdf.where(F.col('timestamp') <= '2000-01-03 00:00:00').show()
print('between string(2000-01-02 00:00:00) and string(2000-01-04 00:00:00)')
sdf.where(F.col('timestamp').between('2000-01-02 00:00:00', '2000-01-04 00:00:00')).show()
出力結果
== string(2000-01-03 00:00:00)
+-------------------+
|          timestamp|
+-------------------+
|2000-01-03 00:00:00|
+-------------------+

>  string(2000-01-03 00:00:00)
+-------------------+
|          timestamp|
+-------------------+
|2000-01-04 00:00:00|
|2000-01-05 00:00:00|
+-------------------+

>= string(2000-01-03 00:00:00)
+-------------------+
|          timestamp|
+-------------------+
|2000-01-03 00:00:00|
|2000-01-04 00:00:00|
|2000-01-05 00:00:00|
+-------------------+

<  string(2000-01-03 00:00:00)
+-------------------+
|          timestamp|
+-------------------+
|2000-01-01 00:00:00|
|2000-01-02 00:00:00|
+-------------------+

<= string(2000-01-03 00:00:00)
+-------------------+
|          timestamp|
+-------------------+
|2000-01-01 00:00:00|
|2000-01-02 00:00:00|
|2000-01-03 00:00:00|
+-------------------+

between string(2000-01-02 00:00:00) and string(2000-01-04 00:00:00)
+-------------------+
|          timestamp|
+-------------------+
|2000-01-02 00:00:00|
|2000-01-03 00:00:00|
|2000-01-04 00:00:00|
+-------------------+

TimeStampTypeとstring(date形式)の演算

最後に、date形式(yyyy-mm-dd)でstringを与えたときの結果を示す。
この場合、直感的に意図する挙動とは異なる結果になるパターンがある。

TimeStampTypeとstring(date形式)の演算
print('== string(2000-01-03)')
sdf.where(F.col('timestamp') == '2000-01-03').show()
print('>  string(2000-01-03)')  # 意図しないパターン
sdf.where(F.col('timestamp') >  '2000-01-03').show()
print('>= string(2000-01-03)')
sdf.where(F.col('timestamp') >= '2000-01-03').show()
print('<  string(2000-01-03)')
sdf.where(F.col('timestamp') <  '2000-01-03').show()
print('<= string(2000-01-03)')  # 意図しないパターン
sdf.where(F.col('timestamp') <= '2000-01-03').show()
print('between string(2000-01-02) and string(2000-01-04)')  # 意図しないパターン
sdf.where(F.col('timestamp').between('2000-01-02', '2000-01-04')).show()
出力結果
== string(2000-01-03)
+-------------------+
|          timestamp|
+-------------------+
|2000-01-03 00:00:00|
+-------------------+

>  string(2000-01-03)
+-------------------+
|          timestamp|
+-------------------+
|2000-01-03 00:00:00|
|2000-01-04 00:00:00|
|2000-01-05 00:00:00|
+-------------------+

>= string(2000-01-03)
+-------------------+
|          timestamp|
+-------------------+
|2000-01-03 00:00:00|
|2000-01-04 00:00:00|
|2000-01-05 00:00:00|
+-------------------+

<  string(2000-01-03)
+-------------------+
|          timestamp|
+-------------------+
|2000-01-01 00:00:00|
|2000-01-02 00:00:00|
+-------------------+

<= string(2000-01-03)
+-------------------+
|          timestamp|
+-------------------+
|2000-01-01 00:00:00|
|2000-01-02 00:00:00|
+-------------------+

between string(2000-01-02) and string(2000-01-04)
+-------------------+
|          timestamp|
+-------------------+
|2000-01-02 00:00:00|
|2000-01-03 00:00:00|
+-------------------+

挙動について

演算時に暗黙的にstringがTimeStampへキャストされた結果、TimeStamp(2000-01-01 00:00:00)TimeStamp(string(2000-01-01 00:00:00))が等価であり、TimeStamp(2000-01-01 00:00:00) < TimeStamp(string(2000-01-01))のような結果となっている。

このことから、時間分(上記の例における00:00:00)の値が適切に処理されてないと想像できる。(厳密な仕様は、演算の詳細はScalaのソースを確認する必要がある)

(番外編) StringTypeからTimeStampTypeへのキャスト

ちなみに、StringTypeからTimeStampTypeへキャストする際は、正しくキャストできているようである。
ここでは例は示さないが、上記のパターンと同様に、TimeStampTypeとの演算処理を行っても、(TimeStampType型同士なので当然だが)正常に動作する。

StringTypeからTimeStampTypeへのキャスト
df = spark.createDataFrame([('2000',), ('2000-01-01',), ('2000-01-01 00:00:00',) ], ['str'])
df = df.withColumn('timestamp', F.col('str').cast('timestamp'))
df.show()
出力結果
+-------------------+-------------------+
|                str|          timestamp|
+-------------------+-------------------+
|               2000|2000-01-01 00:00:00|
|         2000-01-01|2000-01-01 00:00:00|
|2000-01-01 00:00:00|2000-01-01 00:00:00|
+-------------------+-------------------+
3
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
3
2